acpx 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,6 +33,7 @@ One command surface for Pi, OpenClaw ACP, Codex, Claude, and other ACP-compatibl
33
33
  - **Prompt from file/stdin**: `--file <path>` or piped stdin for prompt content
34
34
  - **Config files**: global + project JSON config with `acpx config show|init`
35
35
  - **Session inspect/history**: `sessions show` and `sessions history --limit <n>`
36
+ - **Session export/import**: move portable session archives between machines
36
37
  - **Local status checks**: `status` reports running/idle/dead/no-session, pid, uptime, last prompt
37
38
  - **Client methods**: stable `fs/*` and `terminal/*` handlers with permission controls and cwd sandboxing
38
39
  - **Auth handshake**: stable `authenticate` support via env/config credentials
@@ -1,6 +1,6 @@
1
- import { j as permissionModeSatisfies } from "./live-checkpoint-D5d-K9s1.js";
2
- import { _ as resolveOutputPolicy, a as hasExplicitPermissionModeFlag, b as loadPermissionPolicySpec, g as resolveGlobalFlags, h as resolveAgentInvocation, v as resolvePermissionMode } from "./flags-C-rwARqg.js";
3
- import { c as validateFlowDefinition, h as isDefinedFlow, o as FlowRunner } from "./flows-WLs26_5Y.js";
1
+ import { D as permissionModeSatisfies } from "./live-checkpoint-CuFft_Nd.js";
2
+ import { _ as resolveOutputPolicy, a as hasExplicitPermissionModeFlag, b as loadPermissionPolicySpec, g as resolveGlobalFlags, h as resolveAgentInvocation, v as resolvePermissionMode } from "./flags--2oX_ubW.js";
3
+ import { c as validateFlowDefinition, h as isDefinedFlow, o as FlowRunner } from "./flows-e4umXVbY.js";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import path from "node:path";
6
6
  import { InvalidArgumentError } from "commander";
@@ -194,4 +194,4 @@ function printFlowRunResult(result, globalFlags) {
194
194
  //#endregion
195
195
  export { handleFlowRun };
196
196
 
197
- //# sourceMappingURL=cli-Bf3yjqzE.js.map
197
+ //# sourceMappingURL=cli-8dP_TqBp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-Bf3yjqzE.js","names":[],"sources":["../src/flows/cli.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { InvalidArgumentError, type Command } from \"commander\";\nimport type { ResolvedAcpxConfig } from \"../cli/config.js\";\nimport {\n hasExplicitPermissionModeFlag,\n resolveAgentInvocation,\n resolveGlobalFlags,\n resolveOutputPolicy,\n resolvePermissionMode,\n type GlobalFlags,\n} from \"../cli/flags.js\";\nimport { type FlowDefinition, FlowRunner } from \"../flows.js\";\nimport { loadPermissionPolicySpec } from \"../permission-policy.js\";\nimport { permissionModeSatisfies } from \"../permissions.js\";\nimport type { PermissionMode } from \"../types.js\";\nimport { isDefinedFlow } from \"./authoring.js\";\nimport { validateFlowDefinition } from \"./graph.js\";\n\ntype FlowRunFlags = {\n inputJson?: string;\n inputFile?: string;\n defaultAgent?: string;\n};\n\nconst FLOW_RUNTIME_SPECIFIER = \"acpx/flows\";\nconst TEXT_MODULE_EXTENSIONS = new Set([\".js\", \".mjs\", \".cjs\", \".ts\", \".tsx\", \".mts\", \".cts\"]);\n\nexport async function handleFlowRun(\n flowFile: string,\n flags: FlowRunFlags,\n command: Command,\n config: ResolvedAcpxConfig,\n): Promise<void> {\n const globalFlags = resolveGlobalFlags(command, config);\n const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);\n const permissionPolicy = await resolveFlowPermissionPolicy(globalFlags);\n const outputPolicy = resolveOutputPolicy(globalFlags.format, globalFlags.jsonStrict === true);\n const input = await readFlowInput(flags);\n const flowPath = path.resolve(flowFile);\n const flow = await loadFlowModule(flowPath);\n assertFlowPermissionRequirements(flow, permissionMode, globalFlags);\n\n const runner = new FlowRunner({\n resolveAgent: (profile?: string) => {\n return resolveAgentInvocation(profile ?? flags.defaultAgent, globalFlags, config);\n },\n permissionMode,\n mcpServers: config.mcpServers,\n nonInteractivePermissions: globalFlags.nonInteractivePermissions,\n permissionPolicy,\n authCredentials: config.auth,\n authPolicy: globalFlags.authPolicy,\n timeoutMs: globalFlags.timeout,\n ttlMs: globalFlags.ttl,\n verbose: globalFlags.verbose,\n suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,\n sessionOptions: {\n model: globalFlags.model,\n allowedTools: globalFlags.allowedTools,\n maxTurns: globalFlags.maxTurns,\n },\n });\n\n const result = await runner.run(flow, input, {\n flowPath,\n });\n\n printFlowRunResult(result, globalFlags);\n}\n\nasync function resolveFlowPermissionPolicy(globalFlags: GlobalFlags) {\n try {\n return await loadPermissionPolicySpec(globalFlags.permissionPolicy, globalFlags.cwd);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new InvalidArgumentError(`Invalid permission policy: ${message}`);\n }\n}\n\nfunction assertFlowPermissionRequirements(\n flow: FlowDefinition,\n permissionMode: PermissionMode,\n globalFlags: GlobalFlags,\n): void {\n const permissions = flow.permissions;\n if (!permissions) {\n return;\n }\n\n if (permissions.requireExplicitGrant && !hasExplicitPermissionModeFlag(globalFlags)) {\n throw new InvalidArgumentError(\n buildFlowPermissionFailureMessage(flow, permissions.requiredMode, permissions.reason, true),\n );\n }\n\n if (!permissionModeSatisfies(permissionMode, permissions.requiredMode)) {\n throw new InvalidArgumentError(\n buildFlowPermissionFailureMessage(flow, permissions.requiredMode, permissions.reason, false),\n );\n }\n}\n\nfunction buildFlowPermissionFailureMessage(\n flow: FlowDefinition,\n requiredMode: PermissionMode,\n reason?: string,\n explicit = false,\n): string {\n return [\n explicit\n ? `Flow \"${flow.name}\" requires an explicit ${requiredMode} grant.`\n : `Flow \"${flow.name}\" requires permission mode ${requiredMode}.`,\n `Rerun with --${requiredMode}.`,\n ...(reason ? [`Reason: ${reason}`] : []),\n ].join(\" \");\n}\n\nasync function readFlowInput(flags: FlowRunFlags): Promise<unknown> {\n if (flags.inputJson && flags.inputFile) {\n throw new InvalidArgumentError(\"Use only one of --input-json or --input-file\");\n }\n\n if (flags.inputJson) {\n return parseJsonInput(flags.inputJson, \"--input-json\");\n }\n\n if (flags.inputFile) {\n const inputPath = path.resolve(flags.inputFile);\n const payload = await fs.readFile(inputPath, \"utf8\");\n return parseJsonInput(payload, \"--input-file\");\n }\n\n return {};\n}\n\nasync function loadFlowModule(flowPath: string): Promise<FlowDefinition> {\n const extension = path.extname(flowPath).toLowerCase();\n const prepared = await prepareFlowModuleImport(flowPath, extension);\n try {\n const module = await loadFlowRuntimeModule(prepared.flowUrl, extension);\n\n const candidate = findFlowDefinition(module);\n if (!candidate) {\n throw new Error(\n `Flow module must export default defineFlow({...}) from \"acpx/flows\": ${flowPath}`,\n );\n }\n validateFlowDefinition(candidate);\n return candidate;\n } finally {\n await prepared.cleanup?.();\n }\n}\n\nasync function prepareFlowModuleImport(\n flowPath: string,\n extension: string,\n): Promise<{\n flowUrl: string;\n cleanup?: () => Promise<void>;\n}> {\n const flowUrl = pathToFileURL(flowPath).href;\n if (!TEXT_MODULE_EXTENSIONS.has(extension)) {\n return { flowUrl };\n }\n\n const source = await fs.readFile(flowPath, \"utf8\");\n if (!source.includes(FLOW_RUNTIME_SPECIFIER)) {\n return { flowUrl };\n }\n\n const runtimeSpecifier = resolveFlowRuntimeImportSpecifier();\n const rewritten = source.replaceAll(\n /([\"'])acpx\\/flows\\1/g,\n (_match, quote: string) => `${quote}${runtimeSpecifier}${quote}`,\n );\n if (rewritten === source) {\n return { flowUrl };\n }\n\n const tempPath = path.join(path.dirname(flowPath), `.acpx-flow-load-${randomUUID()}${extension}`);\n await fs.writeFile(tempPath, rewritten, \"utf8\");\n return {\n flowUrl: pathToFileURL(tempPath).href,\n cleanup: async () => {\n await fs.rm(tempPath, { force: true });\n },\n };\n}\n\nfunction resolveFlowRuntimeImportSpecifier(): string {\n const selfPath = fileURLToPath(import.meta.url);\n let runtimePath: string;\n\n if (selfPath.endsWith(`${path.sep}src${path.sep}flows${path.sep}cli.ts`)) {\n runtimePath = fileURLToPath(new URL(\"../flows.ts\", import.meta.url));\n } else if (selfPath.endsWith(`${path.sep}src${path.sep}flows${path.sep}cli.js`)) {\n runtimePath = fileURLToPath(new URL(\"../flows.js\", import.meta.url));\n } else {\n runtimePath = fileURLToPath(new URL(\"./flows.js\", import.meta.url));\n }\n return runtimePath.replaceAll(path.sep, \"/\");\n}\n\nasync function loadFlowRuntimeModule(\n flowUrl: string,\n extension: string,\n): Promise<{\n default?: unknown;\n \"module.exports\"?: unknown;\n}> {\n if (extension === \".ts\" || extension === \".tsx\" || extension === \".cts\") {\n const { register } = (await import(\"tsx/cjs/api\")) as {\n register: (options: { namespace: string }) => {\n require: (\n specifier: string,\n parentURL: string,\n ) => {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n unregister: () => void;\n };\n };\n const loader = register({ namespace: randomUUID() });\n try {\n return loader.require(flowUrl, import.meta.url);\n } finally {\n loader.unregister();\n }\n }\n\n if (extension === \".mts\") {\n const { register } = (await import(\"tsx/esm/api\")) as {\n register: (options: { namespace: string }) => {\n import: (\n specifier: string,\n parentURL: string,\n ) => Promise<{\n default?: unknown;\n \"module.exports\"?: unknown;\n }>;\n unregister: () => Promise<void>;\n };\n };\n const loader = register({ namespace: randomUUID() });\n try {\n return (await loader.import(flowUrl, import.meta.url)) as {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n } finally {\n await loader.unregister();\n }\n }\n\n return (await import(flowUrl)) as {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n}\n\nfunction findFlowDefinition(module: {\n default?: unknown;\n \"module.exports\"?: unknown;\n}): FlowDefinition | null {\n const candidates = [\n module.default,\n module[\"module.exports\"],\n getNestedDefault(module.default),\n getNestedDefault(module[\"module.exports\"]),\n ];\n\n for (const candidate of candidates) {\n if (isDefinedFlow(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nfunction getNestedDefault(value: unknown): unknown {\n if (!value || typeof value !== \"object\" || !(\"default\" in value)) {\n return null;\n }\n return (value as { default?: unknown }).default ?? null;\n}\n\nfunction parseJsonInput(raw: string, label: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (error) {\n throw new InvalidArgumentError(\n `${label} must contain valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\nfunction printFlowRunResult(\n result: Awaited<ReturnType<FlowRunner[\"run\"]>>,\n globalFlags: GlobalFlags,\n): void {\n const payload = {\n action: \"flow_run_result\",\n runId: result.state.runId,\n flowName: result.state.flowName,\n runTitle: result.state.runTitle,\n flowPath: result.state.flowPath,\n status: result.state.status,\n currentNode: result.state.currentNode,\n currentNodeType: result.state.currentNodeType,\n currentNodeStartedAt: result.state.currentNodeStartedAt,\n lastHeartbeatAt: result.state.lastHeartbeatAt,\n statusDetail: result.state.statusDetail,\n waitingOn: result.state.waitingOn,\n runDir: result.runDir,\n outputs: result.state.outputs,\n sessionBindings: result.state.sessionBindings,\n };\n\n if (globalFlags.format === \"json\") {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n return;\n }\n\n if (globalFlags.format === \"quiet\") {\n process.stdout.write(`${result.state.runId}\\n`);\n return;\n }\n\n process.stdout.write(`runId: ${payload.runId}\\n`);\n process.stdout.write(`flow: ${payload.flowName}\\n`);\n if (payload.runTitle) {\n process.stdout.write(`title: ${payload.runTitle}\\n`);\n }\n process.stdout.write(`status: ${payload.status}\\n`);\n process.stdout.write(`runDir: ${payload.runDir}\\n`);\n if (payload.currentNode) {\n process.stdout.write(`currentNode: ${payload.currentNode}\\n`);\n }\n if (payload.statusDetail) {\n process.stdout.write(`statusDetail: ${payload.statusDetail}\\n`);\n }\n if (payload.waitingOn) {\n process.stdout.write(`waitingOn: ${payload.waitingOn}\\n`);\n }\n process.stdout.write(`${JSON.stringify(payload.outputs, null, 2)}\\n`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-8dP_TqBp.js","names":[],"sources":["../src/flows/cli.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { InvalidArgumentError, type Command } from \"commander\";\nimport type { ResolvedAcpxConfig } from \"../cli/config.js\";\nimport {\n hasExplicitPermissionModeFlag,\n resolveAgentInvocation,\n resolveGlobalFlags,\n resolveOutputPolicy,\n resolvePermissionMode,\n type GlobalFlags,\n} from \"../cli/flags.js\";\nimport { type FlowDefinition, FlowRunner } from \"../flows.js\";\nimport { loadPermissionPolicySpec } from \"../permission-policy.js\";\nimport { permissionModeSatisfies } from \"../permissions.js\";\nimport type { PermissionMode } from \"../types.js\";\nimport { isDefinedFlow } from \"./authoring.js\";\nimport { validateFlowDefinition } from \"./graph.js\";\n\ntype FlowRunFlags = {\n inputJson?: string;\n inputFile?: string;\n defaultAgent?: string;\n};\n\nconst FLOW_RUNTIME_SPECIFIER = \"acpx/flows\";\nconst TEXT_MODULE_EXTENSIONS = new Set([\".js\", \".mjs\", \".cjs\", \".ts\", \".tsx\", \".mts\", \".cts\"]);\n\nexport async function handleFlowRun(\n flowFile: string,\n flags: FlowRunFlags,\n command: Command,\n config: ResolvedAcpxConfig,\n): Promise<void> {\n const globalFlags = resolveGlobalFlags(command, config);\n const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);\n const permissionPolicy = await resolveFlowPermissionPolicy(globalFlags);\n const outputPolicy = resolveOutputPolicy(globalFlags.format, globalFlags.jsonStrict === true);\n const input = await readFlowInput(flags);\n const flowPath = path.resolve(flowFile);\n const flow = await loadFlowModule(flowPath);\n assertFlowPermissionRequirements(flow, permissionMode, globalFlags);\n\n const runner = new FlowRunner({\n resolveAgent: (profile?: string) => {\n return resolveAgentInvocation(profile ?? flags.defaultAgent, globalFlags, config);\n },\n permissionMode,\n mcpServers: config.mcpServers,\n nonInteractivePermissions: globalFlags.nonInteractivePermissions,\n permissionPolicy,\n authCredentials: config.auth,\n authPolicy: globalFlags.authPolicy,\n timeoutMs: globalFlags.timeout,\n ttlMs: globalFlags.ttl,\n verbose: globalFlags.verbose,\n suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,\n sessionOptions: {\n model: globalFlags.model,\n allowedTools: globalFlags.allowedTools,\n maxTurns: globalFlags.maxTurns,\n },\n });\n\n const result = await runner.run(flow, input, {\n flowPath,\n });\n\n printFlowRunResult(result, globalFlags);\n}\n\nasync function resolveFlowPermissionPolicy(globalFlags: GlobalFlags) {\n try {\n return await loadPermissionPolicySpec(globalFlags.permissionPolicy, globalFlags.cwd);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new InvalidArgumentError(`Invalid permission policy: ${message}`);\n }\n}\n\nfunction assertFlowPermissionRequirements(\n flow: FlowDefinition,\n permissionMode: PermissionMode,\n globalFlags: GlobalFlags,\n): void {\n const permissions = flow.permissions;\n if (!permissions) {\n return;\n }\n\n if (permissions.requireExplicitGrant && !hasExplicitPermissionModeFlag(globalFlags)) {\n throw new InvalidArgumentError(\n buildFlowPermissionFailureMessage(flow, permissions.requiredMode, permissions.reason, true),\n );\n }\n\n if (!permissionModeSatisfies(permissionMode, permissions.requiredMode)) {\n throw new InvalidArgumentError(\n buildFlowPermissionFailureMessage(flow, permissions.requiredMode, permissions.reason, false),\n );\n }\n}\n\nfunction buildFlowPermissionFailureMessage(\n flow: FlowDefinition,\n requiredMode: PermissionMode,\n reason?: string,\n explicit = false,\n): string {\n return [\n explicit\n ? `Flow \"${flow.name}\" requires an explicit ${requiredMode} grant.`\n : `Flow \"${flow.name}\" requires permission mode ${requiredMode}.`,\n `Rerun with --${requiredMode}.`,\n ...(reason ? [`Reason: ${reason}`] : []),\n ].join(\" \");\n}\n\nasync function readFlowInput(flags: FlowRunFlags): Promise<unknown> {\n if (flags.inputJson && flags.inputFile) {\n throw new InvalidArgumentError(\"Use only one of --input-json or --input-file\");\n }\n\n if (flags.inputJson) {\n return parseJsonInput(flags.inputJson, \"--input-json\");\n }\n\n if (flags.inputFile) {\n const inputPath = path.resolve(flags.inputFile);\n const payload = await fs.readFile(inputPath, \"utf8\");\n return parseJsonInput(payload, \"--input-file\");\n }\n\n return {};\n}\n\nasync function loadFlowModule(flowPath: string): Promise<FlowDefinition> {\n const extension = path.extname(flowPath).toLowerCase();\n const prepared = await prepareFlowModuleImport(flowPath, extension);\n try {\n const module = await loadFlowRuntimeModule(prepared.flowUrl, extension);\n\n const candidate = findFlowDefinition(module);\n if (!candidate) {\n throw new Error(\n `Flow module must export default defineFlow({...}) from \"acpx/flows\": ${flowPath}`,\n );\n }\n validateFlowDefinition(candidate);\n return candidate;\n } finally {\n await prepared.cleanup?.();\n }\n}\n\nasync function prepareFlowModuleImport(\n flowPath: string,\n extension: string,\n): Promise<{\n flowUrl: string;\n cleanup?: () => Promise<void>;\n}> {\n const flowUrl = pathToFileURL(flowPath).href;\n if (!TEXT_MODULE_EXTENSIONS.has(extension)) {\n return { flowUrl };\n }\n\n const source = await fs.readFile(flowPath, \"utf8\");\n if (!source.includes(FLOW_RUNTIME_SPECIFIER)) {\n return { flowUrl };\n }\n\n const runtimeSpecifier = resolveFlowRuntimeImportSpecifier();\n const rewritten = source.replaceAll(\n /([\"'])acpx\\/flows\\1/g,\n (_match, quote: string) => `${quote}${runtimeSpecifier}${quote}`,\n );\n if (rewritten === source) {\n return { flowUrl };\n }\n\n const tempPath = path.join(path.dirname(flowPath), `.acpx-flow-load-${randomUUID()}${extension}`);\n await fs.writeFile(tempPath, rewritten, \"utf8\");\n return {\n flowUrl: pathToFileURL(tempPath).href,\n cleanup: async () => {\n await fs.rm(tempPath, { force: true });\n },\n };\n}\n\nfunction resolveFlowRuntimeImportSpecifier(): string {\n const selfPath = fileURLToPath(import.meta.url);\n let runtimePath: string;\n\n if (selfPath.endsWith(`${path.sep}src${path.sep}flows${path.sep}cli.ts`)) {\n runtimePath = fileURLToPath(new URL(\"../flows.ts\", import.meta.url));\n } else if (selfPath.endsWith(`${path.sep}src${path.sep}flows${path.sep}cli.js`)) {\n runtimePath = fileURLToPath(new URL(\"../flows.js\", import.meta.url));\n } else {\n runtimePath = fileURLToPath(new URL(\"./flows.js\", import.meta.url));\n }\n return runtimePath.replaceAll(path.sep, \"/\");\n}\n\nasync function loadFlowRuntimeModule(\n flowUrl: string,\n extension: string,\n): Promise<{\n default?: unknown;\n \"module.exports\"?: unknown;\n}> {\n if (extension === \".ts\" || extension === \".tsx\" || extension === \".cts\") {\n const { register } = (await import(\"tsx/cjs/api\")) as {\n register: (options: { namespace: string }) => {\n require: (\n specifier: string,\n parentURL: string,\n ) => {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n unregister: () => void;\n };\n };\n const loader = register({ namespace: randomUUID() });\n try {\n return loader.require(flowUrl, import.meta.url);\n } finally {\n loader.unregister();\n }\n }\n\n if (extension === \".mts\") {\n const { register } = (await import(\"tsx/esm/api\")) as {\n register: (options: { namespace: string }) => {\n import: (\n specifier: string,\n parentURL: string,\n ) => Promise<{\n default?: unknown;\n \"module.exports\"?: unknown;\n }>;\n unregister: () => Promise<void>;\n };\n };\n const loader = register({ namespace: randomUUID() });\n try {\n return (await loader.import(flowUrl, import.meta.url)) as {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n } finally {\n await loader.unregister();\n }\n }\n\n return (await import(flowUrl)) as {\n default?: unknown;\n \"module.exports\"?: unknown;\n };\n}\n\nfunction findFlowDefinition(module: {\n default?: unknown;\n \"module.exports\"?: unknown;\n}): FlowDefinition | null {\n const candidates = [\n module.default,\n module[\"module.exports\"],\n getNestedDefault(module.default),\n getNestedDefault(module[\"module.exports\"]),\n ];\n\n for (const candidate of candidates) {\n if (isDefinedFlow(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nfunction getNestedDefault(value: unknown): unknown {\n if (!value || typeof value !== \"object\" || !(\"default\" in value)) {\n return null;\n }\n return (value as { default?: unknown }).default ?? null;\n}\n\nfunction parseJsonInput(raw: string, label: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (error) {\n throw new InvalidArgumentError(\n `${label} must contain valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\nfunction printFlowRunResult(\n result: Awaited<ReturnType<FlowRunner[\"run\"]>>,\n globalFlags: GlobalFlags,\n): void {\n const payload = {\n action: \"flow_run_result\",\n runId: result.state.runId,\n flowName: result.state.flowName,\n runTitle: result.state.runTitle,\n flowPath: result.state.flowPath,\n status: result.state.status,\n currentNode: result.state.currentNode,\n currentNodeType: result.state.currentNodeType,\n currentNodeStartedAt: result.state.currentNodeStartedAt,\n lastHeartbeatAt: result.state.lastHeartbeatAt,\n statusDetail: result.state.statusDetail,\n waitingOn: result.state.waitingOn,\n runDir: result.runDir,\n outputs: result.state.outputs,\n sessionBindings: result.state.sessionBindings,\n };\n\n if (globalFlags.format === \"json\") {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n return;\n }\n\n if (globalFlags.format === \"quiet\") {\n process.stdout.write(`${result.state.runId}\\n`);\n return;\n }\n\n process.stdout.write(`runId: ${payload.runId}\\n`);\n process.stdout.write(`flow: ${payload.flowName}\\n`);\n if (payload.runTitle) {\n process.stdout.write(`title: ${payload.runTitle}\\n`);\n }\n process.stdout.write(`status: ${payload.status}\\n`);\n process.stdout.write(`runDir: ${payload.runDir}\\n`);\n if (payload.currentNode) {\n process.stdout.write(`currentNode: ${payload.currentNode}\\n`);\n }\n if (payload.statusDetail) {\n process.stdout.write(`statusDetail: ${payload.statusDetail}\\n`);\n }\n if (payload.waitingOn) {\n process.stdout.write(`waitingOn: ${payload.waitingOn}\\n`);\n }\n process.stdout.write(`${JSON.stringify(payload.outputs, null, 2)}\\n`);\n}\n"],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { f as SessionRecord } from "./session-options-CFudjdkU.js";
1
+ import { f as SessionRecord } from "./session-options-Bh1bIqQ2.js";
2
2
  import { Command } from "commander";
3
3
 
4
4
  //#region src/cli/flags.d.ts
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli/flags.ts","../src/cli/output/render.ts"],"mappings":";;;;iBAkJgB,eAAA,CAAgB,KAAa;AAAA,iBAkD7B,iBAAA,CAAkB,KAAa;AAAA,iBAgB/B,aAAA,CAAc,KAAa;;;KClMtC,uBAAA;AAAA,iBAwLW,6BAAA,CACd,MAAA,EAAQ,aAAA,EACR,UAAA,UACA,gBAAA,GAAkB,uBAA2C"}
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli/flags.ts","../src/cli/output/render.ts"],"mappings":";;;;iBA4JgB,eAAA,CAAgB,KAAa;AAAA,iBAkD7B,iBAAA,CAAkB,KAAa;AAAA,iBAgB/B,aAAA,CAAc,KAAa;;;KC5MtC,uBAAA;AAAA,iBAwLW,6BAAA,CACd,MAAA,EAAQ,aAAA,EACR,UAAA,UACA,gBAAA,GAAkB,uBAA2C"}
package/dist/cli.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { a as runSessionQueueOwner, c as buildQueueOwnerArgOverride, h as __exportAll, l as flushPerfMetricsCapture, n as getTextErrorRemediationHints, p as probeQueueOwnerHealth, t as createOutputFormatter, u as installPerfMetricsCapture } from "./output-DPg20dvn.js";
3
- import { At as EXIT_CODES, F as findSession, I as findSessionByDirectoryWalk, P as findGitRepositoryRoot, Pt as OUTPUT_FORMATS, Rt as AgentSpawnError, St as exitCodeForOutputErrorCode, Tt as normalizeOutputError, bt as normalizeAgentName, ct as PromptInputValidationError, dt as parsePromptSource, mt as InterruptedError, pt as textPrompt, st as normalizeRuntimeSessionId, ut as mergePromptSourceWithText, vt as DEFAULT_AGENT_NAME, yt as listBuiltInAgents } from "./live-checkpoint-D5d-K9s1.js";
4
- import { _ as resolveOutputPolicy, b as loadPermissionPolicySpec, c as parseHistoryLimit, d as parseOutputFormat$1, f as parsePruneBeforeDate, g as resolveGlobalFlags, h as resolveAgentInvocation, i as addSessionOption, l as parseMaxTurns, m as parseTtlSeconds, n as addPromptInputOption, o as parseAllowedTools, p as parseSessionName, r as addSessionNameOption, s as parseDaysOlderThan, t as addGlobalFlags, u as parseNonEmptyValue, v as resolvePermissionMode, y as resolveSessionNameFromFlags } from "./flags-C-rwARqg.js";
2
+ import { a as runSessionQueueOwner, c as buildQueueOwnerArgOverride, g as __exportAll, h as isProcessAlive, l as flushPerfMetricsCapture, n as getTextErrorRemediationHints, p as probeQueueOwnerHealth, t as createOutputFormatter, u as installPerfMetricsCapture } from "./output-Di77Yugq.js";
3
+ import { $ as defaultSessionEventLog, A as findGitRepositoryRoot, At as EXIT_CODES, I as normalizeName, M as findSessionByDirectoryWalk, P as listSessions, Pt as OUTPUT_FORMATS, Rt as AcpxOperationalError, St as exitCodeForOutputErrorCode, Tt as normalizeOutputError, X as serializeSessionRecordForDisk, Y as parseSessionRecord, Z as normalizeRuntimeSessionId, at as isAcpJsonRpcMessage, bt as normalizeAgentName$1, ct as PromptInputValidationError, dt as parsePromptSource, j as findSession, mt as InterruptedError, nt as sessionEventLockPath, pt as textPrompt, rt as sessionEventSegmentPath, tt as sessionEventActivePath, ut as mergePromptSourceWithText, vt as DEFAULT_AGENT_NAME, yt as listBuiltInAgents, z as writeSessionRecord, zt as AgentSpawnError } from "./live-checkpoint-CuFft_Nd.js";
4
+ import { _ as resolveOutputPolicy, b as loadPermissionPolicySpec, c as parseHistoryLimit, d as parseOutputFormat$1, f as parsePruneBeforeDate, g as resolveGlobalFlags, h as resolveAgentInvocation, i as addSessionOption, l as parseMaxTurns, m as parseTtlSeconds, n as addPromptInputOption, o as parseAllowedTools, p as parseSessionName, r as addSessionNameOption, s as parseDaysOlderThan, t as addGlobalFlags, u as parseNonEmptyValue, v as resolvePermissionMode, y as resolveSessionNameFromFlags } from "./flags--2oX_ubW.js";
5
5
  import { readFileSync, realpathSync } from "node:fs";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import path from "node:path";
8
- import { Command, CommanderError, InvalidArgumentError } from "commander";
8
+ import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
9
9
  import fs$1 from "node:fs/promises";
10
10
  import os from "node:os";
11
+ import { randomUUID } from "node:crypto";
12
+ import { ZodError, z } from "zod";
11
13
  //#region src/cli-public.ts
12
14
  function configurePublicCli(options) {
13
15
  const builtInAgents = options.listBuiltInAgents(options.config.agents);
@@ -53,6 +55,319 @@ function isLegacyZedCodexAcpInvocation(agentCommand) {
53
55
  return /@zed-industries\/codex-acp\b/u.test(agentCommand);
54
56
  }
55
57
  //#endregion
58
+ //#region src/session/export.ts
59
+ var SessionExportError = class extends AcpxOperationalError {
60
+ code;
61
+ exitCode = 2;
62
+ constructor(message, code) {
63
+ super(message, {
64
+ outputCode: "USAGE",
65
+ detailCode: code,
66
+ origin: "cli"
67
+ });
68
+ this.code = code;
69
+ }
70
+ };
71
+ function sessionLookupError(message, code) {
72
+ return new SessionExportError(message, code);
73
+ }
74
+ async function loadSessionRecord(sessionLookup) {
75
+ const cwd = path.resolve(sessionLookup.cwd ?? process.cwd());
76
+ const name = normalizeName(sessionLookup.name);
77
+ if (sessionLookup.agentCommand) {
78
+ const active = await findSession({
79
+ agentCommand: sessionLookup.agentCommand,
80
+ cwd,
81
+ name
82
+ });
83
+ if (active) return active;
84
+ return (await listSessions()).find((session) => {
85
+ if (session.agentCommand !== sessionLookup.agentCommand || session.cwd !== cwd) return false;
86
+ if (name == null) return session.name == null;
87
+ return session.name === name;
88
+ });
89
+ }
90
+ const matches = (await listSessions()).filter((session) => {
91
+ if (session.cwd !== cwd) return false;
92
+ if (name == null) return session.name == null;
93
+ return session.name === name;
94
+ });
95
+ if (matches.length > 1) throw sessionLookupError("multiple sessions match export lookup", "ambiguous-session");
96
+ return matches[0];
97
+ }
98
+ function parseEventLockPayload(raw) {
99
+ try {
100
+ const parsed = JSON.parse(raw);
101
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
102
+ const record = parsed;
103
+ return { pid: typeof record.pid === "number" ? record.pid : void 0 };
104
+ } catch {
105
+ return {};
106
+ }
107
+ }
108
+ async function hasLiveEventLock(recordId) {
109
+ try {
110
+ return isProcessAlive(parseEventLockPayload(await fs$1.readFile(sessionEventLockPath(recordId), "utf8")).pid);
111
+ } catch (error) {
112
+ if (error.code === "ENOENT") return false;
113
+ throw error;
114
+ }
115
+ }
116
+ async function isSessionActive(record) {
117
+ if (record.closed) return false;
118
+ return isProcessAlive(record.pid) || await hasLiveEventLock(record.acpxRecordId);
119
+ }
120
+ async function readHistoryFile(filePath) {
121
+ const payload = await fs$1.readFile(filePath, "utf8").catch((error) => {
122
+ if (error.code === "ENOENT") return "";
123
+ throw error;
124
+ });
125
+ const history = [];
126
+ for (const line of payload.split("\n").filter((entry) => entry.trim().length > 0)) try {
127
+ const parsed = JSON.parse(line);
128
+ if (isAcpJsonRpcMessage(parsed)) history.push(parsed);
129
+ } catch {}
130
+ return history;
131
+ }
132
+ async function readSessionHistory(record) {
133
+ const history = [];
134
+ const maxSegments = Number.isInteger(record.eventLog.max_segments) ? record.eventLog.max_segments : 0;
135
+ for (let segment = maxSegments; segment >= 1; segment -= 1) history.push(...await readHistoryFile(sessionEventSegmentPath(record.acpxRecordId, segment)));
136
+ history.push(...await readHistoryFile(sessionEventActivePath(record.acpxRecordId)));
137
+ return history;
138
+ }
139
+ function cwdRelativeToHome(cwd, home) {
140
+ const relative = path.relative(home, cwd);
141
+ if (relative.length === 0) return ".";
142
+ if (relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative)) return relative;
143
+ return cwd;
144
+ }
145
+ function serializeSessionRecordForArchive(record, cwdRelative) {
146
+ const state = serializeSessionRecordForDisk(record);
147
+ state.cwd = cwdRelative;
148
+ if (state.event_log && typeof state.event_log === "object" && !Array.isArray(state.event_log)) state.event_log = {
149
+ ...state.event_log,
150
+ active_path: ".stream.ndjson"
151
+ };
152
+ return state;
153
+ }
154
+ async function exportSession(sessionLookup, outputPath) {
155
+ const record = await loadSessionRecord(sessionLookup);
156
+ if (!record) throw sessionLookupError("session not found", "not-found");
157
+ if (await isSessionActive(record)) throw sessionLookupError("session is currently locked by a running queue owner; close it first with `acpx sessions close`", "session-locked");
158
+ const home = os.homedir();
159
+ const cwdRelative = cwdRelativeToHome(record.cwd, home);
160
+ const exported = {
161
+ format_version: 1,
162
+ exported_at: (/* @__PURE__ */ new Date()).toISOString(),
163
+ exported_by: "acpx",
164
+ session: {
165
+ record_id: record.acpxRecordId,
166
+ name: record.name ?? null,
167
+ agent: record.agentCommand,
168
+ agent_name: normalizeAgentName(sessionLookup.agentName),
169
+ cwd_relative: cwdRelative,
170
+ cwd_original: cwdRelative,
171
+ created_at: record.createdAt,
172
+ updated_at: record.lastUsedAt,
173
+ state: serializeSessionRecordForArchive(record, cwdRelative)
174
+ },
175
+ history: await readSessionHistory(record)
176
+ };
177
+ await fs$1.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
178
+ await fs$1.writeFile(outputPath, `${JSON.stringify(exported, null, 2)}\n`, "utf8");
179
+ }
180
+ function normalizeAgentName(agentName) {
181
+ const normalized = agentName?.trim().toLowerCase();
182
+ if (!normalized || normalized.length === 0) return;
183
+ return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
184
+ }
185
+ //#endregion
186
+ //#region src/session/import.ts
187
+ const SUPPORTED_FORMAT_VERSION = 1;
188
+ const exportedSessionSchema = z.object({
189
+ format_version: z.literal(SUPPORTED_FORMAT_VERSION),
190
+ exported_at: z.string(),
191
+ exported_by: z.string(),
192
+ session: z.object({
193
+ record_id: z.string(),
194
+ name: z.string().nullable(),
195
+ agent: z.string(),
196
+ agent_name: z.string().optional(),
197
+ cwd_relative: z.string(),
198
+ cwd_original: z.string().optional(),
199
+ cwd_absolute_original: z.string().optional(),
200
+ created_at: z.string(),
201
+ updated_at: z.string(),
202
+ state: z.unknown()
203
+ }),
204
+ history: z.array(z.unknown())
205
+ });
206
+ var SessionImportError = class extends AcpxOperationalError {
207
+ code;
208
+ exitCode = 2;
209
+ constructor(message, code) {
210
+ super(message, {
211
+ outputCode: "USAGE",
212
+ detailCode: code,
213
+ origin: "cli"
214
+ });
215
+ this.code = code;
216
+ }
217
+ };
218
+ function importError(message, code) {
219
+ return new SessionImportError(message, code);
220
+ }
221
+ function parseArchiveJson(raw) {
222
+ try {
223
+ return JSON.parse(raw);
224
+ } catch (error) {
225
+ throw importError(`Invalid session export archive JSON: ${error instanceof Error ? error.message : String(error)}`, "invalid-archive");
226
+ }
227
+ }
228
+ function assertSupportedFormatVersion(parsed) {
229
+ const record = parsed && typeof parsed === "object" ? parsed : {};
230
+ if (record.format_version !== SUPPORTED_FORMAT_VERSION) throw importError(`Unsupported session export format_version ${String(record.format_version)}; supported version is ${SUPPORTED_FORMAT_VERSION}`, "unsupported-format-version");
231
+ }
232
+ function parseArchive(raw) {
233
+ const parsed = parseArchiveJson(raw);
234
+ assertSupportedFormatVersion(parsed);
235
+ try {
236
+ return exportedSessionSchema.parse(parsed);
237
+ } catch (error) {
238
+ if (error instanceof ZodError) throw importError(`Invalid session export archive: ${error.issues[0]?.message}`, "invalid-archive");
239
+ throw error;
240
+ }
241
+ }
242
+ async function generateRecordId(sessionsDir) {
243
+ for (;;) {
244
+ const recordId = randomUUID();
245
+ const filePath = path.join(sessionsDir, `${encodeURIComponent(recordId)}.json`);
246
+ try {
247
+ await fs$1.access(filePath);
248
+ } catch (error) {
249
+ if (error.code === "ENOENT") return recordId;
250
+ throw error;
251
+ }
252
+ }
253
+ }
254
+ function resolveImportedCwd(cwdRelative, newCwd) {
255
+ if (newCwd) return path.resolve(newCwd);
256
+ if (path.isAbsolute(cwdRelative)) return cwdRelative;
257
+ return path.join(os.homedir(), cwdRelative);
258
+ }
259
+ function resolveImportedName(parsed, requestedName) {
260
+ return requestedName ?? parsed.session.name ?? void 0;
261
+ }
262
+ function assertExpectedAgentCommand(parsed, sourceRecord, options) {
263
+ const expectedAgentCommand = options.expectedAgentCommand;
264
+ if (!expectedAgentCommand) return;
265
+ const expectedAgentName = normalizeAgentIdentity(options.expectedAgentName);
266
+ const archiveAgentName = normalizeAgentIdentity(parsed.session.agent_name);
267
+ const archiveCommandMatches = agentCommandMatchesExpected(parsed.session.agent, expectedAgentCommand, expectedAgentName);
268
+ const stateCommandMatches = agentCommandMatchesExpected(sourceRecord.agentCommand, expectedAgentCommand, expectedAgentName);
269
+ if (archiveCommandMatches && stateCommandMatches && archiveAgentNameMatches({
270
+ archiveAgentName,
271
+ expectedAgentName,
272
+ archiveCommand: parsed.session.agent,
273
+ stateCommand: sourceRecord.agentCommand,
274
+ expectedAgentCommand
275
+ })) {
276
+ sourceRecord.agentCommand = expectedAgentCommand;
277
+ return;
278
+ }
279
+ throw importError("Session export archive agent does not match the requested agent", "agent-mismatch");
280
+ }
281
+ function archiveAgentNameMatches(params) {
282
+ if (params.archiveCommand === params.expectedAgentCommand && params.stateCommand === params.expectedAgentCommand) return true;
283
+ return params.archiveAgentName == null || params.expectedAgentName == null || params.archiveAgentName === params.expectedAgentName;
284
+ }
285
+ function normalizeAgentIdentity(agentName) {
286
+ const normalized = agentName?.trim().toLowerCase();
287
+ if (!normalized || normalized.length === 0) return;
288
+ return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
289
+ }
290
+ function agentCommandMatchesExpected(archivedCommand, expectedAgentCommand, expectedAgentName) {
291
+ if (archivedCommand === expectedAgentCommand) return true;
292
+ return expectedAgentName ? commandLooksLikeBuiltInAgent(archivedCommand, expectedAgentName) : false;
293
+ }
294
+ function commandLooksLikeBuiltInAgent(command, agentName) {
295
+ const normalized = command.trim();
296
+ switch (agentName) {
297
+ case "pi": return /(?:^|\s)pi-acp(?:@|\s|$)/.test(normalized);
298
+ case "codex": return /(?:^|\s)@agentclientprotocol\/codex-acp(?:@|\s|$)/.test(normalized);
299
+ case "claude": return /(?:^|\s)@agentclientprotocol\/claude-agent-acp(?:@|\s|$)/.test(normalized);
300
+ default: return false;
301
+ }
302
+ }
303
+ async function assertDestinationScopeAvailable(record) {
304
+ if (!await findSession({
305
+ agentCommand: record.agentCommand,
306
+ cwd: record.cwd,
307
+ name: record.name
308
+ })) return;
309
+ throw importError("A session already exists for the import destination scope; pass --name or --cwd to import a separate copy", "session-scope-exists");
310
+ }
311
+ async function assertProviderSessionAvailable(record) {
312
+ if (!(await listSessions()).find((session) => session.acpSessionId === record.acpSessionId)) return;
313
+ throw importError("A local session already uses this provider session id; prune or remove the existing record before importing this archive", "session-provider-exists");
314
+ }
315
+ function buildImportedRecord(parsed, sourceRecord, options) {
316
+ const eventLog = {
317
+ ...defaultSessionEventLog(options.newRecordId),
318
+ max_segment_bytes: sourceRecord.eventLog.max_segment_bytes,
319
+ max_segments: sourceRecord.eventLog.max_segments,
320
+ segment_count: parsed.history.length > 0 ? 1 : sourceRecord.eventLog.segment_count
321
+ };
322
+ return {
323
+ ...sourceRecord,
324
+ acpxRecordId: options.newRecordId,
325
+ cwd: options.cwd,
326
+ name: resolveImportedName(parsed, options.name),
327
+ closed: false,
328
+ closedAt: void 0,
329
+ pid: void 0,
330
+ agentStartedAt: void 0,
331
+ lastAgentExitCode: void 0,
332
+ lastAgentExitSignal: void 0,
333
+ lastAgentExitAt: void 0,
334
+ lastAgentDisconnectReason: void 0,
335
+ eventLog,
336
+ importedFrom: {
337
+ recordId: parsed.session.record_id,
338
+ cwdOriginal: parsed.session.cwd_original ?? parsed.session.cwd_relative,
339
+ exportedBy: parsed.exported_by,
340
+ exportedAt: parsed.exported_at
341
+ }
342
+ };
343
+ }
344
+ async function importSession(archivePath, options = {}) {
345
+ const parsed = parseArchive(await fs$1.readFile(archivePath, "utf8"));
346
+ const sourceRecord = parseSessionRecord(parsed.session.state);
347
+ if (!sourceRecord) throw importError("Invalid session export archive: session.state is not a session record", "invalid-archive");
348
+ assertExpectedAgentCommand(parsed, sourceRecord, options);
349
+ const sessionsDir = path.join(os.homedir(), ".acpx", "sessions");
350
+ await fs$1.mkdir(sessionsDir, { recursive: true });
351
+ const cwd = resolveImportedCwd(parsed.session.cwd_relative, options.newCwd);
352
+ const newRecordId = await generateRecordId(sessionsDir);
353
+ const newRecord = buildImportedRecord(parsed, sourceRecord, {
354
+ newRecordId,
355
+ cwd,
356
+ name: options.name
357
+ });
358
+ await assertDestinationScopeAvailable(newRecord);
359
+ await assertProviderSessionAvailable(newRecord);
360
+ await writeSessionRecord(newRecord);
361
+ if (parsed.history.length > 0) {
362
+ const history = parsed.history;
363
+ await fs$1.writeFile(sessionEventActivePath(newRecordId), `${history.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf8");
364
+ }
365
+ return {
366
+ record_id: newRecordId,
367
+ cwd
368
+ };
369
+ }
370
+ //#endregion
56
371
  //#region src/cli/output/json-output.ts
57
372
  function emitJsonResult(format, payload) {
58
373
  if (format !== "json") return false;
@@ -71,11 +386,11 @@ let sessionModulePromise;
71
386
  let outputModulePromise;
72
387
  let outputRenderModulePromise;
73
388
  function loadSessionModule() {
74
- sessionModulePromise ??= import("./output-DPg20dvn.js").then((n) => n.i);
389
+ sessionModulePromise ??= import("./output-Di77Yugq.js").then((n) => n.i);
75
390
  return sessionModulePromise;
76
391
  }
77
392
  function loadOutputModule() {
78
- outputModulePromise ??= import("./output-DPg20dvn.js").then((n) => n.r);
393
+ outputModulePromise ??= import("./output-Di77Yugq.js").then((n) => n.r);
79
394
  return outputModulePromise;
80
395
  }
81
396
  function loadOutputRenderModule() {
@@ -618,6 +933,46 @@ async function handleSessionsHistory(explicitAgentName, sessionName, flags, comm
618
933
  const globalFlags = resolveGlobalFlags(command, config);
619
934
  printSessionHistoryByFormat(await findScopedSessionOrThrow(resolveAgentInvocation(explicitAgentName, globalFlags, config), sessionName), flags.limit, globalFlags.format);
620
935
  }
936
+ async function handleSessionsExport(explicitAgentName, sessionName, flags, command, config) {
937
+ const globalFlags = resolveGlobalFlags(command, config);
938
+ const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
939
+ const cwd = flags.sourceCwd ? path.resolve(agent.cwd, flags.sourceCwd) : agent.cwd;
940
+ await exportSession({
941
+ agentName: globalFlags.agent ? void 0 : agent.agentName,
942
+ agentCommand: agent.agentCommand,
943
+ cwd,
944
+ name: sessionName
945
+ }, flags.output);
946
+ if (emitJsonResult(globalFlags.format, {
947
+ action: "session_exported",
948
+ output: flags.output
949
+ })) return;
950
+ if (globalFlags.format === "quiet") {
951
+ process.stdout.write(`${flags.output}\n`);
952
+ return;
953
+ }
954
+ process.stdout.write(`exported session to ${flags.output}\n`);
955
+ }
956
+ async function handleSessionsImport(explicitAgentName, archivePath, flags, command, config) {
957
+ const globalFlags = resolveGlobalFlags(command, config);
958
+ const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
959
+ const result = await importSession(archivePath, {
960
+ name: flags.name,
961
+ newCwd: flags.destinationCwd ? path.resolve(globalFlags.cwd, flags.destinationCwd) : void 0,
962
+ expectedAgentName: globalFlags.agent ? void 0 : agent.agentName,
963
+ expectedAgentCommand: agent.agentCommand
964
+ });
965
+ if (emitJsonResult(globalFlags.format, {
966
+ action: "session_imported",
967
+ record_id: result.record_id,
968
+ cwd: result.cwd
969
+ })) return;
970
+ if (globalFlags.format === "quiet") {
971
+ process.stdout.write(`${result.record_id}\n`);
972
+ return;
973
+ }
974
+ process.stdout.write(`imported session ${result.record_id} at ${result.cwd}\n`);
975
+ }
621
976
  async function handleSessionsPrune(explicitAgentName, flags, command, config) {
622
977
  const globalFlags = resolveGlobalFlags(command, config);
623
978
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
@@ -802,7 +1157,7 @@ function parseOutputFormat(value, sourcePath) {
802
1157
  function parseDefaultAgent(value, sourcePath) {
803
1158
  if (value == null) return;
804
1159
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`Invalid config defaultAgent in ${sourcePath}: expected non-empty string`);
805
- return normalizeAgentName(value);
1160
+ return normalizeAgentName$1(value);
806
1161
  }
807
1162
  function parseAgents(value, sourcePath) {
808
1163
  if (value == null) return;
@@ -813,7 +1168,7 @@ function parseAgents(value, sourcePath) {
813
1168
  const command = raw.command;
814
1169
  if (typeof command !== "string" || command.trim().length === 0) throw new Error(`Invalid config agents.${name}.command in ${sourcePath}: expected non-empty string`);
815
1170
  const args = parseAgentArgs(raw.args, name, sourcePath);
816
- parsed[normalizeAgentName(name)] = args.length > 0 ? `${command.trim()} ${args.map(quoteCommandArg).join(" ")}` : command.trim();
1171
+ parsed[normalizeAgentName$1(name)] = args.length > 0 ? `${command.trim()} ${args.map(quoteCommandArg).join(" ")}` : command.trim();
817
1172
  }
818
1173
  return parsed;
819
1174
  }
@@ -1409,6 +1764,16 @@ function registerStatusCommand(parent, explicitAgentName, config, description) {
1409
1764
  }
1410
1765
  //#endregion
1411
1766
  //#region src/cli/command-registration.ts
1767
+ var LocalAttributeOption = class extends Option {
1768
+ localAttributeName;
1769
+ constructor(flags, description, localAttributeName) {
1770
+ super(flags, description);
1771
+ this.localAttributeName = localAttributeName;
1772
+ }
1773
+ attributeName() {
1774
+ return this.localAttributeName;
1775
+ }
1776
+ };
1412
1777
  function addSessionsListOptions(command) {
1413
1778
  return command.option("--local", "List local acpx session records instead of agent protocol sessions").option("--cursor <cursor>", "Opaque ACP session/list cursor", (value) => parseNonEmptyValue("Cursor", value)).option("--filter-cwd <dir>", "Filter agent sessions by working directory", (value) => parseNonEmptyValue("Filter cwd", value));
1414
1779
  }
@@ -1439,6 +1804,12 @@ function registerSessionsCommand(parent, explicitAgentName, config) {
1439
1804
  sessionsCommand.command("read").description("Read full session history").argument("[name]", "Session name", parseSessionName).option("--tail <count>", "Show only the last N entries instead of all history", parseHistoryLimit).action(async function(name, flags) {
1440
1805
  await handleSessionsHistory(explicitAgentName, name, { limit: flags.tail ?? 0 }, this, config);
1441
1806
  });
1807
+ sessionsCommand.command("export").description("Export a portable session archive").argument("[name]", "Session name", parseSessionName).requiredOption("--output <path>", "Output archive path", (value) => parseNonEmptyValue("Output path", value)).addOption(new LocalAttributeOption("--cwd <cwd>", "Session cwd to export", "sourceCwd").argParser((value) => parseNonEmptyValue("Session cwd", value))).action(async function(name, flags) {
1808
+ await handleSessionsExport(explicitAgentName, name, flags, this, config);
1809
+ });
1810
+ sessionsCommand.command("import").description("Import a portable session archive").argument("<archive-path>", "Archive path", (value) => parseNonEmptyValue("Archive path", value)).option("--name <name>", "Imported session name", parseSessionName).addOption(new LocalAttributeOption("--cwd <cwd>", "Imported session cwd", "destinationCwd").argParser((value) => parseNonEmptyValue("Imported session cwd", value))).action(async function(archivePath, flags) {
1811
+ await handleSessionsImport(explicitAgentName, archivePath, flags, this, config);
1812
+ });
1442
1813
  sessionsCommand.command("prune").description("Delete closed sessions and free disk space").option("--dry-run", "Preview what would be pruned without deleting anything").option("--before <date>", "Prune sessions closed before this date", parsePruneBeforeDate).option("--older-than <days>", "Prune sessions closed more than N days ago", parseDaysOlderThan).option("--include-history", "Also delete event stream files (.stream.ndjson)").action(async function(flags) {
1443
1814
  await handleSessionsPrune(explicitAgentName, flags, this, config);
1444
1815
  });
@@ -1491,7 +1862,7 @@ function registerAgentCommand(program, agentName, config) {
1491
1862
  }
1492
1863
  function registerFlowCommand(program, config) {
1493
1864
  program.command("flow").description("Run multi-step ACP workflows from flow files").command("run").description("Run a flow file").argument("<file>", "Flow module path").option("--input-json <json>", "Flow input as JSON").option("--input-file <path>", "Read flow input JSON from file").option("--default-agent <name>", "Default agent profile for ACP nodes without profile", (value) => parseNonEmptyValue("Default agent", value)).action(async function(file, flags) {
1494
- const { handleFlowRun } = await import("./cli-Bf3yjqzE.js");
1865
+ const { handleFlowRun } = await import("./cli-8dP_TqBp.js");
1495
1866
  await handleFlowRun(file, flags, this, config);
1496
1867
  });
1497
1868
  }
@@ -1872,13 +2243,18 @@ async function handleProgramParseError(error, requestedOutputPolicy) {
1872
2243
  process.exit(exitCodeForOutputErrorCode(normalized.code));
1873
2244
  }
1874
2245
  async function main(argv = process.argv) {
1875
- const rawArgs = argv.slice(2);
2246
+ const rawArgs = normalizeLifecycleScriptArgs(argv.slice(2));
2247
+ const normalizedArgv = [
2248
+ argv[0] ?? "node",
2249
+ argv[1] ?? "acpx",
2250
+ ...rawArgs
2251
+ ];
1876
2252
  if (await handleQueueOwnerCommand(argv)) return;
1877
2253
  if (isTopLevelVersionRequest(rawArgs)) {
1878
2254
  process.stdout.write(`${getAcpxVersion()}\n`);
1879
2255
  return;
1880
2256
  }
1881
- await maybeHandleSkillflag(argv);
2257
+ await maybeHandleSkillflag(normalizedArgv);
1882
2258
  const config = await loadResolvedConfig(detectInitialCwd(rawArgs));
1883
2259
  const requestedJsonStrict = detectJsonStrict(rawArgs);
1884
2260
  const requestedOutputPolicy = {
@@ -1907,7 +2283,7 @@ async function main(argv = process.argv) {
1907
2283
  try {
1908
2284
  await runWithOutputPolicy(requestedOutputPolicy, async () => {
1909
2285
  try {
1910
- await program.parseAsync(argv);
2286
+ await program.parseAsync(normalizedArgv);
1911
2287
  } catch (error) {
1912
2288
  await handleProgramParseError(error, requestedOutputPolicy);
1913
2289
  }
@@ -1916,6 +2292,10 @@ async function main(argv = process.argv) {
1916
2292
  flushPerfMetricsCapture();
1917
2293
  }
1918
2294
  }
2295
+ function normalizeLifecycleScriptArgs(rawArgs) {
2296
+ if (rawArgs[0] === "--" && (process.env.npm_lifecycle_event || process.env.npm_lifecycle_script)) return rawArgs.slice(1);
2297
+ return rawArgs;
2298
+ }
1919
2299
  //#endregion
1920
2300
  //#region src/cli.ts
1921
2301
  function installBrokenPipeHandler(stream) {