@neta-art/cohub-cli 1.4.1 → 1.5.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.
@@ -95,6 +95,21 @@ async function uploadFiles(command, paths, opts) {
95
95
  handleHttp(e);
96
96
  }
97
97
  }
98
+ async function confirmRestart(opts) {
99
+ if (opts.yes)
100
+ return;
101
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
102
+ return error("Confirmation required", "Pass --yes to restart the sandbox automatically.");
103
+ process.stdout.write("Changing mods restarts the sandbox and may interrupt running work. Continue? [y/N] ");
104
+ const chunks = [];
105
+ for await (const chunk of process.stdin) {
106
+ chunks.push(chunk);
107
+ break;
108
+ }
109
+ const answer = Buffer.concat(chunks).toString().trim().toLowerCase();
110
+ if (answer !== "y" && answer !== "yes")
111
+ return error("Cancelled");
112
+ }
98
113
  async function readPromptContent(words) {
99
114
  let content = words.join(" ");
100
115
  if (!content && !process.stdin.isTTY) {
@@ -272,6 +287,8 @@ export function registerSpaces(program) {
272
287
  registerAccess(spacesCmd);
273
288
  // ── spaces checkpoints ──
274
289
  registerCheckpoints(spacesCmd);
290
+ // ── spaces mods ──
291
+ registerMods(spacesCmd);
275
292
  // ── spaces usage ──
276
293
  spacesCmd
277
294
  .command("usage [days]")
@@ -298,6 +315,114 @@ export function registerSpaces(program) {
298
315
  }
299
316
  });
300
317
  }
318
+ function registerMods(spacesCmd) {
319
+ const modsCmd = spacesCmd
320
+ .command("mods")
321
+ .description("Manage space mods")
322
+ .hook("preAction", () => { requireSpace(spacesCmd); });
323
+ modsCmd
324
+ .command("ls")
325
+ .alias("list")
326
+ .description("List mods")
327
+ .option("--json", "Output as JSON")
328
+ .action(async (opts) => {
329
+ const spaceId = requireSpace(spacesCmd);
330
+ const client = createClient();
331
+ try {
332
+ const result = await client.space(spaceId).mods.list();
333
+ if (opts.json)
334
+ return outJson(result.items);
335
+ table(result.items, [
336
+ { key: "id", label: "ID" },
337
+ { key: "modSpaceName", label: "Name" },
338
+ { key: "mountPath", label: "Mount" },
339
+ { key: "enabled", label: "On" },
340
+ ]);
341
+ }
342
+ catch (e) {
343
+ handleHttp(e);
344
+ }
345
+ });
346
+ modsCmd
347
+ .command("add <modSpaceId>")
348
+ .description("Add a mod")
349
+ .option("--name <name>", "Display name")
350
+ .option("--slug <slug>", "Mount slug")
351
+ .option("-y, --yes", "Confirm sandbox restart")
352
+ .option("--json", "Output as JSON")
353
+ .action(async (modSpaceId, opts) => {
354
+ await confirmRestart(opts);
355
+ const spaceId = requireSpace(spacesCmd);
356
+ const client = createClient();
357
+ try {
358
+ const result = await client.space(spaceId).mods.create({ modSpaceId, name: opts.name, mountSlug: opts.slug });
359
+ if (opts.json)
360
+ return outJson(result);
361
+ ok(`Mod added — ${result.item.mountPath}; sandbox restarting`);
362
+ }
363
+ catch (e) {
364
+ handleHttp(e);
365
+ }
366
+ });
367
+ modsCmd
368
+ .command("enable <modId>")
369
+ .description("Enable a mod")
370
+ .option("-y, --yes", "Confirm sandbox restart")
371
+ .option("--json", "Output as JSON")
372
+ .action(async (modId, opts) => {
373
+ await confirmRestart(opts);
374
+ const spaceId = requireSpace(spacesCmd);
375
+ const client = createClient();
376
+ try {
377
+ const result = await client.space(spaceId).mods.update(modId, { enabled: true });
378
+ if (opts.json)
379
+ return outJson(result);
380
+ ok("Mod enabled; sandbox restarting");
381
+ }
382
+ catch (e) {
383
+ handleHttp(e);
384
+ }
385
+ });
386
+ modsCmd
387
+ .command("disable <modId>")
388
+ .description("Disable a mod")
389
+ .option("-y, --yes", "Confirm sandbox restart")
390
+ .option("--json", "Output as JSON")
391
+ .action(async (modId, opts) => {
392
+ await confirmRestart(opts);
393
+ const spaceId = requireSpace(spacesCmd);
394
+ const client = createClient();
395
+ try {
396
+ const result = await client.space(spaceId).mods.update(modId, { enabled: false });
397
+ if (opts.json)
398
+ return outJson(result);
399
+ ok("Mod disabled; sandbox restarting");
400
+ }
401
+ catch (e) {
402
+ handleHttp(e);
403
+ }
404
+ });
405
+ modsCmd
406
+ .command("rm <modId>")
407
+ .alias("remove")
408
+ .description("Remove a mod")
409
+ .option("-y, --yes", "Confirm sandbox restart")
410
+ .option("--json", "Output as JSON")
411
+ .action(async (modId, opts) => {
412
+ await confirmRestart(opts);
413
+ const spaceId = requireSpace(spacesCmd);
414
+ const client = createClient();
415
+ try {
416
+ const result = await client.space(spaceId).mods.remove(modId);
417
+ if (opts.json)
418
+ return outJson(result);
419
+ ok("Mod removed; sandbox restarting");
420
+ }
421
+ catch (e) {
422
+ handleHttp(e);
423
+ }
424
+ });
425
+ }
301
426
  // ── File operations ──
302
427
  function registerFiles(spacesCmd) {
303
428
  const filesCmd = spacesCmd
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "dependencies": {
16
16
  "commander": "^13.1.0",
17
- "@neta-art/cohub": "1.10.1"
17
+ "@neta-art/cohub": "1.11.0"
18
18
  },
19
19
  "publishConfig": {
20
20
  "access": "public"