cognitive-modules-cli 2.2.7 → 2.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +25 -3
- package/bin.js +30 -0
- package/dist/audit.d.ts +13 -0
- package/dist/audit.js +25 -0
- package/dist/cli.js +190 -2
- package/dist/commands/add.js +68 -1
- package/dist/commands/compose.d.ts +2 -0
- package/dist/commands/compose.js +60 -1
- package/dist/commands/core.d.ts +31 -0
- package/dist/commands/core.js +338 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/pipe.js +45 -2
- package/dist/commands/run.js +99 -17
- package/dist/commands/search.js +13 -3
- package/dist/commands/update.js +4 -1
- package/dist/modules/composition.d.ts +15 -2
- package/dist/modules/composition.js +16 -6
- package/dist/modules/loader.d.ts +10 -0
- package/dist/modules/loader.js +168 -0
- package/dist/modules/runner.d.ts +3 -1
- package/dist/modules/runner.js +121 -2
- package/dist/profile.d.ts +8 -0
- package/dist/profile.js +59 -0
- package/dist/provenance.d.ts +50 -0
- package/dist/provenance.js +137 -0
- package/dist/registry/assets.d.ts +48 -0
- package/dist/registry/assets.js +723 -0
- package/dist/registry/client.d.ts +8 -1
- package/dist/registry/client.js +83 -29
- package/dist/types.d.ts +31 -0
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 2.2.9 - 2026-02-07
|
|
6
|
+
|
|
7
|
+
- Fix: `cog core run` now prints the error envelope (instead of `Error: undefined`) when execution fails.
|
|
8
|
+
- Packaging: add stable `bin.js` entrypoint so publish/install doesn't depend on prebuilt `dist/` existing at publish-time.
|
|
9
|
+
- Core: align core template placeholders with runtime substitution (`${query}` / `${code}`); missing fields are treated as empty for single-file modules.
|
|
10
|
+
|
|
11
|
+
## 2.2.8 - 2026-02-07
|
|
12
|
+
|
|
13
|
+
- Fix: `npx cogn` alias compatibility on newer Node (exports + ESM). (Alias fix ships in `cogn@2.2.8`.)
|
|
14
|
+
- Hardening: configurable registry index fetch limits (`--registry-timeout-ms`, `--registry-max-bytes`).
|
|
15
|
+
- Hardening: remote registry verification supports bounded concurrency (`--concurrency`).
|
|
16
|
+
|
|
5
17
|
## 2.2.7 - 2026-02-06
|
|
6
18
|
|
|
7
19
|
- Standardized v2.2 runtime behavior and cross-surface error envelope consistency (CLI/HTTP/MCP).
|
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@ Node.js/TypeScript 版本的 Cognitive Modules CLI,提供 `cog` 命令。
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# 全局安装(推荐)
|
|
13
|
-
npm install -g cogn@2.2.
|
|
13
|
+
npm install -g cogn@2.2.9
|
|
14
14
|
# 或使用完整包名(同样提供 `cog` 命令)
|
|
15
|
-
# npm install -g cognitive-modules-cli@2.2.
|
|
15
|
+
# npm install -g cognitive-modules-cli@2.2.9
|
|
16
16
|
|
|
17
17
|
# 或使用 npx 零安装
|
|
18
|
-
npx cogn@2.2.
|
|
18
|
+
npx cogn@2.2.9 --help
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## 快速开始
|
|
@@ -50,6 +50,18 @@ echo "review this code" | cog pipe --module code-reviewer
|
|
|
50
50
|
## 命令
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
+
# Core(单文件极简路径)
|
|
54
|
+
cog core new # 生成 demo.md
|
|
55
|
+
cog core run demo.md --args "..." # 运行单文件模块
|
|
56
|
+
cog core promote demo.md # 升级为 v2 模块目录
|
|
57
|
+
|
|
58
|
+
# 渐进复杂度(Profiles)
|
|
59
|
+
cog run code-reviewer --args "..." --profile core # 极简:跳过校验
|
|
60
|
+
cog run code-reviewer --args "..." --profile default # 默认:开启校验
|
|
61
|
+
cog run code-reviewer --args "..." --profile strict # 更严格:开启校验(更强门禁)
|
|
62
|
+
cog run code-reviewer --args "..." --profile certified # 最严格:v2.2 + 审计 + registry provenance/完整性门禁
|
|
63
|
+
# 覆盖开关:--validate auto|on|off,--audit(写入 ~/.cognitive/audit/)
|
|
64
|
+
|
|
53
65
|
# 模块操作
|
|
54
66
|
cog list # 列出模块
|
|
55
67
|
cog run <module> --args "..." # 运行模块
|
|
@@ -76,6 +88,16 @@ cog mcp # 启动 MCP 服务(Claude Code / Cursor)
|
|
|
76
88
|
|
|
77
89
|
# 环境检查
|
|
78
90
|
cog doctor
|
|
91
|
+
|
|
92
|
+
# Registry(索引与分发)
|
|
93
|
+
# 默认 registry index(latest):
|
|
94
|
+
# https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
|
|
95
|
+
# 可通过环境变量或全局参数覆盖:
|
|
96
|
+
COGNITIVE_REGISTRY_URL=... cog search
|
|
97
|
+
COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 cog search
|
|
98
|
+
cog search --registry https://github.com/Cognary/cognitive/releases/download/v2.2.9/cognitive-registry.v2.json
|
|
99
|
+
cog registry verify --remote --index https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
|
|
100
|
+
cog registry verify --remote --concurrency 2
|
|
79
101
|
```
|
|
80
102
|
|
|
81
103
|
## 开发
|
package/bin.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stable CLI entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* Motivation:
|
|
6
|
+
* - Avoid `npm publish` warnings when `bin` points into `dist/` before build runs.
|
|
7
|
+
* - Keep runtime entrypoint stable even if build output paths change.
|
|
8
|
+
*
|
|
9
|
+
* This file is shipped in the published package and forwards to the compiled CLI.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
|
|
19
|
+
const cliPath = path.join(__dirname, 'dist', 'cli.js');
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
const res = spawnSync(process.execPath, [cliPath, ...args], { stdio: 'inherit' });
|
|
23
|
+
|
|
24
|
+
if (res.error) {
|
|
25
|
+
console.error(res.error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
process.exit(res.status ?? 1);
|
|
30
|
+
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AuditRecord {
|
|
2
|
+
ts: string;
|
|
3
|
+
kind: 'run' | 'pipe' | 'compose';
|
|
4
|
+
policy?: unknown;
|
|
5
|
+
provider?: string;
|
|
6
|
+
module?: unknown;
|
|
7
|
+
input?: unknown;
|
|
8
|
+
result?: unknown;
|
|
9
|
+
notes?: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function writeAuditRecord(record: AuditRecord): Promise<{
|
|
12
|
+
path: string;
|
|
13
|
+
} | null>;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
function safeSlug(s) {
|
|
5
|
+
const out = s.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9._-]/g, '-');
|
|
6
|
+
return out.length > 0 ? out.slice(0, 80) : 'unknown';
|
|
7
|
+
}
|
|
8
|
+
export async function writeAuditRecord(record) {
|
|
9
|
+
// Keep it simple and predictable: write under ~/.cognitive/audit/
|
|
10
|
+
const dir = path.join(os.homedir(), '.cognitive', 'audit');
|
|
11
|
+
const ts = record.ts.replace(/[:.]/g, '-');
|
|
12
|
+
const kind = record.kind;
|
|
13
|
+
const moduleName = safeSlug(typeof record.module?.name === 'string' ? record.module.name : 'module');
|
|
14
|
+
const filename = `${ts}-${kind}-${moduleName}-${Math.random().toString(16).slice(2, 10)}.json`;
|
|
15
|
+
try {
|
|
16
|
+
await fs.mkdir(dir, { recursive: true });
|
|
17
|
+
const outPath = path.join(dir, filename);
|
|
18
|
+
await fs.writeFile(outPath, JSON.stringify(record, null, 2) + '\n', 'utf-8');
|
|
19
|
+
return { path: outPath };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Audit must never break execution.
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { parseArgs } from 'node:util';
|
|
17
17
|
import { getProvider, listProviders } from './providers/index.js';
|
|
18
|
-
import { run, list, pipe, init, add, update, remove, versions, compose, composeInfo, validate, validateAll, migrate, migrateAll, test, testAll, search, listCategories, info } from './commands/index.js';
|
|
18
|
+
import { run, list, pipe, init, add, update, remove, versions, compose, composeInfo, validate, validateAll, migrate, migrateAll, test, testAll, search, listCategories, info, core } from './commands/index.js';
|
|
19
19
|
import { listModules, getDefaultSearchPaths } from './modules/loader.js';
|
|
20
20
|
import { VERSION } from './version.js';
|
|
21
|
+
import { resolveExecutionPolicy } from './profile.js';
|
|
22
|
+
import { buildRegistryAssets, verifyRegistryAssets } from './registry/assets.js';
|
|
23
|
+
import { DEFAULT_REGISTRY_URL } from './registry/client.js';
|
|
21
24
|
async function main() {
|
|
22
25
|
const args = process.argv.slice(2);
|
|
23
26
|
const command = args[0];
|
|
@@ -33,6 +36,12 @@ async function main() {
|
|
|
33
36
|
const { values, positionals } = parseArgs({
|
|
34
37
|
args: args.slice(1),
|
|
35
38
|
options: {
|
|
39
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
40
|
+
stdin: { type: 'boolean', default: false }, // core: read module prompt from stdin
|
|
41
|
+
force: { type: 'boolean', default: false }, // core promote: overwrite existing target dir
|
|
42
|
+
profile: { type: 'string' }, // progressive complexity profile
|
|
43
|
+
validate: { type: 'string' }, // auto|on|off (overrides --no-validate)
|
|
44
|
+
audit: { type: 'boolean', default: false }, // write audit record to ~/.cognitive/audit/
|
|
36
45
|
args: { type: 'string', short: 'a' },
|
|
37
46
|
input: { type: 'string', short: 'i' },
|
|
38
47
|
module: { type: 'string', short: 'm' },
|
|
@@ -62,9 +71,59 @@ async function main() {
|
|
|
62
71
|
format: { type: 'string', short: 'f' },
|
|
63
72
|
// Search options
|
|
64
73
|
category: { type: 'string', short: 'c' },
|
|
74
|
+
registry: { type: 'string' }, // override registry index URL (or use env COGNITIVE_REGISTRY_URL)
|
|
75
|
+
'registry-timeout-ms': { type: 'string' },
|
|
76
|
+
'registry-max-bytes': { type: 'string' },
|
|
77
|
+
// Registry build/verify options
|
|
78
|
+
'modules-dir': { type: 'string' },
|
|
79
|
+
'v1-registry': { type: 'string' },
|
|
80
|
+
'out-dir': { type: 'string' },
|
|
81
|
+
'registry-out': { type: 'string' },
|
|
82
|
+
namespace: { type: 'string' },
|
|
83
|
+
'runtime-min': { type: 'string' },
|
|
84
|
+
repository: { type: 'string' },
|
|
85
|
+
homepage: { type: 'string' },
|
|
86
|
+
license: { type: 'string' },
|
|
87
|
+
timestamp: { type: 'string' },
|
|
88
|
+
only: { type: 'string', multiple: true },
|
|
89
|
+
index: { type: 'string' },
|
|
90
|
+
'assets-dir': { type: 'string' },
|
|
91
|
+
'tarball-base-url': { type: 'string' },
|
|
92
|
+
remote: { type: 'boolean', default: false }, // registry verify: fetch index + tarballs over network
|
|
93
|
+
'fetch-timeout-ms': { type: 'string' },
|
|
94
|
+
'max-index-bytes': { type: 'string' },
|
|
95
|
+
'max-tarball-bytes': { type: 'string' },
|
|
96
|
+
concurrency: { type: 'string' },
|
|
65
97
|
},
|
|
66
98
|
allowPositionals: true,
|
|
67
99
|
});
|
|
100
|
+
if (values.help) {
|
|
101
|
+
if (command === 'core') {
|
|
102
|
+
console.log(JSON.stringify({
|
|
103
|
+
usage: [
|
|
104
|
+
'cog core new [file.md] [--dry-run]',
|
|
105
|
+
'cog core schema <file.md> [--pretty]',
|
|
106
|
+
'cog core run <file.md> [--args \"...\"] [--pretty] [--stream] [--no-validate]',
|
|
107
|
+
'cog core run --stdin [--args \"...\"] [--pretty] [--stream] [--no-validate]',
|
|
108
|
+
'cog core promote <file.md> [outDir] [--dry-run] [--force]',
|
|
109
|
+
],
|
|
110
|
+
}, null, values.pretty ? 2 : 0));
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
printHelp();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
// Guard Core-only flags so we don't silently ignore them on other commands.
|
|
117
|
+
if (command !== 'core') {
|
|
118
|
+
if (values.stdin) {
|
|
119
|
+
console.error('Error: --stdin is only supported for `cog core run --stdin`');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
if (values.force) {
|
|
123
|
+
console.error('Error: --force is only supported for `cog core promote --force`');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
68
127
|
// Get provider
|
|
69
128
|
let provider;
|
|
70
129
|
try {
|
|
@@ -74,13 +133,72 @@ async function main() {
|
|
|
74
133
|
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
75
134
|
process.exit(1);
|
|
76
135
|
}
|
|
136
|
+
let policy;
|
|
137
|
+
try {
|
|
138
|
+
policy = resolveExecutionPolicy({
|
|
139
|
+
profile: values.profile,
|
|
140
|
+
validate: values.validate,
|
|
141
|
+
noValidate: values['no-validate'],
|
|
142
|
+
audit: values.audit,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const parsePositive = (key, raw) => {
|
|
150
|
+
if (raw === undefined || raw === null || raw === '')
|
|
151
|
+
return undefined;
|
|
152
|
+
const n = Number(raw);
|
|
153
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
154
|
+
throw new Error(`Invalid --${key}: ${String(raw)} (expected a positive number)`);
|
|
155
|
+
}
|
|
156
|
+
return Math.floor(n);
|
|
157
|
+
};
|
|
77
158
|
const ctx = {
|
|
78
159
|
cwd: process.cwd(),
|
|
79
160
|
provider,
|
|
80
161
|
verbose: values.verbose,
|
|
162
|
+
policy,
|
|
163
|
+
registryUrl: values.registry ?? undefined,
|
|
164
|
+
registryTimeoutMs: parsePositive('registry-timeout-ms', values['registry-timeout-ms']),
|
|
165
|
+
registryMaxBytes: parsePositive('registry-max-bytes', values['registry-max-bytes']),
|
|
81
166
|
};
|
|
82
167
|
try {
|
|
83
168
|
switch (command) {
|
|
169
|
+
case 'core': {
|
|
170
|
+
const sub = positionals[0];
|
|
171
|
+
const target = positionals[1];
|
|
172
|
+
const rest = positionals.slice(2);
|
|
173
|
+
const result = await core(sub, target, ctx, {
|
|
174
|
+
args: values.args,
|
|
175
|
+
input: values.input,
|
|
176
|
+
noValidate: values['no-validate'],
|
|
177
|
+
pretty: values.pretty,
|
|
178
|
+
verbose: values.verbose,
|
|
179
|
+
stream: values.stream,
|
|
180
|
+
dryRun: values['dry-run'],
|
|
181
|
+
stdin: values.stdin,
|
|
182
|
+
force: values.force,
|
|
183
|
+
}, rest);
|
|
184
|
+
if (!result.success) {
|
|
185
|
+
// Keep parity with `cog run`: if an error envelope exists, print it.
|
|
186
|
+
// This avoids confusing "Error: undefined" when the command returns a valid envelope
|
|
187
|
+
// but `error` string is not set.
|
|
188
|
+
if (result.data) {
|
|
189
|
+
console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.error(`Error: ${result.error ?? 'Unknown error'}`);
|
|
193
|
+
}
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
// Stream mode prints events as NDJSON already (via `cog run`).
|
|
197
|
+
if (!(values.stream && sub === 'run')) {
|
|
198
|
+
console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
84
202
|
case 'run': {
|
|
85
203
|
const moduleName = args[1];
|
|
86
204
|
if (!moduleName || moduleName.startsWith('-')) {
|
|
@@ -90,6 +208,7 @@ async function main() {
|
|
|
90
208
|
const result = await run(moduleName, ctx, {
|
|
91
209
|
args: values.args,
|
|
92
210
|
input: values.input,
|
|
211
|
+
// policy.validate is resolved in run(); keep legacy flag compatibility here.
|
|
93
212
|
noValidate: values['no-validate'],
|
|
94
213
|
pretty: values.pretty,
|
|
95
214
|
verbose: values.verbose,
|
|
@@ -388,6 +507,7 @@ async function main() {
|
|
|
388
507
|
const result = await compose(moduleName, ctx, {
|
|
389
508
|
args: values.args,
|
|
390
509
|
input: values.input,
|
|
510
|
+
noValidate: values['no-validate'],
|
|
391
511
|
maxDepth: values['max-depth'] ? parseInt(values['max-depth'], 10) : undefined,
|
|
392
512
|
timeout: values.timeout ? parseInt(values.timeout, 10) : undefined,
|
|
393
513
|
trace: values.trace,
|
|
@@ -770,6 +890,52 @@ async function main() {
|
|
|
770
890
|
await client.fetchRegistry(true);
|
|
771
891
|
console.log('✓ Registry cache refreshed');
|
|
772
892
|
}
|
|
893
|
+
else if (subCommand === 'build') {
|
|
894
|
+
const result = await buildRegistryAssets({
|
|
895
|
+
tag: values.tag ?? null,
|
|
896
|
+
tarballBaseUrl: values['tarball-base-url'] ?? null,
|
|
897
|
+
modulesDir: values['modules-dir'] ?? 'cognitive/modules',
|
|
898
|
+
v1RegistryPath: values['v1-registry'] ?? 'cognitive-registry.json',
|
|
899
|
+
outDir: values['out-dir'] ?? 'dist/registry-assets',
|
|
900
|
+
registryOut: values['registry-out'] ?? 'cognitive-registry.v2.json',
|
|
901
|
+
namespace: values.namespace ?? 'official',
|
|
902
|
+
runtimeMin: values['runtime-min'] ?? '2.2.0',
|
|
903
|
+
repository: values.repository ?? 'https://github.com/Cognary/cognitive',
|
|
904
|
+
homepage: values.homepage ?? 'https://cognary.github.io/cognitive/',
|
|
905
|
+
license: values.license ?? 'MIT',
|
|
906
|
+
timestamp: values.timestamp ?? null,
|
|
907
|
+
only: Array.isArray(values.only) ? values.only : (values.only ? [String(values.only)] : []),
|
|
908
|
+
});
|
|
909
|
+
console.log(JSON.stringify({ ok: true, ...result }, null, values.pretty ? 2 : 0));
|
|
910
|
+
}
|
|
911
|
+
else if (subCommand === 'verify') {
|
|
912
|
+
const remote = Boolean(values.remote);
|
|
913
|
+
const defaultIndex = (typeof process.env.COGNITIVE_REGISTRY_URL === 'string' && process.env.COGNITIVE_REGISTRY_URL.trim()
|
|
914
|
+
? process.env.COGNITIVE_REGISTRY_URL.trim()
|
|
915
|
+
: undefined) ??
|
|
916
|
+
ctx.registryUrl ??
|
|
917
|
+
DEFAULT_REGISTRY_URL;
|
|
918
|
+
const indexPath = values.index ??
|
|
919
|
+
(remote ? defaultIndex : 'cognitive-registry.v2.json');
|
|
920
|
+
const assetsDir = values['assets-dir'] ?? (remote ? undefined : 'dist/registry-assets');
|
|
921
|
+
const fetchTimeoutMs = parsePositive('fetch-timeout-ms', values['fetch-timeout-ms']);
|
|
922
|
+
const maxIndexBytes = parsePositive('max-index-bytes', values['max-index-bytes']);
|
|
923
|
+
const maxTarballBytes = parsePositive('max-tarball-bytes', values['max-tarball-bytes']);
|
|
924
|
+
const concurrency = parsePositive('concurrency', values.concurrency);
|
|
925
|
+
const verified = await verifyRegistryAssets({
|
|
926
|
+
registryIndexPath: indexPath,
|
|
927
|
+
assetsDir,
|
|
928
|
+
remote,
|
|
929
|
+
fetchTimeoutMs,
|
|
930
|
+
maxIndexBytes,
|
|
931
|
+
maxTarballBytes,
|
|
932
|
+
concurrency,
|
|
933
|
+
});
|
|
934
|
+
console.log(JSON.stringify(verified, null, values.pretty ? 2 : 0));
|
|
935
|
+
if (!verified.ok) {
|
|
936
|
+
process.exit(1);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
773
939
|
else {
|
|
774
940
|
console.error(`Unknown registry subcommand: ${subCommand}`);
|
|
775
941
|
console.error('');
|
|
@@ -778,6 +944,9 @@ async function main() {
|
|
|
778
944
|
console.error(' cog registry categories List categories');
|
|
779
945
|
console.error(' cog registry info <mod> Show module details');
|
|
780
946
|
console.error(' cog registry refresh Refresh cache');
|
|
947
|
+
console.error(' cog registry build Build registry tarballs + v2 index (local)');
|
|
948
|
+
console.error(' cog registry verify Verify local tarballs against v2 index');
|
|
949
|
+
console.error(' cog registry verify --remote --index <url> Verify remote index+tarballs');
|
|
781
950
|
process.exit(1);
|
|
782
951
|
}
|
|
783
952
|
break;
|
|
@@ -805,6 +974,7 @@ USAGE:
|
|
|
805
974
|
cog <command> [options]
|
|
806
975
|
|
|
807
976
|
COMMANDS:
|
|
977
|
+
core <cmd> Minimal "one-file" workflow (new, schema, run)
|
|
808
978
|
run <module> Run a Cognitive Module
|
|
809
979
|
test <module> Run golden tests for a module
|
|
810
980
|
compose <module> Execute a composed module workflow
|
|
@@ -815,7 +985,7 @@ COMMANDS:
|
|
|
815
985
|
remove <module> Remove installed module
|
|
816
986
|
versions <url> List available versions
|
|
817
987
|
search [query] Search modules in registry
|
|
818
|
-
registry <cmd> Registry commands (list, categories, info, refresh)
|
|
988
|
+
registry <cmd> Registry commands (list, categories, info, refresh, build, verify)
|
|
819
989
|
validate <module> Validate module structure
|
|
820
990
|
migrate <module> Migrate module to v2.2 format
|
|
821
991
|
pipe Pipe mode (stdin/stdout)
|
|
@@ -825,6 +995,9 @@ COMMANDS:
|
|
|
825
995
|
doctor Check environment and configuration
|
|
826
996
|
|
|
827
997
|
OPTIONS:
|
|
998
|
+
--profile <name> Progressive complexity profile: core|default|strict|certified
|
|
999
|
+
--validate <mode> Validation mode: auto|on|off (overrides --no-validate)
|
|
1000
|
+
--audit Write an audit record to ~/.cognitive/audit/ (stderr prints path)
|
|
828
1001
|
-a, --args <str> Arguments to pass to module
|
|
829
1002
|
-i, --input <json> JSON input for module
|
|
830
1003
|
-m, --module <name> Module path within repo (for add)
|
|
@@ -836,6 +1009,8 @@ OPTIONS:
|
|
|
836
1009
|
--pretty Pretty-print JSON output
|
|
837
1010
|
-V, --verbose Verbose output
|
|
838
1011
|
--no-validate Skip schema validation
|
|
1012
|
+
--stdin Read module prompt from stdin (for core run)
|
|
1013
|
+
--force Overwrite target directory (for core promote)
|
|
839
1014
|
-H, --host <host> Server host (default: 0.0.0.0)
|
|
840
1015
|
-P, --port <port> Server port (default: 8000)
|
|
841
1016
|
-d, --max-depth <n> Max composition depth (default: 5)
|
|
@@ -847,11 +1022,21 @@ OPTIONS:
|
|
|
847
1022
|
--all Process all modules (for validate/migrate)
|
|
848
1023
|
-f, --format <fmt> Output format: text or json (for validate)
|
|
849
1024
|
-c, --category <cat> Filter by category (for search)
|
|
1025
|
+
--registry <url> Override registry index URL (or set env COGNITIVE_REGISTRY_URL)
|
|
1026
|
+
--registry-timeout-ms <ms> Registry index fetch timeout (overrides env COGNITIVE_REGISTRY_TIMEOUT_MS)
|
|
1027
|
+
--registry-max-bytes <n> Registry index max bytes (overrides env COGNITIVE_REGISTRY_MAX_BYTES)
|
|
850
1028
|
-l, --limit <n> Limit results (for search, versions)
|
|
851
1029
|
-v, --version Show version
|
|
852
1030
|
-h, --help Show this help
|
|
853
1031
|
|
|
854
1032
|
EXAMPLES:
|
|
1033
|
+
# One-file Core (no registry required)
|
|
1034
|
+
cog core new demo.md
|
|
1035
|
+
cog core run demo.md --args "hello" --pretty
|
|
1036
|
+
cog core schema demo.md --pretty
|
|
1037
|
+
cog core promote demo.md
|
|
1038
|
+
cog core promote demo.md ./cognitive/modules/demo
|
|
1039
|
+
|
|
855
1040
|
# Search and discover modules
|
|
856
1041
|
cog search code review # Search by keywords
|
|
857
1042
|
cog search # List all available modules
|
|
@@ -910,6 +1095,9 @@ ENVIRONMENT:
|
|
|
910
1095
|
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
911
1096
|
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
912
1097
|
COG_MODEL Override default model for any provider
|
|
1098
|
+
COGNITIVE_REGISTRY_URL Override registry index URL
|
|
1099
|
+
COGNITIVE_REGISTRY_TIMEOUT_MS Registry index fetch timeout (ms)
|
|
1100
|
+
COGNITIVE_REGISTRY_MAX_BYTES Registry index max bytes
|
|
913
1101
|
`);
|
|
914
1102
|
}
|
|
915
1103
|
main().catch(e => {
|
package/dist/commands/add.js
CHANGED
|
@@ -18,6 +18,7 @@ import { homedir, tmpdir } from 'node:os';
|
|
|
18
18
|
import { createHash } from 'node:crypto';
|
|
19
19
|
import { RegistryClient } from '../registry/client.js';
|
|
20
20
|
import { extractTarGzFile } from '../registry/tar.js';
|
|
21
|
+
import { PROVENANCE_SPEC, computeModuleIntegrity, writeModuleProvenance } from '../provenance.js';
|
|
21
22
|
// Module storage paths
|
|
22
23
|
const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
|
|
23
24
|
const INSTALLED_MANIFEST = join(homedir(), '.cognitive', 'installed.json');
|
|
@@ -340,9 +341,13 @@ function parseModuleSpec(spec) {
|
|
|
340
341
|
export async function addFromRegistry(moduleSpec, ctx, options = {}) {
|
|
341
342
|
const { name: moduleName, version: requestedVersion } = parseModuleSpec(moduleSpec);
|
|
342
343
|
const { name: customName, registry: registryUrl } = options;
|
|
344
|
+
const policy = ctx.policy;
|
|
343
345
|
let tempDir;
|
|
344
346
|
try {
|
|
345
|
-
const client = new RegistryClient(registryUrl
|
|
347
|
+
const client = new RegistryClient(registryUrl, {
|
|
348
|
+
timeoutMs: ctx.registryTimeoutMs,
|
|
349
|
+
maxBytes: ctx.registryMaxBytes,
|
|
350
|
+
});
|
|
346
351
|
const moduleInfo = await client.getModule(moduleName);
|
|
347
352
|
if (!moduleInfo) {
|
|
348
353
|
return {
|
|
@@ -357,6 +362,14 @@ export async function addFromRegistry(moduleSpec, ctx, options = {}) {
|
|
|
357
362
|
// Get download info
|
|
358
363
|
const downloadInfo = await client.getDownloadUrl(moduleName);
|
|
359
364
|
if (downloadInfo.isGitHub && downloadInfo.githubInfo) {
|
|
365
|
+
if (policy?.profile === 'certified') {
|
|
366
|
+
return {
|
|
367
|
+
success: false,
|
|
368
|
+
error: `Certified profile requires registry tarball provenance.\n` +
|
|
369
|
+
`Registry entry for '${moduleName}' resolves to a GitHub source, which is not allowed in --profile certified.\n` +
|
|
370
|
+
`Use a tarball-based registry entry (distribution.tarball + checksum), or run with --profile strict/default.`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
360
373
|
const { org, repo, path, ref } = downloadInfo.githubInfo;
|
|
361
374
|
// Use addFromGitHub() directly (not add() to avoid recursion)
|
|
362
375
|
const result = await addFromGitHub(`${org}/${repo}`, ctx, {
|
|
@@ -458,6 +471,35 @@ export async function addFromRegistry(moduleSpec, ctx, options = {}) {
|
|
|
458
471
|
await mkdir(USER_MODULES_DIR, { recursive: true });
|
|
459
472
|
copyDir(sourcePath, targetPath);
|
|
460
473
|
const version = await getModuleVersion(sourcePath);
|
|
474
|
+
// Write provenance + integrity (enables certified profile gating).
|
|
475
|
+
try {
|
|
476
|
+
const integrity = await computeModuleIntegrity(targetPath);
|
|
477
|
+
await writeModuleProvenance(targetPath, {
|
|
478
|
+
spec: PROVENANCE_SPEC,
|
|
479
|
+
createdAt: new Date().toISOString(),
|
|
480
|
+
source: {
|
|
481
|
+
type: 'registry',
|
|
482
|
+
registryUrl: registryUrl ?? null,
|
|
483
|
+
moduleName,
|
|
484
|
+
requestedVersion: requestedVersion ?? null,
|
|
485
|
+
resolvedVersion: version ?? null,
|
|
486
|
+
tarballUrl: downloadInfo.url,
|
|
487
|
+
checksum: downloadInfo.checksum,
|
|
488
|
+
sha256: actualSha256,
|
|
489
|
+
quality: {
|
|
490
|
+
// moduleInfo is typed loosely (v1/v2); best-effort extract for v2.
|
|
491
|
+
verified: moduleInfo?.quality?.verified,
|
|
492
|
+
conformance_level: moduleInfo?.quality?.conformance_level,
|
|
493
|
+
spec_version: moduleInfo?.identity?.spec_version,
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
integrity,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
catch (e) {
|
|
500
|
+
// If provenance fails, keep install but warn loudly (non-certified users can still run).
|
|
501
|
+
console.error(`Warning: failed to write provenance for ${safeInstallName}: ${e.message}`);
|
|
502
|
+
}
|
|
461
503
|
await recordInstall(safeInstallName, {
|
|
462
504
|
source: downloadInfo.url,
|
|
463
505
|
githubUrl: downloadInfo.url,
|
|
@@ -502,6 +544,14 @@ export async function addFromRegistry(moduleSpec, ctx, options = {}) {
|
|
|
502
544
|
export async function addFromGitHub(url, ctx, options = {}) {
|
|
503
545
|
const { org, repo, fullUrl } = parseGitHubUrl(url);
|
|
504
546
|
const { module: modulePath, name, branch = 'main', tag } = options;
|
|
547
|
+
const policy = ctx.policy;
|
|
548
|
+
if (policy?.profile === 'certified') {
|
|
549
|
+
return {
|
|
550
|
+
success: false,
|
|
551
|
+
error: `Certified profile requires registry tarball provenance; GitHub installs are not allowed.\n` +
|
|
552
|
+
`Use 'cog add <module>' against a tarball-based registry entry, or run with --profile strict/default.`,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
505
555
|
// Determine ref (tag takes priority)
|
|
506
556
|
const ref = tag || branch;
|
|
507
557
|
const isTag = !!tag;
|
|
@@ -546,6 +596,19 @@ export async function addFromGitHub(url, ctx, options = {}) {
|
|
|
546
596
|
installedAt: targetPath,
|
|
547
597
|
installedTime: new Date().toISOString(),
|
|
548
598
|
});
|
|
599
|
+
// Best-effort provenance for non-certified installs (useful for audit/debug).
|
|
600
|
+
try {
|
|
601
|
+
const integrity = await computeModuleIntegrity(targetPath);
|
|
602
|
+
await writeModuleProvenance(targetPath, {
|
|
603
|
+
spec: PROVENANCE_SPEC,
|
|
604
|
+
createdAt: new Date().toISOString(),
|
|
605
|
+
source: { type: 'github', repoUrl: fullUrl, ref, modulePath: modulePath ?? null },
|
|
606
|
+
integrity,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
// ignore
|
|
611
|
+
}
|
|
549
612
|
// Cleanup temp directory
|
|
550
613
|
const tempDir = dirname(repoRoot);
|
|
551
614
|
if (tempDir && tempDir !== '/' && tempDir !== '.' && tempDir !== USER_MODULES_DIR) {
|
|
@@ -585,6 +648,10 @@ export async function addFromGitHub(url, ctx, options = {}) {
|
|
|
585
648
|
* Add a module from GitHub or Registry (auto-detect source)
|
|
586
649
|
*/
|
|
587
650
|
export async function add(source, ctx, options = {}) {
|
|
651
|
+
// Allow a global registry override via ctx.registryUrl unless explicitly set.
|
|
652
|
+
if (!options.registry && ctx.registryUrl) {
|
|
653
|
+
options = { ...options, registry: ctx.registryUrl };
|
|
654
|
+
}
|
|
588
655
|
// Determine source type
|
|
589
656
|
if (isGitHubSource(source)) {
|
|
590
657
|
return addFromGitHub(source, ctx, options);
|
package/dist/commands/compose.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { findModule, getDefaultSearchPaths, executeComposition } from '../modules/index.js';
|
|
11
11
|
import { ErrorCodes, attachContext, makeErrorEnvelope } from '../errors/index.js';
|
|
12
|
+
import { writeAuditRecord } from '../audit.js';
|
|
12
13
|
function looksLikeCode(str) {
|
|
13
14
|
const codeIndicators = [
|
|
14
15
|
/^(def|function|class|const|let|var|import|export|public|private)\s/,
|
|
@@ -34,7 +35,19 @@ export async function compose(moduleName, ctx, options = {}) {
|
|
|
34
35
|
data: errorEnvelope,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
38
|
+
if (ctx.policy?.requireV22) {
|
|
39
|
+
if (module.formatVersion !== 'v2.2') {
|
|
40
|
+
const errorEnvelope = attachContext(makeErrorEnvelope({
|
|
41
|
+
code: ErrorCodes.INVALID_INPUT,
|
|
42
|
+
message: `Certified profile requires v2.2 modules; got: ${module.formatVersion ?? 'unknown'} (${module.format})`,
|
|
43
|
+
suggestion: "Migrate the module to v2.2, or run with `--profile strict` / `--profile default`",
|
|
44
|
+
}), { module: moduleName, provider: ctx.provider.name });
|
|
45
|
+
return { success: false, error: errorEnvelope.error.message, data: errorEnvelope };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
try {
|
|
49
|
+
const policy = ctx.policy;
|
|
50
|
+
const startedAt = Date.now();
|
|
38
51
|
// Parse input if provided as JSON
|
|
39
52
|
let inputData = {};
|
|
40
53
|
if (options.input) {
|
|
@@ -67,7 +80,31 @@ export async function compose(moduleName, ctx, options = {}) {
|
|
|
67
80
|
const result = await executeComposition(moduleName, inputData, ctx.provider, {
|
|
68
81
|
cwd: ctx.cwd,
|
|
69
82
|
maxDepth: options.maxDepth,
|
|
70
|
-
timeoutMs: options.timeout
|
|
83
|
+
timeoutMs: options.timeout,
|
|
84
|
+
policy,
|
|
85
|
+
validateInput: (() => {
|
|
86
|
+
if (options.noValidate)
|
|
87
|
+
return false;
|
|
88
|
+
if (!policy)
|
|
89
|
+
return true;
|
|
90
|
+
if (policy.validate === 'off')
|
|
91
|
+
return false;
|
|
92
|
+
if (policy.validate === 'on')
|
|
93
|
+
return true;
|
|
94
|
+
return policy.profile !== 'core';
|
|
95
|
+
})(),
|
|
96
|
+
validateOutput: (() => {
|
|
97
|
+
if (options.noValidate)
|
|
98
|
+
return false;
|
|
99
|
+
if (!policy)
|
|
100
|
+
return true;
|
|
101
|
+
if (policy.validate === 'off')
|
|
102
|
+
return false;
|
|
103
|
+
if (policy.validate === 'on')
|
|
104
|
+
return true;
|
|
105
|
+
return policy.profile !== 'core';
|
|
106
|
+
})(),
|
|
107
|
+
enableRepair: policy?.enableRepair ?? true,
|
|
71
108
|
});
|
|
72
109
|
if (options.verbose) {
|
|
73
110
|
console.error('--- Composition Trace ---');
|
|
@@ -98,6 +135,28 @@ export async function compose(moduleName, ctx, options = {}) {
|
|
|
98
135
|
data: errorEnvelope,
|
|
99
136
|
};
|
|
100
137
|
}
|
|
138
|
+
if (policy?.audit) {
|
|
139
|
+
const rec = await writeAuditRecord({
|
|
140
|
+
ts: new Date().toISOString(),
|
|
141
|
+
kind: 'compose',
|
|
142
|
+
policy,
|
|
143
|
+
provider: ctx.provider.name,
|
|
144
|
+
module: { name: module.name, version: module.version, location: module.location, formatVersion: module.formatVersion },
|
|
145
|
+
input: inputData,
|
|
146
|
+
result: {
|
|
147
|
+
ok: result.ok,
|
|
148
|
+
result: result.result,
|
|
149
|
+
moduleResults: result.moduleResults,
|
|
150
|
+
trace: result.trace,
|
|
151
|
+
totalTimeMs: result.totalTimeMs,
|
|
152
|
+
error: result.error,
|
|
153
|
+
},
|
|
154
|
+
notes: [`duration_ms=${Date.now() - startedAt}`],
|
|
155
|
+
});
|
|
156
|
+
if (rec) {
|
|
157
|
+
console.error(`Audit: ${rec.path}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
101
160
|
// Return result
|
|
102
161
|
if (options.trace) {
|
|
103
162
|
// Include full result with trace
|