cognitive-modules 0.6.0
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 +165 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +335 -0
- package/dist/commands/add.d.ts +34 -0
- package/dist/commands/add.js +229 -0
- package/dist/commands/index.d.ts +11 -0
- package/dist/commands/index.js +11 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +78 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.js +28 -0
- package/dist/commands/pipe.d.ts +9 -0
- package/dist/commands/pipe.js +59 -0
- package/dist/commands/remove.d.ts +10 -0
- package/dist/commands/remove.js +47 -0
- package/dist/commands/run.d.ts +12 -0
- package/dist/commands/run.js +65 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +105 -0
- package/dist/commands/versions.d.ts +13 -0
- package/dist/commands/versions.js +60 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +11 -0
- package/dist/modules/index.d.ts +5 -0
- package/dist/modules/index.js +5 -0
- package/dist/modules/loader.d.ts +12 -0
- package/dist/modules/loader.js +197 -0
- package/dist/modules/runner.d.ts +12 -0
- package/dist/modules/runner.js +229 -0
- package/dist/providers/anthropic.d.ts +14 -0
- package/dist/providers/anthropic.js +70 -0
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.js +19 -0
- package/dist/providers/deepseek.d.ts +14 -0
- package/dist/providers/deepseek.js +66 -0
- package/dist/providers/gemini.d.ts +19 -0
- package/dist/providers/gemini.js +94 -0
- package/dist/providers/index.d.ts +19 -0
- package/dist/providers/index.js +74 -0
- package/dist/providers/minimax.d.ts +14 -0
- package/dist/providers/minimax.js +64 -0
- package/dist/providers/moonshot.d.ts +14 -0
- package/dist/providers/moonshot.js +65 -0
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/providers/ollama.js +64 -0
- package/dist/providers/openai.d.ts +14 -0
- package/dist/providers/openai.js +67 -0
- package/dist/providers/qwen.d.ts +14 -0
- package/dist/providers/qwen.js +65 -0
- package/dist/types.d.ts +136 -0
- package/dist/types.js +5 -0
- package/package.json +48 -0
- package/src/cli.ts +375 -0
- package/src/commands/add.ts +315 -0
- package/src/commands/index.ts +12 -0
- package/src/commands/init.ts +94 -0
- package/src/commands/list.ts +33 -0
- package/src/commands/pipe.ts +76 -0
- package/src/commands/remove.ts +57 -0
- package/src/commands/run.ts +80 -0
- package/src/commands/update.ts +130 -0
- package/src/commands/versions.ts +79 -0
- package/src/index.ts +44 -0
- package/src/modules/index.ts +6 -0
- package/src/modules/loader.ts +219 -0
- package/src/modules/runner.ts +278 -0
- package/src/providers/anthropic.ts +89 -0
- package/src/providers/base.ts +29 -0
- package/src/providers/deepseek.ts +83 -0
- package/src/providers/gemini.ts +117 -0
- package/src/providers/index.ts +78 -0
- package/src/providers/minimax.ts +81 -0
- package/src/providers/moonshot.ts +82 -0
- package/src/providers/ollama.ts +83 -0
- package/src/providers/openai.ts +84 -0
- package/src/providers/qwen.ts +82 -0
- package/src/types.ts +184 -0
- package/tsconfig.json +17 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cognitive Modules CLI
|
|
4
|
+
*
|
|
5
|
+
* cog run <module> --args "..." - Run a module
|
|
6
|
+
* cog add <url> -m <name> - Add module from GitHub
|
|
7
|
+
* cog update <module> - Update to latest version
|
|
8
|
+
* cog remove <module> - Remove installed module
|
|
9
|
+
* cog versions <url> - List available versions
|
|
10
|
+
* cog list - List available modules
|
|
11
|
+
* cog pipe --module <name> - Pipe mode (stdin/stdout)
|
|
12
|
+
* cog doctor - Check configuration
|
|
13
|
+
*
|
|
14
|
+
* npx cognitive-modules add ziel-io/cognitive-modules -m code-simplifier
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { parseArgs } from 'node:util';
|
|
18
|
+
import { getProvider, listProviders } from './providers/index.js';
|
|
19
|
+
import { run, list, pipe, init, add, update, remove, versions } from './commands/index.js';
|
|
20
|
+
import type { CommandContext } from './types.js';
|
|
21
|
+
|
|
22
|
+
const VERSION = '0.6.0';
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const command = args[0];
|
|
27
|
+
|
|
28
|
+
if (!command || command === '--help' || command === '-h') {
|
|
29
|
+
printHelp();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (command === '--version' || command === '-v') {
|
|
34
|
+
console.log(`Cognitive Runtime v${VERSION}`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Parse common options
|
|
39
|
+
const { values } = parseArgs({
|
|
40
|
+
args: args.slice(1),
|
|
41
|
+
options: {
|
|
42
|
+
args: { type: 'string', short: 'a' },
|
|
43
|
+
input: { type: 'string', short: 'i' },
|
|
44
|
+
module: { type: 'string', short: 'm' },
|
|
45
|
+
model: { type: 'string', short: 'M' },
|
|
46
|
+
provider: { type: 'string', short: 'p' },
|
|
47
|
+
pretty: { type: 'boolean', default: false },
|
|
48
|
+
verbose: { type: 'boolean', short: 'V', default: false },
|
|
49
|
+
'no-validate': { type: 'boolean', default: false },
|
|
50
|
+
// Add/update options
|
|
51
|
+
name: { type: 'string', short: 'n' },
|
|
52
|
+
tag: { type: 'string', short: 't' },
|
|
53
|
+
branch: { type: 'string', short: 'b' },
|
|
54
|
+
limit: { type: 'string', short: 'l' },
|
|
55
|
+
},
|
|
56
|
+
allowPositionals: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Get provider
|
|
60
|
+
let provider;
|
|
61
|
+
try {
|
|
62
|
+
provider = getProvider(values.provider, values.model);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ctx: CommandContext = {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
provider,
|
|
71
|
+
verbose: values.verbose,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'run': {
|
|
77
|
+
const moduleName = args[1];
|
|
78
|
+
if (!moduleName || moduleName.startsWith('-')) {
|
|
79
|
+
console.error('Usage: cog run <module> [--args "..."]');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = await run(moduleName, ctx, {
|
|
84
|
+
args: values.args,
|
|
85
|
+
input: values.input,
|
|
86
|
+
noValidate: values['no-validate'],
|
|
87
|
+
pretty: values.pretty,
|
|
88
|
+
verbose: values.verbose,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
console.error(`Error: ${result.error}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(JSON.stringify(result.data, null, values.pretty ? 2 : 0));
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'list': {
|
|
101
|
+
const result = await list(ctx);
|
|
102
|
+
if (!result.success) {
|
|
103
|
+
console.error(`Error: ${result.error}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data = result.data as { modules: Array<{ name: string; version: string; responsibility: string; location: string }> };
|
|
108
|
+
|
|
109
|
+
if (data.modules.length === 0) {
|
|
110
|
+
console.log('No modules found.');
|
|
111
|
+
} else {
|
|
112
|
+
console.log('Available Modules:');
|
|
113
|
+
console.log('');
|
|
114
|
+
for (const m of data.modules) {
|
|
115
|
+
console.log(` ${m.name} (v${m.version})`);
|
|
116
|
+
console.log(` ${m.responsibility}`);
|
|
117
|
+
console.log(` ${m.location}`);
|
|
118
|
+
console.log('');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case 'pipe': {
|
|
125
|
+
const moduleName = values.module || args[1];
|
|
126
|
+
if (!moduleName) {
|
|
127
|
+
console.error('Usage: cog pipe --module <name>');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await pipe(ctx, {
|
|
132
|
+
module: moduleName,
|
|
133
|
+
noValidate: values['no-validate'],
|
|
134
|
+
});
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
case 'init': {
|
|
139
|
+
const moduleName = args[1];
|
|
140
|
+
const result = await init(ctx, moduleName);
|
|
141
|
+
|
|
142
|
+
if (!result.success) {
|
|
143
|
+
console.error(`Error: ${result.error}`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const data = result.data as { message: string; location: string; files?: string[]; hint?: string };
|
|
148
|
+
console.log(data.message);
|
|
149
|
+
console.log(` Location: ${data.location}`);
|
|
150
|
+
if (data.files) {
|
|
151
|
+
console.log(` Files: ${data.files.join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
if (data.hint) {
|
|
154
|
+
console.log(` ${data.hint}`);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'doctor': {
|
|
160
|
+
console.log('Cognitive Runtime - Environment Check\n');
|
|
161
|
+
|
|
162
|
+
console.log('Providers:');
|
|
163
|
+
for (const p of listProviders()) {
|
|
164
|
+
const status = p.configured ? '✓ configured' : '– not configured';
|
|
165
|
+
console.log(` ${p.name}: ${status} (${p.model})`);
|
|
166
|
+
}
|
|
167
|
+
console.log('');
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const provider = getProvider();
|
|
171
|
+
console.log(`Active provider: ${provider.name}`);
|
|
172
|
+
} catch {
|
|
173
|
+
console.log('Active provider: none (set an API key)');
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'add': {
|
|
179
|
+
const url = args[1];
|
|
180
|
+
if (!url || url.startsWith('-')) {
|
|
181
|
+
console.error('Usage: cog add <url> [--module <name>] [--tag <version>]');
|
|
182
|
+
console.error('');
|
|
183
|
+
console.error('Examples:');
|
|
184
|
+
console.error(' cog add ziel-io/cognitive-modules -m code-simplifier');
|
|
185
|
+
console.error(' cog add org/repo --module my-module --tag v1.0.0');
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(`→ Adding module from: ${url}`);
|
|
190
|
+
if (values.module) console.log(` Module path: ${values.module}`);
|
|
191
|
+
if (values.tag) console.log(` Version: ${values.tag}`);
|
|
192
|
+
|
|
193
|
+
const result = await add(url, ctx, {
|
|
194
|
+
module: values.module,
|
|
195
|
+
name: values.name,
|
|
196
|
+
tag: values.tag,
|
|
197
|
+
branch: values.branch,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!result.success) {
|
|
201
|
+
console.error(`✗ Failed to add module: ${result.error}`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const data = result.data as { message: string; location: string; name: string };
|
|
206
|
+
console.log(`✓ ${data.message}`);
|
|
207
|
+
console.log(` Location: ${data.location}`);
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log('Run with:');
|
|
210
|
+
console.log(` cog run ${data.name} --args "your input"`);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
case 'update': {
|
|
215
|
+
const moduleName = args[1];
|
|
216
|
+
if (!moduleName || moduleName.startsWith('-')) {
|
|
217
|
+
console.error('Usage: cog update <module> [--tag <version>]');
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(`→ Updating module: ${moduleName}`);
|
|
222
|
+
|
|
223
|
+
const result = await update(moduleName, ctx, {
|
|
224
|
+
tag: values.tag,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (!result.success) {
|
|
228
|
+
console.error(`✗ ${result.error}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const data = result.data as { message: string };
|
|
233
|
+
console.log(`✓ ${data.message}`);
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
case 'remove': {
|
|
238
|
+
const moduleName = args[1];
|
|
239
|
+
if (!moduleName || moduleName.startsWith('-')) {
|
|
240
|
+
console.error('Usage: cog remove <module>');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const result = await remove(moduleName, ctx);
|
|
245
|
+
|
|
246
|
+
if (!result.success) {
|
|
247
|
+
console.error(`✗ ${result.error}`);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const data = result.data as { message: string };
|
|
252
|
+
console.log(`✓ ${data.message}`);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
case 'versions': {
|
|
257
|
+
const url = args[1];
|
|
258
|
+
if (!url || url.startsWith('-')) {
|
|
259
|
+
console.error('Usage: cog versions <url>');
|
|
260
|
+
console.error('');
|
|
261
|
+
console.error('Examples:');
|
|
262
|
+
console.error(' cog versions ziel-io/cognitive-modules');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log(`→ Fetching versions from: ${url}\n`);
|
|
267
|
+
|
|
268
|
+
const limit = values.limit ? parseInt(values.limit, 10) : 10;
|
|
269
|
+
const result = await versions(url, ctx, { limit });
|
|
270
|
+
|
|
271
|
+
if (!result.success) {
|
|
272
|
+
console.error(`✗ ${result.error}`);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const data = result.data as { tags: string[]; count: number };
|
|
277
|
+
|
|
278
|
+
if (data.tags.length === 0) {
|
|
279
|
+
console.log('No tags/versions found.');
|
|
280
|
+
} else {
|
|
281
|
+
console.log(`Available Versions (${data.count}):`);
|
|
282
|
+
console.log('');
|
|
283
|
+
for (const tag of data.tags) {
|
|
284
|
+
console.log(` ${tag}`);
|
|
285
|
+
console.log(` cog add ${url} --tag ${tag}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
default:
|
|
292
|
+
console.error(`Unknown command: ${command}`);
|
|
293
|
+
console.error('Run "cog --help" for usage.');
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
} catch (e) {
|
|
297
|
+
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
298
|
+
if (values.verbose && e instanceof Error) {
|
|
299
|
+
console.error(e.stack);
|
|
300
|
+
}
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function printHelp() {
|
|
306
|
+
console.log(`
|
|
307
|
+
Cognitive Runtime v${VERSION}
|
|
308
|
+
Structured AI Task Execution
|
|
309
|
+
|
|
310
|
+
USAGE:
|
|
311
|
+
cog <command> [options]
|
|
312
|
+
|
|
313
|
+
COMMANDS:
|
|
314
|
+
run <module> Run a Cognitive Module
|
|
315
|
+
list List available modules
|
|
316
|
+
add <url> Add module from GitHub
|
|
317
|
+
update <module> Update module to latest version
|
|
318
|
+
remove <module> Remove installed module
|
|
319
|
+
versions <url> List available versions
|
|
320
|
+
pipe Pipe mode (stdin/stdout)
|
|
321
|
+
init [name] Initialize project or create module
|
|
322
|
+
doctor Check configuration
|
|
323
|
+
|
|
324
|
+
OPTIONS:
|
|
325
|
+
-a, --args <str> Arguments to pass to module
|
|
326
|
+
-i, --input <json> JSON input for module
|
|
327
|
+
-m, --module <name> Module path within repo (for add)
|
|
328
|
+
-n, --name <name> Override module name (for add)
|
|
329
|
+
-t, --tag <version> Git tag/version (for add/update)
|
|
330
|
+
-b, --branch <name> Git branch (for add)
|
|
331
|
+
-M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
|
|
332
|
+
-p, --provider <name> LLM provider (gemini, openai, anthropic, deepseek, minimax, moonshot, qwen, ollama)
|
|
333
|
+
--pretty Pretty-print JSON output
|
|
334
|
+
-V, --verbose Verbose output
|
|
335
|
+
--no-validate Skip schema validation
|
|
336
|
+
-v, --version Show version
|
|
337
|
+
-h, --help Show this help
|
|
338
|
+
|
|
339
|
+
EXAMPLES:
|
|
340
|
+
# Add modules from GitHub
|
|
341
|
+
cog add ziel-io/cognitive-modules -m code-simplifier
|
|
342
|
+
cog add org/repo --module my-module --tag v1.0.0
|
|
343
|
+
|
|
344
|
+
# Version management
|
|
345
|
+
cog update code-simplifier
|
|
346
|
+
cog versions ziel-io/cognitive-modules
|
|
347
|
+
cog remove code-simplifier
|
|
348
|
+
|
|
349
|
+
# Run modules
|
|
350
|
+
cog run code-reviewer --args "def foo(): pass"
|
|
351
|
+
cog run code-reviewer --provider openai --model gpt-4o --args "..."
|
|
352
|
+
cog list
|
|
353
|
+
|
|
354
|
+
# Other
|
|
355
|
+
echo "review this code" | cog pipe --module code-reviewer
|
|
356
|
+
cog init my-module
|
|
357
|
+
cog doctor
|
|
358
|
+
|
|
359
|
+
ENVIRONMENT:
|
|
360
|
+
GEMINI_API_KEY Google Gemini
|
|
361
|
+
OPENAI_API_KEY OpenAI
|
|
362
|
+
ANTHROPIC_API_KEY Anthropic Claude
|
|
363
|
+
DEEPSEEK_API_KEY DeepSeek
|
|
364
|
+
MINIMAX_API_KEY MiniMax
|
|
365
|
+
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
366
|
+
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
367
|
+
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
368
|
+
COG_MODEL Override default model for any provider
|
|
369
|
+
`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
main().catch(e => {
|
|
373
|
+
console.error('Fatal error:', e);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
});
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add command - Install modules from GitHub
|
|
3
|
+
*
|
|
4
|
+
* cog add ziel-io/cognitive-modules -m code-simplifier
|
|
5
|
+
* cog add https://github.com/org/repo --module name --tag v1.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createWriteStream, existsSync, mkdirSync, rmSync, readdirSync, statSync, copyFileSync } from 'node:fs';
|
|
9
|
+
import { writeFile, readFile, mkdir, rm } from 'node:fs/promises';
|
|
10
|
+
import { pipeline } from 'node:stream/promises';
|
|
11
|
+
import { Readable } from 'node:stream';
|
|
12
|
+
import { join, basename } from 'node:path';
|
|
13
|
+
import { homedir, tmpdir } from 'node:os';
|
|
14
|
+
import { createGunzip } from 'node:zlib';
|
|
15
|
+
import type { CommandContext, CommandResult } from '../types.js';
|
|
16
|
+
|
|
17
|
+
// Module storage paths
|
|
18
|
+
const USER_MODULES_DIR = join(homedir(), '.cognitive', 'modules');
|
|
19
|
+
const INSTALLED_MANIFEST = join(homedir(), '.cognitive', 'installed.json');
|
|
20
|
+
|
|
21
|
+
export interface AddOptions {
|
|
22
|
+
module?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
branch?: string;
|
|
25
|
+
tag?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InstallManifest {
|
|
29
|
+
[moduleName: string]: {
|
|
30
|
+
source: string;
|
|
31
|
+
githubUrl: string;
|
|
32
|
+
modulePath?: string;
|
|
33
|
+
tag?: string;
|
|
34
|
+
branch?: string;
|
|
35
|
+
version?: string;
|
|
36
|
+
installedAt: string;
|
|
37
|
+
installedTime: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse GitHub URL or shorthand
|
|
43
|
+
*/
|
|
44
|
+
function parseGitHubUrl(url: string): { org: string; repo: string; fullUrl: string } {
|
|
45
|
+
// Handle shorthand: org/repo
|
|
46
|
+
if (!url.startsWith('http')) {
|
|
47
|
+
url = `https://github.com/${url}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const match = url.match(/https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/?/);
|
|
51
|
+
if (!match) {
|
|
52
|
+
throw new Error(`Invalid GitHub URL: ${url}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const org = match[1];
|
|
56
|
+
const repo = match[2].replace(/\.git$/, '');
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
org,
|
|
60
|
+
repo,
|
|
61
|
+
fullUrl: `https://github.com/${org}/${repo}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Download and extract ZIP from GitHub
|
|
67
|
+
*/
|
|
68
|
+
async function downloadAndExtract(
|
|
69
|
+
org: string,
|
|
70
|
+
repo: string,
|
|
71
|
+
ref: string,
|
|
72
|
+
isTag: boolean
|
|
73
|
+
): Promise<string> {
|
|
74
|
+
const zipUrl = isTag
|
|
75
|
+
? `https://github.com/${org}/${repo}/archive/refs/tags/${ref}.zip`
|
|
76
|
+
: `https://github.com/${org}/${repo}/archive/refs/heads/${ref}.zip`;
|
|
77
|
+
|
|
78
|
+
// Create temp directory
|
|
79
|
+
const tempDir = join(tmpdir(), `cog-${Date.now()}`);
|
|
80
|
+
mkdirSync(tempDir, { recursive: true });
|
|
81
|
+
|
|
82
|
+
const zipPath = join(tempDir, 'repo.zip');
|
|
83
|
+
|
|
84
|
+
// Download ZIP
|
|
85
|
+
const response = await fetch(zipUrl, {
|
|
86
|
+
headers: { 'User-Agent': 'cognitive-runtime/1.0' },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error(`Failed to download: ${response.status} ${response.statusText}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Save to file
|
|
94
|
+
const fileStream = createWriteStream(zipPath);
|
|
95
|
+
await pipeline(Readable.fromWeb(response.body as any), fileStream);
|
|
96
|
+
|
|
97
|
+
// Extract using built-in unzip (available on most systems)
|
|
98
|
+
const { execSync } = await import('node:child_process');
|
|
99
|
+
execSync(`unzip -q "${zipPath}" -d "${tempDir}"`, { stdio: 'pipe' });
|
|
100
|
+
|
|
101
|
+
// Find extracted directory
|
|
102
|
+
const entries = readdirSync(tempDir).filter(
|
|
103
|
+
e => e !== 'repo.zip' && statSync(join(tempDir, e)).isDirectory()
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (entries.length === 0) {
|
|
107
|
+
throw new Error('ZIP file was empty');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return join(tempDir, entries[0]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if a directory is a valid module
|
|
115
|
+
*/
|
|
116
|
+
function isValidModule(path: string): boolean {
|
|
117
|
+
return (
|
|
118
|
+
existsSync(join(path, 'module.yaml')) ||
|
|
119
|
+
existsSync(join(path, 'MODULE.md')) ||
|
|
120
|
+
existsSync(join(path, 'module.md'))
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Find module within repository
|
|
126
|
+
*/
|
|
127
|
+
function findModuleInRepo(repoRoot: string, modulePath: string): string {
|
|
128
|
+
const possiblePaths = [
|
|
129
|
+
join(repoRoot, modulePath),
|
|
130
|
+
join(repoRoot, 'cognitive', 'modules', modulePath),
|
|
131
|
+
join(repoRoot, 'modules', modulePath),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
for (const p of possiblePaths) {
|
|
135
|
+
if (existsSync(p) && isValidModule(p)) {
|
|
136
|
+
return p;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Module not found at: ${modulePath}\n` +
|
|
142
|
+
`Searched in: ${possiblePaths.map(p => p.replace(repoRoot, '.')).join(', ')}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Copy directory recursively
|
|
148
|
+
*/
|
|
149
|
+
function copyDir(src: string, dest: string): void {
|
|
150
|
+
mkdirSync(dest, { recursive: true });
|
|
151
|
+
|
|
152
|
+
for (const entry of readdirSync(src)) {
|
|
153
|
+
const srcPath = join(src, entry);
|
|
154
|
+
const destPath = join(dest, entry);
|
|
155
|
+
|
|
156
|
+
if (statSync(srcPath).isDirectory()) {
|
|
157
|
+
copyDir(srcPath, destPath);
|
|
158
|
+
} else {
|
|
159
|
+
copyFileSync(srcPath, destPath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get module version from module.yaml or MODULE.md
|
|
166
|
+
*/
|
|
167
|
+
async function getModuleVersion(modulePath: string): Promise<string | undefined> {
|
|
168
|
+
const yaml = await import('js-yaml');
|
|
169
|
+
|
|
170
|
+
// Try v2 format
|
|
171
|
+
const yamlPath = join(modulePath, 'module.yaml');
|
|
172
|
+
if (existsSync(yamlPath)) {
|
|
173
|
+
const content = await readFile(yamlPath, 'utf-8');
|
|
174
|
+
const data = yaml.load(content) as { version?: string };
|
|
175
|
+
return data?.version;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Try v1 format
|
|
179
|
+
const mdPath = existsSync(join(modulePath, 'MODULE.md'))
|
|
180
|
+
? join(modulePath, 'MODULE.md')
|
|
181
|
+
: join(modulePath, 'module.md');
|
|
182
|
+
|
|
183
|
+
if (existsSync(mdPath)) {
|
|
184
|
+
const content = await readFile(mdPath, 'utf-8');
|
|
185
|
+
if (content.startsWith('---')) {
|
|
186
|
+
const parts = content.split('---');
|
|
187
|
+
if (parts.length >= 3) {
|
|
188
|
+
const meta = yaml.load(parts[1]) as { version?: string };
|
|
189
|
+
return meta?.version;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Record installation info
|
|
199
|
+
*/
|
|
200
|
+
async function recordInstall(
|
|
201
|
+
moduleName: string,
|
|
202
|
+
info: InstallManifest[string]
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
let manifest: InstallManifest = {};
|
|
205
|
+
|
|
206
|
+
if (existsSync(INSTALLED_MANIFEST)) {
|
|
207
|
+
const content = await readFile(INSTALLED_MANIFEST, 'utf-8');
|
|
208
|
+
manifest = JSON.parse(content);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
manifest[moduleName] = info;
|
|
212
|
+
|
|
213
|
+
await mkdir(join(homedir(), '.cognitive'), { recursive: true });
|
|
214
|
+
await writeFile(INSTALLED_MANIFEST, JSON.stringify(manifest, null, 2));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get installation info for a module
|
|
219
|
+
*/
|
|
220
|
+
export async function getInstallInfo(moduleName: string): Promise<InstallManifest[string] | null> {
|
|
221
|
+
if (!existsSync(INSTALLED_MANIFEST)) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const content = await readFile(INSTALLED_MANIFEST, 'utf-8');
|
|
226
|
+
const manifest: InstallManifest = JSON.parse(content);
|
|
227
|
+
return manifest[moduleName] || null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Add a module from GitHub
|
|
232
|
+
*/
|
|
233
|
+
export async function add(
|
|
234
|
+
url: string,
|
|
235
|
+
ctx: CommandContext,
|
|
236
|
+
options: AddOptions = {}
|
|
237
|
+
): Promise<CommandResult> {
|
|
238
|
+
const { org, repo, fullUrl } = parseGitHubUrl(url);
|
|
239
|
+
const { module: modulePath, name, branch = 'main', tag } = options;
|
|
240
|
+
|
|
241
|
+
// Determine ref (tag takes priority)
|
|
242
|
+
const ref = tag || branch;
|
|
243
|
+
const isTag = !!tag;
|
|
244
|
+
|
|
245
|
+
let repoRoot: string;
|
|
246
|
+
let sourcePath: string;
|
|
247
|
+
let moduleName: string;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
// Download repository
|
|
251
|
+
repoRoot = await downloadAndExtract(org, repo, ref, isTag);
|
|
252
|
+
|
|
253
|
+
// Find module
|
|
254
|
+
if (modulePath) {
|
|
255
|
+
sourcePath = findModuleInRepo(repoRoot, modulePath);
|
|
256
|
+
} else {
|
|
257
|
+
// Use repo root as module
|
|
258
|
+
if (!isValidModule(repoRoot)) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
'Repository root is not a valid module. Use --module to specify the module path.'
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
sourcePath = repoRoot;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Determine module name
|
|
267
|
+
moduleName = name || basename(sourcePath);
|
|
268
|
+
|
|
269
|
+
// Get version
|
|
270
|
+
const version = await getModuleVersion(sourcePath);
|
|
271
|
+
|
|
272
|
+
// Install to user modules dir
|
|
273
|
+
const targetPath = join(USER_MODULES_DIR, moduleName);
|
|
274
|
+
|
|
275
|
+
// Remove existing
|
|
276
|
+
if (existsSync(targetPath)) {
|
|
277
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Copy module
|
|
281
|
+
await mkdir(USER_MODULES_DIR, { recursive: true });
|
|
282
|
+
copyDir(sourcePath, targetPath);
|
|
283
|
+
|
|
284
|
+
// Record installation info
|
|
285
|
+
await recordInstall(moduleName, {
|
|
286
|
+
source: sourcePath,
|
|
287
|
+
githubUrl: fullUrl,
|
|
288
|
+
modulePath,
|
|
289
|
+
tag,
|
|
290
|
+
branch,
|
|
291
|
+
version,
|
|
292
|
+
installedAt: targetPath,
|
|
293
|
+
installedTime: new Date().toISOString(),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Cleanup temp directory
|
|
297
|
+
const tempDir = repoRoot.split('/').slice(0, -1).join('/');
|
|
298
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
success: true,
|
|
302
|
+
data: {
|
|
303
|
+
message: `Added: ${moduleName}${version ? ` v${version}` : ''}`,
|
|
304
|
+
name: moduleName,
|
|
305
|
+
version,
|
|
306
|
+
location: targetPath,
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return {
|
|
311
|
+
success: false,
|
|
312
|
+
error: error instanceof Error ? error.message : String(error),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commands - Re-export all commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './run.js';
|
|
6
|
+
export * from './list.js';
|
|
7
|
+
export * from './pipe.js';
|
|
8
|
+
export * from './init.js';
|
|
9
|
+
export * from './add.js';
|
|
10
|
+
export * from './update.js';
|
|
11
|
+
export * from './remove.js';
|
|
12
|
+
export * from './versions.js';
|