aws-runtime-bridge 1.6.5 → 1.6.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAc3C,OAAO,KAAK,EACV,oBAAoB,EAEpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EACd,MAAM,YAAY,CAAC;AA4GpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,SAAS,CAAC,CAAoB;IAEhC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+I5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,OAAO;YA6BP,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAahC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
1
+ {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EACd,MAAM,YAAY,CAAC;AA2GpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,SAAS,CAAC,CAAoB;IAEhC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+I5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,OAAO;YA2BP,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAahC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
@@ -17,6 +17,7 @@ import * as net from 'node:net';
17
17
  import * as os from 'node:os';
18
18
  import * as path from 'node:path';
19
19
  import { v4 as uuidv4 } from 'uuid';
20
+ import { BRIDGE_PACKAGE_ROOT, importBridgeSdkPackage } from '../utils/sdk-package-loader.js';
20
21
  import { getToolActionInfo } from './types.js';
21
22
  // ============ 可执行文件查找 ============
22
23
  /**
@@ -359,7 +360,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
359
360
  async loadSdk() {
360
361
  if (!this.sdkModule) {
361
362
  try {
362
- this.sdkModule = await import('@opencode-ai/sdk');
363
+ this.sdkModule = await importBridgeSdkPackage('@opencode-ai/sdk');
363
364
  }
364
365
  catch (error) {
365
366
  if (isMissingPackageError(error, '@opencode-ai/sdk')) {
@@ -367,13 +368,11 @@ export class OpencodeSdkAdapter extends EventEmitter {
367
368
  'OpenCode SDK provider is not installed.',
368
369
  '',
369
370
  'aws-runtime-bridge keeps Codex/OpenCode provider SDKs optional to reduce default install size.',
370
- 'To use SDK mode with command "opencode", install:',
371
+ 'To use SDK mode with command "opencode", install the SDK into the aws-runtime-bridge package root:',
371
372
  '',
372
- ' npm install -g @opencode-ai/sdk',
373
+ ` npm install --prefix "${BRIDGE_PACKAGE_ROOT}" @opencode-ai/sdk@latest opencode-ai@latest`,
373
374
  '',
374
- 'If aws-runtime-bridge is installed locally, install the SDK in the same project:',
375
- '',
376
- ' npm install @opencode-ai/sdk',
375
+ 'If aws-runtime-bridge is installed locally, run the same command with that local package path.',
377
376
  '',
378
377
  `Original error: ${error instanceof Error ? error.message : String(error)}`,
379
378
  ].join('\n'));
@@ -1,10 +1,28 @@
1
1
  import type { ToolInstallStatus } from "../types.js";
2
+ type ToolCommand = {
3
+ kind: "execFile";
4
+ command: string;
5
+ args: string[];
6
+ display: string;
7
+ } | {
8
+ kind: "shell";
9
+ display: string;
10
+ };
2
11
  export declare const SUPPORTED_INSTALLABLE_TOOLS: readonly string[];
3
12
  export declare const SUPPORTED_UNINSTALLABLE_TOOLS: readonly string[];
4
13
  /**
5
14
  * 返回工具对应的全局卸载命令副本,用于诊断与测试卸载覆盖范围。
6
15
  */
7
16
  export declare function getToolUninstallCommands(tool: string): string[];
17
+ /**
18
+ * 返回工具对应的安装/卸载执行参数副本,用于验证 Windows 下不会把路径引号传给 npm。
19
+ */
20
+ export declare function getToolCommandSpecs(tool: string, phase: "install" | "uninstall"): Array<{
21
+ command: string;
22
+ args: string[];
23
+ display: string;
24
+ kind: ToolCommand["kind"];
25
+ }>;
8
26
  export declare function isVoltaShimPath(commandPath: string): boolean;
9
27
  /**
10
28
  * 检查单个工具的 CLI 可执行状态,供实例状态展示与初始化前判断使用。
@@ -22,4 +40,5 @@ export declare function ensureToolsInstalled(tools: string[]): Promise<Record<st
22
40
  * 按工具定义执行全局卸载命令,随后重新检测并返回最新安装状态。
23
41
  */
24
42
  export declare function uninstallTools(tools: string[]): Promise<Record<string, ToolInstallStatus>>;
43
+ export {};
25
44
  //# sourceMappingURL=tool-installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-installer.d.ts","sourceRoot":"","sources":["../../src/services/tool-installer.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiHrD,eAAO,MAAM,2BAA2B,mBAEvC,CAAC;AAEF,eAAO,MAAM,6BAA6B,mBAIzC,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAK/D;AAoDD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAM5D;AAkOD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAsC5B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAkB5C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAqF5C;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAiD5C"}
1
+ {"version":3,"file":"tool-installer.d.ts","sourceRoot":"","sources":["../../src/services/tool-installer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAwBrD,KAAK,WAAW,GACZ;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAiHN,eAAO,MAAM,2BAA2B,mBAEvC,CAAC;AAEF,eAAO,MAAM,6BAA6B,mBAIzC,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAO/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAAG,WAAW,GAC7B,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAaxF;AAoDD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAM5D;AAgPD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAsC5B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAkB5C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAsF5C;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAiD5C"}
@@ -2,12 +2,12 @@ import { execFile } from "node:child_process";
2
2
  import { access, readFile } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
5
  import { promisify } from "node:util";
7
6
  import { createLogger } from "../utils/logger.js";
7
+ import { BRIDGE_PACKAGE_ROOT, importBridgeSdkPackage } from "../utils/sdk-package-loader.js";
8
8
  const execFileAsync = promisify(execFile);
9
9
  const log = createLogger("tool-installer");
10
- const bridgePackageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
10
+ const bridgePackageRoot = BRIDGE_PACKAGE_ROOT;
11
11
  const isWindows = process.platform === "win32";
12
12
  function quoteCommandArg(value) {
13
13
  if (isWindows) {
@@ -21,6 +21,27 @@ function npmInstallIntoBridgeCommand(packages) {
21
21
  function npmUninstallFromBridgeCommand(packages) {
22
22
  return `npm uninstall --prefix ${quoteCommandArg(bridgePackageRoot)} ${packages.join(" ")}`;
23
23
  }
24
+ function npmExecutableName() {
25
+ return isWindows ? "npm.cmd" : "npm";
26
+ }
27
+ /**
28
+ * 构建 bridge 本地 npm 包安装/卸载命令。
29
+ * 主流程:展示层保留可读命令;执行层使用参数数组,避免 Windows shell 引号被 npm 当作路径内容。
30
+ */
31
+ function npmBridgeCommand(action, packages) {
32
+ const display = action === "install"
33
+ ? npmInstallIntoBridgeCommand(packages)
34
+ : npmUninstallFromBridgeCommand(packages);
35
+ return {
36
+ kind: "execFile",
37
+ command: npmExecutableName(),
38
+ args: [action, "--prefix", bridgePackageRoot, ...packages],
39
+ display,
40
+ };
41
+ }
42
+ function shellToolCommand(command) {
43
+ return { kind: "shell", display: command };
44
+ }
24
45
  const TOOL_DEFINITIONS = {
25
46
  claude: {
26
47
  key: "claude",
@@ -29,10 +50,10 @@ const TOOL_DEFINITIONS = {
29
50
  aliases: [],
30
51
  versionArgs: [],
31
52
  installCommands: [
32
- npmInstallIntoBridgeCommand(["@anthropic-ai/claude-agent-sdk@latest"]),
53
+ npmBridgeCommand("install", ["@anthropic-ai/claude-agent-sdk@latest"]),
33
54
  ],
34
55
  uninstallCommands: [
35
- npmUninstallFromBridgeCommand(["@anthropic-ai/claude-agent-sdk"]),
56
+ npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk"]),
36
57
  ],
37
58
  },
38
59
  claudecode: {
@@ -42,10 +63,10 @@ const TOOL_DEFINITIONS = {
42
63
  aliases: [],
43
64
  versionArgs: [],
44
65
  installCommands: [
45
- npmInstallIntoBridgeCommand(["@anthropic-ai/claude-agent-sdk@latest"]),
66
+ npmBridgeCommand("install", ["@anthropic-ai/claude-agent-sdk@latest"]),
46
67
  ],
47
68
  uninstallCommands: [
48
- npmUninstallFromBridgeCommand(["@anthropic-ai/claude-agent-sdk"]),
69
+ npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk"]),
49
70
  ],
50
71
  },
51
72
  opencode: {
@@ -57,16 +78,16 @@ const TOOL_DEFINITIONS = {
57
78
  : ["opencode"],
58
79
  versionArgs: ["--version"],
59
80
  installCommands: [
60
- npmInstallIntoBridgeCommand(["@opencode-ai/sdk@latest", "opencode-ai@latest"]),
81
+ npmBridgeCommand("install", ["@opencode-ai/sdk@latest", "opencode-ai@latest"]),
61
82
  ],
62
83
  uninstallCommands: isWindows
63
84
  ? [
64
- npmUninstallFromBridgeCommand(["@opencode-ai/sdk", "opencode-ai"]),
85
+ npmBridgeCommand("uninstall", ["@opencode-ai/sdk", "opencode-ai"]),
65
86
  ]
66
87
  : [
67
- npmUninstallFromBridgeCommand(["@opencode-ai/sdk", "opencode-ai"]),
68
- "opencode uninstall --force",
69
- "rm -f ~/.opencode/bin/opencode",
88
+ npmBridgeCommand("uninstall", ["@opencode-ai/sdk", "opencode-ai"]),
89
+ shellToolCommand("opencode uninstall --force"),
90
+ shellToolCommand("rm -f ~/.opencode/bin/opencode"),
70
91
  ],
71
92
  extraSearchPaths: () => {
72
93
  const home = os.homedir();
@@ -85,8 +106,8 @@ const TOOL_DEFINITIONS = {
85
106
  sdkPackageName: "@openai/codex-sdk",
86
107
  aliases: [],
87
108
  versionArgs: [],
88
- installCommands: [npmInstallIntoBridgeCommand(["@openai/codex-sdk@latest"])],
89
- uninstallCommands: [npmUninstallFromBridgeCommand(["@openai/codex-sdk"])],
109
+ installCommands: [npmBridgeCommand("install", ["@openai/codex-sdk@latest"])],
110
+ uninstallCommands: [npmBridgeCommand("uninstall", ["@openai/codex-sdk"])],
90
111
  },
91
112
  };
92
113
  export const SUPPORTED_INSTALLABLE_TOOLS = Object.freeze(Object.keys(TOOL_DEFINITIONS));
@@ -98,7 +119,24 @@ export function getToolUninstallCommands(tool) {
98
119
  const normalizedTool = String(tool || "")
99
120
  .trim()
100
121
  .toLowerCase();
101
- return [...(TOOL_DEFINITIONS[normalizedTool]?.uninstallCommands || [])];
122
+ return (TOOL_DEFINITIONS[normalizedTool]?.uninstallCommands || []).map((command) => command.display);
123
+ }
124
+ /**
125
+ * 返回工具对应的安装/卸载执行参数副本,用于验证 Windows 下不会把路径引号传给 npm。
126
+ */
127
+ export function getToolCommandSpecs(tool, phase) {
128
+ const normalizedTool = String(tool || "")
129
+ .trim()
130
+ .toLowerCase();
131
+ const commands = phase === "install"
132
+ ? TOOL_DEFINITIONS[normalizedTool]?.installCommands
133
+ : TOOL_DEFINITIONS[normalizedTool]?.uninstallCommands;
134
+ return (commands || []).map((command) => ({
135
+ command: command.kind === "execFile" ? command.command : command.display,
136
+ args: command.kind === "execFile" ? [...command.args] : [],
137
+ display: command.display,
138
+ kind: command.kind,
139
+ }));
102
140
  }
103
141
  function parseVersion(output) {
104
142
  const normalized = String(output || "").trim();
@@ -288,7 +326,7 @@ async function resolveSdkPackageCandidate(definition) {
288
326
  return resolveExecutableCandidate(definition);
289
327
  }
290
328
  try {
291
- await import(sdkPackageName);
329
+ await importBridgeSdkPackage(sdkPackageName);
292
330
  return {
293
331
  executable: sdkPackageName,
294
332
  version: await readInstalledPackageVersion(sdkPackageName),
@@ -320,13 +358,26 @@ function describeCommandFailure(error) {
320
358
  };
321
359
  }
322
360
  async function runToolCommand(command) {
361
+ if (command.kind === "execFile") {
362
+ const needsWindowsCmdShim = isWindows && /\.(?:cmd|bat)$/i.test(command.command);
363
+ const result = needsWindowsCmdShim
364
+ ? await execFileAsync("cmd.exe", ["/d", "/s", "/c", command.command, ...command.args], {
365
+ timeout: 10 * 60 * 1000,
366
+ windowsVerbatimArguments: false,
367
+ })
368
+ : await execFileAsync(command.command, command.args, {
369
+ timeout: 10 * 60 * 1000,
370
+ windowsVerbatimArguments: false,
371
+ });
372
+ return { stdout: result.stdout || "", stderr: result.stderr || "" };
373
+ }
323
374
  if (isWindows) {
324
- const result = await execFileAsync("cmd.exe", ["/d", "/s", "/c", command], {
375
+ const result = await execFileAsync("cmd.exe", ["/d", "/s", "/c", command.display], {
325
376
  timeout: 10 * 60 * 1000,
326
377
  });
327
378
  return { stdout: result.stdout || "", stderr: result.stderr || "" };
328
379
  }
329
- const result = await execFileAsync("/bin/sh", ["-lc", command], {
380
+ const result = await execFileAsync("/bin/sh", ["-lc", command.display], {
330
381
  timeout: 10 * 60 * 1000,
331
382
  });
332
383
  return { stdout: result.stdout || "", stderr: result.stderr || "" };
@@ -420,14 +471,14 @@ export async function ensureToolsInstalled(tools) {
420
471
  try {
421
472
  log.info("Running install command", {
422
473
  tool,
423
- command,
474
+ command: command.display,
424
475
  sdkPackageName: definition.sdkPackageName || null,
425
476
  packageName: definition.packageName,
426
477
  });
427
478
  const commandResult = await runToolCommand(command);
428
479
  log.info("Install command completed", {
429
480
  tool,
430
- command,
481
+ command: command.display,
431
482
  stdout: commandResult.stdout,
432
483
  stderr: commandResult.stderr,
433
484
  });
@@ -443,7 +494,7 @@ export async function ensureToolsInstalled(tools) {
443
494
  catch (error) {
444
495
  log.error("Install command failed", {
445
496
  tool,
446
- command,
497
+ command: command.display,
447
498
  failure: describeCommandFailure(error),
448
499
  });
449
500
  lastError =
@@ -464,6 +515,7 @@ export async function ensureToolsInstalled(tools) {
464
515
  tool,
465
516
  status: nextStatuses[tool],
466
517
  attemptedCommands: definition.installCommands,
518
+ attemptedCommandDisplays: definition.installCommands.map((command) => command.display),
467
519
  });
468
520
  }
469
521
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { detectToolInstallStatus, SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, detectToolStatuses, getToolUninstallCommands, isVoltaShimPath } from './tool-installer.js';
2
+ import { SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, detectToolInstallStatus, detectToolStatuses, getToolCommandSpecs, getToolUninstallCommands, isVoltaShimPath } from './tool-installer.js';
3
3
  describe('tool installer service', () => {
4
4
  it('returns structured status for supported tools', async () => {
5
5
  const statuses = await detectToolStatuses(['claude', 'opencode', 'codex']);
@@ -115,6 +115,25 @@ describe('tool installer service', () => {
115
115
  expect(commands.some(command => command.includes('npm uninstall --prefix'))).toBe(true);
116
116
  expect(commands.some(command => command.includes('@openai/codex-sdk'))).toBe(true);
117
117
  });
118
+ it('executes npm-backed install and uninstall commands with prefix as an unquoted argument', () => {
119
+ const expectedPackagesByTool = {
120
+ claude: '@anthropic-ai/claude-agent-sdk',
121
+ claudecode: '@anthropic-ai/claude-agent-sdk',
122
+ opencode: '@opencode-ai/sdk',
123
+ codex: '@openai/codex-sdk'
124
+ };
125
+ for (const [tool, packageName] of Object.entries(expectedPackagesByTool)) {
126
+ for (const phase of ['install', 'uninstall']) {
127
+ const [command] = getToolCommandSpecs(tool, phase);
128
+ expect(command.kind).toBe('execFile');
129
+ expect(command.command).toMatch(/^npm(?:\.cmd)?$/);
130
+ expect(command.args.slice(0, 3)).toEqual([phase, '--prefix', expect.any(String)]);
131
+ expect(command.args[2]).not.toMatch(/^"|"$/);
132
+ expect(command.args.some(arg => arg.startsWith(packageName))).toBe(true);
133
+ expect(command.display).toContain(`npm ${phase} --prefix`);
134
+ }
135
+ }
136
+ });
118
137
  it('recognizes Volta shim paths for package-manager-aware detection', () => {
119
138
  expect(isVoltaShimPath('C:\\Users\\tester\\AppData\\Local\\Volta\\bin\\codex.cmd')).toBe(true);
120
139
  expect(isVoltaShimPath('/home/tester/.volta/bin/codex')).toBe(true);
@@ -0,0 +1,12 @@
1
+ export declare const BRIDGE_PACKAGE_ROOT: string;
2
+ /**
3
+ * 主干流程:从 bridge 自身安装根下的 node_modules 读取包元数据,
4
+ * 按 package.json 的 exports/module/main 定位真实 ESM 入口,避免猜测根目录 index.js。
5
+ */
6
+ export declare function resolveBridgePackageEntryPath(packageName: string, packageRoot?: string): Promise<string>;
7
+ /**
8
+ * 具体实现逻辑:优先从 bridge-local 安装目录显式导入真实入口;
9
+ * 若没有本地 package.json,则退回 Node 的包名解析以兼容开发环境。
10
+ */
11
+ export declare function importBridgeSdkPackage<TModule>(packageName: string): Promise<TModule>;
12
+ //# sourceMappingURL=sdk-package-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-package-loader.d.ts","sourceRoot":"","sources":["../../src/utils/sdk-package-loader.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,mBAAmB,QAI/B,CAAC;AA4DF;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,EAAE,MAAM,EACnB,WAAW,SAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAClD,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAUlB"}
@@ -0,0 +1,78 @@
1
+ import { access, readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4
+ export const BRIDGE_PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
8
+ function getPackageDirectory(packageName, packageRoot) {
9
+ return path.join(packageRoot, 'node_modules', ...packageName.split('/'));
10
+ }
11
+ function resolveConditionalExport(value) {
12
+ if (typeof value === 'string') {
13
+ return value;
14
+ }
15
+ if (!isRecord(value)) {
16
+ return null;
17
+ }
18
+ for (const condition of ['import', 'module', 'default', 'node']) {
19
+ const resolved = resolveConditionalExport(value[condition]);
20
+ if (resolved) {
21
+ return resolved;
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ function resolvePackageRootExport(exportsField) {
27
+ if (typeof exportsField === 'string') {
28
+ return exportsField;
29
+ }
30
+ if (!isRecord(exportsField)) {
31
+ return null;
32
+ }
33
+ const rootExport = resolveConditionalExport(exportsField['.']);
34
+ return rootExport ?? resolveConditionalExport(exportsField);
35
+ }
36
+ function resolveEntryFromPackageJson(packageJson) {
37
+ const exportedEntry = resolvePackageRootExport(packageJson.exports);
38
+ if (exportedEntry) {
39
+ return exportedEntry;
40
+ }
41
+ if (typeof packageJson.module === 'string') {
42
+ return packageJson.module;
43
+ }
44
+ if (typeof packageJson.main === 'string') {
45
+ return packageJson.main;
46
+ }
47
+ return 'index.js';
48
+ }
49
+ /**
50
+ * 主干流程:从 bridge 自身安装根下的 node_modules 读取包元数据,
51
+ * 按 package.json 的 exports/module/main 定位真实 ESM 入口,避免猜测根目录 index.js。
52
+ */
53
+ export async function resolveBridgePackageEntryPath(packageName, packageRoot = BRIDGE_PACKAGE_ROOT) {
54
+ const packageDirectory = getPackageDirectory(packageName, packageRoot);
55
+ const packageJsonPath = path.join(packageDirectory, 'package.json');
56
+ const rawPackageJson = await readFile(packageJsonPath, 'utf8');
57
+ const packageJson = JSON.parse(rawPackageJson);
58
+ const entry = resolveEntryFromPackageJson(packageJson);
59
+ const entryPath = path.resolve(packageDirectory, entry);
60
+ await access(entryPath);
61
+ return entryPath;
62
+ }
63
+ /**
64
+ * 具体实现逻辑:优先从 bridge-local 安装目录显式导入真实入口;
65
+ * 若没有本地 package.json,则退回 Node 的包名解析以兼容开发环境。
66
+ */
67
+ export async function importBridgeSdkPackage(packageName) {
68
+ try {
69
+ const entryPath = await resolveBridgePackageEntryPath(packageName);
70
+ return (await import(pathToFileURL(entryPath).href));
71
+ }
72
+ catch (error) {
73
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
74
+ return (await import(packageName));
75
+ }
76
+ throw error;
77
+ }
78
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sdk-package-loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-package-loader.test.d.ts","sourceRoot":"","sources":["../../src/utils/sdk-package-loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { resolveBridgePackageEntryPath } from './sdk-package-loader.js';
6
+ const tempRoots = [];
7
+ afterEach(async () => {
8
+ await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
9
+ });
10
+ async function createTempPackageRoot() {
11
+ const root = await mkdtemp(path.join(os.tmpdir(), 'aws-sdk-package-loader-'));
12
+ tempRoots.push(root);
13
+ return root;
14
+ }
15
+ describe('sdk package loader', () => {
16
+ it('resolves package exports instead of assuming a root index.js file', async () => {
17
+ const packageRoot = await createTempPackageRoot();
18
+ const packageDirectory = path.join(packageRoot, 'node_modules', '@opencode-ai', 'sdk');
19
+ await mkdir(path.join(packageDirectory, 'dist'), { recursive: true });
20
+ await writeFile(path.join(packageDirectory, 'package.json'), JSON.stringify({
21
+ name: '@opencode-ai/sdk',
22
+ type: 'module',
23
+ exports: {
24
+ '.': {
25
+ import: './dist/index.js',
26
+ types: './dist/index.d.ts',
27
+ },
28
+ },
29
+ }));
30
+ await writeFile(path.join(packageDirectory, 'dist', 'index.js'), 'export const ok = true;');
31
+ await expect(resolveBridgePackageEntryPath('@opencode-ai/sdk', packageRoot)).resolves.toBe(path.join(packageDirectory, 'dist', 'index.js'));
32
+ });
33
+ it('resolves default conditional exports', async () => {
34
+ const packageRoot = await createTempPackageRoot();
35
+ const packageDirectory = path.join(packageRoot, 'node_modules', 'default-only-sdk');
36
+ await mkdir(path.join(packageDirectory, 'dist'), { recursive: true });
37
+ await writeFile(path.join(packageDirectory, 'package.json'), JSON.stringify({ exports: { '.': { default: './dist/default.js' } } }));
38
+ await writeFile(path.join(packageDirectory, 'dist', 'default.js'), 'export const ok = true;');
39
+ await expect(resolveBridgePackageEntryPath('default-only-sdk', packageRoot)).resolves.toBe(path.join(packageDirectory, 'dist', 'default.js'));
40
+ });
41
+ it('falls back to module when exports is absent', async () => {
42
+ const packageRoot = await createTempPackageRoot();
43
+ const packageDirectory = path.join(packageRoot, 'node_modules', 'module-sdk');
44
+ await mkdir(path.join(packageDirectory, 'esm'), { recursive: true });
45
+ await writeFile(path.join(packageDirectory, 'package.json'), JSON.stringify({ module: './esm/index.js' }));
46
+ await writeFile(path.join(packageDirectory, 'esm', 'index.js'), 'export const ok = true;');
47
+ await expect(resolveBridgePackageEntryPath('module-sdk', packageRoot)).resolves.toBe(path.join(packageDirectory, 'esm', 'index.js'));
48
+ });
49
+ it('falls back to main when exports and module are absent', async () => {
50
+ const packageRoot = await createTempPackageRoot();
51
+ const packageDirectory = path.join(packageRoot, 'node_modules', 'main-sdk');
52
+ await mkdir(path.join(packageDirectory, 'lib'), { recursive: true });
53
+ await writeFile(path.join(packageDirectory, 'package.json'), JSON.stringify({ main: './lib/main.js' }));
54
+ await writeFile(path.join(packageDirectory, 'lib', 'main.js'), 'export const ok = true;');
55
+ await expect(resolveBridgePackageEntryPath('main-sdk', packageRoot)).resolves.toBe(path.join(packageDirectory, 'lib', 'main.js'));
56
+ });
57
+ it('rejects when the resolved package entry is missing', async () => {
58
+ const packageRoot = await createTempPackageRoot();
59
+ const packageDirectory = path.join(packageRoot, 'node_modules', 'broken-sdk');
60
+ await mkdir(packageDirectory, { recursive: true });
61
+ await writeFile(path.join(packageDirectory, 'package.json'), JSON.stringify({ exports: './dist/missing.js' }));
62
+ await expect(resolveBridgePackageEntryPath('broken-sdk', packageRoot)).rejects.toMatchObject({
63
+ code: 'ENOENT',
64
+ });
65
+ });
66
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.6.5",
3
+ "version": "1.6.8",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -49,6 +49,7 @@
49
49
  "@anthropic-ai/claude-agent-sdk": "^0.2.87",
50
50
  "@cc-switch/sdk": "file:package/cc-switch-sdk",
51
51
  "@modelcontextprotocol/sdk": "^1.27.1",
52
+ "@openai/codex-sdk": "^0.130.0",
52
53
  "archiver": "^8.0.0",
53
54
  "axios": "^1.7.9",
54
55
  "cors": "^2.8.5",
@@ -64,7 +65,6 @@
64
65
  "zod": "^4.1.12"
65
66
  },
66
67
  "peerDependencies": {
67
- "@openai/codex-sdk": "^0.125.0",
68
68
  "@opencode-ai/sdk": "^1.3.13"
69
69
  },
70
70
  "peerDependenciesMeta": {
@@ -77,7 +77,6 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@eslint/js": "^9.0.0",
80
- "@openai/codex-sdk": "^0.125.0",
81
80
  "@opencode-ai/sdk": "^1.3.13",
82
81
  "@types/archiver": "^7.0.0",
83
82
  "@types/cors": "^2.8.19",