fix: resolve merge-induced type errors and lint issues

- Deduplicate SkillEntry/SkillSnapshot imports in workspace.ts
- Add missing skillFilter/applySkillsPromptLimits in workspace skills
- Add sessionEventLog/sessionSubscriptions to runtime state return type
- Add allowRealIpFallback to GatewayConfig type
- Fix agent.subscribe/unsubscribe validation to use AJV pattern
- Add missing imports (RuntimeEnv, inheritOptionFromParent, etc.)
- Fix spread type in run-main.exit.test.ts
- Type chat.ts appendMessage call correctly
- Rename synology-chat workspace ref from openclaw to ironclaw
- Regenerate pnpm-lock.yaml for merged dependencies

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
kumarabhirup 2026-02-21 18:25:26 -08:00
parent 6594de6186
commit 45f86187ed
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167
45 changed files with 478 additions and 476 deletions

View File

@ -5,7 +5,7 @@
"description": "Synology Chat channel plugin for OpenClaw",
"type": "module",
"devDependencies": {
"openclaw": "workspace:*"
"ironclaw": "workspace:*"
},
"openclaw": {
"extensions": [

570
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,9 @@
*/
import type { ThinkLevel } from "../../auto-reply/thinking.js";
import { resolveUserPath } from "../../utils.js";
import type { RunEmbeddedPiAgentParams } from "../pi-embedded-runner/run/params.js";
import type { EmbeddedPiRunResult, EmbeddedPiAgentMeta } from "../pi-embedded-runner/types.js";
import type { AiSdkConfig, ResolvedModel } from "./types.js";
import { resolveUserPath } from "../../utils.js";
import {
resolveSkillsPromptForRun,
applySkillEnvOverrides,
@ -22,6 +21,7 @@ import {
import { streamWithPiAgentEvents, type EventAdapterInput } from "./event-adapter.js";
import { resolveModel, getDefaultConfig, validateConfig } from "./provider.js";
import { createAiSdkTools, type ToolExecutionContext, type ConvertedAiSdkTool } from "./tools.js";
import type { AiSdkConfig, ResolvedModel } from "./types.js";
/**
* Configuration for the AI SDK agent runner.

View File

@ -1,18 +1,18 @@
import type { AgentTool } from "@mariozechner/pi-agent-core";
import type { ImageContent } from "@mariozechner/pi-ai";
import crypto from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import type { AgentTool } from "@mariozechner/pi-agent-core";
import type { ImageContent } from "@mariozechner/pi-ai";
import type { ThinkLevel } from "../../auto-reply/thinking.js";
import { resolveCliName } from "../../cli/cli-name.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { CliBackendConfig } from "../../config/types.js";
import type { EmbeddedContextFile } from "../pi-embedded-helpers.js";
import { resolveCliName } from "../../cli/cli-name.js";
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
import { isRecord } from "../../utils.js";
import { buildModelAliasLines } from "../model-alias-lines.js";
import { resolveDefaultModelForAgent } from "../model-selection.js";
import type { EmbeddedContextFile } from "../pi-embedded-helpers.js";
import { detectRuntimeShell } from "../shell-utils.js";
import { buildSystemPromptParams } from "../system-prompt-params.js";
import { buildAgentSystemPrompt } from "../system-prompt.js";

View File

@ -1,3 +1,5 @@
import fs from "node:fs/promises";
import os from "node:os";
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { ImageContent } from "@mariozechner/pi-ai";
import { streamSimple } from "@mariozechner/pi-ai";
@ -7,9 +9,6 @@ import {
SessionManager,
SettingsManager,
} from "@mariozechner/pi-coding-agent";
import fs from "node:fs/promises";
import os from "node:os";
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
import { getMachineDisplayName } from "../../../infra/machine-name.js";
@ -110,6 +109,7 @@ import {
shouldFlagCompactionTimeout,
} from "./compaction-timeout.js";
import { detectAndLoadPromptImages } from "./images.js";
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
export function injectHistoryImagesIntoMessages(
messages: AgentMessage[],

View File

@ -3,10 +3,10 @@ import type { AgentSession } from "@mariozechner/pi-coding-agent";
import type { MemoryCitationsMode } from "../../config/types.memory.js";
import type { ResolvedTimeFormat } from "../date-time.js";
import type { EmbeddedContextFile } from "../pi-embedded-helpers.js";
import type { EmbeddedSandboxInfo } from "./types.js";
import type { ReasoningLevel, ThinkLevel } from "./utils.js";
import { buildAgentSystemPrompt, type PromptMode } from "../system-prompt.js";
import { buildToolSummaryMap } from "../tool-summaries.js";
import type { EmbeddedSandboxInfo } from "./types.js";
import type { ReasoningLevel, ThinkLevel } from "./utils.js";
export function buildEmbeddedSystemPrompt(params: {
workspaceDir: string;

View File

@ -1,5 +1,4 @@
import type { AgentEvent, AgentMessage } from "@mariozechner/pi-agent-core";
import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js";
import { parseReplyDirectives } from "../auto-reply/reply/reply-directives.js";
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
import { emitAgentEvent } from "../infra/agent-events.js";
@ -8,6 +7,7 @@ import {
isMessagingToolDuplicateNormalized,
normalizeTextForComparison,
} from "./pi-embedded-helpers.js";
import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js";
import { appendRawStream } from "./pi-embedded-subscribe.raw-stream.js";
import {
extractAssistantText,

View File

@ -1,15 +1,10 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { InlineCodeState } from "../markdown/code-spans.js";
import type {
EmbeddedPiSubscribeContext,
EmbeddedPiSubscribeState,
} from "./pi-embedded-subscribe.handlers.types.js";
import type { SubscribeEmbeddedPiSessionParams } from "./pi-embedded-subscribe.types.js";
import { parseReplyDirectives } from "../auto-reply/reply/reply-directives.js";
import { createStreamingDirectiveAccumulator } from "../auto-reply/reply/streaming-directives.js";
import { formatToolAggregate } from "../auto-reply/tool-meta.js";
import { emitAgentEvent } from "../infra/agent-events.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import type { InlineCodeState } from "../markdown/code-spans.js";
import { buildCodeSpanIndex, createInlineCodeState } from "../markdown/code-spans.js";
import { EmbeddedBlockChunker } from "./pi-embedded-block-chunker.js";
import {
@ -17,7 +12,12 @@ import {
normalizeTextForComparison,
} from "./pi-embedded-helpers.js";
import { createEmbeddedPiSessionEventHandler } from "./pi-embedded-subscribe.handlers.js";
import type {
EmbeddedPiSubscribeContext,
EmbeddedPiSubscribeState,
} from "./pi-embedded-subscribe.handlers.types.js";
import { filterToolResultMediaUrls } from "./pi-embedded-subscribe.tools.js";
import type { SubscribeEmbeddedPiSessionParams } from "./pi-embedded-subscribe.types.js";
import { formatReasoningMessage, stripDowngradedToolCallText } from "./pi-embedded-utils.js";
import { hasNonzeroUsage, normalizeUsage, type UsageLike } from "./usage.js";

View File

@ -1,5 +1,4 @@
import type { OpenClawConfig, SkillConfig } from "../../config/config.js";
import type { SkillEligibilityContext, SkillEntry } from "./types.js";
import {
evaluateRuntimeRequires,
hasBinary,
@ -8,6 +7,7 @@ import {
resolveRuntimePlatform,
} from "../../shared/config-eval.js";
import { resolveSkillKey } from "./frontmatter.js";
import type { SkillEligibilityContext, SkillEntry } from "./types.js";
const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
"browser.enabled": true,

View File

@ -1,11 +1,4 @@
import type { Skill } from "@mariozechner/pi-coding-agent";
import type {
OpenClawSkillMetadata,
ParsedSkillFrontmatter,
SkillEntry,
SkillInstallSpec,
SkillInvocationPolicy,
} from "./types.js";
import { parseFrontmatterBlock } from "../../markdown/frontmatter.js";
import {
getFrontmatterString,
@ -17,6 +10,13 @@ import {
resolveOpenClawManifestOs,
resolveOpenClawManifestRequires,
} from "../../shared/frontmatter.js";
import type {
OpenClawSkillMetadata,
ParsedSkillFrontmatter,
SkillEntry,
SkillInstallSpec,
SkillInvocationPolicy,
} from "./types.js";
export function parseFrontmatter(content: string): ParsedSkillFrontmatter {
return parseFrontmatterBlock(content);

View File

@ -82,7 +82,8 @@ describe("ensureSkillsWatcher", () => {
mod.ensureSkillsWatcher({ workspaceDir: "/tmp/workspace-bundled-test" });
expect(watchMock).toHaveBeenCalledTimes(1);
const watchedPaths = watchMock.mock.calls[0]?.[0] as string[];
const watchedPaths =
(watchMock.mock.calls as unknown as Array<[string[], { ignored?: unknown }]>)[0]?.[0] ?? [];
// Should include workspace skills and bundled skills (as SKILL.md globs)
expect(watchedPaths.some((p) => p.startsWith("/tmp/workspace-bundled-test/skills"))).toBe(true);

View File

@ -1,6 +1,6 @@
import chokidar, { type FSWatcher } from "chokidar";
import os from "node:os";
import path from "node:path";
import chokidar, { type FSWatcher } from "chokidar";
import type { OpenClawConfig } from "../../config/config.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { CONFIG_DIR, resolveUserPath } from "../../utils.js";

View File

@ -1,27 +1,12 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
formatSkillsForPrompt,
loadSkillsFromDir,
type Skill,
} from "@mariozechner/pi-coding-agent";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import type { OpenClawConfig } from "../../config/config.js";
import type {
InjectedSkillContent,
ParsedSkillFrontmatter,
SkillEligibilityContext,
SkillCommandSpec,
SkillEntry,
SkillSnapshot,
} from "./types.js";
import type {
ParsedSkillFrontmatter,
SkillEligibilityContext,
SkillCommandSpec,
SkillEntry,
SkillSnapshot,
} from "./types.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { CONFIG_DIR, resolveUserPath } from "../../utils.js";
import { resolveSandboxPath } from "../sandbox-paths.js";
@ -35,6 +20,14 @@ import {
} from "./frontmatter.js";
import { resolvePluginSkillDirs } from "./plugin-skills.js";
import { serializeByKey } from "./serialize.js";
import type {
InjectedSkillContent,
ParsedSkillFrontmatter,
SkillEligibilityContext,
SkillCommandSpec,
SkillEntry,
SkillSnapshot,
} from "./types.js";
const fsp = fs.promises;
const skillsLogger = createSubsystemLogger("skills");
@ -430,6 +423,44 @@ function readSkillContent(filePath: string): string | undefined {
}
}
function applySkillsPromptLimits(params: { skills: Skill[]; config?: OpenClawConfig }): {
skillsForPrompt: Skill[];
truncated: boolean;
truncatedReason: "count" | "chars" | null;
} {
const limits = resolveSkillsLimits(params.config);
const total = params.skills.length;
const byCount = params.skills.slice(0, Math.max(0, limits.maxSkillsInPrompt));
let skillsForPrompt = byCount;
let truncated = total > byCount.length;
let truncatedReason: "count" | "chars" | null = truncated ? "count" : null;
const fits = (skills: Skill[]): boolean => {
const block = formatSkillsForPrompt(skills);
return block.length <= limits.maxSkillsPromptChars;
};
if (!fits(skillsForPrompt)) {
// Binary search the largest prefix that fits in the char budget.
let lo = 0;
let hi = skillsForPrompt.length;
while (lo < hi) {
const mid = Math.ceil((lo + hi) / 2);
if (fits(skillsForPrompt.slice(0, mid))) {
lo = mid;
} else {
hi = mid - 1;
}
}
skillsForPrompt = skillsForPrompt.slice(0, lo);
truncated = true;
truncatedReason = "chars";
}
return { skillsForPrompt, truncated, truncatedReason };
}
export function buildWorkspaceSkillSnapshot(
workspaceDir: string,
opts?: {
@ -468,8 +499,21 @@ export function buildWorkspaceSkillSnapshot(
(entry) => entry.invocation?.disableModelInvocation !== true,
);
const resolvedSkills = promptEntries.map((entry) => entry.skill);
const { skillsForPrompt, truncated } = applySkillsPromptLimits({
skills: resolvedSkills,
config: opts?.config,
});
const truncationNote = truncated
? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`openclaw skills check\` to audit.`
: "";
const remoteNote = opts?.eligibility?.remote?.note?.trim();
const prompt = [remoteNote, formatSkillsForPrompt(resolvedSkills)].filter(Boolean).join("\n");
const prompt = [
remoteNote,
truncationNote,
formatSkillsForPrompt(compactSkillPaths(skillsForPrompt)),
]
.filter(Boolean)
.join("\n");
// Read full content of injected skills, substituting workspace path placeholders.
// We replace both the tilde form and the expanded default path to handle
@ -497,6 +541,7 @@ export function buildWorkspaceSkillSnapshot(
}
}
const skillFilter = normalizeSkillFilter(opts?.skillFilter);
return {
prompt,
skills: eligible.map((entry) => ({

View File

@ -1,11 +1,11 @@
import { createHmac, createHash } from "node:crypto";
import type { ReasoningLevel, ThinkLevel } from "../auto-reply/thinking.js";
import type { MemoryCitationsMode } from "../config/types.memory.js";
import type { ResolvedTimeFormat } from "./date-time.js";
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
import { DEFAULT_CLI_NAME } from "../cli/cli-name.js";
import type { MemoryCitationsMode } from "../config/types.memory.js";
import { listDeliverableMessageChannels } from "../utils/message-channel.js";
import type { ResolvedTimeFormat } from "./date-time.js";
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
import { sanitizeForPromptLiteral } from "./sanitize-for-prompt.js";
/**

View File

@ -1,13 +1,6 @@
import crypto from "node:crypto";
import type { ExecToolDefaults } from "../../agents/bash-tools.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { MsgContext, TemplateContext } from "../templating.js";
import type { GetReplyOptions, ReplyPayload } from "../types.js";
import type { buildCommandContext } from "./commands.js";
import type { InlineDirectives } from "./directive-handling.js";
import type { createModelSelectionState } from "./model-selection.js";
import type { TypingController } from "./typing.js";
import { resolveSessionAuthProfileOverride } from "../../agents/auth-profiles/session-override.js";
import type { ExecToolDefaults } from "../../agents/bash-tools.js";
import { resolveModelAuthLabel } from "../../agents/model-auth-label.js";
import {
abortEmbeddedPiRun,
@ -15,6 +8,7 @@ import {
isEmbeddedPiRunStreaming,
resolveEmbeddedSessionLane,
} from "../../agents/pi-embedded.js";
import type { OpenClawConfig } from "../../config/config.js";
import {
resolveGroupSessionKey,
resolveSessionFilePath,
@ -29,6 +23,7 @@ import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
import { isReasoningTagProvider } from "../../utils/provider-utils.js";
import { hasControlCommand } from "../command-detection.js";
import { buildInboundMediaNote } from "../media-note.js";
import type { MsgContext, TemplateContext } from "../templating.js";
import {
type ElevatedLevel,
formatXHighModelHint,
@ -39,15 +34,20 @@ import {
type VerboseLevel,
} from "../thinking.js";
import { SILENT_REPLY_TOKEN } from "../tokens.js";
import type { GetReplyOptions, ReplyPayload } from "../types.js";
import { runReplyAgent } from "./agent-runner.js";
import { applySessionHints } from "./body.js";
import type { buildCommandContext } from "./commands.js";
import type { InlineDirectives } from "./directive-handling.js";
import { buildGroupChatContext, buildGroupIntro } from "./groups.js";
import { buildInboundMetaSystemPrompt, buildInboundUserContextPrefix } from "./inbound-meta.js";
import type { createModelSelectionState } from "./model-selection.js";
import { resolveQueueSettings } from "./queue.js";
import { routeReply } from "./route-reply.js";
import { BARE_SESSION_RESET_PROMPT } from "./session-reset-prompt.js";
import { ensureSkillSnapshot, prependSystemEvents } from "./session-updates.js";
import { resolveTypingMode } from "./typing-mode.js";
import type { TypingController } from "./typing.js";
import { appendUntrustedContext } from "./untrusted-context.js";
type AgentDefaults = NonNullable<OpenClawConfig["agents"]>["defaults"];

View File

@ -1,4 +1,3 @@
import type { DaemonInstallOptions } from "./types.js";
import { buildGatewayInstallPlan } from "../../commands/daemon-install-helpers.js";
import {
DEFAULT_GATEWAY_DAEMON_RUNTIME,
@ -24,6 +23,7 @@ import {
installDaemonServiceAndEmit,
} from "./response.js";
import { parsePort } from "./shared.js";
import type { DaemonInstallOptions } from "./types.js";
export async function runDaemonInstall(opts: DaemonInstallOptions) {
const json = Boolean(opts.json);

View File

@ -16,11 +16,8 @@ vi.mock("../infra/dotenv.js", () => ({
}));
vi.mock("../infra/env.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
normalizeEnv: normalizeEnvMock,
};
const actual = await importOriginal<typeof import("../infra/env.js")>();
return Object.assign({}, actual, { normalizeEnv: normalizeEnvMock });
});
vi.mock("../infra/path-env.js", () => ({

View File

@ -3,6 +3,7 @@ import { defaultRuntime } from "../runtime.js";
import { formatDocsLink } from "../terminal/links.js";
import { theme } from "../terminal/theme.js";
import { replaceCliName, resolveCliName } from "./cli-name.js";
import { inheritOptionFromParent } from "./command-options.js";
import { formatHelpExamples } from "./help-format.js";
import { updateStatusCommand } from "./update-cli/status.js";
import { updateCommand } from "./update-cli/update-command.js";

View File

@ -67,6 +67,7 @@ vi.mock("../gateway/client.js", () => {
import type { OpenClawConfig } from "../config/config.js";
import * as configModule from "../config/config.js";
import { callGateway } from "../gateway/call.js";
import type { RuntimeEnv } from "../runtime.js";
import { agentCliCommand, emitNdjsonLine } from "./agent-via-gateway.js";
import { agentCommand } from "./agent.js";
@ -252,7 +253,7 @@ describe("agentCliCommand", () => {
const store = path.join(dir, "sessions.json");
mockConfig(store);
vi.mocked(agentCommand).mockResolvedValueOnce(undefined);
mockLocalAgentReply();
try {
await agentCliCommand({ message: "hi", to: "+1555", local: true, streamJson: true }, runtime);

View File

@ -1,8 +1,7 @@
import type { CliDeps } from "../cli/deps.js";
import type { RuntimeEnv } from "../runtime.js";
import { listAgentIds } from "../agents/agent-scope.js";
import { DEFAULT_CHAT_CHANNEL } from "../channels/registry.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { CliDeps } from "../cli/deps.js";
import { withProgress } from "../cli/progress.js";
import { loadConfig, resolveConfigPath, resolveStateDir } from "../config/config.js";
import {
@ -15,6 +14,7 @@ import { PROTOCOL_VERSION } from "../gateway/protocol/index.js";
import { loadOrCreateDeviceIdentity } from "../infra/device-identity.js";
import { loadGatewayTlsRuntime } from "../infra/tls/gateway.js";
import { normalizeAgentId } from "../routing/session-key.js";
import type { RuntimeEnv } from "../runtime.js";
import {
GATEWAY_CLIENT_MODES,
GATEWAY_CLIENT_NAMES,

View File

@ -1,5 +1,4 @@
import path from "node:path";
import type { AgentCommandOpts } from "./agent/types.js";
import {
listAgentIds,
resolveAgentDir,
@ -68,6 +67,7 @@ import { deliverAgentCommandResult } from "./agent/delivery.js";
import { resolveAgentRunContext } from "./agent/run-context.js";
import { updateSessionStoreAfterAgentRun } from "./agent/session-store.js";
import { resolveSession } from "./agent/session.js";
import type { AgentCommandOpts } from "./agent/types.js";
type PersistSessionEntryParams = {
sessionStore: Record<string, SessionEntry>;

View File

@ -1,15 +1,10 @@
import type { OpenClawConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import type {
ChannelsWizardMode,
ConfigureWizardParams,
WizardSection,
} from "./configure.shared.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { OpenClawConfig } from "../config/config.js";
import { readConfigFileSnapshot, resolveGatewayPort, writeConfigFile } from "../config/config.js";
import { logConfigUpdated } from "../config/logging.js";
import { ensureWebAppBuilt } from "../gateway/server-web-app.js";
import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js";
import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { note } from "../terminal/note.js";
import { resolveUserPath } from "../utils.js";
@ -19,6 +14,11 @@ import { removeChannelConfigWizard } from "./configure.channels.js";
import { maybeInstallDaemon } from "./configure.daemon.js";
import { promptAuthConfig } from "./configure.gateway-auth.js";
import { promptGatewayConfig } from "./configure.gateway.js";
import type {
ChannelsWizardMode,
ConfigureWizardParams,
WizardSection,
} from "./configure.shared.js";
import {
CONFIGURE_SECTION_OPTIONS,
confirm,

View File

@ -1,11 +1,12 @@
import type { OpenClawConfig } from "../../../config/config.js";
import type { OnboardOptions } from "../../onboard-types.js";
import { resolveGatewayService } from "../../../daemon/service.js";
import { isSystemdUserServiceAvailable } from "../../../daemon/systemd.js";
import { ensureWebAppBuilt } from "../../../gateway/server-web-app.js";
import { ensureControlUiAssetsBuilt } from "../../../infra/control-ui-assets.js";
import type { RuntimeEnv } from "../../../runtime.js";
import { buildGatewayInstallPlan, gatewayInstallErrorHint } from "../../daemon-install-helpers.js";
import { DEFAULT_GATEWAY_DAEMON_RUNTIME, isGatewayDaemonRuntime } from "../../daemon-runtime.js";
import type { OnboardOptions } from "../../onboard-types.js";
import { ensureSystemdUserLingerNonInteractive } from "../../systemd-linger.js";
export async function installGatewayDaemonNonInteractive(params: {

View File

@ -1,7 +1,7 @@
import type { OpenClawConfig } from "../../../config/config.js";
import type { RuntimeEnv } from "../../../runtime.js";
import type { OnboardOptions } from "../../onboard-types.js";
import { randomToken } from "../../onboard-helpers.js";
import type { OnboardOptions } from "../../onboard-types.js";
export function applyNonInteractiveGatewayConfig(params: {
nextConfig: OpenClawConfig;

View File

@ -1,5 +1,5 @@
import type { Skill } from "@mariozechner/pi-coding-agent";
import crypto from "node:crypto";
import type { Skill } from "@mariozechner/pi-coding-agent";
import type { ChatType } from "../../channels/chat-type.js";
import type { ChannelId } from "../../channels/plugins/types.js";
import type { DeliveryContext } from "../../utils/delivery-context.js";

View File

@ -323,6 +323,11 @@ export type GatewayConfig = {
* to determine the client IP for local pairing and HTTP checks.
*/
trustedProxies?: string[];
/**
* When true, fall back to X-Real-IP header if X-Forwarded-For is absent.
* Only applies when trustedProxies is configured.
*/
allowRealIpFallback?: boolean;
/** Ironclaw Next.js web app served alongside the gateway. */
webApp?: GatewayWebAppConfig;
/** Tool access restrictions for HTTP /tools/invoke endpoint. */

View File

@ -1,11 +1,11 @@
import type { Server as HttpServer } from "node:http";
import type { WebSocketServer } from "ws";
import type { CanvasHostHandler, CanvasHostServer } from "../canvas-host/server.js";
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
import { stopGmailWatcher } from "../hooks/gmail-watcher.js";
import type { HeartbeatRunner } from "../infra/heartbeat-runner.js";
import type { PluginServicesHandle } from "../plugins/services.js";
import type { WebAppHandle } from "./server-web-app.js";
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
import { stopGmailWatcher } from "../hooks/gmail-watcher.js";
export function createGatewayCloseHandler(params: {
bonjourStop: (() => Promise<void>) | null;

View File

@ -1,5 +1,4 @@
import { randomUUID } from "node:crypto";
import type { GatewayRequestHandlerOptions, GatewayRequestHandlers } from "./types.js";
import { listAgentIds } from "../../agents/agent-scope.js";
import { BARE_SESSION_RESET_PROMPT } from "../../auto-reply/reply/session-reset-prompt.js";
import { agentCommand } from "../../commands/agent.js";
@ -52,6 +51,7 @@ import { waitForAgentJob } from "./agent-job.js";
import { injectTimestamp, timestampOptsFromConfig } from "./agent-timestamp.js";
import { normalizeRpcAttachmentsToChatAttachments } from "./attachment-normalize.js";
import { sessionsHandlers } from "./sessions.js";
import type { GatewayRequestHandlerOptions, GatewayRequestHandlers } from "./types.js";
const RESET_COMMAND_RE = /^\/(new|reset)(?:\s+([\s\S]*))?$/i;
@ -694,19 +694,21 @@ export const agentHandlers: GatewayRequestHandlers = {
},
"agent.subscribe": ({ params, client, respond, context }) => {
const validated = validateAgentSubscribeParams(params);
if (!validated.ok) {
if (!validateAgentSubscribeParams(params)) {
respond(
false,
undefined,
errorShape(ErrorCodes.INVALID_PARAMS, formatValidationErrors(validated.errors)),
errorShape(
ErrorCodes.INVALID_REQUEST,
`invalid agent.subscribe params: ${formatValidationErrors(validateAgentSubscribeParams.errors)}`,
),
);
return;
}
const p = validated.value;
const p = params;
const connId = client?.connId;
if (!connId) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_PARAMS, "no connection id"));
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "no connection id"));
return;
}
context.registerSessionSubscription(p.sessionKey, connId);
@ -718,21 +720,23 @@ export const agentHandlers: GatewayRequestHandlers = {
},
"agent.unsubscribe": ({ params, client, respond, context }) => {
const validated = validateAgentUnsubscribeParams(params);
if (!validated.ok) {
if (!validateAgentUnsubscribeParams(params)) {
respond(
false,
undefined,
errorShape(ErrorCodes.INVALID_PARAMS, formatValidationErrors(validated.errors)),
errorShape(
ErrorCodes.INVALID_REQUEST,
`invalid agent.unsubscribe params: ${formatValidationErrors(validateAgentUnsubscribeParams.errors)}`,
),
);
return;
}
const connId = client?.connId;
if (!connId) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_PARAMS, "no connection id"));
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "no connection id"));
return;
}
context.unregisterSessionSubscription(validated.value.sessionKey, connId);
context.unregisterSessionSubscription(params.sessionKey, connId);
respond(true, {});
},
};

View File

@ -1,13 +1,12 @@
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
import fs from "node:fs";
import path from "node:path";
import type { MsgContext } from "../../auto-reply/templating.js";
import type { GatewayRequestContext, GatewayRequestHandlers } from "./types.js";
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
import { resolveThinkingDefault } from "../../agents/model-selection.js";
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
import { dispatchInboundMessage } from "../../auto-reply/dispatch.js";
import { createReplyDispatcher } from "../../auto-reply/reply/reply-dispatcher.js";
import type { MsgContext } from "../../auto-reply/templating.js";
import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
import { resolveSessionFilePath } from "../../config/sessions.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
@ -43,6 +42,7 @@ import {
import { formatForLog } from "../ws-log.js";
import { injectTimestamp, timestampOptsFromConfig } from "./agent-timestamp.js";
import { normalizeRpcAttachmentsToChatAttachments } from "./attachment-normalize.js";
import type { GatewayRequestContext, GatewayRequestHandlers } from "./types.js";
type TranscriptAppendResult = {
ok: boolean;
@ -417,7 +417,7 @@ function appendAssistantTranscriptMessage(params: {
// IMPORTANT: Use SessionManager so the entry is attached to the current leaf via parentId.
// Raw jsonl appends break the parent chain and can hide compaction summaries from context.
const sessionManager = SessionManager.open(transcriptPath);
const messageId = sessionManager.appendMessage(messageBody);
const messageId = sessionManager.appendMessage(messageBody as unknown as AppendMessageArg);
return { ok: true, messageId, message: messageBody };
} catch (err) {
return { ok: false, error: err instanceof Error ? err.message : String(err) };
@ -478,7 +478,7 @@ function appendUserTranscriptMessage(params: {
// IMPORTANT: Use SessionManager so the entry is attached to the current leaf via parentId.
// Raw jsonl appends break the parent chain and can hide compaction summaries from context.
const sessionManager = SessionManager.open(transcriptPath);
const messageId = sessionManager.appendMessage(messageBody);
const messageId = sessionManager.appendMessage(messageBody as unknown as AppendMessageArg);
return { ok: true, messageId, message: messageBody };
} catch (err) {
return { ok: false, error: err instanceof Error ? err.message : String(err) };

View File

@ -1,5 +1,7 @@
import type { Server as HttpServer } from "node:http";
import { WebSocketServer } from "ws";
import { CANVAS_HOST_PATH } from "../canvas-host/a2ui.js";
import { type CanvasHostHandler, createCanvasHostHandler } from "../canvas-host/server.js";
import type { CliDeps } from "../cli/deps.js";
import type { createSubsystemLogger } from "../logging/subsystem.js";
import type { PluginRegistry } from "../plugins/registry.js";
@ -9,11 +11,6 @@ import type { ResolvedGatewayAuth } from "./auth.js";
import type { ChatAbortControllerEntry } from "./chat-abort.js";
import type { ControlUiRootState } from "./control-ui.js";
import type { HooksConfigResolved } from "./hooks.js";
import type { DedupeEntry } from "./server-shared.js";
import type { GatewayTlsRuntime } from "./server/tls.js";
import type { GatewayWsClient } from "./server/ws-types.js";
import { CANVAS_HOST_PATH } from "../canvas-host/a2ui.js";
import { type CanvasHostHandler, createCanvasHostHandler } from "../canvas-host/server.js";
import { resolveGatewayListenHosts } from "./net.js";
import {
createGatewayBroadcaster,
@ -22,6 +19,8 @@ import {
} from "./server-broadcast.js";
import {
type ChatRunEntry,
type SessionEventLog,
type SessionSubscriptionRegistry,
createChatRunState,
createToolEventRecipientRegistry,
createSessionEventLog,
@ -29,9 +28,12 @@ import {
} from "./server-chat.js";
import { MAX_PAYLOAD_BYTES } from "./server-constants.js";
import { attachGatewayUpgradeHandler, createGatewayHttpServer } from "./server-http.js";
import type { DedupeEntry } from "./server-shared.js";
import { createGatewayHooksRequestHandler } from "./server/hooks.js";
import { listenGatewayHttpServer } from "./server/http-listen.js";
import { createGatewayPluginRequestHandler } from "./server/plugins-http.js";
import type { GatewayTlsRuntime } from "./server/tls.js";
import type { GatewayWsClient } from "./server/ws-types.js";
export async function createGatewayRuntimeState(params: {
cfg: import("../config/config.js").OpenClawConfig;
@ -79,6 +81,8 @@ export async function createGatewayRuntimeState(params: {
) => ChatRunEntry | undefined;
chatAbortControllers: Map<string, ChatAbortControllerEntry>;
toolEventRecipients: ReturnType<typeof createToolEventRecipientRegistry>;
sessionEventLog: SessionEventLog;
sessionSubscriptions: SessionSubscriptionRegistry;
}> {
let canvasHost: CanvasHostHandler | null = null;
if (params.canvasHostEnabled) {

View File

@ -1,6 +1,3 @@
import type { CliDeps } from "../cli/deps.js";
import type { loadConfig } from "../config/config.js";
import type { loadOpenClawPlugins } from "../plugins/loader.js";
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
import { loadModelCatalog } from "../agents/model-catalog.js";
import {
@ -10,6 +7,8 @@ import {
} from "../agents/model-selection.js";
import { resolveAgentSessionDirs } from "../agents/session-dirs.js";
import { cleanStaleLockFiles } from "../agents/session-write-lock.js";
import type { CliDeps } from "../cli/deps.js";
import type { loadConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import { startGmailWatcherWithLogs } from "../hooks/gmail-watcher-lifecycle.js";
import {
@ -19,6 +18,7 @@ import {
} from "../hooks/internal-hooks.js";
import { loadInternalHooks } from "../hooks/loader.js";
import { isTruthyEnvValue } from "../infra/env.js";
import type { loadOpenClawPlugins } from "../plugins/loader.js";
import { type PluginServicesHandle, startPluginServices } from "../plugins/services.js";
import { startBrowserControlServerIfEnabled } from "./server-browser.js";
import {

View File

@ -42,7 +42,7 @@ describe("server-web-app", () => {
it("returns true when standalone server.js exists", async () => {
const { hasStandaloneBuild, resolveStandaloneServerJs } = await import("./server-web-app.js");
const webAppDir = "/pkg/apps/web";
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
return String(p) === resolveStandaloneServerJs(webAppDir);
});
expect(hasStandaloneBuild(webAppDir)).toBe(true);
@ -60,7 +60,7 @@ describe("server-web-app", () => {
describe("hasLegacyNextBuild", () => {
it("returns true when .next/BUILD_ID exists", async () => {
const { hasLegacyNextBuild } = await import("./server-web-app.js");
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
return String(p).endsWith(path.join(".next", "BUILD_ID"));
});
expect(hasLegacyNextBuild("/pkg/apps/web")).toBe(true);
@ -78,7 +78,7 @@ describe("server-web-app", () => {
describe("isInWorkspace", () => {
it("returns true when pnpm-workspace.yaml exists at root", async () => {
const { isInWorkspace } = await import("./server-web-app.js");
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
return String(p).endsWith("pnpm-workspace.yaml");
});
expect(isInWorkspace("/proj/apps/web")).toBe(true);
@ -132,7 +132,7 @@ describe("server-web-app", () => {
it("returns ok when standalone build exists", async () => {
const { ensureWebAppBuilt } = await import("./server-web-app.js");
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -148,7 +148,7 @@ describe("server-web-app", () => {
it("returns ok when legacy .next/BUILD_ID exists (dev workspace)", async () => {
const { ensureWebAppBuilt } = await import("./server-web-app.js");
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -164,7 +164,7 @@ describe("server-web-app", () => {
it("returns error for global install when no build found", async () => {
const { ensureWebAppBuilt } = await import("./server-web-app.js");
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
// Only the package.json exists — no build, no workspace.
if (s.endsWith(path.join("apps", "web", "package.json"))) {
@ -266,7 +266,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -304,7 +304,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -337,7 +337,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
// Only package.json exists — no builds, no workspace.
if (s.endsWith(path.join("apps", "web", "package.json"))) {
@ -359,7 +359,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled, DEFAULT_WEB_APP_PORT } = await import("./server-web-app.js");
mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -383,7 +383,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
const child = mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -415,7 +415,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
const child = mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -450,7 +450,7 @@ describe("server-web-app", () => {
// resolveWebAppDir() needs to find a valid directory before reaching
// the port check, so mock existsSync for the package.json check.
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;
@ -491,7 +491,7 @@ describe("server-web-app", () => {
const { startWebAppIfEnabled } = await import("./server-web-app.js");
mockChildProcess();
existsSyncSpy.mockImplementation((p) => {
existsSyncSpy.mockImplementation((p: unknown) => {
const s = String(p);
if (s.endsWith(path.join("apps", "web", "package.json"))) {
return true;

View File

@ -1,14 +1,10 @@
import path from "node:path";
import type { CanvasHostServer } from "../canvas-host/server.js";
import type { PluginServicesHandle } from "../plugins/services.js";
import type { RuntimeEnv } from "../runtime.js";
import type { ControlUiRootState } from "./control-ui.js";
import type { startBrowserControlServerIfEnabled } from "./server-browser.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { getActiveEmbeddedRunCount } from "../agents/pi-embedded-runner/runs.js";
import { registerSkillsChangeListener } from "../agents/skills/refresh.js";
import { initSubagentRegistry } from "../agents/subagent-registry.js";
import { getTotalPendingReplies } from "../auto-reply/reply/dispatcher-registry.js";
import type { CanvasHostServer } from "../canvas-host/server.js";
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
import { formatCliCommand } from "../cli/command-format.js";
import { createDefaultDeps } from "../cli/deps.js";
@ -46,17 +42,21 @@ import { startDiagnosticHeartbeat, stopDiagnosticHeartbeat } from "../logging/di
import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js";
import { getGlobalHookRunner, runGlobalGatewayStopSafely } from "../plugins/hook-runner-global.js";
import { createEmptyPluginRegistry } from "../plugins/registry.js";
import type { PluginServicesHandle } from "../plugins/services.js";
import { getTotalQueueSize } from "../process/command-queue.js";
import type { RuntimeEnv } from "../runtime.js";
import { runOnboardingWizard } from "../wizard/onboarding.js";
import { createAuthRateLimiter, type AuthRateLimiter } from "./auth-rate-limit.js";
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
import { startGatewayConfigReloader } from "./config-reload.js";
import type { ControlUiRootState } from "./control-ui.js";
import {
GATEWAY_EVENT_UPDATE_AVAILABLE,
type GatewayUpdateAvailableEventPayload,
} from "./events.js";
import { ExecApprovalManager } from "./exec-approval-manager.js";
import { NodeRegistry } from "./node-registry.js";
import type { startBrowserControlServerIfEnabled } from "./server-browser.js";
import { createChannelManager } from "./server-channels.js";
import { createAgentEventHandler } from "./server-chat.js";
import { createGatewayCloseHandler } from "./server-close.js";

View File

@ -5,7 +5,6 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import type { OpenClawConfig } from "../config/config.js";
import {
resolveDefaultAgentId,
resolveAgentWorkspaceDir,
@ -14,6 +13,7 @@ import {
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
import { resolveConfiguredModelRef } from "../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
import type { OpenClawConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
const log = createSubsystemLogger("llm-slug-generator");

View File

@ -1,5 +1,5 @@
import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js";
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
const raw = `${listener.commandLine ?? ""} ${listener.command ?? ""}`.trim().toLowerCase();

View File

@ -1,5 +1,5 @@
import type { Llama, LlamaEmbeddingContext, LlamaModel } from "node-llama-cpp";
import fsSync from "node:fs";
import type { Llama, LlamaEmbeddingContext, LlamaModel } from "node-llama-cpp";
import type { OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { resolveUserPath } from "../utils.js";

View File

@ -1,11 +1,10 @@
import type { OpenClawConfig } from "../config/config.js";
import type { ExecFn } from "./windows-acl.js";
import { resolveSandboxConfigForAgent } from "../agents/sandbox.js";
import { execDockerRaw } from "../agents/sandbox/docker.js";
import { resolveBrowserConfig, resolveProfile } from "../browser/config.js";
import { resolveBrowserControlAuth } from "../browser/control-auth.js";
import { listChannelPlugins } from "../channels/plugins/index.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
import { resolveGatewayAuth } from "../gateway/auth.js";
import { buildGatewayConnectionDetails } from "../gateway/call.js";
@ -40,6 +39,7 @@ import {
inspectPathPermissions,
} from "./audit-fs.js";
import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools.js";
import type { ExecFn } from "./windows-acl.js";
export type SecurityAuditSeverity = "info" | "warn" | "critical";

View File

@ -1,7 +1,7 @@
import type { Chat, Message } from "@grammyjs/types";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import type { Chat, Message } from "@grammyjs/types";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js";
import { withEnvAsync } from "../test-utils/env.js";

View File

@ -13,10 +13,12 @@ import {
enqueueSystemEventSpy,
getLoadConfigMock,
getReadChannelAllowFromStoreMock,
getUpsertChannelPairingRequestMock,
getOnHandler,
listSkillCommandsForAgents,
onSpy,
replySpy,
sendChatActionSpy,
sendMessageSpy,
setMyCommandsSpy,
wasSentByBot,
@ -25,6 +27,7 @@ import { createTelegramBot } from "./bot.js";
const loadConfig = getLoadConfigMock();
const readChannelAllowFromStore = getReadChannelAllowFromStoreMock();
const upsertChannelPairingRequest = getUpsertChannelPairingRequestMock();
function resolveSkillCommands(config: Parameters<typeof listNativeCommandSpecsForConfig>[0]) {
void config;
@ -280,7 +283,6 @@ describe("createTelegramBot", () => {
try {
onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset();
createTelegramBot({ token: "tok" });
@ -321,7 +323,6 @@ describe("createTelegramBot", () => {
it("requests pairing by default for unknown DM senders", async () => {
onSpy.mockReset();
sendMessageSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset();
loadConfig.mockReturnValue({
@ -361,7 +362,6 @@ describe("createTelegramBot", () => {
it("does not resend pairing code when a request is already pending", async () => {
onSpy.mockReset();
sendMessageSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset();
loadConfig.mockReturnValue({
@ -414,7 +414,6 @@ describe("createTelegramBot", () => {
it("accepts group messages when mentionPatterns match (without @botUsername)", async () => {
onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset();
loadConfig.mockReturnValue({

View File

@ -8,13 +8,6 @@ import {
Text,
TUI,
} from "@mariozechner/pi-tui";
import type {
AgentSummary,
SessionInfo,
SessionScope,
TuiOptions,
TuiStateAccess,
} from "./tui-types.js";
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
import { loadConfig } from "../config/config.js";
import {
@ -34,6 +27,13 @@ import { formatTokens } from "./tui-formatters.js";
import { createLocalShellRunner } from "./tui-local-shell.js";
import { createOverlayHandlers } from "./tui-overlays.js";
import { createSessionActions } from "./tui-session-actions.js";
import type {
AgentSummary,
SessionInfo,
SessionScope,
TuiOptions,
TuiStateAccess,
} from "./tui-types.js";
import { buildWaitingStatusMessage, defaultWaitingPhrases } from "./tui-waiting.js";
export { resolveFinalAssistantText } from "./tui-formatters.js";

View File

@ -1,8 +1,3 @@
import type { OnboardOptions } from "../commands/onboard-types.js";
import type { OpenClawConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import type { GatewayWizardSettings, WizardFlow } from "./onboarding.types.js";
import type { WizardPrompter } from "./prompts.js";
import { formatCliCommand } from "../cli/command-format.js";
import {
buildGatewayInstallPlan,
@ -22,6 +17,8 @@ import {
waitForGatewayReachable,
resolveControlUiLinks,
} from "../commands/onboard-helpers.js";
import type { OnboardOptions } from "../commands/onboard-types.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveGatewayService } from "../daemon/service.js";
import { isSystemdUserServiceAvailable } from "../daemon/systemd.js";
import {
@ -30,7 +27,10 @@ import {
probeForWebApp,
} from "../gateway/server-web-app.js";
import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js";
import type { RuntimeEnv } from "../runtime.js";
import { setupOnboardingShellCompletion } from "./onboarding.completion.js";
import type { GatewayWizardSettings, WizardFlow } from "./onboarding.types.js";
import type { WizardPrompter } from "./prompts.js";
type FinalizeOnboardingOptions = {
flow: WizardFlow;

View File

@ -1,24 +1,24 @@
import type { GatewayAuthChoice } from "../commands/onboard-types.js";
import type { GatewayBindMode, GatewayTailscaleMode, OpenClawConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import type {
GatewayWizardSettings,
QuickstartGatewayDefaults,
WizardFlow,
} from "./onboarding.types.js";
import type { WizardPrompter } from "./prompts.js";
import {
normalizeGatewayTokenInput,
randomToken,
validateGatewayPasswordInput,
} from "../commands/onboard-helpers.js";
import type { GatewayAuthChoice } from "../commands/onboard-types.js";
import type { GatewayBindMode, GatewayTailscaleMode, OpenClawConfig } from "../config/config.js";
import {
TAILSCALE_DOCS_LINES,
TAILSCALE_EXPOSURE_OPTIONS,
TAILSCALE_MISSING_BIN_NOTE_LINES,
} from "../gateway/gateway-config-prompts.shared.js";
import { findTailscaleBinary } from "../infra/tailscale.js";
import type { RuntimeEnv } from "../runtime.js";
import { validateIPv4AddressInput } from "../shared/net/ipv4.js";
import type {
GatewayWizardSettings,
QuickstartGatewayDefaults,
WizardFlow,
} from "./onboarding.types.js";
import type { WizardPrompter } from "./prompts.js";
// These commands are "high risk" (privacy writes/recording) and should be
// explicitly armed by the user when they want to use them.

View File

@ -2,10 +2,10 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js";
import type { WizardPrompter, WizardSelectParams } from "./prompts.js";
import { DEFAULT_BOOTSTRAP_FILENAME } from "../agents/workspace.js";
import type { RuntimeEnv } from "../runtime.js";
import { runOnboardingWizard } from "./onboarding.js";
import type { WizardPrompter, WizardSelectParams } from "./prompts.js";
const ensureAuthProfileStore = vi.hoisted(() => vi.fn(() => ({ profiles: {} })));
const promptAuthChoiceGrouped = vi.hoisted(() => vi.fn(async () => "skip"));

View File

@ -1,3 +1,4 @@
import { formatCliCommand } from "../cli/command-format.js";
import type {
GatewayAuthChoice,
OnboardMode,
@ -5,17 +6,16 @@ import type {
ResetScope,
} from "../commands/onboard-types.js";
import type { OpenClawConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import type { QuickstartGatewayDefaults, WizardFlow } from "./onboarding.types.js";
import { formatCliCommand } from "../cli/command-format.js";
import {
DEFAULT_GATEWAY_PORT,
readConfigFileSnapshot,
resolveGatewayPort,
writeConfigFile,
} from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { resolveUserPath } from "../utils.js";
import type { QuickstartGatewayDefaults, WizardFlow } from "./onboarding.types.js";
import { WizardCancelledError, type WizardPrompter } from "./prompts.js";
async function requireRiskAcknowledgement(params: {

View File

@ -1,13 +1,13 @@
import type { OpenClawApp } from "./app.ts";
import type { GatewayHelloOk } from "./gateway.ts";
import type { ChatAttachment, ChatQueueItem } from "./ui-types.ts";
import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js";
import { scheduleChatScroll } from "./app-scroll.ts";
import { setLastActiveSessionKey } from "./app-settings.ts";
import { resetToolStream } from "./app-tool-stream.ts";
import type { OpenClawApp } from "./app.ts";
import { abortChatRun, loadChatHistory, sendChatMessage } from "./controllers/chat.ts";
import { loadSessions } from "./controllers/sessions.ts";
import type { GatewayHelloOk } from "./gateway.ts";
import { normalizeBasePath } from "./navigation.ts";
import type { ChatAttachment, ChatQueueItem } from "./ui-types.ts";
import { generateUUID } from "./uuid.ts";
export type ChatHost = {