chainlesschain 0.37.12 → 0.40.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.
Files changed (48) hide show
  1. package/package.json +3 -2
  2. package/src/commands/agent.js +7 -1
  3. package/src/commands/ask.js +24 -9
  4. package/src/commands/chat.js +7 -1
  5. package/src/commands/cli-anything.js +266 -0
  6. package/src/commands/compliance.js +216 -0
  7. package/src/commands/dao.js +312 -0
  8. package/src/commands/dlp.js +278 -0
  9. package/src/commands/evomap.js +558 -0
  10. package/src/commands/hardening.js +230 -0
  11. package/src/commands/matrix.js +168 -0
  12. package/src/commands/nostr.js +185 -0
  13. package/src/commands/pqc.js +162 -0
  14. package/src/commands/scim.js +218 -0
  15. package/src/commands/serve.js +109 -0
  16. package/src/commands/siem.js +156 -0
  17. package/src/commands/social.js +480 -0
  18. package/src/commands/terraform.js +148 -0
  19. package/src/constants.js +1 -0
  20. package/src/index.js +60 -0
  21. package/src/lib/autonomous-agent.js +487 -0
  22. package/src/lib/cli-anything-bridge.js +379 -0
  23. package/src/lib/cli-context-engineering.js +472 -0
  24. package/src/lib/compliance-manager.js +290 -0
  25. package/src/lib/content-recommender.js +205 -0
  26. package/src/lib/dao-governance.js +296 -0
  27. package/src/lib/dlp-engine.js +304 -0
  28. package/src/lib/evomap-client.js +135 -0
  29. package/src/lib/evomap-federation.js +240 -0
  30. package/src/lib/evomap-governance.js +250 -0
  31. package/src/lib/evomap-manager.js +227 -0
  32. package/src/lib/git-integration.js +1 -1
  33. package/src/lib/hardening-manager.js +275 -0
  34. package/src/lib/llm-providers.js +14 -1
  35. package/src/lib/matrix-bridge.js +196 -0
  36. package/src/lib/nostr-bridge.js +195 -0
  37. package/src/lib/permanent-memory.js +370 -0
  38. package/src/lib/plan-mode.js +211 -0
  39. package/src/lib/pqc-manager.js +196 -0
  40. package/src/lib/scim-manager.js +212 -0
  41. package/src/lib/session-manager.js +38 -0
  42. package/src/lib/siem-exporter.js +137 -0
  43. package/src/lib/social-manager.js +283 -0
  44. package/src/lib/task-model-selector.js +232 -0
  45. package/src/lib/terraform-manager.js +201 -0
  46. package/src/lib/ws-server.js +474 -0
  47. package/src/repl/agent-repl.js +796 -41
  48. package/src/repl/chat-repl.js +14 -6
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Social commands
3
+ * chainlesschain social contact|friend|post|chat|stats
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ ensureSocialTables,
11
+ addContact,
12
+ listContacts,
13
+ deleteContact,
14
+ showContact,
15
+ addFriend,
16
+ listFriends,
17
+ removeFriend,
18
+ pendingRequests,
19
+ publishPost,
20
+ listPosts,
21
+ likePost,
22
+ sendChatMessage,
23
+ getChatMessages,
24
+ getChatThreads,
25
+ getSocialStats,
26
+ } from "../lib/social-manager.js";
27
+
28
+ export function registerSocialCommand(program) {
29
+ const social = program
30
+ .command("social")
31
+ .description("Social platform — contacts, friends, posts, chat");
32
+
33
+ // ── Contact subcommands ─────────────────────────────────────
34
+
35
+ const contact = social.command("contact").description("Contact management");
36
+
37
+ contact
38
+ .command("add <name>")
39
+ .description("Add a contact")
40
+ .option("-d, --did <did>", "Contact DID")
41
+ .option("-e, --email <email>", "Contact email")
42
+ .option("-n, --notes <text>", "Notes")
43
+ .action(async (name, options) => {
44
+ try {
45
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
46
+ if (!ctx.db) {
47
+ logger.error("Database not available");
48
+ process.exit(1);
49
+ }
50
+ const db = ctx.db.getDatabase();
51
+ ensureSocialTables(db);
52
+
53
+ const c = addContact(
54
+ db,
55
+ name,
56
+ options.did,
57
+ options.email,
58
+ options.notes,
59
+ );
60
+ logger.success(`Contact ${chalk.cyan(c.name)} added`);
61
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(c.id)}`);
62
+ await shutdown();
63
+ } catch (err) {
64
+ logger.error(`Failed: ${err.message}`);
65
+ process.exit(1);
66
+ }
67
+ });
68
+
69
+ contact
70
+ .command("list")
71
+ .description("List contacts")
72
+ .option("--json", "Output as JSON")
73
+ .action(async (options) => {
74
+ try {
75
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
76
+ if (!ctx.db) {
77
+ logger.error("Database not available");
78
+ process.exit(1);
79
+ }
80
+ const db = ctx.db.getDatabase();
81
+ ensureSocialTables(db);
82
+
83
+ const contacts = listContacts();
84
+ if (options.json) {
85
+ console.log(JSON.stringify(contacts, null, 2));
86
+ } else if (contacts.length === 0) {
87
+ logger.info("No contacts.");
88
+ } else {
89
+ for (const c of contacts) {
90
+ logger.log(
91
+ ` ${chalk.cyan(c.id.slice(0, 8))} ${c.name} ${c.email || ""} ${c.did || ""}`,
92
+ );
93
+ }
94
+ }
95
+ await shutdown();
96
+ } catch (err) {
97
+ logger.error(`Failed: ${err.message}`);
98
+ process.exit(1);
99
+ }
100
+ });
101
+
102
+ contact
103
+ .command("delete <contact-id>")
104
+ .description("Delete a contact")
105
+ .action(async (contactId) => {
106
+ try {
107
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
108
+ if (!ctx.db) {
109
+ logger.error("Database not available");
110
+ process.exit(1);
111
+ }
112
+ const db = ctx.db.getDatabase();
113
+ ensureSocialTables(db);
114
+
115
+ deleteContact(db, contactId);
116
+ logger.success(`Contact deleted`);
117
+ await shutdown();
118
+ } catch (err) {
119
+ logger.error(`Failed: ${err.message}`);
120
+ process.exit(1);
121
+ }
122
+ });
123
+
124
+ contact
125
+ .command("show <contact-id>")
126
+ .description("Show contact details")
127
+ .option("--json", "Output as JSON")
128
+ .action(async (contactId, options) => {
129
+ try {
130
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
131
+ if (!ctx.db) {
132
+ logger.error("Database not available");
133
+ process.exit(1);
134
+ }
135
+ const db = ctx.db.getDatabase();
136
+ ensureSocialTables(db);
137
+
138
+ const c = showContact(contactId);
139
+ if (options.json) {
140
+ console.log(JSON.stringify(c, null, 2));
141
+ } else {
142
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(c.id)}`);
143
+ logger.log(` ${chalk.bold("Name:")} ${c.name}`);
144
+ logger.log(` ${chalk.bold("DID:")} ${c.did || "N/A"}`);
145
+ logger.log(` ${chalk.bold("Email:")} ${c.email || "N/A"}`);
146
+ logger.log(` ${chalk.bold("Notes:")} ${c.notes || "N/A"}`);
147
+ }
148
+ await shutdown();
149
+ } catch (err) {
150
+ logger.error(`Failed: ${err.message}`);
151
+ process.exit(1);
152
+ }
153
+ });
154
+
155
+ // ── Friend subcommands ──────────────────────────────────────
156
+
157
+ const friend = social.command("friend").description("Friend management");
158
+
159
+ friend
160
+ .command("add <contact-id>")
161
+ .description("Send a friend request")
162
+ .action(async (contactId) => {
163
+ try {
164
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
165
+ if (!ctx.db) {
166
+ logger.error("Database not available");
167
+ process.exit(1);
168
+ }
169
+ const db = ctx.db.getDatabase();
170
+ ensureSocialTables(db);
171
+
172
+ const f = addFriend(db, contactId);
173
+ logger.success(`Friend request sent (${f.status})`);
174
+ await shutdown();
175
+ } catch (err) {
176
+ logger.error(`Failed: ${err.message}`);
177
+ process.exit(1);
178
+ }
179
+ });
180
+
181
+ friend
182
+ .command("list")
183
+ .description("List friends")
184
+ .option("--json", "Output as JSON")
185
+ .action(async (options) => {
186
+ try {
187
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
188
+ if (!ctx.db) {
189
+ logger.error("Database not available");
190
+ process.exit(1);
191
+ }
192
+ const db = ctx.db.getDatabase();
193
+ ensureSocialTables(db);
194
+
195
+ const friends = listFriends();
196
+ if (options.json) {
197
+ console.log(JSON.stringify(friends, null, 2));
198
+ } else if (friends.length === 0) {
199
+ logger.info("No friends.");
200
+ } else {
201
+ for (const f of friends) {
202
+ logger.log(
203
+ ` ${chalk.cyan(f.contactId.slice(0, 8))} [${f.status}]`,
204
+ );
205
+ }
206
+ }
207
+ await shutdown();
208
+ } catch (err) {
209
+ logger.error(`Failed: ${err.message}`);
210
+ process.exit(1);
211
+ }
212
+ });
213
+
214
+ friend
215
+ .command("remove <contact-id>")
216
+ .description("Remove a friend")
217
+ .action(async (contactId) => {
218
+ try {
219
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
220
+ if (!ctx.db) {
221
+ logger.error("Database not available");
222
+ process.exit(1);
223
+ }
224
+ const db = ctx.db.getDatabase();
225
+ ensureSocialTables(db);
226
+
227
+ removeFriend(db, contactId);
228
+ logger.success("Friend removed");
229
+ await shutdown();
230
+ } catch (err) {
231
+ logger.error(`Failed: ${err.message}`);
232
+ process.exit(1);
233
+ }
234
+ });
235
+
236
+ friend
237
+ .command("pending")
238
+ .description("List pending friend requests")
239
+ .option("--json", "Output as JSON")
240
+ .action(async (options) => {
241
+ try {
242
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
243
+ if (!ctx.db) {
244
+ logger.error("Database not available");
245
+ process.exit(1);
246
+ }
247
+ const db = ctx.db.getDatabase();
248
+ ensureSocialTables(db);
249
+
250
+ const pending = pendingRequests();
251
+ if (options.json) {
252
+ console.log(JSON.stringify(pending, null, 2));
253
+ } else if (pending.length === 0) {
254
+ logger.info("No pending requests.");
255
+ } else {
256
+ for (const p of pending) {
257
+ logger.log(
258
+ ` ${chalk.cyan(p.contactId.slice(0, 8))} [${p.status}]`,
259
+ );
260
+ }
261
+ }
262
+ await shutdown();
263
+ } catch (err) {
264
+ logger.error(`Failed: ${err.message}`);
265
+ process.exit(1);
266
+ }
267
+ });
268
+
269
+ // ── Post subcommands ────────────────────────────────────────
270
+
271
+ const post = social.command("post").description("Social posts");
272
+
273
+ post
274
+ .command("publish <content>")
275
+ .description("Publish a post")
276
+ .option("-a, --author <name>", "Author name", "cli-user")
277
+ .action(async (content, options) => {
278
+ try {
279
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
280
+ if (!ctx.db) {
281
+ logger.error("Database not available");
282
+ process.exit(1);
283
+ }
284
+ const db = ctx.db.getDatabase();
285
+ ensureSocialTables(db);
286
+
287
+ const p = publishPost(db, content, options.author);
288
+ logger.success("Post published");
289
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(p.id)}`);
290
+ await shutdown();
291
+ } catch (err) {
292
+ logger.error(`Failed: ${err.message}`);
293
+ process.exit(1);
294
+ }
295
+ });
296
+
297
+ post
298
+ .command("list")
299
+ .description("List posts")
300
+ .option("-a, --author <name>", "Filter by author")
301
+ .option("--json", "Output as JSON")
302
+ .action(async (options) => {
303
+ try {
304
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
305
+ if (!ctx.db) {
306
+ logger.error("Database not available");
307
+ process.exit(1);
308
+ }
309
+ const db = ctx.db.getDatabase();
310
+ ensureSocialTables(db);
311
+
312
+ const posts = listPosts({ author: options.author });
313
+ if (options.json) {
314
+ console.log(JSON.stringify(posts, null, 2));
315
+ } else if (posts.length === 0) {
316
+ logger.info("No posts.");
317
+ } else {
318
+ for (const p of posts) {
319
+ logger.log(
320
+ ` ${chalk.cyan(p.id.slice(0, 8))} by ${p.author} — "${p.content.slice(0, 60)}" ♥ ${p.likes}`,
321
+ );
322
+ }
323
+ }
324
+ await shutdown();
325
+ } catch (err) {
326
+ logger.error(`Failed: ${err.message}`);
327
+ process.exit(1);
328
+ }
329
+ });
330
+
331
+ post
332
+ .command("like <post-id>")
333
+ .description("Like a post")
334
+ .action(async (postId) => {
335
+ try {
336
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
337
+ if (!ctx.db) {
338
+ logger.error("Database not available");
339
+ process.exit(1);
340
+ }
341
+ const db = ctx.db.getDatabase();
342
+ ensureSocialTables(db);
343
+
344
+ const p = likePost(db, postId);
345
+ logger.success(`Post liked (${p.likes} total)`);
346
+ await shutdown();
347
+ } catch (err) {
348
+ logger.error(`Failed: ${err.message}`);
349
+ process.exit(1);
350
+ }
351
+ });
352
+
353
+ // ── Chat subcommands ────────────────────────────────────────
354
+
355
+ const chat = social.command("chat").description("Direct messaging");
356
+
357
+ chat
358
+ .command("send <recipient> <message>")
359
+ .description("Send a chat message")
360
+ .option("-s, --sender <name>", "Sender name", "cli-user")
361
+ .action(async (recipient, message, options) => {
362
+ try {
363
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
364
+ if (!ctx.db) {
365
+ logger.error("Database not available");
366
+ process.exit(1);
367
+ }
368
+ const db = ctx.db.getDatabase();
369
+ ensureSocialTables(db);
370
+
371
+ const msg = sendChatMessage(db, recipient, message, options.sender);
372
+ logger.success(`Message sent to ${chalk.cyan(recipient)}`);
373
+ logger.log(` ${chalk.bold("Thread:")} ${msg.threadId}`);
374
+ await shutdown();
375
+ } catch (err) {
376
+ logger.error(`Failed: ${err.message}`);
377
+ process.exit(1);
378
+ }
379
+ });
380
+
381
+ chat
382
+ .command("messages <thread-id>")
383
+ .description("Get messages in a thread")
384
+ .option("-n, --limit <n>", "Max messages", "50")
385
+ .option("--json", "Output as JSON")
386
+ .action(async (threadId, options) => {
387
+ try {
388
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
389
+ if (!ctx.db) {
390
+ logger.error("Database not available");
391
+ process.exit(1);
392
+ }
393
+ const db = ctx.db.getDatabase();
394
+ ensureSocialTables(db);
395
+
396
+ const messages = getChatMessages(threadId, {
397
+ limit: parseInt(options.limit),
398
+ });
399
+ if (options.json) {
400
+ console.log(JSON.stringify(messages, null, 2));
401
+ } else if (messages.length === 0) {
402
+ logger.info("No messages in this thread.");
403
+ } else {
404
+ for (const m of messages) {
405
+ logger.log(` ${chalk.gray(m.sender)}: ${m.content}`);
406
+ }
407
+ }
408
+ await shutdown();
409
+ } catch (err) {
410
+ logger.error(`Failed: ${err.message}`);
411
+ process.exit(1);
412
+ }
413
+ });
414
+
415
+ chat
416
+ .command("threads")
417
+ .description("List chat threads")
418
+ .option("--json", "Output as JSON")
419
+ .action(async (options) => {
420
+ try {
421
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
422
+ if (!ctx.db) {
423
+ logger.error("Database not available");
424
+ process.exit(1);
425
+ }
426
+ const db = ctx.db.getDatabase();
427
+ ensureSocialTables(db);
428
+
429
+ const threads = getChatThreads();
430
+ if (options.json) {
431
+ console.log(JSON.stringify(threads, null, 2));
432
+ } else if (threads.length === 0) {
433
+ logger.info("No chat threads.");
434
+ } else {
435
+ for (const t of threads) {
436
+ logger.log(
437
+ ` ${chalk.cyan(t.threadId)} (${t.messageCount} messages)`,
438
+ );
439
+ }
440
+ }
441
+ await shutdown();
442
+ } catch (err) {
443
+ logger.error(`Failed: ${err.message}`);
444
+ process.exit(1);
445
+ }
446
+ });
447
+
448
+ // ── Stats ───────────────────────────────────────────────────
449
+
450
+ social
451
+ .command("stats")
452
+ .description("Show social statistics")
453
+ .option("--json", "Output as JSON")
454
+ .action(async (options) => {
455
+ try {
456
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
457
+ if (!ctx.db) {
458
+ logger.error("Database not available");
459
+ process.exit(1);
460
+ }
461
+ const db = ctx.db.getDatabase();
462
+ ensureSocialTables(db);
463
+
464
+ const stats = getSocialStats();
465
+ if (options.json) {
466
+ console.log(JSON.stringify(stats, null, 2));
467
+ } else {
468
+ logger.log(` ${chalk.bold("Contacts:")} ${stats.contacts}`);
469
+ logger.log(` ${chalk.bold("Friends:")} ${stats.friends}`);
470
+ logger.log(` ${chalk.bold("Posts:")} ${stats.posts}`);
471
+ logger.log(` ${chalk.bold("Messages:")} ${stats.messages}`);
472
+ logger.log(` ${chalk.bold("Pending:")} ${stats.pendingRequests}`);
473
+ }
474
+ await shutdown();
475
+ } catch (err) {
476
+ logger.error(`Failed: ${err.message}`);
477
+ process.exit(1);
478
+ }
479
+ });
480
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Terraform commands
3
+ * chainlesschain terraform workspaces|create|plan|runs
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ ensureTerraformTables,
11
+ listWorkspaces,
12
+ createWorkspace,
13
+ planRun,
14
+ listRuns,
15
+ } from "../lib/terraform-manager.js";
16
+
17
+ export function registerTerraformCommand(program) {
18
+ const terraform = program
19
+ .command("terraform")
20
+ .description("Terraform IaC — workspace and run management");
21
+
22
+ terraform
23
+ .command("workspaces")
24
+ .description("List Terraform workspaces")
25
+ .option("--status <status>", "Filter by status")
26
+ .option("--json", "Output as JSON")
27
+ .action(async (options) => {
28
+ try {
29
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
30
+ if (!ctx.db) {
31
+ logger.error("Database not available");
32
+ process.exit(1);
33
+ }
34
+ const db = ctx.db.getDatabase();
35
+ ensureTerraformTables(db);
36
+
37
+ const workspaces = listWorkspaces({ status: options.status });
38
+ if (options.json) {
39
+ console.log(JSON.stringify(workspaces, null, 2));
40
+ } else if (workspaces.length === 0) {
41
+ logger.info("No workspaces. Use `terraform create` to add one.");
42
+ } else {
43
+ for (const w of workspaces) {
44
+ logger.log(
45
+ ` ${chalk.cyan(w.id.slice(0, 8))} ${w.name} [${w.status}] tf=${w.terraformVersion} state=v${w.stateVersion}`,
46
+ );
47
+ }
48
+ }
49
+ await shutdown();
50
+ } catch (err) {
51
+ logger.error(`Failed: ${err.message}`);
52
+ process.exit(1);
53
+ }
54
+ });
55
+
56
+ terraform
57
+ .command("create <name>")
58
+ .description("Create a Terraform workspace")
59
+ .option("-d, --description <text>", "Workspace description")
60
+ .option("--tf-version <version>", "Terraform version", "1.9.0")
61
+ .option("--auto-apply", "Enable auto-apply")
62
+ .action(async (name, options) => {
63
+ try {
64
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
65
+ if (!ctx.db) {
66
+ logger.error("Database not available");
67
+ process.exit(1);
68
+ }
69
+ const db = ctx.db.getDatabase();
70
+ ensureTerraformTables(db);
71
+
72
+ const ws = createWorkspace(db, name, {
73
+ description: options.description,
74
+ terraformVersion: options.tfVersion,
75
+ autoApply: options.autoApply,
76
+ });
77
+ logger.success("Workspace created");
78
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(ws.id)}`);
79
+ logger.log(` ${chalk.bold("Name:")} ${ws.name}`);
80
+ logger.log(` ${chalk.bold("Version:")} ${ws.terraformVersion}`);
81
+ await shutdown();
82
+ } catch (err) {
83
+ logger.error(`Failed: ${err.message}`);
84
+ process.exit(1);
85
+ }
86
+ });
87
+
88
+ terraform
89
+ .command("plan <workspace-id>")
90
+ .description("Run a Terraform plan")
91
+ .option("-t, --type <type>", "Run type: plan, apply, destroy", "plan")
92
+ .action(async (workspaceId, options) => {
93
+ try {
94
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
95
+ if (!ctx.db) {
96
+ logger.error("Database not available");
97
+ process.exit(1);
98
+ }
99
+ const db = ctx.db.getDatabase();
100
+ ensureTerraformTables(db);
101
+
102
+ const run = planRun(db, workspaceId, { runType: options.type });
103
+ logger.success(`Run completed: ${run.planOutput}`);
104
+ logger.log(` ${chalk.bold("Run ID:")} ${chalk.cyan(run.id)}`);
105
+ logger.log(` ${chalk.bold("Added:")} ${run.resourcesAdded}`);
106
+ logger.log(` ${chalk.bold("Changed:")} ${run.resourcesChanged}`);
107
+ logger.log(` ${chalk.bold("Destroyed:")} ${run.resourcesDestroyed}`);
108
+ await shutdown();
109
+ } catch (err) {
110
+ logger.error(`Failed: ${err.message}`);
111
+ process.exit(1);
112
+ }
113
+ });
114
+
115
+ terraform
116
+ .command("runs")
117
+ .description("List Terraform runs")
118
+ .option("-w, --workspace <id>", "Filter by workspace ID")
119
+ .option("--json", "Output as JSON")
120
+ .action(async (options) => {
121
+ try {
122
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
123
+ if (!ctx.db) {
124
+ logger.error("Database not available");
125
+ process.exit(1);
126
+ }
127
+ const db = ctx.db.getDatabase();
128
+ ensureTerraformTables(db);
129
+
130
+ const runs = listRuns({ workspaceId: options.workspace });
131
+ if (options.json) {
132
+ console.log(JSON.stringify(runs, null, 2));
133
+ } else if (runs.length === 0) {
134
+ logger.info("No runs found.");
135
+ } else {
136
+ for (const r of runs) {
137
+ logger.log(
138
+ ` ${chalk.cyan(r.id.slice(0, 8))} ${r.runType} [${r.status}] +${r.resourcesAdded} ~${r.resourcesChanged} -${r.resourcesDestroyed}`,
139
+ );
140
+ }
141
+ }
142
+ await shutdown();
143
+ } catch (err) {
144
+ logger.error(`Failed: ${err.message}`);
145
+ process.exit(1);
146
+ }
147
+ });
148
+ }
package/src/constants.js CHANGED
@@ -23,6 +23,7 @@ export const DEFAULT_PORTS = {
23
23
  redis: 6379,
24
24
  projectService: 9090,
25
25
  aiService: 8001,
26
+ wsServer: 18800,
26
27
  };
27
28
 
28
29
  export const LLM_PROVIDERS = {