@nggaigc/cli 0.1.44 → 0.1.47

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
@@ -1,6 +1,6 @@
1
1
  # @nggaigc/cli
2
2
 
3
- NGGAIGC Codex Skill 的一命令安装器。
3
+ NGGAIGC Skill 的受信安装器。Codex 与 WorkBuddy 使用同一条安装命令,自动识别当前宿主。
4
4
 
5
5
  ```powershell
6
6
  npx --yes @nggaigc/cli@latest install
@@ -11,3 +11,31 @@ npx --yes @nggaigc/cli@latest install
11
11
  CLI 保留 macOS npm symlink 入口与 packed-TGZ launcher 检查,并验证随包的已签名 Plugin;实际版本以包内签名清单为准。支持 Windows x64/arm64 与 Apple 芯片 Mac arm64,使用本机 Node.js 22.x(22.13 起)或 24.x,不下载或内嵌 Node.js。Windows PowerShell 5.1 默认 `Restricted` 时,安装器仅为受信凭据辅助脚本的子进程指定 `RemoteSigned`;不修改系统或用户执行策略。安装成功但连接失败会明确分开报告,无需重装;由于系统 URL 处理器退出码不能证明浏览器窗口已显示,安装器会同时给出短期一次性手动授权链接。
12
12
 
13
13
  macOS arm64 辅助程序沿用已签名 Plugin 中通过完整性校验的字节;Intel Mac 不在支持范围内。实际公开版本和 `@latest` 状态以 npm registry 为准,Apple Silicon 真机验收需单独进行。
14
+
15
+ ## 统一安装入口(0.1.46 起)
16
+
17
+ 不带目录参数时,`install`、`start-skill`、`client` 使用同一套宿主识别:Codex 的当前会话标记,或 WorkBuddy 的会话标记/实际工作会话与专属应用标记组合。配置目录、已安装应用和通用终端变量本身不代表当前宿主;环境标记只是便利提示,不用于认证或授权。
18
+
19
+ 无法识别或同时命中两个宿主时,有交互终端可选择 Codex 或 WorkBuddy;非交互调用立即返回 `status: selection-required`、两项 `choices` 和退出码 3,不安装、不打开授权网页。当前 Agent 应让用户选择一次,再把 `--agent codex` 或 `--agent workbuddy` 加到原命令继续;`client` 的目标选项放在 `--` 分隔符之前,业务参数保持原样。安装结果始终返回真实 `skillsDirectory` 和 `skillPath`,连接失败也保留该位置,后续继续使用同一位置。
20
+
21
+ 本次仅 Codex 与 WorkBuddy 为自动识别支持范围;旧 `universal` 与自定义目录参数仍兼容,但不代表其他 Agent 已获验收。
22
+
23
+ ## 显式目标(0.1.45 起)
24
+
25
+ 下列目标参数与 `client` 桥要求 CLI 0.1.45 或更高版本。旧版本不支持新参数;`@latest` 的实际可用版本以 npm registry 与对应签名制品为准。
26
+
27
+ ```text
28
+ npx --yes @nggaigc/cli@latest install --agent workbuddy
29
+ npx --yes @nggaigc/cli@latest install --agent universal --no-login
30
+ npx --yes @nggaigc/cli@latest install --skills-dir "/absolute/project/.codebuddy/skills" --no-login
31
+ npx --yes @nggaigc/cli@latest start-skill skill.<id> [version] --skills-dir "/absolute/project/.codebuddy/skills"
32
+ npx --yes @nggaigc/cli@latest client --skills-dir "/absolute/project/.codebuddy/skills" -- <原command> <原args>
33
+ ```
34
+
35
+ `install`、`start-skill` 和 `client` 支持互斥的 `--agent codex|workbuddy|universal` 或 `--skills-dir <绝对目录>`。识别或选择 Codex 后使用 `CODEX_HOME/skills`,未设置时为 `~/.codex/skills`。WorkBuddy 优先使用实际进程提供的 `WORKBUDDY_CONFIG_DIR/skills`(其次 `CODEBUDDY_CONFIG_DIR/skills`),否则使用 `~/.workbuddy/skills`;在宿主外升级时,若现代目录没有根 Skill 而旧 `~/.codebuddy/skills` 已安装,则继续使用旧位置。不会删除或迁移旧安装。`--skills-dir` 始终精确指定项目级或其他现有安装。universal 使用 `~/.agents/skills`,不保证所有 Agent 自动识别。`--no-login` 仅跳过安装后的登录,不绕过签名、哈希、路径检查或后续鉴权。
36
+
37
+ 根 Skill 按实际读取的 `SKILL.md` 定位父目录及其上一级 `skillsDirectory`;后续升级与 `start-skill` 显式使用同一目录。准备回执增加 `skillsDirectory`、`rootLauncherPath`,保留 `preparedSkills`;宿主应显式读取返回的真实路径。独立包的原客户端调用使用回执的受信根启动器;`client` 是同目录根包验签桥,保留原命令/参数,不增加 API 专用业务分支。安装新包不证明当前宿主热加载,必要时开启新会话。
38
+
39
+ 本地技能包导入可能复制文件,需核导入后的实际路径和独立包同级关系。交互使用宿主可用的提问工具或当前聊天,交付使用宿主可用的媒体/附件能力;不依赖 Codex 专用工具。切换宿主继续复用当前系统用户的系统凭证库和任务历史,不迁移凭据、不要求重新粘贴密钥。资金、权限与未知结果同幂等键恢复规则不变。
40
+
41
+ 此功能不增加 Linux、Intel Mac 支持,也不代表已接入或认证 WorkBuddy 官方连接器市场;现有系统、Node.js 与正式发布门禁保持适用。
@@ -0,0 +1,91 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { resolveSkillLocation, } from "./skill-location.js";
3
+ export const agentChoices = [
4
+ { agent: "codex", label: "Codex" },
5
+ { agent: "workbuddy", label: "WorkBuddy" },
6
+ ];
7
+ export class AgentSelectionRequired extends Error {
8
+ reason;
9
+ code = "AGENT_SELECTION_REQUIRED";
10
+ constructor(reason) {
11
+ super("AGENT_SELECTION_REQUIRED");
12
+ this.reason = reason;
13
+ }
14
+ }
15
+ /** Configuration paths and installed applications are not caller identity. */
16
+ export function detectCallingAgent(environment) {
17
+ const present = (value) => typeof value === "string" && value.trim().length > 0;
18
+ const codex = present(environment.CODEX_THREAD_ID);
19
+ // WorkBuddy's prewarmed runtime omits WORKBUDDY_CONVERSATION_ID. Its shell
20
+ // still has a CodeBuddy session AND WorkBuddy-specific application markers.
21
+ // A standalone CodeBuddy session is deliberately not treated as WorkBuddy.
22
+ const workbuddy = present(environment.WORKBUDDY_CONVERSATION_ID) ||
23
+ (present(environment.CODEBUDDY_SESSION_ID) &&
24
+ present(environment.WORKBUDDY_APP_VERSION) &&
25
+ present(environment.WORKBUDDY_APP_PATH));
26
+ if (codex && workbuddy)
27
+ return { reason: "conflicting" };
28
+ if (codex)
29
+ return { agent: "codex" };
30
+ if (workbuddy)
31
+ return { agent: "workbuddy" };
32
+ return { reason: "unknown" };
33
+ }
34
+ async function chooseInTerminal() {
35
+ const terminal = createInterface({
36
+ input: process.stdin,
37
+ output: process.stderr,
38
+ });
39
+ const controller = new AbortController();
40
+ const cancel = () => controller.abort();
41
+ terminal.once("SIGINT", cancel);
42
+ terminal.once("close", cancel);
43
+ try {
44
+ const answer = (await terminal.question("请选择使用 NGGAIGC 的 AI 助手:1. Codex 2. WorkBuddy\n输入 1 或 2:", { signal: controller.signal }))
45
+ .trim()
46
+ .toLowerCase();
47
+ if (answer === "1" || answer === "codex")
48
+ return "codex";
49
+ if (answer === "2" || answer === "workbuddy")
50
+ return "workbuddy";
51
+ return undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ finally {
57
+ terminal.off("SIGINT", cancel);
58
+ terminal.off("close", cancel);
59
+ terminal.close();
60
+ }
61
+ }
62
+ /** Resolve before any installation, browser authorization or client request. */
63
+ export async function selectCommandLocation(options, runtime = {}) {
64
+ const environment = runtime.environment ?? process.env;
65
+ if (options.agent !== undefined ||
66
+ options.skillsDirectory !== undefined ||
67
+ options.codexHome !== undefined) {
68
+ resolveSkillLocation(options, environment);
69
+ return options;
70
+ }
71
+ const detected = detectCallingAgent(environment);
72
+ if ("agent" in detected)
73
+ return { ...options, agent: detected.agent };
74
+ const choose = runtime.choose ??
75
+ (process.stdin.isTTY && process.stderr.isTTY
76
+ ? chooseInTerminal
77
+ : undefined);
78
+ const agent = await choose?.();
79
+ if (agent === "codex" || agent === "workbuddy")
80
+ return { ...options, agent };
81
+ throw new AgentSelectionRequired(detected.reason);
82
+ }
83
+ export function selectionRequiredResult(error) {
84
+ return {
85
+ status: "selection-required",
86
+ error: { code: error.code, reason: error.reason },
87
+ message: "请选择当前使用的 AI 助手,然后在原命令中添加 --agent codex 或 --agent workbuddy 继续;client 命令的选项须放在 -- 分隔符之前。",
88
+ choices: agentChoices,
89
+ nextAction: "select-agent-and-retry",
90
+ };
91
+ }
package/dist/bundle.js CHANGED
@@ -47,7 +47,7 @@ export async function verifyBundle(input) {
47
47
  !exactKeys(manifestValue, manifestKeys) ||
48
48
  manifestValue.schemaVersion !== "nggaigc-plugin-package/v1" ||
49
49
  manifestValue.artifactId !== "plugin.nggaigc" ||
50
- manifestValue.version !== "1.1.43" ||
50
+ manifestValue.version !== "1.1.45" ||
51
51
  manifestValue.artifactPath !==
52
52
  `packages/plugins/nggaigc-plugin-${manifestValue.version}.zip` ||
53
53
  typeof manifestValue.sourceCommit !== "string" ||
package/dist/errors.js CHANGED
@@ -1,9 +1,57 @@
1
+ const localCauseCodes = new Set([
2
+ "EPERM",
3
+ "EACCES",
4
+ "ENOENT",
5
+ "EAGAIN",
6
+ "EMFILE",
7
+ "ENFILE",
8
+ "ENOMEM",
9
+ "EINVAL",
10
+ "ERR_ACCESS_DENIED",
11
+ "ERR_MODULE_NOT_FOUND",
12
+ "SETUP_REQUIRED",
13
+ "CREDENTIAL_FRAME_INVALID",
14
+ "CREDENTIAL_STORE_FAILED",
15
+ "INVALID_INPUT",
16
+ "CREDENTIAL_CHANGED",
17
+ "ALREADY_CONFIGURED",
18
+ "CREDENTIAL_STORE_UNAVAILABLE",
19
+ "CREDENTIAL_HELPER_START_FAILED",
20
+ "CREDENTIAL_HELPER_FAILED",
21
+ "DISTRIBUTION_INVALID",
22
+ "LOCAL_SESSION_FAILED",
23
+ "LOCAL_SESSION_TIMEOUT",
24
+ "POWERSHELL_EXECUTION_POLICY_BLOCKED",
25
+ "RUNTIME_UNSUPPORTED",
26
+ "UNKNOWN",
27
+ ]);
28
+ export function safeConnectionDiagnostics(value) {
29
+ if (value === null || typeof value !== "object" || !("stage" in value))
30
+ return undefined;
31
+ const stage = value.stage;
32
+ if (stage !== "helper_start" && stage !== "credential_verification")
33
+ return undefined;
34
+ const cause = "causeCode" in value ? value.causeCode : undefined;
35
+ return {
36
+ stage,
37
+ causeCode: typeof cause === "string" && localCauseCodes.has(cause)
38
+ ? cause
39
+ : "UNKNOWN",
40
+ };
41
+ }
1
42
  export class CliError extends Error {
2
43
  code;
3
- constructor(code) {
44
+ stage;
45
+ causeCode;
46
+ constructor(code, diagnostics) {
4
47
  super(code);
5
48
  this.code = code;
6
49
  this.name = "CliError";
50
+ const safe = safeConnectionDiagnostics(diagnostics);
51
+ if (safe) {
52
+ this.stage = safe.stage;
53
+ this.causeCode = safe.causeCode;
54
+ }
7
55
  }
8
56
  }
9
57
  export function fixedErrorCode(error) {
@@ -1,6 +1,7 @@
1
1
  import { createHash, createPublicKey, randomUUID, verify } from "node:crypto";
2
- import { open, lstat, mkdir, readFile, readdir, rename, rm, writeFile, } from "node:fs/promises";
2
+ import { open, lstat, mkdir, readFile, readdir, realpath, rename, rm, writeFile, } from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { resolveSkillLocation, stateDirectoryForSkills, } from "./skill-location.js";
4
5
  const ID = /^skill\.[a-z0-9][a-z0-9.-]{0,79}$/;
5
6
  const VERSION = /^\d+\.\d+\.\d+$/;
6
7
  const DIGEST = /^[a-f0-9]{64}$/;
@@ -17,6 +18,33 @@ export class IndependentSkillError extends Error {
17
18
  function fail(code) {
18
19
  throw new IndependentSkillError(code);
19
20
  }
21
+ async function independentLocation(options, create = false) {
22
+ // Missing programmatic arguments must not select the user's real home.
23
+ if (options.codexHome === undefined &&
24
+ options.skillsDirectory === undefined &&
25
+ options.agent === undefined)
26
+ fail("INSTALL_PATH_UNSAFE");
27
+ try {
28
+ const selected = resolveSkillLocation(options).skillsDirectory;
29
+ if (await hasLinkedPathComponent(selected, create))
30
+ fail("INSTALL_PATH_UNSAFE");
31
+ if (create)
32
+ await mkdir(selected, { recursive: true });
33
+ // One physical target must use one journal, including case aliases on
34
+ // case-insensitive disks. Check links before resolving the actual casing.
35
+ const skillsDirectory = await realpath(selected);
36
+ const legacyDirectory = await realpath(path.join(path.dirname(skillsDirectory), "skills")).catch(() => null);
37
+ return {
38
+ skillsDirectory,
39
+ stateDirectory: legacyDirectory === skillsDirectory
40
+ ? path.join(path.dirname(skillsDirectory), ".nggaigc-managed")
41
+ : stateDirectoryForSkills(skillsDirectory),
42
+ };
43
+ }
44
+ catch {
45
+ return fail("INSTALL_PATH_UNSAFE");
46
+ }
47
+ }
20
48
  async function hasLinkedPathComponent(value, allowMissing = false) {
21
49
  const absolute = path.resolve(value);
22
50
  const root = path.parse(absolute).root;
@@ -795,10 +823,7 @@ async function acquireInstallLock(lock) {
795
823
  }
796
824
  export async function prepareIndependentSkill(options) {
797
825
  const ordered = resolve(options.index, options.skillId, options.skillVersion, options.rootSkillVersion, options.platformVersion);
798
- if (!path.isAbsolute(options.codexHome))
799
- fail("INSTALL_PATH_UNSAFE");
800
- const skillsRoot = path.join(options.codexHome, "skills");
801
- const stateRoot = path.join(options.codexHome, ".nggaigc-managed");
826
+ const { skillsDirectory: skillsRoot, stateDirectory: stateRoot } = await independentLocation(options, true);
802
827
  if (await hasLinkedPathComponent(skillsRoot, true).catch(() => true))
803
828
  fail("INSTALL_PATH_UNSAFE");
804
829
  if (await hasLinkedPathComponent(stateRoot, true).catch(() => true))
@@ -943,10 +968,7 @@ export async function rollbackIndependentSkill(options) {
943
968
  entry.version === options.installedVersion);
944
969
  if (!current)
945
970
  fail("ROLLBACK_UNAVAILABLE");
946
- if (!path.isAbsolute(options.codexHome))
947
- fail("INSTALL_PATH_UNSAFE");
948
- const skillsRoot = path.join(options.codexHome, "skills");
949
- const stateRoot = path.join(options.codexHome, ".nggaigc-managed");
971
+ const { skillsDirectory: skillsRoot, stateDirectory: stateRoot } = await independentLocation(options);
950
972
  if (await hasLinkedPathComponent(skillsRoot).catch(() => true))
951
973
  fail("INSTALL_PATH_UNSAFE");
952
974
  if (await hasLinkedPathComponent(stateRoot).catch(() => true))
package/dist/install.js CHANGED
@@ -4,11 +4,12 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { extractBundle, verifyBundle, verifyExtractedBundle, verifyInstalledSkill, } from "./bundle.js";
7
- import { CliError, fixedErrorCode } from "./errors.js";
7
+ import { CliError, fixedErrorCode, safeConnectionDiagnostics, } from "./errors.js";
8
+ import { resolveSkillLocation, } from "./skill-location.js";
8
9
  function defaultBundle() {
9
10
  const vendor = fileURLToPath(new URL("../vendor/", import.meta.url));
10
11
  return {
11
- archive: path.join(vendor, "nggaigc-plugin-1.1.43.zip"),
12
+ archive: path.join(vendor, "nggaigc-plugin-1.1.45.zip"),
12
13
  manifest: path.join(vendor, "manifest.json"),
13
14
  trust: path.join(vendor, "trusted-keys.json"),
14
15
  };
@@ -36,6 +37,47 @@ function installationError(error) {
36
37
  return new CliError("SKILL_PAIR_RECOVERY_REQUIRED");
37
38
  return new CliError("INSTALLATION_FAILED");
38
39
  }
40
+ function connectionError(error, fallback) {
41
+ const code = fixedErrorCode(error);
42
+ const localCodes = [
43
+ "CREDENTIAL_HELPER_START_FAILED",
44
+ "CREDENTIAL_VERIFICATION_FAILED",
45
+ "CREDENTIAL_STORE_FAILED",
46
+ "CREDENTIAL_STORE_UNAVAILABLE",
47
+ "CREDENTIAL_FRAME_INVALID",
48
+ "CREDENTIAL_HELPER_FAILED",
49
+ "DISTRIBUTION_INVALID",
50
+ "LOCAL_SESSION_FAILED",
51
+ "LOCAL_SESSION_TIMEOUT",
52
+ "POWERSHELL_EXECUTION_POLICY_BLOCKED",
53
+ "RUNTIME_UNSUPPORTED",
54
+ "UNSUPPORTED_PLATFORM",
55
+ "BROWSER_OPEN_FAILED",
56
+ ];
57
+ if (localCodes.includes(code))
58
+ return new CliError(code, error);
59
+ // Older signed launchers can throw a synchronous OS spawn error before
60
+ // they can return a protected error envelope. Keep only this closed cause.
61
+ if (error !== null &&
62
+ typeof error === "object" &&
63
+ "syscall" in error &&
64
+ typeof error.syscall === "string" &&
65
+ /^spawn(?: |$)/.test(error.syscall)) {
66
+ return new CliError("CREDENTIAL_HELPER_START_FAILED", {
67
+ stage: "helper_start",
68
+ causeCode: code,
69
+ });
70
+ }
71
+ if (fallback === "BROWSER_AUTHORIZATION_FAILED" &&
72
+ [
73
+ "AUTHORIZATION_REJECTED",
74
+ "AUTHORIZATION_CANCELLED",
75
+ "ACTIVATION_REJECTED",
76
+ "ACTIVATION_INPUT_INVALID",
77
+ ].includes(code ?? ""))
78
+ return new CliError(fallback);
79
+ return new CliError("CONNECTION_VERIFICATION_FAILED");
80
+ }
39
81
  async function installedVersion(target) {
40
82
  try {
41
83
  const marker = JSON.parse(await readFile(path.join(target, ".nggaigc-install.json"), "utf8"));
@@ -59,11 +101,11 @@ async function installedVersion(target) {
59
101
  return undefined;
60
102
  }
61
103
  }
62
- async function installSkill(installer, source, profile, codexHome, expectedVersion, bundleFiles) {
63
- const target = path.join(codexHome, "skills", "nggaigc");
104
+ async function installSkill(installer, source, profile, skillsDirectory, expectedVersion, bundleFiles) {
105
+ const target = path.join(skillsDirectory, "nggaigc");
64
106
  try {
65
107
  const result = await installer.main(["install", "--profile", profile], {
66
- codexHome,
108
+ skillsDirectory,
67
109
  source,
68
110
  });
69
111
  if (result.status !== "installed" || result.version !== expectedVersion)
@@ -84,7 +126,7 @@ async function installSkill(installer, source, profile, codexHome, expectedVersi
84
126
  }
85
127
  try {
86
128
  const result = await installer.main(["update", "--profile", profile], {
87
- codexHome,
129
+ skillsDirectory,
88
130
  source,
89
131
  });
90
132
  if (result.status !== "updated" || result.version !== expectedVersion)
@@ -115,10 +157,8 @@ async function connect(launcher, options, report, probe) {
115
157
  catch (error) {
116
158
  if (!probe && fixedErrorCode(error) === "SETUP_REQUIRED")
117
159
  configured = false;
118
- else if (fixedErrorCode(error) === "POWERSHELL_EXECUTION_POLICY_BLOCKED")
119
- throw new CliError("POWERSHELL_EXECUTION_POLICY_BLOCKED");
120
160
  else
121
- throw new CliError("CONNECTION_VERIFICATION_FAILED");
161
+ throw connectionError(error, "CONNECTION_VERIFICATION_FAILED");
122
162
  }
123
163
  if (!configured) {
124
164
  report("launching");
@@ -156,11 +196,7 @@ async function connect(launcher, options, report, probe) {
156
196
  throw new Error();
157
197
  }
158
198
  catch (error) {
159
- if (fixedErrorCode(error) === "BROWSER_OPEN_FAILED")
160
- throw new CliError("BROWSER_OPEN_FAILED");
161
- if (fixedErrorCode(error) === "POWERSHELL_EXECUTION_POLICY_BLOCKED")
162
- throw new CliError("POWERSHELL_EXECUTION_POLICY_BLOCKED");
163
- throw new CliError("BROWSER_AUTHORIZATION_FAILED");
199
+ throw connectionError(error, "BROWSER_AUTHORIZATION_FAILED");
164
200
  }
165
201
  finally {
166
202
  process.off("SIGINT", interrupt);
@@ -180,9 +216,7 @@ function assertDisposableRoot(value, expectedParent) {
180
216
  export async function runInstall(options = {}) {
181
217
  validateRuntime(options.platform ?? process.platform, options.architecture ?? process.arch);
182
218
  const profile = safeProfile(options.profile ?? os.homedir());
183
- const codexHome = safeProfile(options.codexHome ??
184
- (options.profile ? undefined : process.env.CODEX_HOME) ??
185
- path.join(profile, ".codex"));
219
+ const { skillsDirectory } = resolveSkillLocation(options);
186
220
  const report = options.reporter ?? (() => undefined);
187
221
  report("verifying");
188
222
  const bundle = await verifyBundle(options.bundle ?? defaultBundle());
@@ -200,8 +234,10 @@ export async function runInstall(options = {}) {
200
234
  const installer = require(path.join(extracted, "installer", "install.cjs"));
201
235
  if (!installer || typeof installer.main !== "function")
202
236
  throw new CliError("INSTALLATION_FAILED");
203
- const installation = await installSkill(installer, path.join(extracted, "skills", "nggaigc"), profile, codexHome, bundle.version, bundle.files);
204
- const installedScripts = path.join(codexHome, "skills", "nggaigc", "scripts");
237
+ const installation = await installSkill(installer, path.join(extracted, "skills", "nggaigc"), profile, skillsDirectory, bundle.version, bundle.files);
238
+ const installedScripts = path.join(skillsDirectory, "nggaigc", "scripts");
239
+ if (options.connect === false)
240
+ return { status: "installed", installation, version: bundle.version };
205
241
  const launcher = require(path.join(installedScripts, "launch-ngg-client.cjs"));
206
242
  if (!launcher ||
207
243
  typeof launcher.main !== "function" ||
@@ -214,11 +250,13 @@ export async function runInstall(options = {}) {
214
250
  catch (error) {
215
251
  if (!(error instanceof CliError))
216
252
  throw error;
253
+ const diagnostics = safeConnectionDiagnostics(error);
217
254
  return {
218
255
  status: "installed-unconnected",
219
256
  installation,
220
257
  version: bundle.version,
221
258
  connectionError: error.code,
259
+ ...(diagnostics ? { connectionDiagnostics: diagnostics } : {}),
222
260
  };
223
261
  }
224
262
  }
package/dist/main.js CHANGED
@@ -1,6 +1,9 @@
1
- import { CliError, fixedErrorCode } from "./errors.js";
1
+ import path from "node:path";
2
+ import { CliError, fixedErrorCode, safeConnectionDiagnostics, } from "./errors.js";
2
3
  import { runInstall } from "./install.js";
3
- import { runSkillStartCommand, } from "./skill-command.js";
4
+ import { runSkillStartCommand, runClientCommand, } from "./skill-command.js";
5
+ import { parseSkillLocation, resolveSkillLocation } from "./skill-location.js";
6
+ import { AgentSelectionRequired, selectCommandLocation, selectionRequiredResult, } from "./agent-selection.js";
4
7
  const messages = {
5
8
  authorizing: "浏览器已成功启动,请在 NGGAIGC 页面确认连接…",
6
9
  connected: "NGGAIGC Skill 已安装,设备连接验证成功。",
@@ -11,14 +14,23 @@ const messages = {
11
14
  };
12
15
  const errorMessages = {
13
16
  ARCHIVE_EXTRACTION_FAILED: "安装包无法安全解压,请稍后重试。",
14
- BROWSER_AUTHORIZATION_FAILED: "网页授权未完成;请在新 Codex 任务中调用 NGGAIGC 继续连接,无需重装。",
15
- BROWSER_OPEN_FAILED: "系统未能打开默认浏览器,也无法提供手动授权入口;请在新 Codex 任务中调用 NGGAIGC 重试连接,无需重装。",
16
- CONNECTION_VERIFICATION_FAILED: "设备连接验证未完成;请在新 Codex 任务中检查连接状态,无需重装。",
17
+ BROWSER_AUTHORIZATION_FAILED: "网页授权未完成;请在当前 Agent 中调用 NGGAIGC 继续连接,无需重装。",
18
+ BROWSER_OPEN_FAILED: "系统未能打开默认浏览器,也无法提供手动授权入口;请在当前 Agent 中调用 NGGAIGC 重试连接,无需重装。",
19
+ CONNECTION_VERIFICATION_FAILED: "设备连接验证未完成;请在当前 Agent 中检查连接状态,无需重装。",
20
+ CREDENTIAL_HELPER_START_FAILED: "本地凭据助手未能启动;请先处理当前运行错误,不要重复网页授权或重装。安装器没有更改系统保护。",
21
+ CREDENTIAL_VERIFICATION_FAILED: "凭据写入后的本地验证未完成,已尝试安全清理本次凭据;请先处理原本地错误,不要重复网页授权或重装。",
22
+ CREDENTIAL_STORE_FAILED: "本地凭据保存未完成;请先检查本地存储错误,不要重复网页授权。",
23
+ CREDENTIAL_STORE_UNAVAILABLE: "本地凭据存储暂不可用;请先检查本地运行条件,不要重复网页授权。",
24
+ CREDENTIAL_FRAME_INVALID: "本地凭据数据未能通过校验;请先处理当前本地错误,不要重复网页授权。",
25
+ CREDENTIAL_HELPER_FAILED: "本地凭据助手未能完成操作;请先处理当前本地错误,不要重复网页授权。",
26
+ DISTRIBUTION_INVALID: "本地受信客户端文件无法通过校验;请先检查当前安装,不要重复网页授权或手工修改签名文件。",
27
+ LOCAL_SESSION_FAILED: "本地凭据会话未能完成;请先处理本地错误,不要重复网页授权。",
28
+ LOCAL_SESSION_TIMEOUT: "本地凭据会话超时;请先检查当前本地进程,不要重复网页授权。",
17
29
  POWERSHELL_EXECUTION_POLICY_BLOCKED: "Windows 脚本执行受到系统或组织策略限制,设备连接未完成。安装器没有更改执行策略。",
18
30
  INSTALLATION_FAILED: "本地安装未完成,请检查现有文件状态后重试。",
19
31
  SKILL_PAIR_UPGRADE_REQUIRED: "检测到无法安全配套迁移的本地 Seedream Skill,已停止升级。请保留旧 Skill 和任务文件并联系支持。",
20
32
  SKILL_PAIR_RECOVERY_REQUIRED: "检测到本地组合安装中断或目录变化,已停止自动操作。请保留相关目录和任务文件并联系支持。",
21
- INVALID_INPUT: "命令格式无效。请使用 install,或 start-skill skill.<id> [version]。",
33
+ INVALID_INPUT: "命令格式无效。使用 --help 查看 install、start-skill 和 client 的目录选项。",
22
34
  PACKAGE_VERIFICATION_FAILED: "安装包签名或完整性验证失败,已停止安装。",
23
35
  RUNTIME_UNSUPPORTED: "请使用 Node.js 22.x(22.13 起)或 24.x 版本。",
24
36
  UNSUPPORTED_PLATFORM: "当前系统或架构尚未获得此签名安装包支持,请使用已支持的平台。",
@@ -26,24 +38,91 @@ const errorMessages = {
26
38
  export async function main(args, runtime = {}) {
27
39
  const output = runtime.output ?? ((line) => process.stdout.write(line));
28
40
  const errorOutput = runtime.error ?? ((line) => process.stderr.write(line));
29
- if (args[0] === "start-skill" && (args.length === 2 || args.length === 3)) {
41
+ if (args.length === 1 && ["--help", "-h", "help"].includes(args[0])) {
42
+ output([
43
+ "NGGAIGC CLI",
44
+ "install [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>] [--no-login]",
45
+ "start-skill skill.<id> [version] [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>]",
46
+ "client [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>] -- <command> [arguments]",
47
+ "Default: automatically detect Codex or WorkBuddy. If unknown or conflicting, select one; non-interactive calls return selection-required (exit 3).",
48
+ "Codex: existing CODEX_HOME is preserved, otherwise ~/.codex/skills. WorkBuddy: its runtime config directory/skills, otherwise ~/.workbuddy/skills; existing legacy ~/.codebuddy installs are reused. Explicit legacy universal/custom targets remain supported.",
49
+ "Use the same target for install and subsequent commands. --no-login installs verified files without opening a browser.",
50
+ "client examples: -- status, -- login, -- account-status, -- workflows, -- history, -- disconnect",
51
+ "",
52
+ ].join("\n"));
53
+ return 0;
54
+ }
55
+ if (args[0] === "client") {
56
+ const controller = new AbortController();
57
+ const interrupt = () => controller.abort();
58
+ try {
59
+ const separator = args.indexOf("--");
60
+ if (separator < 1 || separator === args.length - 1)
61
+ throw new CliError("INVALID_INPUT");
62
+ const { location, positional } = parseSkillLocation(args.slice(1, separator));
63
+ if (positional.length)
64
+ throw new CliError("INVALID_INPUT");
65
+ process.once("SIGINT", interrupt);
66
+ const selectedLocation = await selectCommandLocation(location, runtime.agentSelection);
67
+ const result = await (runtime.client ?? runClientCommand)(args.slice(separator + 1), selectedLocation, {
68
+ signal: controller.signal,
69
+ browserManualLink: (url) => errorOutput(`如果没有看到授权网页,请在本机浏览器打开此一次性链接;不要转发或保存:\n${url}\n`),
70
+ browserFallback: (url) => errorOutput(`浏览器未能自动打开,请在本机手动打开此一次性链接;不要转发或保存:\n${url}\n`),
71
+ });
72
+ output(`${JSON.stringify(result)}\n`);
73
+ return 0;
74
+ }
75
+ catch (error) {
76
+ if (error instanceof AgentSelectionRequired) {
77
+ output(`${JSON.stringify(selectionRequiredResult(error))}\n`);
78
+ return 3;
79
+ }
80
+ errorOutput(`${JSON.stringify({ error: { code: fixedErrorCode(error) ?? "CLIENT_UNAVAILABLE", ...safeConnectionDiagnostics(error) } })}\n`);
81
+ return 1;
82
+ }
83
+ finally {
84
+ process.off("SIGINT", interrupt);
85
+ }
86
+ }
87
+ let parsed;
88
+ try {
89
+ parsed = parseSkillLocation(args.slice(1));
90
+ }
91
+ catch {
92
+ errorOutput(`${errorMessages.INVALID_INPUT}\n`);
93
+ return 1;
94
+ }
95
+ const parameters = parsed.positional;
96
+ if (args[0] === "start-skill" &&
97
+ (parameters.length === 1 || parameters.length === 2)) {
30
98
  try {
31
- const result = await (runtime.startSkill ?? runSkillStartCommand)(args[1], args[2], runtime.skillOptions);
99
+ const location = await selectCommandLocation({ ...runtime.skillOptions, ...parsed.location }, runtime.agentSelection);
100
+ const result = await (runtime.startSkill ?? runSkillStartCommand)(parameters[0], parameters[1], location);
32
101
  output(`${JSON.stringify({ status: "ready", ...result })}\n`);
33
102
  return 0;
34
103
  }
35
104
  catch (error) {
105
+ if (error instanceof AgentSelectionRequired) {
106
+ output(`${JSON.stringify(selectionRequiredResult(error))}\n`);
107
+ return 3;
108
+ }
36
109
  const code = fixedErrorCode(error) ?? "SKILL_START_UNAVAILABLE";
37
110
  errorOutput(`${JSON.stringify({ error: { code } })}\n`);
38
111
  return 1;
39
112
  }
40
113
  }
41
- if (args.length !== 1 || args[0] !== "install") {
114
+ if (args[0] !== "install" ||
115
+ !(parameters.length === 0 ||
116
+ (parameters.length === 1 && parameters[0] === "--no-login"))) {
42
117
  errorOutput(`${errorMessages.INVALID_INPUT}\n`);
43
118
  return 1;
44
119
  }
45
120
  try {
121
+ const location = await selectCommandLocation(parsed.location, runtime.agentSelection);
122
+ const { skillsDirectory } = resolveSkillLocation(location);
46
123
  const result = await (runtime.install ?? runInstall)({
124
+ ...location,
125
+ ...(parameters[0] === "--no-login" ? { connect: false } : {}),
47
126
  reporter: (event) => output(`${messages[event]}\n`),
48
127
  launcherOptionsFactory: () => ({
49
128
  browserManualLink: (url) => {
@@ -56,13 +135,24 @@ export async function main(args, runtime = {}) {
56
135
  },
57
136
  }),
58
137
  });
138
+ output(`${JSON.stringify({
139
+ ...result,
140
+ ...(location.agent ? { agent: location.agent } : {}),
141
+ skillsDirectory,
142
+ skillPath: path.join(skillsDirectory, "nggaigc", "SKILL.md"),
143
+ guidance: "在当前 AI 助手中调用 NGGAIGC;若未发现新 Skill,请新开一个对话。后续安装、更新和工作流加载沿用此 skillsDirectory。",
144
+ })}\n`);
59
145
  if (result.status === "installed-unconnected") {
60
- errorOutput(`NGGAIGC Skill ${result.version} 文件已${result.installation === "unchanged" ? "存在" : "安装"},但设备尚未连接。${errorMessages[result.connectionError] ?? "请在新 Codex 任务中检查连接状态。"}\n`);
146
+ errorOutput(`NGGAIGC Skill ${result.version} 文件已${result.installation === "unchanged" ? "存在" : "安装"},但设备尚未连接。${errorMessages[result.connectionError] ?? "请在当前 Agent 中检查连接状态。"}\n`);
61
147
  return 2;
62
148
  }
63
149
  return 0;
64
150
  }
65
151
  catch (error) {
152
+ if (error instanceof AgentSelectionRequired) {
153
+ output(`${JSON.stringify(selectionRequiredResult(error))}\n`);
154
+ return 3;
155
+ }
66
156
  const code = error instanceof CliError ? error.code : "INSTALLATION_FAILED";
67
157
  errorOutput(`${errorMessages[code] ?? errorMessages.INSTALLATION_FAILED}\n`);
68
158
  return 1;
@@ -1,9 +1,9 @@
1
1
  import { createHash, createPublicKey } from "node:crypto";
2
2
  import { readFile, realpath } from "node:fs/promises";
3
3
  import { createRequire } from "node:module";
4
- import os from "node:os";
5
4
  import path from "node:path";
6
5
  import { fileURLToPath } from "node:url";
6
+ import { resolveSkillLocation, } from "./skill-location.js";
7
7
  import { trustedKeyId, trustedKeySpkiSha256, verifyBundle, verifyInstalledSkill, } from "./bundle.js";
8
8
  import { IndependentSkillError, projectActiveSkillCatalog, startIndependentSkill, } from "./independent-skill.js";
9
9
  function fail(code) {
@@ -12,14 +12,14 @@ function fail(code) {
12
12
  function record(value) {
13
13
  return value !== null && typeof value === "object" && !Array.isArray(value);
14
14
  }
15
- async function productionDependencies(codexHome) {
15
+ export async function productionDependencies(skillsDirectory) {
16
16
  const vendor = fileURLToPath(new URL("../vendor/", import.meta.url));
17
17
  const bundle = await verifyBundle({
18
- archive: path.join(vendor, "nggaigc-plugin-1.1.43.zip"),
18
+ archive: path.join(vendor, "nggaigc-plugin-1.1.45.zip"),
19
19
  manifest: path.join(vendor, "manifest.json"),
20
20
  trust: path.join(vendor, "trusted-keys.json"),
21
21
  });
22
- const root = path.join(codexHome, "skills", "nggaigc");
22
+ const root = path.join(skillsDirectory, "nggaigc");
23
23
  await verifyInstalledSkill(root, bundle.files);
24
24
  const source = JSON.parse(await readFile(path.join(root, "references", "source.json"), "utf8"));
25
25
  if (!record(source) ||
@@ -58,24 +58,28 @@ async function productionDependencies(codexHome) {
58
58
  rootSkillVersion: source.version,
59
59
  };
60
60
  }
61
+ /** Generic bridge only: the verified launcher owns command and API contracts. */
62
+ export async function runClientCommand(args, options = {}, launcherOptions = {}) {
63
+ if (args.length === 0)
64
+ fail("INVALID_INPUT");
65
+ const { skillsDirectory } = resolveSkillLocation(options);
66
+ const { launcher } = await productionDependencies(skillsDirectory);
67
+ return launcher.main(args, launcherOptions);
68
+ }
61
69
  /** Candidate npm entry: active catalog → server admission → verified local Skill path. */
62
70
  export async function runSkillStartCommand(skillId, skillVersion, options = {}) {
63
71
  if (!/^skill\.[a-z0-9][a-z0-9.-]{0,79}$/.test(skillId) ||
64
72
  (skillVersion !== undefined && !/^\d+\.\d+\.\d+$/.test(skillVersion)))
65
73
  fail("INVALID_INPUT");
66
- const configuredCodexHome = path.resolve(options.codexHome ??
67
- process.env.CODEX_HOME ??
68
- path.join(os.homedir(), ".codex"));
69
- if (!path.isAbsolute(configuredCodexHome))
70
- fail("INSTALL_PATH_UNSAFE");
71
- const codexHome = process.platform === "win32"
72
- ? await realpath(configuredCodexHome).catch((error) => record(error) && error.code === "ENOENT"
73
- ? configuredCodexHome
74
+ const configuredDirectory = resolveSkillLocation(options).skillsDirectory;
75
+ const skillsDirectory = process.platform === "win32"
76
+ ? await realpath(configuredDirectory).catch((error) => record(error) && error.code === "ENOENT"
77
+ ? configuredDirectory
74
78
  : fail("INSTALL_PATH_UNSAFE"))
75
- : configuredCodexHome;
79
+ : configuredDirectory;
76
80
  const production = options.launcher && options.trust && options.rootSkillVersion
77
81
  ? null
78
- : await productionDependencies(codexHome);
82
+ : await productionDependencies(skillsDirectory);
79
83
  const launcher = options.launcher ?? production.launcher;
80
84
  const trust = options.trust ?? production.trust;
81
85
  const rootSkillVersion = options.rootSkillVersion ?? production.rootSkillVersion;
@@ -96,13 +100,13 @@ export async function runSkillStartCommand(skillId, skillVersion, options = {})
96
100
  if (matches.length !== 1)
97
101
  fail("SKILL_UNAVAILABLE");
98
102
  const selected = matches[0];
99
- return startIndependentSkill({
103
+ const result = await startIndependentSkill({
100
104
  index,
101
105
  skillId: selected.id,
102
106
  skillVersion: selected.version,
103
107
  rootSkillVersion,
104
108
  platformVersion,
105
- codexHome,
109
+ skillsDirectory,
106
110
  trust,
107
111
  fetcher: options.fetcher,
108
112
  admit: async (identity) => {
@@ -117,4 +121,9 @@ export async function runSkillStartCommand(skillId, skillVersion, options = {})
117
121
  return answer;
118
122
  },
119
123
  });
124
+ return {
125
+ ...result,
126
+ skillsDirectory,
127
+ rootLauncherPath: path.join(skillsDirectory, "nggaigc", "scripts", "launch-ngg-client.cjs"),
128
+ };
120
129
  }
@@ -0,0 +1,96 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { lstatSync } from "node:fs";
4
+ import { CliError } from "./errors.js";
5
+ export function absoluteDirectory(value) {
6
+ if (!path.isAbsolute(value) || /[\0\r\n]/u.test(value))
7
+ throw new CliError("INVALID_INPUT");
8
+ const resolved = path.resolve(value);
9
+ if (resolved === path.parse(resolved).root)
10
+ throw new CliError("INVALID_INPUT");
11
+ return resolved;
12
+ }
13
+ export function stateDirectoryForSkills(skillsDirectory) {
14
+ // Preserve existing */skills journals. Other targets use a different leaf:
15
+ // /x and /x/skills must never share backups or installation locks.
16
+ return path.basename(skillsDirectory) === "skills"
17
+ ? path.join(path.dirname(skillsDirectory), ".nggaigc-managed")
18
+ : path.join(skillsDirectory, ".nggaigc-managed", "custom-target");
19
+ }
20
+ function hasInstalledRoot(home) {
21
+ try {
22
+ lstatSync(path.join(home, "skills", "nggaigc"));
23
+ return true;
24
+ }
25
+ catch (error) {
26
+ if (error.code === "ENOENT")
27
+ return false;
28
+ throw new CliError("INSTALLATION_FAILED");
29
+ }
30
+ }
31
+ function workbuddyHome(profile, environment) {
32
+ const configured = environment.WORKBUDDY_CONFIG_DIR ?? environment.CODEBUDDY_CONFIG_DIR;
33
+ if (configured !== undefined)
34
+ return absoluteDirectory(configured);
35
+ const current = path.join(profile, ".workbuddy");
36
+ const legacy = path.join(profile, ".codebuddy");
37
+ // Upgrade an existing legacy global installation in place when outside the
38
+ // host. Inside WorkBuddy, its actual runtime configuration takes precedence.
39
+ // The installer still verifies ownership/signatures before changing files.
40
+ return !hasInstalledRoot(current) && hasInstalledRoot(legacy)
41
+ ? legacy
42
+ : current;
43
+ }
44
+ /** One explicit location for root installation and every dependent Skill. */
45
+ export function resolveSkillLocation(options = {}, environment = process.env) {
46
+ if ([options.agent, options.skillsDirectory, options.codexHome].filter((value) => value !== undefined).length > 1)
47
+ throw new CliError("INVALID_INPUT");
48
+ if (options.skillsDirectory !== undefined) {
49
+ const skillsDirectory = absoluteDirectory(options.skillsDirectory);
50
+ return {
51
+ skillsDirectory,
52
+ stateDirectory: stateDirectoryForSkills(skillsDirectory),
53
+ };
54
+ }
55
+ const profile = absoluteDirectory(options.profile ?? os.homedir());
56
+ const agent = options.agent ?? "codex";
57
+ if (!["codex", "workbuddy", "universal"].includes(agent))
58
+ throw new CliError("INVALID_INPUT");
59
+ const home = absoluteDirectory(options.codexHome ??
60
+ (agent === "codex"
61
+ ? ((options.profile === undefined
62
+ ? environment.CODEX_HOME
63
+ : undefined) ?? path.join(profile, ".codex"))
64
+ : agent === "workbuddy"
65
+ ? workbuddyHome(profile, options.profile === undefined ? environment : {})
66
+ : path.join(profile, ".agents")));
67
+ return {
68
+ skillsDirectory: path.join(home, "skills"),
69
+ // Explicit and named-host calls for the same directory share one journal.
70
+ stateDirectory: stateDirectoryForSkills(path.join(home, "skills")),
71
+ };
72
+ }
73
+ export function parseSkillLocation(args) {
74
+ const location = {};
75
+ const positional = [];
76
+ for (let index = 0; index < args.length; index += 1) {
77
+ const value = args[index];
78
+ if (value !== "--agent" && value !== "--skills-dir") {
79
+ positional.push(value);
80
+ continue;
81
+ }
82
+ if (location.agent !== undefined || location.skillsDirectory !== undefined)
83
+ throw new CliError("INVALID_INPUT");
84
+ const argument = args[++index];
85
+ if (!argument || argument.startsWith("--"))
86
+ throw new CliError("INVALID_INPUT");
87
+ if (value === "--agent") {
88
+ if (!["codex", "workbuddy", "universal"].includes(argument))
89
+ throw new CliError("INVALID_INPUT");
90
+ location.agent = argument;
91
+ }
92
+ else
93
+ location.skillsDirectory = absoluteDirectory(argument);
94
+ }
95
+ return { location, positional };
96
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nggaigc/cli",
3
- "version": "0.1.44",
4
- "description": "Secure one-command NGGAIGC Skill installer for Codex.",
3
+ "version": "0.1.47",
4
+ "description": "Secure NGGAIGC Skill installer for Codex, WorkBuddy and compatible agents.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "nggaigc": "dist/bin.js"
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": "nggaigc-plugin-package/v1",
3
3
  "artifactId": "plugin.nggaigc",
4
- "version": "1.1.43",
5
- "sourceCommit": "8ad838c5d286a3d321e0d12e8583d1a833575a20",
6
- "artifactPath": "packages/plugins/nggaigc-plugin-1.1.43.zip",
7
- "sha256": "46ed3cfd4701c51d3d916a307c6975448f326af12d3c58f17d0d9f88658d94ff",
8
- "sizeBytes": 493785,
4
+ "version": "1.1.45",
5
+ "sourceCommit": "241601b7d034ce36442e7738438cbc6c6b3b867d",
6
+ "artifactPath": "packages/plugins/nggaigc-plugin-1.1.45.zip",
7
+ "sha256": "e23b7781579124a1b90ece1558d58d418dfef6a6557152f1581cbb81145281da",
8
+ "sizeBytes": 502168,
9
9
  "fileCount": 24,
10
10
  "platforms": [
11
11
  "win32-x64",
@@ -13,8 +13,8 @@
13
13
  "darwin-arm64"
14
14
  ],
15
15
  "files": {
16
- ".codex-plugin/plugin.json": "df32ed1e3fa7732fe712b9021336f629f8f0dfaf0df9813b2b31089828ec61b6",
17
- "installer/install.cjs": "a0c94ed71edf4cd7eb4a66609a4dcf73049abbd14aa273ee65b9b3029be2da2b",
16
+ ".codex-plugin/plugin.json": "89478e096e5cbd46ad156cb06abee9a59973f530e08be815283bf6d4ddc3903f",
17
+ "installer/install.cjs": "76f4ed73233fd39900fb0bedbc434dea39e52e6b6b4622f3fd388a5ca09c6f62",
18
18
  "installer/legacy-root-digests.json": "922552646a5aa6cff8616c372912a37b1d5f263351206b85785ed9b2eae31641",
19
19
  "installer/signed-pair-1.1.16.json": "ccb22a90302cc5c9c585067ebc211a27b0c9d8a177c2740a5678a58a3d70bfe9",
20
20
  "installer/signed-pair-1.1.17.json": "a3697506c929aaa085a6d994fc3a1b9a87e522ec6e1e32f3f760e6a534effa2b",
@@ -24,24 +24,24 @@
24
24
  "migration/seedream-1.1.3/scripts/seedream.mjs": "dcc6af4d945790d9dfd7fac21c60934759d7ac2576a14507d0299f4833942a96",
25
25
  "migration/seedream-1.1.3/SKILL.md": "5c5cf3c2d99376f246425e873c5566436ed528256372ac3208abb60364c4517c",
26
26
  "skills/nggaigc/agents/openai.yaml": "8936d97b9821e687e224e9349cc42be2ee5fc20988562efffbcb70080aedf87c",
27
- "skills/nggaigc/references/catalog-and-updates.md": "c609a560ad8245d825511a4c47b6eb0a663d01a9beb57cecc7451928226a277f",
28
- "skills/nggaigc/references/source.json": "ca9231612752cb06e43596f13fa9976236b3ead304c7683546f5dc0af6107bec",
27
+ "skills/nggaigc/references/catalog-and-updates.md": "a43bee94a480aa2bf0b1e7038cee6f27fb86d2bd8fa88a28a63678963d7c1530",
28
+ "skills/nggaigc/references/source.json": "26838dd1c3df2347abb97ac47da61148c7099ce9f12c935b788c6fe2e9e26f0c",
29
29
  "skills/nggaigc/scripts/api_client.mjs": "e16bf750a297ef7e88e4843d2fba9c33b412a66516bfe76822d7deec6c153069",
30
30
  "skills/nggaigc/scripts/api_package.mjs": "6beee22aed770cbf501abe6d1d9d252f7a53893eeb9b6399bd26c4ef68470d5a",
31
- "skills/nggaigc/scripts/bootstrap-public.json": "a203a838fbb8c1b200e1cd30044a11ee851d2a6b1161d634e2f87f52dbcbf6f2",
32
- "skills/nggaigc/scripts/launch-ngg-client.cjs": "ada77f2d7505e908248cd7d2840b068559a21ad518f0e92560973faa8c633d0d",
31
+ "skills/nggaigc/scripts/bootstrap-public.json": "4b1aba50683bc50d3ac9c2ec10479366fa10e7452874b67f2cd6b93e3e23952d",
32
+ "skills/nggaigc/scripts/launch-ngg-client.cjs": "5953b9211b31e72b9f7d22a92f954e9b526387f6d86ac4387e0c810f3e01c0da",
33
33
  "skills/nggaigc/scripts/macos-keychain": "b0bdf29d14219d38ca18c7e5c599deb0daa8c8ac70b0babbc7d4fb23673079ec",
34
34
  "skills/nggaigc/scripts/macos-keychain.swift": "1b57e74fde66c824fe970547dd8ca0883a3334c34c065e85a8a2a90d4556a7f5",
35
- "skills/nggaigc/scripts/ngg_client.mjs": "4d572106c3bb2c84b13ecb3b68c9293768b821fe7b78a11281ccccee7916ba3c",
36
- "skills/nggaigc/scripts/ngg-session.mjs": "f42d6f0db94c9f66a6a7a5bc7754ba238e32ea12052ac706cb1e97ffe148ec55",
35
+ "skills/nggaigc/scripts/ngg_client.mjs": "2b4903ad8afe76b0dc970b5519a50d04dcc0e12dfa8f4267fb85d878187c3789",
36
+ "skills/nggaigc/scripts/ngg-session.mjs": "21df7cd76e729a2109e2f8ec53d5a5998e801ab9259ce8d20b9c62f9926b1135",
37
37
  "skills/nggaigc/scripts/supported-platforms.json": "ec8b0754c489e04534a1d9880dd6664ba9c5bcd07789e06d138e0923f23575ff",
38
38
  "skills/nggaigc/scripts/windows-credential.ps1": "80d470fb0c9473d26bd599cafa9f1c2ad69056d4a835888ddcbb5c1548f9d059",
39
- "skills/nggaigc/SKILL.md": "b871cbd03eb551c58282effd4c1e7d07df560fd4f95b460ddc0d9e570b912db1"
39
+ "skills/nggaigc/SKILL.md": "8cb3eb8a03cf144ce15fa01d7d342495c2b11ba0f6a0cf61a493e1e43c83a11f"
40
40
  },
41
41
  "signature": {
42
42
  "algorithm": "ed25519",
43
43
  "keyId": "ngg-signing-skill-release-1",
44
44
  "scope": "artifact-bytes",
45
- "value": "dDgL9aFqIt/xqrZI8R3fCCINYrs329MFzJt+GKdwXjEOGA4y87OXEle6UM6dRMPTZCHxZWSu6HLPOtdd8HfABA=="
45
+ "value": "OlUz+AuTVQN/u8Kp+d91twCKkU6VtRPUrzez8iGYLRmLck5lXvG6KUbVabPMIkEirKD/fOCcwJ47SKSsS8x+BQ=="
46
46
  }
47
47
  }
index bbbb0a7..bc17d2a 100644
Binary file