cnagent 2.0.7 → 2.1.1

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/bin/cn CHANGED
@@ -1,364 +1,15 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env sh
2
+ # cn - Coherent Network agent CLI
3
+ # OCaml source: tools/src/cn/
4
+ # Bundled JS: tools/dist/cn.js
2
5
 
3
- /**
4
- * cn - Coherent Network agent CLI
5
- *
6
- * This is a thin wrapper that routes to the appropriate tool.
7
- * OCaml source in tools/src/cn/, bundled JS in dist/cn.js
8
- */
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ DIST="$SCRIPT_DIR/../tools/dist/cn.js"
9
8
 
10
- const { execSync, spawn } = require('child_process');
11
- const path = require('path');
12
- const fs = require('fs');
9
+ if [ ! -f "$DIST" ]; then
10
+ echo "Error: tools/dist/cn.js not found."
11
+ echo "Run 'npm run build' in the cn-agent directory first."
12
+ exit 1
13
+ fi
13
14
 
14
- const VERSION = '2.0.7';
15
-
16
- // Colors (respects NO_COLOR)
17
- const noColor = process.env.NO_COLOR !== undefined;
18
- const c = {
19
- reset: noColor ? '' : '\x1b[0m',
20
- green: noColor ? '' : '\x1b[32m',
21
- red: noColor ? '' : '\x1b[31m',
22
- yellow: noColor ? '' : '\x1b[33m',
23
- cyan: noColor ? '' : '\x1b[36m',
24
- magenta: noColor ? '' : '\x1b[35m',
25
- dim: noColor ? '' : '\x1b[2m',
26
- };
27
-
28
- const ok = (msg) => console.log(`${c.green}✓ ${msg}${c.reset}`);
29
- const fail = (msg) => console.log(`${c.red}✗ ${msg}${c.reset}`);
30
- const info = (msg) => console.log(`${c.cyan}${msg}${c.reset}`);
31
- const warn = (msg) => console.log(`${c.yellow}⚠ ${msg}${c.reset}`);
32
- const cmd = (msg) => `${c.magenta}${msg}${c.reset}`;
33
-
34
- const HELP = `cn - Coherent Network agent CLI
35
-
36
- Usage: cn <command> [options]
37
-
38
- Commands:
39
- init [name] Create new hub
40
- status Show hub state
41
- inbox Manage inbound messages
42
- check List inbound branches
43
- process Materialize as threads
44
- flush Execute decisions
45
- peer Manage peers
46
- doctor Check system health
47
- update Update cn to latest version
48
-
49
- Aliases:
50
- i = inbox, s = status, d = doctor
51
-
52
- Flags:
53
- --help, -h Show help
54
- --version, -V Show version
55
-
56
- Examples:
57
- cn init sigma Create hub named 'sigma'
58
- cn inbox check List inbound branches
59
- cn doctor Check system health
60
- `;
61
-
62
- // Expand aliases
63
- const aliases = { i: 'inbox', s: 'status', d: 'doctor', p: 'peer', t: 'thread' };
64
- const expandAlias = (cmd) => aliases[cmd] || cmd;
65
-
66
- // Find hub path
67
- function findHubPath() {
68
- let dir = process.cwd();
69
- while (dir !== '/') {
70
- if (fs.existsSync(path.join(dir, '.cn', 'config.json'))) {
71
- return dir;
72
- }
73
- // Also check for cn-* pattern with state/peers.md
74
- if (fs.existsSync(path.join(dir, 'state', 'peers.md'))) {
75
- return dir;
76
- }
77
- dir = path.dirname(dir);
78
- }
79
- return null;
80
- }
81
-
82
- // Derive name from path
83
- function deriveName(hubPath) {
84
- const base = path.basename(hubPath);
85
- return base.startsWith('cn-') ? base.slice(3) : base;
86
- }
87
-
88
- // Run inbox tool
89
- function runInbox(subCmd, hubPath, name) {
90
- const inboxJs = path.join(__dirname, '..', 'dist', 'inbox.js');
91
- if (!fs.existsSync(inboxJs)) {
92
- fail('inbox.js not found. Run from cn-agent directory or install globally.');
93
- process.exit(1);
94
- }
95
- const args = [inboxJs, subCmd, hubPath, name];
96
- const result = spawn('node', args, { stdio: 'inherit' });
97
- result.on('close', (code) => process.exit(code));
98
- }
99
-
100
- // Doctor command
101
- function doctor(hubPath) {
102
- console.log(`cn v${VERSION}`);
103
- info(`Checking health...`);
104
- console.log('');
105
-
106
- let checks = [];
107
- let warnings = [];
108
-
109
- // Git
110
- try {
111
- const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim();
112
- checks.push({ name: 'git', ok: true, val: gitVersion.replace('git version ', '') });
113
- } catch {
114
- checks.push({ name: 'git', ok: false, val: 'not installed' });
115
- }
116
-
117
- // Git config
118
- try {
119
- const userName = execSync('git config user.name', { encoding: 'utf8' }).trim();
120
- checks.push({ name: 'git user.name', ok: true, val: userName });
121
- } catch {
122
- checks.push({ name: 'git user.name', ok: false, val: 'not set' });
123
- }
124
-
125
- try {
126
- const userEmail = execSync('git config user.email', { encoding: 'utf8' }).trim();
127
- checks.push({ name: 'git user.email', ok: true, val: userEmail });
128
- } catch {
129
- checks.push({ name: 'git user.email', ok: false, val: 'not set' });
130
- }
131
-
132
- // Hub directory
133
- checks.push({ name: 'hub directory', ok: fs.existsSync(hubPath), val: fs.existsSync(hubPath) ? 'exists' : 'not found' });
134
-
135
- // .cn/config.json
136
- const configPath = path.join(hubPath, '.cn', 'config.json');
137
- checks.push({ name: '.cn/config.json', ok: fs.existsSync(configPath), val: fs.existsSync(configPath) ? 'exists' : 'missing' });
138
-
139
- // spec/SOUL.md
140
- const soulPath = path.join(hubPath, 'spec', 'SOUL.md');
141
- if (fs.existsSync(soulPath)) {
142
- checks.push({ name: 'spec/SOUL.md', ok: true, val: 'exists' });
143
- } else {
144
- warnings.push({ name: 'spec/SOUL.md', val: 'missing (optional)' });
145
- }
146
-
147
- // state/peers.md
148
- const peersPath = path.join(hubPath, 'state', 'peers.md');
149
- if (fs.existsSync(peersPath)) {
150
- const content = fs.readFileSync(peersPath, 'utf8');
151
- const peerCount = (content.match(/- name:/g) || []).length;
152
- checks.push({ name: 'state/peers.md', ok: true, val: `${peerCount} peer(s)` });
153
- } else {
154
- checks.push({ name: 'state/peers.md', ok: false, val: 'missing' });
155
- }
156
-
157
- // origin remote
158
- try {
159
- execSync('git remote get-url origin', { cwd: hubPath, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
160
- checks.push({ name: 'origin remote', ok: true, val: 'configured' });
161
- } catch {
162
- checks.push({ name: 'origin remote', ok: false, val: 'not configured' });
163
- }
164
-
165
- // inbox status
166
- try {
167
- const result = execSync(`node ${path.join(__dirname, '..', 'dist', 'inbox.js')} check ${hubPath} ${deriveName(hubPath)} 2>&1`, { encoding: 'utf8' });
168
- const inboundMatch = result.match(/(\d+) inbound/);
169
- if (inboundMatch) {
170
- const count = parseInt(inboundMatch[1]);
171
- if (count > 0) {
172
- warnings.push({ name: 'inbox', val: `${count} pending` });
173
- } else {
174
- checks.push({ name: 'inbox', ok: true, val: 'clear' });
175
- }
176
- } else if (result.includes('All clear')) {
177
- checks.push({ name: 'inbox', ok: true, val: 'clear' });
178
- }
179
- } catch {
180
- warnings.push({ name: 'inbox', val: 'check failed' });
181
- }
182
-
183
- // Print checks
184
- const width = 22;
185
- checks.forEach(({ name, ok: isOk, val }) => {
186
- const dots = '.'.repeat(Math.max(1, width - name.length));
187
- const status = isOk
188
- ? `${c.green}✓ ${val}${c.reset}`
189
- : `${c.red}✗ ${val}${c.reset}`;
190
- console.log(`${name}${dots} ${status}`);
191
- });
192
-
193
- // Print warnings
194
- warnings.forEach(({ name, val }) => {
195
- const dots = '.'.repeat(Math.max(1, width - name.length));
196
- console.log(`${name}${dots} ${c.yellow}⚠ ${val}${c.reset}`);
197
- });
198
-
199
- console.log('');
200
- const fails = checks.filter(c => !c.ok).length;
201
- const warns = warnings.length;
202
-
203
- if (fails === 0) {
204
- ok('All critical checks passed.');
205
- } else {
206
- fail(`${fails} issue(s) found.`);
207
- }
208
-
209
- console.log(`${c.dim}[status] ok=${checks.filter(c=>c.ok).length} warn=${warns} fail=${fails} version=${VERSION}${c.reset}`);
210
- process.exit(fails > 0 ? 1 : 0);
211
- }
212
-
213
- // Status command
214
- function status(hubPath, name) {
215
- info(`cn hub: ${name}`);
216
- console.log('');
217
- console.log(`hub..................... ${c.green}✓${c.reset}`);
218
- console.log(`name.................... ${c.green}✓ ${name}${c.reset}`);
219
- console.log(`path.................... ${c.green}✓ ${hubPath}${c.reset}`);
220
- console.log('');
221
- console.log(`${c.dim}[status] ok version=${VERSION}${c.reset}`);
222
- }
223
-
224
- // Main
225
- const args = process.argv.slice(2);
226
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
227
- console.log(HELP);
228
- process.exit(0);
229
- }
230
-
231
- if (args[0] === '--version' || args[0] === '-V') {
232
- console.log(`cn ${VERSION}`);
233
- process.exit(0);
234
- }
235
-
236
- const command = expandAlias(args[0]);
237
- const subArgs = args.slice(1);
238
-
239
- // Commands that don't need hub
240
- if (command === 'init') {
241
- warn('cn init not yet implemented');
242
- process.exit(1);
243
- }
244
-
245
- if (command === 'update') {
246
- // Check if already on latest
247
- try {
248
- const latest = execSync('npm view cnagent version', { encoding: 'utf8' }).trim();
249
- if (VERSION === latest) {
250
- ok(`Already up to date (v${VERSION})`);
251
- // Still write runtime.md if in a hub
252
- writeRuntimeMd(findHubPath(), latest);
253
- process.exit(0);
254
- }
255
- info(`Updating cnagent v${VERSION} → v${latest}...`);
256
- execSync('npm install -g cnagent@latest', { stdio: 'inherit' });
257
- ok(`Updated to v${latest}`);
258
- // Write runtime.md if in a hub
259
- writeRuntimeMd(findHubPath(), latest);
260
- } catch (e) {
261
- fail('Update failed. Try: npm install -g cnagent@latest');
262
- process.exit(1);
263
- }
264
- process.exit(0);
265
- }
266
-
267
- // Write state/runtime.md after update
268
- function writeRuntimeMd(hubPath, cnVersion) {
269
- if (!hubPath) return; // Not in a hub, skip
270
-
271
- const runtimePath = path.join(hubPath, 'state', 'runtime.md');
272
- const stateDir = path.join(hubPath, 'state');
273
-
274
- // Ensure state/ exists
275
- if (!fs.existsSync(stateDir)) {
276
- fs.mkdirSync(stateDir, { recursive: true });
277
- }
278
-
279
- // Get template info
280
- let templateVersion = 'unknown';
281
- let templateCommit = 'unknown';
282
- try {
283
- const pkgPath = path.join(__dirname, '..', 'package.json');
284
- if (fs.existsSync(pkgPath)) {
285
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
286
- templateVersion = pkg.version || 'unknown';
287
- }
288
- templateCommit = execSync('git rev-parse --short HEAD 2>/dev/null || echo unknown', {
289
- encoding: 'utf8',
290
- cwd: hubPath
291
- }).trim();
292
- } catch {}
293
-
294
- // Gather runtime info
295
- const nodeVersion = process.version;
296
- const platform = `${process.platform} ${process.arch}`;
297
- const hubName = deriveName(hubPath);
298
-
299
- let peerCount = 0;
300
- try {
301
- const peersPath = path.join(hubPath, 'state', 'peers.md');
302
- if (fs.existsSync(peersPath)) {
303
- const content = fs.readFileSync(peersPath, 'utf8');
304
- peerCount = (content.match(/- name:/g) || []).length;
305
- }
306
- } catch {}
307
-
308
- const content = `# Runtime State
309
-
310
- Auto-generated by \`cn update\`. Do not edit manually.
311
-
312
- \`\`\`yaml
313
- session_start: ${new Date().toISOString()}
314
- cn_version: ${cnVersion}
315
- template_version: ${templateVersion}
316
- template_commit: ${templateCommit}
317
- node_version: ${nodeVersion}
318
- platform: ${platform}
319
- hub_name: ${hubName}
320
- hub_path: ${hubPath}
321
- peer_count: ${peerCount}
322
- \`\`\`
323
- `;
324
-
325
- fs.writeFileSync(runtimePath, content);
326
- info(`Wrote ${runtimePath}`);
327
- }
328
-
329
- // Find hub
330
- const hubPath = findHubPath();
331
- if (!hubPath) {
332
- fail('Not in a cn hub.');
333
- console.log('');
334
- console.log('Either:');
335
- console.log(` 1) ${cmd('cd')} into an existing hub (cn-sigma, cn-pi, etc.)`);
336
- console.log(` 2) ${cmd('cn init <name>')} to create a new one`);
337
- process.exit(1);
338
- }
339
-
340
- const name = deriveName(hubPath);
341
-
342
- switch (command) {
343
- case 'status':
344
- status(hubPath, name);
345
- break;
346
-
347
- case 'doctor':
348
- doctor(hubPath);
349
- break;
350
-
351
- case 'inbox':
352
- const subCmd = subArgs[0] || 'check';
353
- if (!['check', 'process', 'flush'].includes(subCmd)) {
354
- fail(`Unknown inbox command: ${subCmd}`);
355
- process.exit(1);
356
- }
357
- runInbox(subCmd, hubPath, name);
358
- break;
359
-
360
- default:
361
- fail(`Unknown command: ${command}`);
362
- console.log(`Run ${cmd('cn --help')} for usage.`);
363
- process.exit(1);
364
- }
15
+ exec node "$DIST" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cnagent",
3
- "version": "2.0.7",
3
+ "version": "2.1.1",
4
4
  "description": "Coherent Network agent CLI — everything runs through cn",
5
5
  "keywords": [
6
6
  "cn-agent",
@@ -22,12 +22,14 @@
22
22
  },
23
23
  "files": [
24
24
  "bin/",
25
- "dist/"
25
+ "tools/dist/"
26
26
  ],
27
27
  "scripts": {
28
28
  "build": "dune build && npm run bundle",
29
- "bundle": "npx esbuild _build/default/tools/src/inbox/output/tools/src/inbox/inbox.js --bundle --platform=node --outfile=dist/inbox.js",
30
- "test": "node --test test/*.test.js"
29
+ "bundle": "npm run bundle:inbox && npm run bundle:cn",
30
+ "bundle:inbox": "npx esbuild _build/default/tools/src/inbox/output/tools/src/inbox/inbox.js --bundle --platform=node --outfile=tools/dist/inbox.js",
31
+ "bundle:cn": "npx esbuild _build/default/tools/src/cn/output/tools/src/cn/cn.js --bundle --platform=node --outfile=tools/dist/cn.js",
32
+ "test": "dune runtest"
31
33
  },
32
34
  "author": "usurobor",
33
35
  "license": "Apache-2.0",