@nggaigc/cli 0.1.48 → 0.1.50

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
@@ -39,3 +39,25 @@ npx --yes @nggaigc/cli@latest client --skills-dir "/absolute/project/.codebuddy/
39
39
  本地技能包导入可能复制文件,需核导入后的实际路径和独立包同级关系。交互使用宿主可用的提问工具或当前聊天,交付使用宿主可用的媒体/附件能力;不依赖 Codex 专用工具。切换宿主继续复用当前系统用户的系统凭证库和任务历史,不迁移凭据、不要求重新粘贴密钥。资金、权限与未知结果同幂等键恢复规则不变。
40
40
 
41
41
  此功能不增加 Linux、Intel Mac 支持,也不代表已接入或认证 WorkBuddy 官方连接器市场;现有系统、Node.js 与正式发布门禁保持适用。
42
+
43
+ ## 受信本地 Python 工具文件合同(开发候选)
44
+
45
+ 纯文档包继续使用 `ngg-skill-package/v1`,原结构及字节生成规则不变。本地工具包使用内部元数据 `schemaVersion: "ngg-skill-package/v2"`;ZIP中的元数据文件名仍为 `skill-package.v1.json`。正式发布时须把该包的root/platform最低兼容版本绑定到实际支持v2的新版根包及CLI;本节不指定尚未冻结的发布版本。
46
+
47
+ v2须声明以下有限元数据:
48
+
49
+ ```json
50
+ {
51
+ "localTools": {
52
+ "kind": "python",
53
+ "entrypoints": ["scripts/run.py"],
54
+ "runtimeLock": "references/runtime-lock.json"
55
+ }
56
+ }
57
+ ```
58
+
59
+ 仅此声明允许额外交付直接位于 `scripts/` 的Python源文件(文件名为ASCII字母/数字开头,后接字母、数字、下划线或连字符及`.py`)。入口必须存在且不重复;内部模块无需列作入口。全部源文件仍须列入已签名的逐文件清单,`executable=false`。不接受shell、pyc、任意YAML、嵌套脚本目录或根级runtime-lock。
60
+
61
+ 运行清单是已签名的 `references/runtime-lock.json`,至少有 `schemaVersion: 1`及`files`路径到SHA256的映射。每个打包Python文件必须与其中同路径摘要一致;清单中不能出现未打包的scripts文件,也不能自引用运行清单。文档及同级依赖的摘要可继续由独立工具按自身范围复核;安装器不执行或解释其业务规则。
62
+
63
+ 安装、复用、API合同加载和回滚只验证/交付字节,**不会执行Python、安装解释器/FFmpeg或调用付费服务**。工作流按实际阶段检查宿主依赖并明确调用已声明入口。全部既有签名、逐文件摘要、安全ZIP、32文件/4MiB、路径检查、原子替换及回滚规则继续生效。旧v1 loader明确拒绝v2;不能给旧包加脚本但保留v1声明,或把源码改成文档后缀绕过规则。
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.46" ||
50
+ manifestValue.version !== "1.1.48" ||
51
51
  manifestValue.artifactPath !==
52
52
  `packages/plugins/nggaigc-plugin-${manifestValue.version}.zip` ||
53
53
  typeof manifestValue.sourceCommit !== "string" ||
@@ -345,7 +345,8 @@ function storedZipEntries(input) {
345
345
  localOffset + 30 > centralOffset)
346
346
  fail("PACKAGE_VERIFICATION_FAILED");
347
347
  const unixMode = externalAttributes >>> 16;
348
- if (unixMode !== 0 && (unixMode & 0xf000) !== 0x8000)
348
+ if (unixMode !== 0 &&
349
+ ((unixMode & 0xf000) !== 0x8000 || (unixMode & 0o111) !== 0))
349
350
  fail("PACKAGE_VERIFICATION_FAILED");
350
351
  const nameBytes = bytes.subarray(cursor + 46, cursor + 46 + nameLength);
351
352
  let name;
@@ -382,12 +383,60 @@ function storedZipEntries(input) {
382
383
  fail("PACKAGE_VERIFICATION_FAILED");
383
384
  return result;
384
385
  }
385
- function packageFileType(value) {
386
+ const PYTHON_SCRIPT = /^scripts\/[A-Za-z0-9][A-Za-z0-9_-]*\.py$/;
387
+ const PYTHON_RUNTIME_LOCK = "references/runtime-lock.json";
388
+ function validLocalPythonTools(value, paths) {
389
+ if (!record(value) ||
390
+ !exactKeys(value, ["kind", "entrypoints", "runtimeLock"]) ||
391
+ value.kind !== "python" ||
392
+ value.runtimeLock !== PYTHON_RUNTIME_LOCK ||
393
+ !paths.includes(PYTHON_RUNTIME_LOCK) ||
394
+ !Array.isArray(value.entrypoints) ||
395
+ value.entrypoints.length < 1 ||
396
+ value.entrypoints.length > MAX_FILES)
397
+ return false;
398
+ const seen = new Set();
399
+ for (const entry of value.entrypoints) {
400
+ if (typeof entry !== "string" ||
401
+ !PYTHON_SCRIPT.test(entry) ||
402
+ !paths.includes(entry) ||
403
+ seen.has(entry.toLowerCase()))
404
+ return false;
405
+ seen.add(entry.toLowerCase());
406
+ }
407
+ return true;
408
+ }
409
+ function validLocalPythonRuntimeLock(bytes, files) {
410
+ try {
411
+ const lock = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
412
+ if (!record(lock) ||
413
+ lock.schemaVersion !== 1 ||
414
+ !record(lock.files) ||
415
+ Object.hasOwn(lock.files, PYTHON_RUNTIME_LOCK) ||
416
+ Object.values(lock.files).some((value) => typeof value !== "string" || !DIGEST.test(value)))
417
+ return false;
418
+ const locked = lock.files;
419
+ return (files
420
+ .filter((file) => PYTHON_SCRIPT.test(file.path))
421
+ .every((file) => locked[file.path] === file.sha256) &&
422
+ Object.keys(locked)
423
+ .filter((name) => name.startsWith("scripts/"))
424
+ .every((name) => files.some((file) => PYTHON_SCRIPT.test(file.path) &&
425
+ file.path === name &&
426
+ file.sha256 === locked[name])));
427
+ }
428
+ catch {
429
+ return false;
430
+ }
431
+ }
432
+ function packageFileType(value, localPython = false) {
386
433
  if (value === "SKILL.md" ||
387
434
  /^references\/[A-Za-z0-9._/-]+\.(md|json)$/.test(value))
388
435
  return "document";
389
436
  if (/^assets\/[A-Za-z0-9._/-]+\.png$/.test(value))
390
437
  return "png";
438
+ if (localPython && PYTHON_SCRIPT.test(value))
439
+ return "python";
391
440
  return undefined;
392
441
  }
393
442
  function validStaticPng(input) {
@@ -459,8 +508,9 @@ function decodePackage(bytes, entry, trust) {
459
508
  catch {
460
509
  fail("PACKAGE_VERIFICATION_FAILED");
461
510
  }
511
+ const localPython = payload?.schemaVersion === "ngg-skill-package/v2";
462
512
  if (!record(payload) ||
463
- payload.schemaVersion !== "ngg-skill-package/v1" ||
513
+ (!localPython && payload.schemaVersion !== "ngg-skill-package/v1") ||
464
514
  payload.artifactId !== entry.id ||
465
515
  payload.packageVersion !== entry.version ||
466
516
  canonical(payload.compatibility) !== canonical(metadata.compatibility) ||
@@ -473,8 +523,12 @@ function decodePackage(bytes, entry, trust) {
473
523
  "packageVersion",
474
524
  "compatibility",
475
525
  "files",
526
+ ...(localPython ? ["localTools"] : []),
476
527
  ]))
477
528
  fail("PACKAGE_VERIFICATION_FAILED");
529
+ if (localPython &&
530
+ !validLocalPythonTools(payload.localTools, payload.files.map((file) => file?.path)))
531
+ fail("PACKAGE_VERIFICATION_FAILED");
478
532
  const seen = new Set();
479
533
  let total = 0;
480
534
  for (const file of payload.files) {
@@ -490,7 +544,7 @@ function decodePackage(bytes, entry, trust) {
490
544
  file.path
491
545
  .split("/")
492
546
  .some((part) => !part || part === "." || part === ".." || /[. ]$/.test(part)) ||
493
- !packageFileType(file.path))
547
+ !packageFileType(file.path, localPython))
494
548
  fail("PACKAGE_VERIFICATION_FAILED");
495
549
  const folded = file.path.toLowerCase();
496
550
  if (seen.has(folded) ||
@@ -511,6 +565,9 @@ function decodePackage(bytes, entry, trust) {
511
565
  }
512
566
  if (!seen.has("skill.md") || archive.size !== payload.files.length + 1)
513
567
  fail("PACKAGE_VERIFICATION_FAILED");
568
+ if (localPython &&
569
+ !validLocalPythonRuntimeLock(archive.get(PYTHON_RUNTIME_LOCK), payload.files))
570
+ fail("PACKAGE_VERIFICATION_FAILED");
514
571
  return payload.files.map((file) => ({
515
572
  path: file.path,
516
573
  sha256: file.sha256,
@@ -565,6 +622,9 @@ async function existingReceipt(target, entry, trust) {
565
622
  try {
566
623
  const packageBytes = await readFile(path.join(target, ".nggaigc-package.bin"));
567
624
  const signedFiles = decodePackage(packageBytes, entry, trust);
625
+ // The signed archive's versioned verifier already checked each file type
626
+ // and local-tools declaration. Never infer Python permission from receipt data.
627
+ const signedPaths = new Set(signedFiles.map((file) => file.path));
568
628
  if (JSON.stringify(Object.fromEntries(signedFiles.map((file) => [file.path, file.sha256]))) !== JSON.stringify(receipt.files))
569
629
  return false;
570
630
  const names = [];
@@ -590,7 +650,7 @@ async function existingReceipt(target, entry, trust) {
590
650
  ].sort()))
591
651
  return false;
592
652
  for (const [name, digest] of Object.entries(receipt.files)) {
593
- if (!packageFileType(name) ||
653
+ if (!signedPaths.has(name) ||
594
654
  name.split("/").some((part) => part === "." || part === ".."))
595
655
  return false;
596
656
  const file = path.join(target, ...name.split("/"));
package/dist/install.js CHANGED
@@ -9,7 +9,7 @@ import { resolveSkillLocation, } from "./skill-location.js";
9
9
  function defaultBundle() {
10
10
  const vendor = fileURLToPath(new URL("../vendor/", import.meta.url));
11
11
  return {
12
- archive: path.join(vendor, "nggaigc-plugin-1.1.46.zip"),
12
+ archive: path.join(vendor, "nggaigc-plugin-1.1.48.zip"),
13
13
  manifest: path.join(vendor, "manifest.json"),
14
14
  trust: path.join(vendor, "trusted-keys.json"),
15
15
  };
@@ -15,7 +15,7 @@ function record(value) {
15
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.46.zip"),
18
+ archive: path.join(vendor, "nggaigc-plugin-1.1.48.zip"),
19
19
  manifest: path.join(vendor, "manifest.json"),
20
20
  trust: path.join(vendor, "trusted-keys.json"),
21
21
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nggaigc/cli",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "description": "Secure NGGAIGC Skill installer for Codex, WorkBuddy and compatible agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": "nggaigc-plugin-package/v1",
3
3
  "artifactId": "plugin.nggaigc",
4
- "version": "1.1.46",
5
- "sourceCommit": "890744f5f9d7687c8edb182b78a308f883495109",
6
- "artifactPath": "packages/plugins/nggaigc-plugin-1.1.46.zip",
7
- "sha256": "9e419880e02ad7e578708e36ced067f75a073f9f0fac19a35041607b60571489",
8
- "sizeBytes": 502681,
4
+ "version": "1.1.48",
5
+ "sourceCommit": "17038f7181c59b72b968866e68547f248069a3c5",
6
+ "artifactPath": "packages/plugins/nggaigc-plugin-1.1.48.zip",
7
+ "sha256": "92831457de7bd2f3e60ccd3805c4b7846ec32df7fd255b6b552dee225a83acc3",
8
+ "sizeBytes": 508669,
9
9
  "fileCount": 24,
10
10
  "platforms": [
11
11
  "win32-x64",
@@ -13,7 +13,7 @@
13
13
  "darwin-arm64"
14
14
  ],
15
15
  "files": {
16
- ".codex-plugin/plugin.json": "41aa7f0e7d6769b1fef20e103390a1e1ad404f0ea43db80000026231a82b6076",
16
+ ".codex-plugin/plugin.json": "54daccc21f12195f2c44a1cd049e10bff5a73fa6daba245e201b3affe2e1ad7d",
17
17
  "installer/install.cjs": "76f4ed73233fd39900fb0bedbc434dea39e52e6b6b4622f3fd388a5ca09c6f62",
18
18
  "installer/legacy-root-digests.json": "922552646a5aa6cff8616c372912a37b1d5f263351206b85785ed9b2eae31641",
19
19
  "installer/signed-pair-1.1.16.json": "ccb22a90302cc5c9c585067ebc211a27b0c9d8a177c2740a5678a58a3d70bfe9",
@@ -25,23 +25,23 @@
25
25
  "migration/seedream-1.1.3/SKILL.md": "5c5cf3c2d99376f246425e873c5566436ed528256372ac3208abb60364c4517c",
26
26
  "skills/nggaigc/agents/openai.yaml": "8936d97b9821e687e224e9349cc42be2ee5fc20988562efffbcb70080aedf87c",
27
27
  "skills/nggaigc/references/catalog-and-updates.md": "a43bee94a480aa2bf0b1e7038cee6f27fb86d2bd8fa88a28a63678963d7c1530",
28
- "skills/nggaigc/references/source.json": "7a1409d6cad2a629191a0f57ff0dc5a0ff956cf9d6e045174f6d33bdd7f576c4",
28
+ "skills/nggaigc/references/source.json": "5fb7443d94c522a22cd03e508bb8b1bb2c6f67983b719ce85f5bf88bef2f01d0",
29
29
  "skills/nggaigc/scripts/api_client.mjs": "e16bf750a297ef7e88e4843d2fba9c33b412a66516bfe76822d7deec6c153069",
30
- "skills/nggaigc/scripts/api_package.mjs": "6beee22aed770cbf501abe6d1d9d252f7a53893eeb9b6399bd26c4ef68470d5a",
31
- "skills/nggaigc/scripts/bootstrap-public.json": "1f713cba932651902d4e14073bc8da5e2eb3d219ae50ee6ae3d00e1469435e2a",
32
- "skills/nggaigc/scripts/launch-ngg-client.cjs": "898d29a42ebddee6ab6e17765f3f56e2e4d588d23d3878592dfe20ea82a99b17",
30
+ "skills/nggaigc/scripts/api_package.mjs": "824ea8f935e2c3aec7ad5ea5c6ad043f1b77ccd9918c86687a9bd61558954fe4",
31
+ "skills/nggaigc/scripts/bootstrap-public.json": "6d0a7d79ae4364fb5fba5786d87417732af6f559e32592373031daf24db078d2",
32
+ "skills/nggaigc/scripts/launch-ngg-client.cjs": "4f4786c2e70fbac8e24fa2de1ba02aa713d68d3f36773f4c7c242f91364a005e",
33
33
  "skills/nggaigc/scripts/macos-keychain": "b0bdf29d14219d38ca18c7e5c599deb0daa8c8ac70b0babbc7d4fb23673079ec",
34
34
  "skills/nggaigc/scripts/macos-keychain.swift": "1b57e74fde66c824fe970547dd8ca0883a3334c34c065e85a8a2a90d4556a7f5",
35
- "skills/nggaigc/scripts/ngg_client.mjs": "5534b95177394a6a12d2f205f3ab5884d39412b4b13622c372d66b945d07b715",
36
- "skills/nggaigc/scripts/ngg-session.mjs": "678be2fd81166a884f0b4a3515042e350658d46779ee16bf9d976dacd32ad9a9",
35
+ "skills/nggaigc/scripts/ngg_client.mjs": "254f36fad2e0fb600c91b9ae22e8f35cfd602fbb36c25ef56ab494436a2e4a77",
36
+ "skills/nggaigc/scripts/ngg-session.mjs": "026a1d9d480712ee3e2f4d505e1448e78001800c88025cd048658b38d3c040ce",
37
37
  "skills/nggaigc/scripts/supported-platforms.json": "ec8b0754c489e04534a1d9880dd6664ba9c5bcd07789e06d138e0923f23575ff",
38
- "skills/nggaigc/scripts/windows-credential.ps1": "80d470fb0c9473d26bd599cafa9f1c2ad69056d4a835888ddcbb5c1548f9d059",
38
+ "skills/nggaigc/scripts/windows-credential.ps1": "641888d8c6016bb84f18df19315322ed802c5709fbe5209250eaf1b0ce498aec",
39
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": "ZUj0GmKZYNe+4wgBznW3BKBBNoSErTVlIB8MgqQvEe27Pnc2MsKepzkMVGGhd6GpGsqaukdD3gENuOhiclpsDg=="
45
+ "value": "FQ+2ziXnms95GTjoiJcdjVxekqOjSBcsfpZrGh+0IKCChqXt18AZi59WSPRDfhdM1BQO8AEiqZtM1ge5gI+JDA=="
46
46
  }
47
47
  }
index 4e2828f..9711cc3 100644
Binary file