@neta-art/cohub-cli 1.5.1 → 1.6.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/dist/commands/auth.js +1 -0
- package/dist/commands/search.js +7 -6
- package/dist/commands/spaces.js +63 -2
- package/package.json +2 -2
package/dist/commands/auth.js
CHANGED
|
@@ -98,6 +98,7 @@ async function showSignedIn(asJson) {
|
|
|
98
98
|
console.log(` Token: ${payload.refreshable ? "refreshable" : "ephemeral"}\n`);
|
|
99
99
|
table([u], [
|
|
100
100
|
{ key: "id", label: "ID" },
|
|
101
|
+
{ key: "username", label: "Username" },
|
|
101
102
|
{ key: "name", label: "Name" },
|
|
102
103
|
{ key: "email", label: "Email" },
|
|
103
104
|
{ key: "created_at", label: "Created" },
|
package/dist/commands/search.js
CHANGED
|
@@ -18,11 +18,12 @@ function truncate(value, maxLength) {
|
|
|
18
18
|
return `${text.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
|
|
19
19
|
}
|
|
20
20
|
function contextFor(item) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const owner = item.ownerProfile;
|
|
22
|
+
if (owner?.username)
|
|
23
|
+
return `@${owner.username}`;
|
|
24
|
+
if (owner?.displayName)
|
|
25
|
+
return owner.displayName;
|
|
26
|
+
return item.href;
|
|
26
27
|
}
|
|
27
28
|
function parseTypes(value) {
|
|
28
29
|
const types = value
|
|
@@ -46,7 +47,7 @@ function parseSearchInput(opts) {
|
|
|
46
47
|
function rowsFor(items) {
|
|
47
48
|
return items.map((item) => ({
|
|
48
49
|
type: item.type,
|
|
49
|
-
title: truncate(item.title
|
|
50
|
+
title: truncate(item.title, MAX_TITLE_LENGTH),
|
|
50
51
|
context: truncate(contextFor(item), MAX_CONTEXT_LENGTH),
|
|
51
52
|
match: item.matchedField,
|
|
52
53
|
updated: item.updatedAt ? item.updatedAt.slice(0, 10) : "",
|
package/dist/commands/spaces.js
CHANGED
|
@@ -14,6 +14,33 @@ function requireSpace(program) {
|
|
|
14
14
|
}
|
|
15
15
|
return error("Missing required option", "Add -s, --space <id> to target a space");
|
|
16
16
|
}
|
|
17
|
+
const parseAutoDestroy = (opts) => {
|
|
18
|
+
const mode = opts.autoDestroy ?? (opts.idleTtl ? "idle" : undefined);
|
|
19
|
+
if (!mode)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (mode === "never")
|
|
22
|
+
return { mode: "never" };
|
|
23
|
+
if (mode !== "idle")
|
|
24
|
+
return error("Invalid auto destroy mode", "Use --auto-destroy idle or --auto-destroy never");
|
|
25
|
+
const ttlSeconds = Number.parseInt(opts.idleTtl ?? "172800", 10);
|
|
26
|
+
if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds < 60 || ttlSeconds > 30 * 24 * 60 * 60) {
|
|
27
|
+
return error("Invalid idle TTL", "--idle-ttl must be an integer between 60 and 2592000 seconds");
|
|
28
|
+
}
|
|
29
|
+
return { mode: "idle", ttlSeconds };
|
|
30
|
+
};
|
|
31
|
+
const formatAutoDestroy = (policy) => {
|
|
32
|
+
if (!policy)
|
|
33
|
+
return "48h (default)";
|
|
34
|
+
if (policy.mode === "never")
|
|
35
|
+
return "never";
|
|
36
|
+
if (policy.ttlSeconds % 86400 === 0)
|
|
37
|
+
return `${policy.ttlSeconds / 86400}d`;
|
|
38
|
+
if (policy.ttlSeconds % 3600 === 0)
|
|
39
|
+
return `${policy.ttlSeconds / 3600}h`;
|
|
40
|
+
if (policy.ttlSeconds % 60 === 0)
|
|
41
|
+
return `${policy.ttlSeconds / 60}m`;
|
|
42
|
+
return `${policy.ttlSeconds}s`;
|
|
43
|
+
};
|
|
17
44
|
const slashPath = (value) => value.split(sep).join("/");
|
|
18
45
|
const walkUploadPath = async (input, root, prefix = "") => {
|
|
19
46
|
const localPath = resolve(input);
|
|
@@ -68,7 +95,7 @@ async function uploadFiles(command, paths, opts) {
|
|
|
68
95
|
try {
|
|
69
96
|
const files = await collectUploadFiles(paths);
|
|
70
97
|
const plan = await client.space(spaceId).files.createUpload({
|
|
71
|
-
targetDir: opts.dir,
|
|
98
|
+
destination: { kind: "workspace", targetDir: opts.dir },
|
|
72
99
|
entries: files.map((file) => ({
|
|
73
100
|
id: file.id,
|
|
74
101
|
name: file.name,
|
|
@@ -89,7 +116,7 @@ async function uploadFiles(command, paths, opts) {
|
|
|
89
116
|
});
|
|
90
117
|
if (opts.json)
|
|
91
118
|
return outJson({ ...result, uploadId: plan.uploadId, files: files.length });
|
|
92
|
-
ok(`Uploaded ${files.length} file${files.length === 1 ? "" : "s"}
|
|
119
|
+
ok(`Uploaded ${files.length} file${files.length === 1 ? "" : "s"}`);
|
|
93
120
|
}
|
|
94
121
|
catch (e) {
|
|
95
122
|
handleHttp(e);
|
|
@@ -227,13 +254,17 @@ export function registerSpaces(program) {
|
|
|
227
254
|
.description("Create a new space")
|
|
228
255
|
.option("-n, --name <name>", "Space name")
|
|
229
256
|
.option("-d, --description <desc>", "Space description")
|
|
257
|
+
.option("--auto-destroy <mode>", "Sandbox auto destroy mode: idle or never")
|
|
258
|
+
.option("--idle-ttl <seconds>", "Idle auto destroy TTL in seconds, max 2592000 (30d)")
|
|
230
259
|
.option("--json", "Output as JSON")
|
|
231
260
|
.action(async (opts) => {
|
|
232
261
|
const client = createClient();
|
|
233
262
|
try {
|
|
263
|
+
const autoDestroy = parseAutoDestroy(opts);
|
|
234
264
|
const result = await client.spaces.create({
|
|
235
265
|
name: opts.name,
|
|
236
266
|
description: opts.description,
|
|
267
|
+
...(autoDestroy ? { config: { sandbox: { autoDestroy } } } : {}),
|
|
237
268
|
});
|
|
238
269
|
if (opts.json)
|
|
239
270
|
return outJson(result);
|
|
@@ -262,6 +293,36 @@ export function registerSpaces(program) {
|
|
|
262
293
|
handleHttp(e);
|
|
263
294
|
}
|
|
264
295
|
});
|
|
296
|
+
// ── spaces config ──
|
|
297
|
+
spacesCmd
|
|
298
|
+
.command("config <id>")
|
|
299
|
+
.description("Show or update space configuration")
|
|
300
|
+
.option("--auto-destroy <mode>", "Sandbox auto destroy mode: idle or never")
|
|
301
|
+
.option("--idle-ttl <seconds>", "Idle auto destroy TTL in seconds, max 2592000 (30d)")
|
|
302
|
+
.option("--json", "Output as JSON")
|
|
303
|
+
.action(async (id, opts) => {
|
|
304
|
+
const client = createClient();
|
|
305
|
+
try {
|
|
306
|
+
const autoDestroy = parseAutoDestroy(opts);
|
|
307
|
+
if (autoDestroy) {
|
|
308
|
+
const result = await client.space(id).updateConfig({ sandbox: { autoDestroy } });
|
|
309
|
+
if (opts.json)
|
|
310
|
+
return outJson(result);
|
|
311
|
+
ok(`Space config updated — sandbox auto destroy: ${formatAutoDestroy(autoDestroy)}`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const result = await client.space(id).getConfig();
|
|
315
|
+
if (opts.json)
|
|
316
|
+
return outJson(result);
|
|
317
|
+
table([{ key: "sandbox.autoDestroy", value: formatAutoDestroy(result.config.sandbox.autoDestroy) }], [
|
|
318
|
+
{ key: "key", label: "Key" },
|
|
319
|
+
{ key: "value", label: "Value" },
|
|
320
|
+
]);
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
handleHttp(e);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
265
326
|
// ── spaces prompt ──
|
|
266
327
|
spacesCmd
|
|
267
328
|
.command("prompt [content...]", { hidden: true })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
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.
|
|
17
|
+
"@neta-art/cohub": "1.13.1"
|
|
18
18
|
},
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|