@actiondock/cli 2.6.0 → 2.8.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.
Files changed (66) hide show
  1. package/README.md +34 -22
  2. package/bin/ad.js +4 -4
  3. package/dist/commands/action/create.js +9 -25
  4. package/dist/commands/add.js +9 -17
  5. package/dist/commands/build.js +3 -18
  6. package/dist/commands/config/delete.js +8 -7
  7. package/dist/commands/config/env.js +7 -11
  8. package/dist/commands/config/get.js +8 -5
  9. package/dist/commands/config/list.js +16 -22
  10. package/dist/commands/config/schema.d.ts +7 -1
  11. package/dist/commands/config/schema.js +44 -67
  12. package/dist/commands/config/set.js +9 -7
  13. package/dist/commands/describe.js +15 -26
  14. package/dist/commands/doctor.js +4 -3
  15. package/dist/commands/export.js +5 -13
  16. package/dist/commands/generate.js +4 -3
  17. package/dist/commands/info.js +3 -1
  18. package/dist/commands/init.js +3 -3
  19. package/dist/commands/link.js +4 -4
  20. package/dist/commands/list.js +19 -10
  21. package/dist/commands/mcp.js +27 -66
  22. package/dist/commands/pack.js +3 -12
  23. package/dist/commands/playbook.js +30 -10
  24. package/dist/commands/profile.js +10 -19
  25. package/dist/commands/remove.js +5 -14
  26. package/dist/commands/run.d.ts +6 -2
  27. package/dist/commands/run.js +60 -37
  28. package/dist/commands/runs.js +26 -35
  29. package/dist/commands/serve.js +66 -44
  30. package/dist/commands/state-list.d.ts +2 -2
  31. package/dist/commands/state-list.js +10 -8
  32. package/dist/commands/state.d.ts +2 -2
  33. package/dist/commands/state.js +110 -103
  34. package/dist/commands/test.js +1 -1
  35. package/dist/commands/validate.js +4 -17
  36. package/dist/errors.d.ts +14 -0
  37. package/dist/errors.js +78 -2
  38. package/dist/index.d.ts +8 -1
  39. package/dist/index.js +60 -8
  40. package/dist/renderer.d.ts +3 -2
  41. package/dist/renderer.js +4 -19
  42. package/dist/services/config-merge.d.ts +6 -5
  43. package/dist/services/config-merge.js +10 -8
  44. package/dist/services/inspect.js +3 -1
  45. package/dist/standalone/index.d.ts +7 -11
  46. package/dist/standalone/index.js +8 -17
  47. package/dist/types.d.ts +7 -1
  48. package/dist/types.js +1 -1
  49. package/dist/utils/cert.js +1 -1
  50. package/dist/utils/duration.d.ts +1 -1
  51. package/dist/utils/duration.js +1 -1
  52. package/dist/utils/index.d.ts +1 -2
  53. package/dist/utils/index.js +1 -2
  54. package/dist/utils/input.d.ts +5 -42
  55. package/dist/utils/input.js +7 -84
  56. package/dist/utils/server-launch.d.ts +61 -0
  57. package/dist/utils/server-launch.js +114 -0
  58. package/dist/utils/target.d.ts +14 -13
  59. package/dist/utils/target.js +20 -39
  60. package/dist/utils/utils.d.ts +101 -1
  61. package/dist/utils/utils.js +97 -7
  62. package/package.json +5 -6
  63. package/dist/utils/bytes.d.ts +0 -1
  64. package/dist/utils/bytes.js +0 -1
  65. package/dist/utils/filter.d.ts +0 -1
  66. package/dist/utils/filter.js +0 -1
package/dist/renderer.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { formatActionDetail, } from "@actiondock/core/project";
1
2
  import { formatError } from "./errors.js";
2
3
  /**
3
4
  * 构造标准成功结果信封。
@@ -254,27 +255,11 @@ export function renderActionList(items, title = "Actions", isFallback = false, i
254
255
  return lines.join("\n");
255
256
  }
256
257
  /**
257
- * 格式化渲染 Action 详情。
258
+ * 格式化渲染 Action 详情(编码顾问 Encoding Advisor)。
259
+ * 统一复用 core 下沉的 formatActionDetail 实现,与独立分发器保持完全一致。
258
260
  */
259
261
  export function renderActionDetail(action) {
260
- const lines = [];
261
- lines.push(`Action: ${action.id}`);
262
- if (action.packageId) {
263
- const rootDesc = action.projectRoot ? ` (${action.projectRoot})` : "";
264
- lines.push(`Package: ${action.packageId}${rootDesc}`);
265
- }
266
- if (action.description) {
267
- lines.push(`Description: ${action.description}`);
268
- }
269
- if (action.inputSchema) {
270
- lines.push("\nInput Schema:");
271
- lines.push(JSON.stringify(action.inputSchema, null, 2));
272
- }
273
- if (action.outputSchema) {
274
- lines.push("\nOutput Schema:");
275
- lines.push(JSON.stringify(action.outputSchema, null, 2));
276
- }
277
- return lines.join("\n");
262
+ return formatActionDetail(action);
278
263
  }
279
264
  /**
280
265
  * 格式化渲染 Action 校验结果。
@@ -1,4 +1,5 @@
1
- import { type ActionDockTarget, type ConfigItemDefinition } from "@actiondock/core";
1
+ import { type ConfigItemDefinition } from "@actiondock/core/project";
2
+ import { type ActionDockService } from "@actiondock/core";
2
3
  /**
3
4
  * 配置合并视图条目:跨作用域合并后的键值展示结构。
4
5
  * source 标签语义:project(包级持久化)> global(全局持久化)> env(环境变量)> default(声明默认值)。
@@ -19,10 +20,10 @@ export interface MergedConfigEntry {
19
20
  * @param key 配置键名
20
21
  * @param packageId 包标识(用于包级作用域寻址与环境变量解析)
21
22
  * @param declaredItem 工程声明的配置项定义
22
- * @param target 已创建的 Target 门面实例
23
+ * @param service 已创建的 Service 服务实例
23
24
  * @param reveal 是否揭示敏感值明文(为假且敏感键时打码)
24
25
  */
25
- export declare function resolveMergedConfigEntry(key: string, packageId: string, declaredItem: ConfigItemDefinition | undefined, target: ActionDockTarget, reveal: boolean): Promise<{
26
+ export declare function resolveMergedConfigEntry(key: string, packageId: string, declaredItem: ConfigItemDefinition | undefined, service: ActionDockService, reveal: boolean): Promise<{
26
27
  value: unknown;
27
28
  source: MergedConfigEntry["source"];
28
29
  secret: boolean;
@@ -35,7 +36,7 @@ export declare function resolveMergedConfigEntry(key: string, packageId: string,
35
36
  * 消除 config get 与 config list 两命令间的合并实现差异。
36
37
  *
37
38
  * @param root 工程根目录
38
- * @param target 已创建的 Target 门面实例
39
+ * @param service 已创建的 Service 服务实例
39
40
  * @param reveal 是否揭示敏感值明文
40
41
  */
41
- export declare function buildMergedConfigEntries(root: string, target: ActionDockTarget, reveal: boolean): Promise<MergedConfigEntry[]>;
42
+ export declare function buildMergedConfigEntries(root: string, service: ActionDockService, reveal: boolean): Promise<MergedConfigEntry[]>;
@@ -1,4 +1,6 @@
1
- import { isSecretConfigKey, loadProjectConfig, maskSecretValue, resolveEnvValue, } from "@actiondock/core";
1
+ import { isSecretConfigKey, maskSecretValue, } from "@actiondock/core/project";
2
+ import { loadProjectConfig, } from "@actiondock/core";
3
+ import { resolveEnvValue, } from "@actiondock/core/profile";
2
4
  /**
3
5
  * 解析单个配置键的合并视图(项目包级 > 全局持久化 > 环境变量 > 声明默认值)。
4
6
  *
@@ -8,11 +10,11 @@ import { isSecretConfigKey, loadProjectConfig, maskSecretValue, resolveEnvValue,
8
10
  * @param key 配置键名
9
11
  * @param packageId 包标识(用于包级作用域寻址与环境变量解析)
10
12
  * @param declaredItem 工程声明的配置项定义
11
- * @param target 已创建的 Target 门面实例
13
+ * @param service 已创建的 Service 服务实例
12
14
  * @param reveal 是否揭示敏感值明文(为假且敏感键时打码)
13
15
  */
14
- export async function resolveMergedConfigEntry(key, packageId, declaredItem, target, reveal) {
15
- const confView = await target.getConfig(packageId, key);
16
+ export async function resolveMergedConfigEntry(key, packageId, declaredItem, service, reveal) {
17
+ const confView = await service.management?.config.get(packageId, key);
16
18
  const envResolved = resolveEnvValue(key, declaredItem, packageId);
17
19
  let rawValue;
18
20
  let source;
@@ -40,14 +42,14 @@ export async function resolveMergedConfigEntry(key, packageId, declaredItem, tar
40
42
  * 消除 config get 与 config list 两命令间的合并实现差异。
41
43
  *
42
44
  * @param root 工程根目录
43
- * @param target 已创建的 Target 门面实例
45
+ * @param service 已创建的 Service 服务实例
44
46
  * @param reveal 是否揭示敏感值明文
45
47
  */
46
- export async function buildMergedConfigEntries(root, target, reveal) {
48
+ export async function buildMergedConfigEntries(root, service, reveal) {
47
49
  const projConfig = loadProjectConfig(root);
48
50
  const declared = projConfig.config || {};
49
- const projectConfigList = await target.listConfig(projConfig.id);
50
- const globalConfigList = await target.listConfig("global");
51
+ const projectConfigList = (await service.management?.config.list(projConfig.id)) ?? [];
52
+ const globalConfigList = (await service.management?.config.list("global")) ?? [];
51
53
  const projectConfigMap = new Map(projectConfigList.map((c) => [c.key, c]));
52
54
  const globalConfigMap = new Map(globalConfigList.map((c) => [c.key, c]));
53
55
  const allKeys = new Set([
@@ -1,5 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
- import { findProjectRoot, listLinkedPackages, loadManifest, loadPlaybooks, loadProjectConfig, } from "@actiondock/core";
2
+ import { findProjectRoot, loadProjectConfig, } from "@actiondock/core";
3
+ import { listLinkedPackages, } from "@actiondock/core/registry";
4
+ import { loadManifest, loadPlaybooks, } from "@actiondock/core/project";
3
5
  /**
4
6
  * 装配单个工程的详情自省信息(清单、动作映射与 Playbook 映射)。
5
7
  *
@@ -1,16 +1,12 @@
1
- import { StandaloneDispatcher as CoreStandaloneDispatcher } from "@actiondock/core";
1
+ import { type InvocationControl } from "@actiondock/core/server";
2
2
  import type { StandaloneOptions } from "../types.js";
3
+ export { type InvocationControl };
3
4
  /**
4
- * 独立二进制轻量参数解析分发器(委托 Core 统一实现,消除重复逻辑)。
5
+ * 运行独立二进制参数解析分发器并返回退出状态码(委托 Core 统一实现)。
5
6
  */
6
- export declare class StandaloneDispatcher extends CoreStandaloneDispatcher {
7
- constructor(options: StandaloneOptions);
8
- }
7
+ export declare function runStandaloneCli(argv: string[] | undefined, options: StandaloneOptions, control?: InvocationControl): Promise<number>;
9
8
  /**
10
- * 运行独立二进制参数解析分发器并返回退出状态码。
9
+ * 独立二进制进程入口适配器(事实源位于 core 包,此处 re-export 维持既有导入路径兼容)。
10
+ * 仅在命令行可执行入口调用,负责全局 SIGINT 监听与退出状态码写入。
11
11
  */
12
- export declare function runStandaloneCli(argv: string[] | undefined, options: StandaloneOptions): Promise<number>;
13
- /**
14
- * 创建独立二进制运行时分发器实例。
15
- */
16
- export declare function createStandaloneDispatcher(options: StandaloneOptions): StandaloneDispatcher;
12
+ export { runStandaloneProcess } from "@actiondock/core/server";
@@ -1,22 +1,13 @@
1
- import { StandaloneDispatcher as CoreStandaloneDispatcher, } from "@actiondock/core";
1
+ import { StandaloneDispatcher as CoreStandaloneDispatcher, } from "@actiondock/core/server";
2
2
  /**
3
- * 独立二进制轻量参数解析分发器(委托 Core 统一实现,消除重复逻辑)。
3
+ * 运行独立二进制参数解析分发器并返回退出状态码(委托 Core 统一实现)。
4
4
  */
5
- export class StandaloneDispatcher extends CoreStandaloneDispatcher {
6
- constructor(options) {
7
- super(options);
8
- }
5
+ export async function runStandaloneCli(argv = process.argv.slice(2), options, control) {
6
+ const dispatcher = new CoreStandaloneDispatcher(options);
7
+ return dispatcher.dispatch(argv, control);
9
8
  }
10
9
  /**
11
- * 运行独立二进制参数解析分发器并返回退出状态码。
10
+ * 独立二进制进程入口适配器(事实源位于 core 包,此处 re-export 维持既有导入路径兼容)。
11
+ * 仅在命令行可执行入口调用,负责全局 SIGINT 监听与退出状态码写入。
12
12
  */
13
- export async function runStandaloneCli(argv = process.argv.slice(2), options) {
14
- const dispatcher = new StandaloneDispatcher(options);
15
- return dispatcher.dispatch(argv);
16
- }
17
- /**
18
- * 创建独立二进制运行时分发器实例。
19
- */
20
- export function createStandaloneDispatcher(options) {
21
- return new StandaloneDispatcher(options);
22
- }
13
+ export { runStandaloneProcess } from "@actiondock/core/server";
package/dist/types.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { ActionDefinition } from "@actiondock/sdk";
2
- import type { ConfigItemDefinition } from "@actiondock/core";
2
+ import type { ConfigItemDefinition } from "@actiondock/core/project";
3
+ import type { InvocationControl } from "@actiondock/core/server";
4
+ export type { InvocationControl };
3
5
  /**
4
6
  * 统一退出码定义(事实源位于 core 包 StandaloneDispatcher,此处仅 re-export 维持既有导入路径兼容)。
5
7
  * - SUCCESS: 执行成功(0)
@@ -78,6 +80,10 @@ export interface CliContext {
78
80
  secret?: boolean;
79
81
  mask?: string;
80
82
  }) => Promise<string>;
83
+ /** 可选的中断与取消控制契约 */
84
+ control?: InvocationControl;
85
+ /** 退出码收集器(避免污染全局 process.exitCode) */
86
+ exitCode?: number;
81
87
  }
82
88
  /**
83
89
  * 工程自省详情信息结构。
package/dist/types.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ExitCode as CoreExitCode } from "@actiondock/core";
1
+ import { ExitCode as CoreExitCode, } from "@actiondock/core/server";
2
2
  /**
3
3
  * 统一退出码定义(事实源位于 core 包 StandaloneDispatcher,此处仅 re-export 维持既有导入路径兼容)。
4
4
  * - SUCCESS: 执行成功(0)
@@ -3,7 +3,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSy
3
3
  import { isIP } from "node:net";
4
4
  import { hostname, networkInterfaces } from "node:os";
5
5
  import { join } from "node:path";
6
- import { getActionDockHome } from "@actiondock/core";
6
+ import { getActionDockHome, } from "@actiondock/core/registry";
7
7
  import { generate } from "selfsigned";
8
8
  /**
9
9
  * 判定字符串是否为有效 IPv4 或 IPv6 地址。
@@ -1 +1 @@
1
- export { parseDuration } from "@actiondock/core";
1
+ export { parseDuration } from "@actiondock/core/project";
@@ -1 +1 @@
1
- export { parseDuration } from "@actiondock/core";
1
+ export { parseDuration } from "@actiondock/core/project";
@@ -1,8 +1,7 @@
1
1
  export * from "./utils.js";
2
- export * from "./bytes.js";
3
2
  export * from "./duration.js";
4
- export * from "./filter.js";
5
3
  export * from "./target.js";
6
4
  export * from "./spawn.js";
7
5
  export * from "./cert.js";
8
6
  export * from "./input.js";
7
+ export * from "./server-launch.js";
@@ -1,8 +1,7 @@
1
1
  export * from "./utils.js";
2
- export * from "./bytes.js";
3
2
  export * from "./duration.js";
4
- export * from "./filter.js";
5
3
  export * from "./target.js";
6
4
  export * from "./spawn.js";
7
5
  export * from "./cert.js";
8
6
  export * from "./input.js";
7
+ export * from "./server-launch.js";
@@ -1,46 +1,9 @@
1
- import type { JsonValue } from "@actiondock/sdk";
2
- /**
3
- * 获取异常的标准描述信息。
4
- *
5
- * @param err 待提取消息的异常对象
6
- */
7
- export declare function getErrorMessage(err: unknown): string;
8
- /**
9
- * 剔除 UTF-8 文本起始处的 BOM 字节标记(\uFEFF)。
10
- *
11
- * @param text 原始输入文本
12
- */
13
- export declare function stripBom(text: string): string;
14
- /**
15
- * 统一解析 JSON 字符串为标准 JSON 值对象,并在失败时抛出结构化参数异常。
16
- *
17
- * @param text 待解析文本内容
18
- * @param source 输入源描述(如 "--input"、"stdin" 或文件物理路径)
19
- */
20
- export declare function parseJson(text: string, source: string): JsonValue;
1
+ import { stripBom } from "@actiondock/core/project";
2
+ export { resolveActionInput, type ResolveActionInputOptions, parseJson, InputError, FlatInputError, buildActionInputAdvice, formatActionDetail, } from "@actiondock/core/project";
3
+ export { stripBom };
21
4
  /**
22
5
  * 从标准输入流中完整读取全部数据并转换为 UTF-8 字符串。
23
- *
24
- * @param stream 标准输入可读流,默认为 process.stdin
6
+ * 底层委托 core 的有界读取实现:保持宽松 UTF-8 解码语义与 BOM 剥离行为,
7
+ * 同时获得默认 10MB 输入上限保护,超限时抛出结构化 INPUT_LIMIT_EXCEEDED 异常。
25
8
  */
26
9
  export declare function readStdin(stream?: NodeJS.ReadableStream): Promise<string>;
27
- /**
28
- * Action 执行输入解析选项。
29
- */
30
- export interface ResolveActionInputOptions {
31
- input?: string;
32
- inputFile?: string;
33
- stdin?: NodeJS.ReadableStream;
34
- }
35
- /**
36
- * 统一解析 Action 执行入参。
37
- *
38
- * 遵循确定性入参契约:
39
- * - 当未指定任何输入参数时,默认返回空对象 `{}`。
40
- * - 简单输入通过 `--input` 传递内联 JSON 字符串。
41
- * - 复杂输入或标准输入通过 `--input-file` 传递(`-` 表示标准输入 stdin)。
42
- * - 严禁同时指定 `--input` 与 `--input-file`,检测到冲突时明确抛出参数错误。
43
- *
44
- * @param options 输入解析选项
45
- */
46
- export declare function resolveActionInput(options: ResolveActionInputOptions): Promise<JsonValue>;
@@ -1,89 +1,12 @@
1
- import { readFile } from "node:fs/promises";
2
- import { ArgumentError } from "../errors.js";
3
- /**
4
- * 获取异常的标准描述信息。
5
- *
6
- * @param err 待提取消息的异常对象
7
- */
8
- export function getErrorMessage(err) {
9
- if (err instanceof Error) {
10
- return err.message;
11
- }
12
- return String(err);
13
- }
14
- /**
15
- * 剔除 UTF-8 文本起始处的 BOM 字节标记(\uFEFF)。
16
- *
17
- * @param text 原始输入文本
18
- */
19
- export function stripBom(text) {
20
- return text.replace(/^\uFEFF/, "");
21
- }
22
- /**
23
- * 统一解析 JSON 字符串为标准 JSON 值对象,并在失败时抛出结构化参数异常。
24
- *
25
- * @param text 待解析文本内容
26
- * @param source 输入源描述(如 "--input"、"stdin" 或文件物理路径)
27
- */
28
- export function parseJson(text, source) {
29
- try {
30
- return JSON.parse(stripBom(text));
31
- }
32
- catch (err) {
33
- throw new ArgumentError(`Invalid JSON input from ${source}: ${getErrorMessage(err)}`, undefined, "INVALID_JSON");
34
- }
35
- }
1
+ import { readStdinBounded, stripBom } from "@actiondock/core/project";
2
+ export { resolveActionInput, parseJson, InputError, FlatInputError, buildActionInputAdvice, formatActionDetail, } from "@actiondock/core/project";
3
+ // BOM 剥离单一事实源转引:core 已提供逐字一致的实现,此处仅保留导出面兼容。
4
+ export { stripBom };
36
5
  /**
37
6
  * 从标准输入流中完整读取全部数据并转换为 UTF-8 字符串。
38
- *
39
- * @param stream 标准输入可读流,默认为 process.stdin
7
+ * 底层委托 core 的有界读取实现:保持宽松 UTF-8 解码语义与 BOM 剥离行为,
8
+ * 同时获得默认 10MB 输入上限保护,超限时抛出结构化 INPUT_LIMIT_EXCEEDED 异常。
40
9
  */
41
10
  export async function readStdin(stream = process.stdin) {
42
- const chunks = [];
43
- for await (const chunk of stream) {
44
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
45
- }
46
- return Buffer.concat(chunks).toString("utf8");
47
- }
48
- /**
49
- * 统一解析 Action 执行入参。
50
- *
51
- * 遵循确定性入参契约:
52
- * - 当未指定任何输入参数时,默认返回空对象 `{}`。
53
- * - 简单输入通过 `--input` 传递内联 JSON 字符串。
54
- * - 复杂输入或标准输入通过 `--input-file` 传递(`-` 表示标准输入 stdin)。
55
- * - 严禁同时指定 `--input` 与 `--input-file`,检测到冲突时明确抛出参数错误。
56
- *
57
- * @param options 输入解析选项
58
- */
59
- export async function resolveActionInput(options) {
60
- if (options.input !== undefined && options.inputFile !== undefined) {
61
- throw new ArgumentError("--input and --input-file cannot be used together", undefined, "INPUT_CONFLICT");
62
- }
63
- if (options.input !== undefined) {
64
- return parseJson(options.input, "--input");
65
- }
66
- if (options.inputFile !== undefined) {
67
- let text;
68
- if (options.inputFile === "-") {
69
- try {
70
- text = await readStdin(options.stdin || process.stdin);
71
- }
72
- catch (err) {
73
- throw new ArgumentError(`Failed to read stdin: ${getErrorMessage(err)}`, undefined, "INPUT_FILE_READ_FAILED");
74
- }
75
- return parseJson(text, "stdin");
76
- }
77
- try {
78
- text = await readFile(options.inputFile, "utf8");
79
- }
80
- catch (err) {
81
- if (err && err.code === "ENOENT") {
82
- throw new ArgumentError(`Input file not found: ${options.inputFile}`, undefined, "INPUT_FILE_NOT_FOUND");
83
- }
84
- throw new ArgumentError(`Failed to read input file '${options.inputFile}': ${getErrorMessage(err)}`, undefined, "INPUT_FILE_READ_FAILED");
85
- }
86
- return parseJson(text, options.inputFile);
87
- }
88
- return {};
11
+ return stripBom(await readStdinBounded(stream, { strictUtf8: false }));
89
12
  }
@@ -0,0 +1,61 @@
1
+ import type { CliContext } from "../types.js";
2
+ /**
3
+ * 服务类命令(ad serve、ad mcp serve、ad mcp、ad run)的共享启动脚手架(单一事实源)。
4
+ *
5
+ * 收敛端口/主机/token 解析(含 ACTIONDOCK_TOKEN 环境变量回退)、
6
+ * maxBodyBytes 与 timeoutMs 的 try-catch 包装、corsOrigins 归一化、
7
+ * 停止信号注册与横幅打印,消除 serve.ts 与 mcp.ts 的逐行重复。
8
+ */
9
+ /**
10
+ * 解析监听端口:解析失败时回退缺省端口。
11
+ *
12
+ * @param rawPort 命令行原始端口串
13
+ * @param defaultPort 缺省端口(ad serve 为 5177,ad mcp serve 为 5178)
14
+ * @param falsyFallsBack 是否对零值也回退(mcp serve 旧行为为 parseInt || default,
15
+ * 端口 0 会回退缺省;ad serve 为 NaN 判定,端口 0 保留)
16
+ */
17
+ export declare function resolveServerPort(rawPort: string, defaultPort: number, falsyFallsBack?: boolean): number;
18
+ /**
19
+ * 解析监听主机:未指定时回退 127.0.0.1。
20
+ */
21
+ export declare function resolveServerHost(rawHost: string | undefined): string;
22
+ /**
23
+ * 解析鉴权 Token:显式参数优先,其次 --token-env 指定的环境变量,最后 ACTIONDOCK_TOKEN 兜底。
24
+ *
25
+ * @param token 显式 -t/--token 参数
26
+ * @param tokenEnv --token-env 指定的环境变量名
27
+ */
28
+ export declare function resolveServerToken(token?: string, tokenEnv?: string): string | undefined;
29
+ /**
30
+ * 解析请求体大小上限(--max-body):格式非法时抛 ArgumentError。
31
+ */
32
+ export declare function resolveMaxBodyBytes(maxBody: string | undefined): number | undefined;
33
+ /**
34
+ * 解析执行超时(--timeout):格式非法时抛 ArgumentError。
35
+ */
36
+ export declare function resolveTimeoutMs(timeout: string | undefined): number | undefined;
37
+ /**
38
+ * 归一化 CORS 来源列表:空列表归一为 undefined。
39
+ */
40
+ export declare function normalizeCorsOrigins(corsOrigin: string[] | undefined): string[] | undefined;
41
+ /**
42
+ * 注册 SIGINT/SIGTERM 停止信号处理器(serve 与 mcp serve 共享同一语义)。
43
+ *
44
+ * 停止失败必须可见:写入 stderr 一行并标记失败退出码;
45
+ * 无论成功与否最终强制进程退出。
46
+ *
47
+ * @param server 目标服务实例(提供 stop 方法)
48
+ * @param serverName 横幅与停止消息中的服务名(如 ActionDock server / MCP HTTP server)
49
+ */
50
+ export declare function registerStopSignalHandler(server: {
51
+ stop(): Promise<unknown> | unknown;
52
+ }, serverName: string, context?: CliContext): void;
53
+ /**
54
+ * 打印服务就绪横幅(顶部固定段,serve 与 mcp serve 共享排版)。
55
+ *
56
+ * @param title 横幅主体标题(如 ActionDock 2.0 HTTP Runner Server)
57
+ * @param scheme 端点协议(http 或 https)
58
+ * @param displayHost 面向展示的主机地址
59
+ * @param port 实际监听端口
60
+ */
61
+ export declare function printServerBanner(title: string, scheme: string, displayHost: string, port: number, context?: CliContext): void;
@@ -0,0 +1,114 @@
1
+ import { parseDuration, } from "@actiondock/core/project";
2
+ import { ArgumentError } from "../errors.js";
3
+ import { writeStderr, writeStdout } from "../renderer.js";
4
+ import { parseByteSize } from "./utils.js";
5
+ /**
6
+ * 服务类命令(ad serve、ad mcp serve、ad mcp、ad run)的共享启动脚手架(单一事实源)。
7
+ *
8
+ * 收敛端口/主机/token 解析(含 ACTIONDOCK_TOKEN 环境变量回退)、
9
+ * maxBodyBytes 与 timeoutMs 的 try-catch 包装、corsOrigins 归一化、
10
+ * 停止信号注册与横幅打印,消除 serve.ts 与 mcp.ts 的逐行重复。
11
+ */
12
+ /**
13
+ * 解析监听端口:解析失败时回退缺省端口。
14
+ *
15
+ * @param rawPort 命令行原始端口串
16
+ * @param defaultPort 缺省端口(ad serve 为 5177,ad mcp serve 为 5178)
17
+ * @param falsyFallsBack 是否对零值也回退(mcp serve 旧行为为 parseInt || default,
18
+ * 端口 0 会回退缺省;ad serve 为 NaN 判定,端口 0 保留)
19
+ */
20
+ export function resolveServerPort(rawPort, defaultPort, falsyFallsBack) {
21
+ const parsed = parseInt(rawPort, 10);
22
+ if (falsyFallsBack) {
23
+ return parsed || defaultPort;
24
+ }
25
+ return Number.isNaN(parsed) ? defaultPort : parsed;
26
+ }
27
+ /**
28
+ * 解析监听主机:未指定时回退 127.0.0.1。
29
+ */
30
+ export function resolveServerHost(rawHost) {
31
+ return rawHost || "127.0.0.1";
32
+ }
33
+ /**
34
+ * 解析鉴权 Token:显式参数优先,其次 --token-env 指定的环境变量,最后 ACTIONDOCK_TOKEN 兜底。
35
+ *
36
+ * @param token 显式 -t/--token 参数
37
+ * @param tokenEnv --token-env 指定的环境变量名
38
+ */
39
+ export function resolveServerToken(token, tokenEnv) {
40
+ return (token ||
41
+ (tokenEnv && typeof process !== "undefined" ? process.env?.[tokenEnv] : undefined) ||
42
+ (typeof process !== "undefined" ? process.env?.ACTIONDOCK_TOKEN : undefined));
43
+ }
44
+ /**
45
+ * 解析请求体大小上限(--max-body):格式非法时抛 ArgumentError。
46
+ */
47
+ export function resolveMaxBodyBytes(maxBody) {
48
+ if (!maxBody)
49
+ return undefined;
50
+ try {
51
+ return parseByteSize(maxBody);
52
+ }
53
+ catch (err) {
54
+ throw new ArgumentError(`Invalid max-body format: ${err.message}`);
55
+ }
56
+ }
57
+ /**
58
+ * 解析执行超时(--timeout):格式非法时抛 ArgumentError。
59
+ */
60
+ export function resolveTimeoutMs(timeout) {
61
+ if (!timeout)
62
+ return undefined;
63
+ try {
64
+ return parseDuration(timeout);
65
+ }
66
+ catch (err) {
67
+ throw new ArgumentError(`Invalid timeout format: ${err.message}`);
68
+ }
69
+ }
70
+ /**
71
+ * 归一化 CORS 来源列表:空列表归一为 undefined。
72
+ */
73
+ export function normalizeCorsOrigins(corsOrigin) {
74
+ return corsOrigin && corsOrigin.length > 0 ? corsOrigin : undefined;
75
+ }
76
+ /**
77
+ * 注册 SIGINT/SIGTERM 停止信号处理器(serve 与 mcp serve 共享同一语义)。
78
+ *
79
+ * 停止失败必须可见:写入 stderr 一行并标记失败退出码;
80
+ * 无论成功与否最终强制进程退出。
81
+ *
82
+ * @param server 目标服务实例(提供 stop 方法)
83
+ * @param serverName 横幅与停止消息中的服务名(如 ActionDock server / MCP HTTP server)
84
+ */
85
+ export function registerStopSignalHandler(server, serverName, context) {
86
+ const stopSignalHandler = () => {
87
+ writeStdout(`\nStopping ${serverName}...`, context);
88
+ Promise.resolve(server.stop())
89
+ .catch((err) => {
90
+ // 停止失败必须可见:写入 stderr 一行并标记失败退出码
91
+ writeStderr(`[ERROR] Failed to stop ${serverName} gracefully: ${err instanceof Error ? err.message : String(err)}`, context);
92
+ process.exitCode = 1;
93
+ })
94
+ .finally(() => {
95
+ process.exit(typeof process.exitCode === "number" ? process.exitCode : 0);
96
+ });
97
+ };
98
+ process.once("SIGINT", stopSignalHandler);
99
+ process.once("SIGTERM", stopSignalHandler);
100
+ }
101
+ /**
102
+ * 打印服务就绪横幅(顶部固定段,serve 与 mcp serve 共享排版)。
103
+ *
104
+ * @param title 横幅主体标题(如 ActionDock 2.0 HTTP Runner Server)
105
+ * @param scheme 端点协议(http 或 https)
106
+ * @param displayHost 面向展示的主机地址
107
+ * @param port 实际监听端口
108
+ */
109
+ export function printServerBanner(title, scheme, displayHost, port, context) {
110
+ writeStdout(`\n======================================================`, context);
111
+ writeStdout(` ${title}`, context);
112
+ writeStdout(`======================================================`, context);
113
+ writeStdout(` * Listening on: ${scheme}://${displayHost}:${port}`, context);
114
+ }
@@ -1,4 +1,5 @@
1
- import { type ActionDockTarget, type ResolvedTarget } from "@actiondock/core";
1
+ import { type ActionDockService } from "@actiondock/core";
2
+ import { type ResolvedTarget } from "@actiondock/core/profile";
2
3
  import type { Command } from "commander";
3
4
  import type { CliContext } from "../types.js";
4
5
  /**
@@ -10,7 +11,7 @@ import type { CliContext } from "../types.js";
10
11
  */
11
12
  export declare function applyTargetOptions(command: Command): Command;
12
13
  /**
13
- * 目标解析与 Target 创建所需的统一选项视图。
14
+ * 目标解析与 Service 创建所需的统一选项视图。
14
15
  * 与既有命令内联解析逻辑保持逐字段一致,作为唯一事实源供各命令复用。
15
16
  */
16
17
  export interface TargetResolutionOptions {
@@ -37,14 +38,14 @@ export interface TargetResolutionOptions {
37
38
  *
38
39
  * @param options 命令选项视图(支持直接传入 Commander 解析后的 options 对象)
39
40
  * @param context CLI 上下文
40
- * @returns 解析后的目标拓扑信息(仅信息解析,不创建 Target 实例)
41
+ * @returns 解析后的目标拓扑信息(仅信息解析,不创建 Service 实例)
41
42
  */
42
43
  export declare function resolveTargetFromOptions(options: TargetResolutionOptions, context?: CliContext): ResolvedTarget;
43
44
  /**
44
- * 高阶目标执行辅助:统一封装「目标解析 → Target 创建 → 业务回调 → try/finally close」样板。
45
+ * 高阶服务执行辅助:统一封装「目标解析 → Service 创建 → 业务回调 → try/finally close」样板。
45
46
  *
46
- * 回调获得创建好的 target 与解析后的目标拓扑信息;无论回调成功或抛出,
47
- * 都保证 target 资源被正确释放,业务异常原样透传。
47
+ * 回调获得创建好的 service 与解析后的目标拓扑信息;无论回调成功或抛出,
48
+ * 都保证 service 资源被正确释放,业务异常原样透传。
48
49
  *
49
50
  * 默认旁观打开:内部创建的本地 Host 以非收割模式打开存储(不置 recoverOrphans),
50
51
  * 供 ad state / ad runs / ad config 等查询命令与运行中的 serve 进程并发访问同一库;
@@ -52,10 +53,10 @@ export declare function resolveTargetFromOptions(options: TargetResolutionOption
52
53
  *
53
54
  * @param options 命令选项视图(profile/server/token/package/dataDir/insecure/allowInsecureHttp)
54
55
  * @param context CLI 上下文
55
- * @param fn 业务回调(target 为已创建的门面实例,resolved 为拓扑解析结果)
56
+ * @param fn 业务回调(service 为已创建的服务端口实例,resolved 为拓扑解析结果)
56
57
  * @param localOptions 本地分支附加选项(如 scanLinkedPackages)与 localRoot 回退策略
57
58
  */
58
- export declare function withTarget(options: TargetResolutionOptions, context: CliContext | undefined, fn: (target: ActionDockTarget, resolved: ResolvedTarget) => Promise<void>, localOptions?: {
59
+ export declare function withService(options: TargetResolutionOptions, context: CliContext | undefined, fn: (service: ActionDockService, resolved: ResolvedTarget) => Promise<void>, localOptions?: {
59
60
  /** 本地工程根目录(支持惰性工厂,仅在 local 分支求值);未提供时按 findProjectRoot 回退 */
60
61
  localRoot?: string | (() => string | undefined);
61
62
  /** 是否扫描全局链接包(对应 scanLinkedPackages) */
@@ -66,12 +67,12 @@ export declare function withTarget(options: TargetResolutionOptions, context: Cl
66
67
  * 保持缺省 false,避免误收割并发 serve 进程的在途运行记录。
67
68
  */
68
69
  ownDataDir?: boolean;
70
+ /**
71
+ * 是否强制要求远端目标:true 时 local 分支直接抛 ArgumentError(如 ad runs cancel),
72
+ * 替代原 withRemoteService 的专用实现。
73
+ */
74
+ requireRemote?: boolean;
69
75
  }): Promise<void>;
70
- /**
71
- * 仅远端模式目标执行辅助:创建远端 Target 并保证资源释放。
72
- * 适用于 runs cancel 等明确要求远端目标的命令。
73
- */
74
- export declare function withRemoteTarget(options: TargetResolutionOptions, context: CliContext | undefined, fn: (target: ActionDockTarget, resolved: ResolvedTarget) => Promise<void>): Promise<void>;
75
76
  /**
76
77
  * 目标包根目录解析(含 -P 显式寻址与当前工程回退)。
77
78
  * 供命令在创建 Target 前统一完成包寻址,返回 null 表示当前目录无工程。