@actiondock/core 2.0.7 → 2.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -27,7 +27,7 @@
27
27
  "test": "bun test"
28
28
  },
29
29
  "dependencies": {
30
- "@actiondock/sdk": "^2.0.0",
30
+ "@actiondock/sdk": "^2.0.9",
31
31
  "ajv": "^8.17.1",
32
32
  "ajv-formats": "^3.0.1",
33
33
  "yaml": "^2.7.0"
@@ -36,7 +36,13 @@
36
36
  "@types/bun": "latest",
37
37
  "typescript": "^5.7.0"
38
38
  },
39
- "keywords": ["actiondock", "core", "runtime", "storage", "builder"],
39
+ "keywords": [
40
+ "actiondock",
41
+ "core",
42
+ "runtime",
43
+ "storage",
44
+ "builder"
45
+ ],
40
46
  "author": "team4u",
41
47
  "license": "Apache-2.0",
42
48
  "repository": {
@@ -108,10 +108,10 @@ export function initProject(targetDir: string, options: InitOptions = {}): void
108
108
  node: ">=22.12.0",
109
109
  },
110
110
  dependencies: {
111
- "@actiondock/sdk": "^2.0.7",
111
+ "@actiondock/sdk": "^2.0.9",
112
112
  },
113
113
  devDependencies: {
114
- "@actiondock/testing": "^2.0.7",
114
+ "@actiondock/testing": "^2.0.9",
115
115
  "@types/node": "^22.12.0",
116
116
  "tsx": "^4.19.0",
117
117
  "typescript": "^5.7.0",
@@ -1,10 +1,21 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { homedir } from "node:os";
3
4
  import { basename, dirname, join, resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
4
6
  import YAML from "yaml";
5
7
  import type { ActionDefinition } from "@actiondock/sdk";
6
8
  import type { PlaybookDefinition, PlaybookFrontmatter, ProjectConfig } from "./types";
7
9
 
10
+ /**
11
+ * 将绝对路径规范化为 ESM 动态导入可用的 file:// URL 标识符。
12
+ * Windows 绝对路径(如 D:\a\b.ts)不是合法的 ESM 标识符,会被解析为 "d:" 协议导致
13
+ * 加载失败;POSIX 绝对路径虽可直接导入,统一转换后跨平台行为一致。
14
+ */
15
+ function toImportSpecifier(filePath: string): string {
16
+ return pathToFileURL(filePath).href;
17
+ }
18
+
8
19
  /**
9
20
  * 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
10
21
  *
@@ -70,8 +81,15 @@ export function loadProjectConfig(projectRoot: string): ProjectConfig {
70
81
 
71
82
  /**
72
83
  * 探测宿主系统中可用的包管理工具(优先级:pnpm > npm > yarn > bun)。
84
+ * 可通过环境变量 ACTIONDOCK_INSTALLER 强制指定(如 npm、pnpm、yarn、bun)。
85
+ * 探测必须经 shell 执行:Windows 下 npm/pnpm/yarn 均为 .cmd 垫片,
86
+ * 不经 shell 的 spawnSync 无法解析,将错误地回退到原生 exe 的 bun。
73
87
  */
74
88
  function getInstallCommand(): string[] {
89
+ const preferred = process.env.ACTIONDOCK_INSTALLER?.trim();
90
+ if (preferred) {
91
+ return [preferred, "install"];
92
+ }
75
93
  const candidates: [string, string][] = [
76
94
  ["pnpm", "install"],
77
95
  ["npm", "install"],
@@ -80,8 +98,9 @@ function getInstallCommand(): string[] {
80
98
  ];
81
99
  for (const [pm, action] of candidates) {
82
100
  try {
83
- const check = spawnSync(pm, ["--version"], {
101
+ const check = spawnSync(`${pm} --version`, {
84
102
  stdio: "pipe",
103
+ shell: true,
85
104
  });
86
105
  if (check.status === 0) {
87
106
  return [pm, action];
@@ -93,6 +112,30 @@ function getInstallCommand(): string[] {
93
112
  return ["npm", "install"];
94
113
  }
95
114
 
115
+ /**
116
+ * 解析 npm 风格 .npmrc 中的 strict-ssl 配置(项目级优先于用户级)。
117
+ * 返回 undefined 表示各级配置均未声明该键。
118
+ */
119
+ function resolveNpmStrictSsl(projectRoot: string): boolean | undefined {
120
+ const configPaths = [join(projectRoot, ".npmrc"), join(homedir(), ".npmrc")];
121
+ for (const configPath of configPaths) {
122
+ try {
123
+ if (!existsSync(configPath)) continue;
124
+ const raw = readFileSync(configPath, "utf-8");
125
+ for (const line of raw.split(/\r?\n/)) {
126
+ const matched = line.match(/^\s*strict-ssl\s*=\s*(\S+)\s*$/i);
127
+ if (matched) {
128
+ const falsy = ["false", "0", "no", "off"];
129
+ return !falsy.includes(matched[1].toLowerCase());
130
+ }
131
+ }
132
+ } catch {
133
+ // 配置不可读时继续检查下一级
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+
96
139
  /**
97
140
  * 确保项目依赖(node_modules)已正确安装。
98
141
  * 若尚未安装或加载失败时,自动触发包管理器执行依赖安装。
@@ -131,14 +174,30 @@ export function ensureProjectDependencies(projectRoot: string, force = false): b
131
174
  `[actiondock] Installing dependencies using ${installCmd[0]} for '${pkg.name || basename(projectRoot)}'...\n`
132
175
  );
133
176
 
134
- const proc = spawnSync(installCmd[0], installCmd.slice(1), {
177
+ // bun 不读取 .npmrc 的 strict-ssl 配置;当 npm 侧已声明 strict-ssl=false 时,
178
+ // 桥接为 bun 子进程的 NODE_TLS_REJECT_UNAUTHORIZED=0,保证内网自签名证书源下行为一致
179
+ const childEnv = { ...process.env };
180
+ if (installCmd[0] === "bun" && resolveNpmStrictSsl(projectRoot) === false) {
181
+ childEnv.NODE_TLS_REJECT_UNAUTHORIZED = "0";
182
+ }
183
+
184
+ // shell 模式下须传入单一命令字符串;候选命令均来自内置白名单,无注入面
185
+ const proc = spawnSync(installCmd.join(" "), {
135
186
  cwd: projectRoot,
136
187
  stdio: "pipe",
188
+ shell: true,
189
+ env: childEnv,
137
190
  });
138
191
 
139
192
  if (proc.status !== 0) {
140
193
  const errText = proc.stderr?.toString() || `Unknown error during ${installCmd[0]} install`;
141
194
  process.stderr.write(`[actiondock] Warning: Dependency installation failed: ${errText}\n`);
195
+ if (installCmd[0] === "bun" && /SELF_SIGNED_CERT|CERT_|UNABLE_TO_VERIFY|ERR_TLS/i.test(errText)) {
196
+ process.stderr.write(
197
+ `[actiondock] Hint: bun ignores 'strict-ssl=false' from .npmrc. ` +
198
+ `Add 'strict-ssl=false' to the project or user .npmrc, or set ACTIONDOCK_INSTALLER=npm.\n`
199
+ );
200
+ }
142
201
  return false;
143
202
  }
144
203
 
@@ -220,7 +279,7 @@ export async function loadActions(
220
279
  // 动态导入,若缺失模块则自动触发依赖重装与二次重试
221
280
  let imported: any;
222
281
  try {
223
- imported = await import(file);
282
+ imported = await import(toImportSpecifier(file));
224
283
  } catch (err: any) {
225
284
  const msg = String(err.message || "");
226
285
  if (
@@ -232,7 +291,7 @@ export async function loadActions(
232
291
  ) {
233
292
  const installed = ensureProjectDependencies(projectRoot, true);
234
293
  if (installed) {
235
- imported = await import(file);
294
+ imported = await import(toImportSpecifier(file));
236
295
  } else {
237
296
  throw err;
238
297
  }
@@ -283,7 +342,7 @@ export async function loadActionFileMap(
283
342
 
284
343
  for (const file of files) {
285
344
  try {
286
- const imported = await import(file);
345
+ const imported = await import(toImportSpecifier(file));
287
346
  const act = imported.default || imported.action;
288
347
  if (act && typeof act === "object" && typeof act.id === "string") {
289
348
  map.set(act.id, {