@nggaigc/cli 0.1.43 → 0.1.45

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。
4
4
 
5
5
  ```powershell
6
6
  npx --yes @nggaigc/cli@latest install
@@ -11,3 +11,23 @@ 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
+ ## 跨 Agent 目标(0.1.45 起)
16
+
17
+ 下列目标参数与 `client` 桥要求 CLI 0.1.45 或更高版本。旧版本不支持新参数;`@latest` 的实际可用版本以 npm registry 与对应签名制品为准。
18
+
19
+ ```text
20
+ npx --yes @nggaigc/cli@latest install --agent workbuddy
21
+ npx --yes @nggaigc/cli@latest install --agent universal --no-login
22
+ npx --yes @nggaigc/cli@latest install --skills-dir "/absolute/project/.codebuddy/skills" --no-login
23
+ npx --yes @nggaigc/cli@latest start-skill skill.<id> [version] --skills-dir "/absolute/project/.codebuddy/skills"
24
+ npx --yes @nggaigc/cli@latest client --skills-dir "/absolute/project/.codebuddy/skills" -- <原command> <原args>
25
+ ```
26
+
27
+ `install`、`start-skill` 和 `client` 支持互斥的 `--agent codex|workbuddy|universal` 或 `--skills-dir <绝对目录>`。未指定目标时仍安装至 Codex 的 `CODEX_HOME/skills`,未设置 `CODEX_HOME` 时为 `~/.codex/skills`。WorkBuddy 使用官方兼容配置目录 `~/.codebuddy/skills`;universal 使用 `~/.agents/skills`,不保证所有 Agent 自动识别。明确目录可用于宿主项目级技能路径。`--no-login` 仅跳过安装后的登录,不绕过签名、哈希、路径检查或后续鉴权。
28
+
29
+ 根 Skill 按实际读取的 `SKILL.md` 定位父目录及其上一级 `skillsDirectory`;后续升级与 `start-skill` 显式使用同一目录。准备回执增加 `skillsDirectory`、`rootLauncherPath`,保留 `preparedSkills`;宿主应显式读取返回的真实路径。独立包的原客户端调用使用回执的受信根启动器;`client` 是同目录根包验签桥,保留原命令/参数,不增加 API 专用业务分支。安装新包不证明当前宿主热加载,必要时开启新会话。
30
+
31
+ 本地技能包导入可能复制文件,需核导入后的实际路径和独立包同级关系。交互使用宿主可用的提问工具或当前聊天,交付使用宿主可用的媒体/附件能力;不依赖 Codex 专用工具。切换宿主继续复用当前系统用户的系统凭证库和任务历史,不迁移凭据、不要求重新粘贴密钥。资金、权限与未知结果同幂等键恢复规则不变。
32
+
33
+ 此功能不增加 Linux、Intel Mac 支持,也不代表已接入或认证 WorkBuddy 官方连接器市场;现有系统、Node.js 与正式发布门禁保持适用。
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.42" ||
50
+ manifestValue.version !== "1.1.44" ||
51
51
  manifestValue.artifactPath !==
52
52
  `packages/plugins/nggaigc-plugin-${manifestValue.version}.zip` ||
53
53
  typeof manifestValue.sourceCommit !== "string" ||
@@ -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
@@ -5,10 +5,11 @@ import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { extractBundle, verifyBundle, verifyExtractedBundle, verifyInstalledSkill, } from "./bundle.js";
7
7
  import { CliError, fixedErrorCode } 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.42.zip"),
12
+ archive: path.join(vendor, "nggaigc-plugin-1.1.44.zip"),
12
13
  manifest: path.join(vendor, "manifest.json"),
13
14
  trust: path.join(vendor, "trusted-keys.json"),
14
15
  };
@@ -59,11 +60,11 @@ async function installedVersion(target) {
59
60
  return undefined;
60
61
  }
61
62
  }
62
- async function installSkill(installer, source, profile, codexHome, expectedVersion, bundleFiles) {
63
- const target = path.join(codexHome, "skills", "nggaigc");
63
+ async function installSkill(installer, source, profile, skillsDirectory, expectedVersion, bundleFiles) {
64
+ const target = path.join(skillsDirectory, "nggaigc");
64
65
  try {
65
66
  const result = await installer.main(["install", "--profile", profile], {
66
- codexHome,
67
+ skillsDirectory,
67
68
  source,
68
69
  });
69
70
  if (result.status !== "installed" || result.version !== expectedVersion)
@@ -84,7 +85,7 @@ async function installSkill(installer, source, profile, codexHome, expectedVersi
84
85
  }
85
86
  try {
86
87
  const result = await installer.main(["update", "--profile", profile], {
87
- codexHome,
88
+ skillsDirectory,
88
89
  source,
89
90
  });
90
91
  if (result.status !== "updated" || result.version !== expectedVersion)
@@ -180,9 +181,7 @@ function assertDisposableRoot(value, expectedParent) {
180
181
  export async function runInstall(options = {}) {
181
182
  validateRuntime(options.platform ?? process.platform, options.architecture ?? process.arch);
182
183
  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"));
184
+ const { skillsDirectory } = resolveSkillLocation(options);
186
185
  const report = options.reporter ?? (() => undefined);
187
186
  report("verifying");
188
187
  const bundle = await verifyBundle(options.bundle ?? defaultBundle());
@@ -200,8 +199,10 @@ export async function runInstall(options = {}) {
200
199
  const installer = require(path.join(extracted, "installer", "install.cjs"));
201
200
  if (!installer || typeof installer.main !== "function")
202
201
  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");
202
+ const installation = await installSkill(installer, path.join(extracted, "skills", "nggaigc"), profile, skillsDirectory, bundle.version, bundle.files);
203
+ const installedScripts = path.join(skillsDirectory, "nggaigc", "scripts");
204
+ if (options.connect === false)
205
+ return { status: "installed", installation, version: bundle.version };
205
206
  const launcher = require(path.join(installedScripts, "launch-ngg-client.cjs"));
206
207
  if (!launcher ||
207
208
  typeof launcher.main !== "function" ||
package/dist/main.js CHANGED
@@ -1,6 +1,8 @@
1
+ import path from "node:path";
1
2
  import { CliError, fixedErrorCode } 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";
4
6
  const messages = {
5
7
  authorizing: "浏览器已成功启动,请在 NGGAIGC 页面确认连接…",
6
8
  connected: "NGGAIGC Skill 已安装,设备连接验证成功。",
@@ -11,14 +13,14 @@ const messages = {
11
13
  };
12
14
  const errorMessages = {
13
15
  ARCHIVE_EXTRACTION_FAILED: "安装包无法安全解压,请稍后重试。",
14
- BROWSER_AUTHORIZATION_FAILED: "网页授权未完成;请在新 Codex 任务中调用 NGGAIGC 继续连接,无需重装。",
15
- BROWSER_OPEN_FAILED: "系统未能打开默认浏览器,也无法提供手动授权入口;请在新 Codex 任务中调用 NGGAIGC 重试连接,无需重装。",
16
- CONNECTION_VERIFICATION_FAILED: "设备连接验证未完成;请在新 Codex 任务中检查连接状态,无需重装。",
16
+ BROWSER_AUTHORIZATION_FAILED: "网页授权未完成;请在当前 Agent 中调用 NGGAIGC 继续连接,无需重装。",
17
+ BROWSER_OPEN_FAILED: "系统未能打开默认浏览器,也无法提供手动授权入口;请在当前 Agent 中调用 NGGAIGC 重试连接,无需重装。",
18
+ CONNECTION_VERIFICATION_FAILED: "设备连接验证未完成;请在当前 Agent 中检查连接状态,无需重装。",
17
19
  POWERSHELL_EXECUTION_POLICY_BLOCKED: "Windows 脚本执行受到系统或组织策略限制,设备连接未完成。安装器没有更改执行策略。",
18
20
  INSTALLATION_FAILED: "本地安装未完成,请检查现有文件状态后重试。",
19
21
  SKILL_PAIR_UPGRADE_REQUIRED: "检测到无法安全配套迁移的本地 Seedream Skill,已停止升级。请保留旧 Skill 和任务文件并联系支持。",
20
22
  SKILL_PAIR_RECOVERY_REQUIRED: "检测到本地组合安装中断或目录变化,已停止自动操作。请保留相关目录和任务文件并联系支持。",
21
- INVALID_INPUT: "命令格式无效。请使用 install,或 start-skill skill.<id> [version]。",
23
+ INVALID_INPUT: "命令格式无效。使用 --help 查看 install、start-skill 和 client 的目录选项。",
22
24
  PACKAGE_VERIFICATION_FAILED: "安装包签名或完整性验证失败,已停止安装。",
23
25
  RUNTIME_UNSUPPORTED: "请使用 Node.js 22.x(22.13 起)或 24.x 版本。",
24
26
  UNSUPPORTED_PLATFORM: "当前系统或架构尚未获得此签名安装包支持,请使用已支持的平台。",
@@ -26,9 +28,59 @@ const errorMessages = {
26
28
  export async function main(args, runtime = {}) {
27
29
  const output = runtime.output ?? ((line) => process.stdout.write(line));
28
30
  const errorOutput = runtime.error ?? ((line) => process.stderr.write(line));
29
- if (args[0] === "start-skill" && (args.length === 2 || args.length === 3)) {
31
+ if (args.length === 1 && ["--help", "-h", "help"].includes(args[0])) {
32
+ output([
33
+ "NGGAIGC CLI",
34
+ "install [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>] [--no-login]",
35
+ "start-skill skill.<id> [version] [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>]",
36
+ "client [--agent codex|workbuddy|universal | --skills-dir <absolute-directory>] -- <command> [arguments]",
37
+ "Default: Codex (existing CODEX_HOME is preserved). WorkBuddy: ~/.codebuddy/skills; universal: ~/.agents/skills.",
38
+ "Use the same target for install and subsequent commands. --no-login installs verified files without opening a browser.",
39
+ "client examples: -- status, -- login, -- account-status, -- workflows, -- history, -- disconnect",
40
+ "",
41
+ ].join("\n"));
42
+ return 0;
43
+ }
44
+ if (args[0] === "client") {
45
+ const controller = new AbortController();
46
+ const interrupt = () => controller.abort();
47
+ try {
48
+ const separator = args.indexOf("--");
49
+ if (separator < 1 || separator === args.length - 1)
50
+ throw new CliError("INVALID_INPUT");
51
+ const { location, positional } = parseSkillLocation(args.slice(1, separator));
52
+ if (positional.length)
53
+ throw new CliError("INVALID_INPUT");
54
+ process.once("SIGINT", interrupt);
55
+ const result = await (runtime.client ?? runClientCommand)(args.slice(separator + 1), location, {
56
+ signal: controller.signal,
57
+ browserManualLink: (url) => errorOutput(`如果没有看到授权网页,请在本机浏览器打开此一次性链接;不要转发或保存:\n${url}\n`),
58
+ browserFallback: (url) => errorOutput(`浏览器未能自动打开,请在本机手动打开此一次性链接;不要转发或保存:\n${url}\n`),
59
+ });
60
+ output(`${JSON.stringify(result)}\n`);
61
+ return 0;
62
+ }
63
+ catch (error) {
64
+ errorOutput(`${JSON.stringify({ error: { code: fixedErrorCode(error) ?? "CLIENT_UNAVAILABLE" } })}\n`);
65
+ return 1;
66
+ }
67
+ finally {
68
+ process.off("SIGINT", interrupt);
69
+ }
70
+ }
71
+ let parsed;
72
+ try {
73
+ parsed = parseSkillLocation(args.slice(1));
74
+ }
75
+ catch {
76
+ errorOutput(`${errorMessages.INVALID_INPUT}\n`);
77
+ return 1;
78
+ }
79
+ const parameters = parsed.positional;
80
+ if (args[0] === "start-skill" &&
81
+ (parameters.length === 1 || parameters.length === 2)) {
30
82
  try {
31
- const result = await (runtime.startSkill ?? runSkillStartCommand)(args[1], args[2], runtime.skillOptions);
83
+ const result = await (runtime.startSkill ?? runSkillStartCommand)(parameters[0], parameters[1], { ...runtime.skillOptions, ...parsed.location });
32
84
  output(`${JSON.stringify({ status: "ready", ...result })}\n`);
33
85
  return 0;
34
86
  }
@@ -38,12 +90,16 @@ export async function main(args, runtime = {}) {
38
90
  return 1;
39
91
  }
40
92
  }
41
- if (args.length !== 1 || args[0] !== "install") {
93
+ if (args[0] !== "install" ||
94
+ !(parameters.length === 0 ||
95
+ (parameters.length === 1 && parameters[0] === "--no-login"))) {
42
96
  errorOutput(`${errorMessages.INVALID_INPUT}\n`);
43
97
  return 1;
44
98
  }
45
99
  try {
46
100
  const result = await (runtime.install ?? runInstall)({
101
+ ...parsed.location,
102
+ ...(parameters[0] === "--no-login" ? { connect: false } : {}),
47
103
  reporter: (event) => output(`${messages[event]}\n`),
48
104
  launcherOptionsFactory: () => ({
49
105
  browserManualLink: (url) => {
@@ -57,9 +113,14 @@ export async function main(args, runtime = {}) {
57
113
  }),
58
114
  });
59
115
  if (result.status === "installed-unconnected") {
60
- errorOutput(`NGGAIGC Skill ${result.version} 文件已${result.installation === "unchanged" ? "存在" : "安装"},但设备尚未连接。${errorMessages[result.connectionError] ?? "请在新 Codex 任务中检查连接状态。"}\n`);
116
+ errorOutput(`NGGAIGC Skill ${result.version} 文件已${result.installation === "unchanged" ? "存在" : "安装"},但设备尚未连接。${errorMessages[result.connectionError] ?? "请在当前 Agent 中检查连接状态。"}\n`);
61
117
  return 2;
62
118
  }
119
+ if (result.status === "installed" ||
120
+ Object.keys(parsed.location).length > 0) {
121
+ const { skillsDirectory } = resolveSkillLocation(parsed.location);
122
+ output(`${JSON.stringify({ ...result, skillsDirectory, skillPath: path.join(skillsDirectory, "nggaigc", "SKILL.md") })}\n`);
123
+ }
63
124
  return 0;
64
125
  }
65
126
  catch (error) {
@@ -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.42.zip"),
18
+ archive: path.join(vendor, "nggaigc-plugin-1.1.44.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,69 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { CliError } from "./errors.js";
4
+ export function absoluteDirectory(value) {
5
+ if (!path.isAbsolute(value) || /[\0\r\n]/u.test(value))
6
+ throw new CliError("INVALID_INPUT");
7
+ const resolved = path.resolve(value);
8
+ if (resolved === path.parse(resolved).root)
9
+ throw new CliError("INVALID_INPUT");
10
+ return resolved;
11
+ }
12
+ export function stateDirectoryForSkills(skillsDirectory) {
13
+ // Preserve existing */skills journals. Other targets use a different leaf:
14
+ // /x and /x/skills must never share backups or installation locks.
15
+ return path.basename(skillsDirectory) === "skills"
16
+ ? path.join(path.dirname(skillsDirectory), ".nggaigc-managed")
17
+ : path.join(skillsDirectory, ".nggaigc-managed", "custom-target");
18
+ }
19
+ /** One explicit location for root installation and every dependent Skill. */
20
+ export function resolveSkillLocation(options = {}, environment = process.env) {
21
+ if ([options.agent, options.skillsDirectory, options.codexHome].filter((value) => value !== undefined).length > 1)
22
+ throw new CliError("INVALID_INPUT");
23
+ if (options.skillsDirectory !== undefined) {
24
+ const skillsDirectory = absoluteDirectory(options.skillsDirectory);
25
+ return {
26
+ skillsDirectory,
27
+ stateDirectory: stateDirectoryForSkills(skillsDirectory),
28
+ };
29
+ }
30
+ const profile = absoluteDirectory(options.profile ?? os.homedir());
31
+ const agent = options.agent ?? "codex";
32
+ if (!["codex", "workbuddy", "universal"].includes(agent))
33
+ throw new CliError("INVALID_INPUT");
34
+ const home = absoluteDirectory(options.codexHome ??
35
+ (agent === "codex"
36
+ ? ((options.profile === undefined
37
+ ? environment.CODEX_HOME
38
+ : undefined) ?? path.join(profile, ".codex"))
39
+ : path.join(profile, agent === "workbuddy" ? ".codebuddy" : ".agents")));
40
+ return {
41
+ skillsDirectory: path.join(home, "skills"),
42
+ // Explicit and named-host calls for the same directory share one journal.
43
+ stateDirectory: stateDirectoryForSkills(path.join(home, "skills")),
44
+ };
45
+ }
46
+ export function parseSkillLocation(args) {
47
+ const location = {};
48
+ const positional = [];
49
+ for (let index = 0; index < args.length; index += 1) {
50
+ const value = args[index];
51
+ if (value !== "--agent" && value !== "--skills-dir") {
52
+ positional.push(value);
53
+ continue;
54
+ }
55
+ if (location.agent !== undefined || location.skillsDirectory !== undefined)
56
+ throw new CliError("INVALID_INPUT");
57
+ const argument = args[++index];
58
+ if (!argument || argument.startsWith("--"))
59
+ throw new CliError("INVALID_INPUT");
60
+ if (value === "--agent") {
61
+ if (!["codex", "workbuddy", "universal"].includes(argument))
62
+ throw new CliError("INVALID_INPUT");
63
+ location.agent = argument;
64
+ }
65
+ else
66
+ location.skillsDirectory = absoluteDirectory(argument);
67
+ }
68
+ return { location, positional };
69
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nggaigc/cli",
3
- "version": "0.1.43",
4
- "description": "Secure one-command NGGAIGC Skill installer for Codex.",
3
+ "version": "0.1.45",
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.42",
5
- "sourceCommit": "27582fb9f56276e7f80f2681795fb70a35d5f547",
6
- "artifactPath": "packages/plugins/nggaigc-plugin-1.1.42.zip",
7
- "sha256": "d7520abf2754fcc48e663b42a581c222591af33d8de099015ca9aed17ff22a7e",
8
- "sizeBytes": 493914,
4
+ "version": "1.1.44",
5
+ "sourceCommit": "daa6585bd3c9c57b77a92ac452a866566efe546c",
6
+ "artifactPath": "packages/plugins/nggaigc-plugin-1.1.44.zip",
7
+ "sha256": "d3e9c98a65df60f9ffb6426e60341a3c621a62e1779a172f6d7939023be7fb16",
8
+ "sizeBytes": 498924,
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": "8bb8627e1a56f869d77811358c0223d990439dea64fefea6aaeb6b9c3edb8971",
17
- "installer/install.cjs": "a0c94ed71edf4cd7eb4a66609a4dcf73049abbd14aa273ee65b9b3029be2da2b",
16
+ ".codex-plugin/plugin.json": "44ffcd18f569942856d9b955dacca52dd7559972ba7e42ca5c875084b7c36bb8",
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": "d02773256fc4d2324415942bdc64593e768a49a4124f22eb7d3f74797f934c1e",
28
- "skills/nggaigc/references/source.json": "02cb7319bdb554391d96fa292910b41e26bfc3ed78cab12cb283d94870399769",
27
+ "skills/nggaigc/references/catalog-and-updates.md": "990abe5d4e6d34283ad957a372e1892fda2dc1620aa2fb6c5adca28ad3727d89",
28
+ "skills/nggaigc/references/source.json": "49025cea97197ff8644448fe35d54addc2f27154f8dfeb7791099badcba28ee8",
29
29
  "skills/nggaigc/scripts/api_client.mjs": "e16bf750a297ef7e88e4843d2fba9c33b412a66516bfe76822d7deec6c153069",
30
- "skills/nggaigc/scripts/api_package.mjs": "59fe904544a474ee9803bb390f1f60d9ab0c0ff2efb0539653e966fe01770147",
31
- "skills/nggaigc/scripts/bootstrap-public.json": "421e13235116fd515dc9b861f12ad9ea1ae4acc65aa96f893f40397d94ac8f6c",
32
- "skills/nggaigc/scripts/launch-ngg-client.cjs": "17a9d33636b849413a1dca0ead1373f341a746ff2fcb7f55d33fbb67f3cd87fc",
30
+ "skills/nggaigc/scripts/api_package.mjs": "6beee22aed770cbf501abe6d1d9d252f7a53893eeb9b6399bd26c4ef68470d5a",
31
+ "skills/nggaigc/scripts/bootstrap-public.json": "67bc46ee22771782b2186613ecb5ef04ec813e2ce68d5365eb42012703edad94",
32
+ "skills/nggaigc/scripts/launch-ngg-client.cjs": "c9295fa3838015d0614cb663a1419e3965ed673b87bba42856828ef8bcf1ade9",
33
33
  "skills/nggaigc/scripts/macos-keychain": "b0bdf29d14219d38ca18c7e5c599deb0daa8c8ac70b0babbc7d4fb23673079ec",
34
34
  "skills/nggaigc/scripts/macos-keychain.swift": "1b57e74fde66c824fe970547dd8ca0883a3334c34c065e85a8a2a90d4556a7f5",
35
- "skills/nggaigc/scripts/ngg_client.mjs": "fd32ab06fd7cac41a4a7de69f13048f1668393aa580e1ed4692fc8befa58be6d",
36
- "skills/nggaigc/scripts/ngg-session.mjs": "7bc7b192709c7a955c97f7bbb394de5ef2b0db8533315226fbb7e45a8d7f141a",
35
+ "skills/nggaigc/scripts/ngg_client.mjs": "9a8bcb2557dbcb8d8d97c6a930c22ca6833bbfbdbffa742ab6dd0bf00799ee4c",
36
+ "skills/nggaigc/scripts/ngg-session.mjs": "4ab35bae06c02a5825d36a8d860273ed9c512f9ee40debd7f8cf32c50fc9d94f",
37
37
  "skills/nggaigc/scripts/supported-platforms.json": "ec8b0754c489e04534a1d9880dd6664ba9c5bcd07789e06d138e0923f23575ff",
38
38
  "skills/nggaigc/scripts/windows-credential.ps1": "80d470fb0c9473d26bd599cafa9f1c2ad69056d4a835888ddcbb5c1548f9d059",
39
- "skills/nggaigc/SKILL.md": "f6ccf3c685cb118ec6ec495da344a5e57ca69ea84d3e5d1f7cd817e5a594a52e"
39
+ "skills/nggaigc/SKILL.md": "7985e5c6543d56bfb16502f050b2e24bd59172346f6cba3e159b1d1e8a923e52"
40
40
  },
41
41
  "signature": {
42
42
  "algorithm": "ed25519",
43
43
  "keyId": "ngg-signing-skill-release-1",
44
44
  "scope": "artifact-bytes",
45
- "value": "cu1FYhgq1p+AtEI8drzD4FzFThrif2/ib4xFrfa2RMTVQxWbH1W8JflClYC7PpH4ntkMIStYCamR/fjRIhkuDA=="
45
+ "value": "zWO1/CE7wHcX6+KPdluqCzsmbqUp8xy4piqj/Znj7ZpnfZJCMJnfqxn5Nc9G/mjDW1Yqznigg39bs78LrED5BA=="
46
46
  }
47
47
  }
index 60e6ea5..b07c405 100644
Binary file