cognitive-modules-cli 2.2.7 → 2.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +25 -3
- package/dist/audit.d.ts +13 -0
- package/dist/audit.js +25 -0
- package/dist/cli.js +182 -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 +113 -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 +1 -1
|
@@ -134,10 +134,17 @@ export interface SearchResult {
|
|
|
134
134
|
score: number;
|
|
135
135
|
keywords: string[];
|
|
136
136
|
}
|
|
137
|
+
export declare const DEFAULT_REGISTRY_URL = "https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json";
|
|
138
|
+
export interface RegistryClientOptions {
|
|
139
|
+
timeoutMs?: number;
|
|
140
|
+
maxBytes?: number;
|
|
141
|
+
}
|
|
137
142
|
export declare class RegistryClient {
|
|
138
143
|
private registryUrl;
|
|
144
|
+
private timeoutMs;
|
|
145
|
+
private maxBytes;
|
|
139
146
|
private cache;
|
|
140
|
-
constructor(registryUrl?: string);
|
|
147
|
+
constructor(registryUrl?: string, options?: RegistryClientOptions);
|
|
141
148
|
private parseRegistryResponse;
|
|
142
149
|
/**
|
|
143
150
|
* Generate a unique cache filename based on registry URL
|
package/dist/registry/client.js
CHANGED
|
@@ -16,26 +16,60 @@ import { createHash } from 'node:crypto';
|
|
|
16
16
|
// =============================================================================
|
|
17
17
|
// Constants
|
|
18
18
|
// =============================================================================
|
|
19
|
-
|
|
19
|
+
// "Latest" registry strategy:
|
|
20
|
+
// Prefer GitHub Releases "latest" download, so clients get a coherent set of
|
|
21
|
+
// (index + tarballs) that match an actual published release.
|
|
22
|
+
//
|
|
23
|
+
// This URL is stable across releases:
|
|
24
|
+
// https://github.com/<org>/<repo>/releases/latest/download/cognitive-registry.v2.json
|
|
25
|
+
export const DEFAULT_REGISTRY_URL = 'https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json';
|
|
26
|
+
const FALLBACK_REGISTRY_URL = 'https://raw.githubusercontent.com/Cognary/cognitive/main/cognitive-registry.v2.json';
|
|
20
27
|
const CACHE_DIR = join(homedir(), '.cognitive', 'cache');
|
|
21
28
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
22
|
-
const
|
|
23
|
-
const
|
|
29
|
+
const DEFAULT_REGISTRY_FETCH_TIMEOUT_MS = 10_000; // 10s
|
|
30
|
+
const DEFAULT_MAX_REGISTRY_BYTES = 2 * 1024 * 1024; // 2MB
|
|
31
|
+
const HARD_MAX_REGISTRY_BYTES = 20 * 1024 * 1024; // 20MB (absolute safety cap)
|
|
32
|
+
function parsePositiveIntEnv(name) {
|
|
33
|
+
const raw = process.env[name];
|
|
34
|
+
if (!raw)
|
|
35
|
+
return undefined;
|
|
36
|
+
const n = Number(raw);
|
|
37
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
38
|
+
return undefined;
|
|
39
|
+
return Math.floor(n);
|
|
40
|
+
}
|
|
41
|
+
function clamp(n, min, max) {
|
|
42
|
+
return Math.max(min, Math.min(max, n));
|
|
43
|
+
}
|
|
24
44
|
// =============================================================================
|
|
25
45
|
// Registry Client
|
|
26
46
|
// =============================================================================
|
|
27
47
|
export class RegistryClient {
|
|
28
48
|
registryUrl;
|
|
49
|
+
timeoutMs;
|
|
50
|
+
maxBytes;
|
|
29
51
|
cache = { data: null, timestamp: 0 };
|
|
30
|
-
constructor(registryUrl =
|
|
31
|
-
|
|
52
|
+
constructor(registryUrl, options = {}) {
|
|
53
|
+
const fromEnv = process.env.COGNITIVE_REGISTRY_URL;
|
|
54
|
+
const selected = (typeof registryUrl === 'string' && registryUrl.trim() ? registryUrl.trim() : undefined) ??
|
|
55
|
+
(typeof fromEnv === 'string' && fromEnv.trim() ? fromEnv.trim() : undefined) ??
|
|
56
|
+
DEFAULT_REGISTRY_URL;
|
|
57
|
+
this.registryUrl = selected;
|
|
58
|
+
const envTimeout = parsePositiveIntEnv('COGNITIVE_REGISTRY_TIMEOUT_MS');
|
|
59
|
+
const envMaxBytes = parsePositiveIntEnv('COGNITIVE_REGISTRY_MAX_BYTES');
|
|
60
|
+
const timeout = options.timeoutMs ?? envTimeout ?? DEFAULT_REGISTRY_FETCH_TIMEOUT_MS;
|
|
61
|
+
// keep timeouts bounded for predictable UX
|
|
62
|
+
this.timeoutMs = clamp(timeout, 1000, 120_000);
|
|
63
|
+
const maxBytes = options.maxBytes ?? envMaxBytes ?? DEFAULT_MAX_REGISTRY_BYTES;
|
|
64
|
+
// enforce an absolute upper bound to avoid accidental OOM on hostile endpoints
|
|
65
|
+
this.maxBytes = clamp(maxBytes, 1024, HARD_MAX_REGISTRY_BYTES);
|
|
32
66
|
}
|
|
33
67
|
async parseRegistryResponse(response) {
|
|
34
68
|
const contentLengthHeader = response.headers?.get('content-length');
|
|
35
69
|
if (contentLengthHeader) {
|
|
36
70
|
const contentLength = Number(contentLengthHeader);
|
|
37
|
-
if (!Number.isNaN(contentLength) && contentLength >
|
|
38
|
-
throw new Error(`Registry payload too large: ${contentLength} bytes (max ${
|
|
71
|
+
if (!Number.isNaN(contentLength) && contentLength > this.maxBytes) {
|
|
72
|
+
throw new Error(`Registry payload too large: ${contentLength} bytes (max ${this.maxBytes})`);
|
|
39
73
|
}
|
|
40
74
|
}
|
|
41
75
|
if (response.body && typeof response.body.getReader === 'function') {
|
|
@@ -50,8 +84,8 @@ export class RegistryClient {
|
|
|
50
84
|
break;
|
|
51
85
|
if (value) {
|
|
52
86
|
totalBytes += value.byteLength;
|
|
53
|
-
if (totalBytes >
|
|
54
|
-
throw new Error(`Registry payload too large: ${totalBytes} bytes (max ${
|
|
87
|
+
if (totalBytes > this.maxBytes) {
|
|
88
|
+
throw new Error(`Registry payload too large: ${totalBytes} bytes (max ${this.maxBytes})`);
|
|
55
89
|
}
|
|
56
90
|
buffer += decoder.decode(value, { stream: true });
|
|
57
91
|
}
|
|
@@ -71,8 +105,8 @@ export class RegistryClient {
|
|
|
71
105
|
if (typeof response.text === 'function') {
|
|
72
106
|
const text = await response.text();
|
|
73
107
|
const byteLen = Buffer.byteLength(text, 'utf-8');
|
|
74
|
-
if (byteLen >
|
|
75
|
-
throw new Error(`Registry payload too large: ${byteLen} bytes (max ${
|
|
108
|
+
if (byteLen > this.maxBytes) {
|
|
109
|
+
throw new Error(`Registry payload too large: ${byteLen} bytes (max ${this.maxBytes})`);
|
|
76
110
|
}
|
|
77
111
|
try {
|
|
78
112
|
return JSON.parse(text);
|
|
@@ -118,28 +152,48 @@ export class RegistryClient {
|
|
|
118
152
|
// Ignore cache read errors
|
|
119
153
|
}
|
|
120
154
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
const fetchOnce = async (url) => {
|
|
156
|
+
const controller = new AbortController();
|
|
157
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
158
|
+
try {
|
|
159
|
+
const response = await fetch(url, {
|
|
160
|
+
headers: { 'User-Agent': 'cognitive-runtime/2.2' },
|
|
161
|
+
signal: controller.signal,
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
// Allow callers to inspect status for fallback logic.
|
|
165
|
+
const err = new Error(`Failed to fetch registry: ${response.status} ${response.statusText}`);
|
|
166
|
+
err.status = response.status;
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
return await this.parseRegistryResponse(response);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
173
|
+
throw new Error(`Registry fetch timed out after ${this.timeoutMs}ms`);
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
clearTimeout(timeout);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
124
181
|
let data;
|
|
125
182
|
try {
|
|
126
|
-
|
|
127
|
-
headers: { 'User-Agent': 'cognitive-runtime/2.2' },
|
|
128
|
-
signal: controller.signal,
|
|
129
|
-
});
|
|
130
|
-
if (!response.ok) {
|
|
131
|
-
throw new Error(`Failed to fetch registry: ${response.status} ${response.statusText}`);
|
|
132
|
-
}
|
|
133
|
-
data = await this.parseRegistryResponse(response);
|
|
183
|
+
data = await fetchOnce(this.registryUrl);
|
|
134
184
|
}
|
|
135
|
-
catch (
|
|
136
|
-
|
|
137
|
-
|
|
185
|
+
catch (e) {
|
|
186
|
+
// Harden the "latest" strategy: right after publishing a GitHub Release,
|
|
187
|
+
// the index asset may not be uploaded yet (short 404 window). In that case,
|
|
188
|
+
// fall back to the repo-tracked index to keep `cog search/add` usable.
|
|
189
|
+
const status = e?.status;
|
|
190
|
+
const isDefaultUrl = this.registryUrl === DEFAULT_REGISTRY_URL;
|
|
191
|
+
if (isDefaultUrl && status === 404) {
|
|
192
|
+
data = await fetchOnce(FALLBACK_REGISTRY_URL);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
throw e;
|
|
138
196
|
}
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
clearTimeout(timeout);
|
|
143
197
|
}
|
|
144
198
|
// Update cache
|
|
145
199
|
this.cache = { data, timestamp: now };
|
package/dist/types.d.ts
CHANGED
|
@@ -352,6 +352,16 @@ export interface CommandContext {
|
|
|
352
352
|
cwd: string;
|
|
353
353
|
provider: Provider;
|
|
354
354
|
verbose?: boolean;
|
|
355
|
+
policy?: ExecutionPolicy;
|
|
356
|
+
/**
|
|
357
|
+
* Registry index override for commands that talk to the registry.
|
|
358
|
+
* If omitted, the RegistryClient default strategy is used.
|
|
359
|
+
*/
|
|
360
|
+
registryUrl?: string;
|
|
361
|
+
/** Override registry fetch timeout (ms). */
|
|
362
|
+
registryTimeoutMs?: number;
|
|
363
|
+
/** Override registry max index bytes. */
|
|
364
|
+
registryMaxBytes?: number;
|
|
355
365
|
}
|
|
356
366
|
/**
|
|
357
367
|
* CLI command result (legacy format).
|
|
@@ -373,6 +383,27 @@ export interface CommandResultV2 {
|
|
|
373
383
|
data?: unknown;
|
|
374
384
|
error?: EnvelopeError;
|
|
375
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Execution profile:
|
|
388
|
+
* - core: minimal constraints (5-minute path)
|
|
389
|
+
* - default: safe defaults for day-to-day usage
|
|
390
|
+
* - strict: higher assurance (more enforcement)
|
|
391
|
+
* - certified: strongest gates (intended for publishable / regulated flows)
|
|
392
|
+
*/
|
|
393
|
+
export type ExecutionProfile = 'core' | 'default' | 'strict' | 'certified';
|
|
394
|
+
/** Validation mode for input/output schemas and runtime enforcement. */
|
|
395
|
+
export type ValidateMode = 'auto' | 'on' | 'off';
|
|
396
|
+
export interface ExecutionPolicy {
|
|
397
|
+
profile: ExecutionProfile;
|
|
398
|
+
validate: ValidateMode;
|
|
399
|
+
audit: boolean;
|
|
400
|
+
enableRepair: boolean;
|
|
401
|
+
/**
|
|
402
|
+
* If true, refuse to run non-v2.2 modules (legacy/v0/v1 or missing formatVersion).
|
|
403
|
+
* This is a gate for certification-style flows.
|
|
404
|
+
*/
|
|
405
|
+
requireV22: boolean;
|
|
406
|
+
}
|
|
376
407
|
export interface ModuleInput {
|
|
377
408
|
code?: string;
|
|
378
409
|
query?: string;
|