@neta-art/cohub-cli 6.7.0 → 6.8.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 +6 -6
- package/dist/commands/apps.js +8 -5
- package/dist/commands/auth.js +2 -0
- package/dist/commands/boards/context.js +1 -1
- package/dist/commands/boards.js +9 -9
- package/dist/commands/desktop.js +1 -0
- package/dist/commands/generations.js +1 -1
- package/dist/commands/public.js +3 -3
- package/dist/commands/run.js +6 -6
- package/dist/commands/sandbox.js +16 -12
- package/dist/commands/sandboxd-binary.js +1 -1
- package/dist/commands/space-activity.js +1 -1
- package/dist/commands/space-commerce.js +15 -15
- package/dist/commands/space-invitations.js +3 -3
- package/dist/commands/space-turns.js +4 -4
- package/dist/commands/spaces.js +57 -57
- package/dist/index.js +8 -4
- package/dist/space.d.ts +31 -1
- package/dist/space.js +117 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -299,10 +299,9 @@ Confirm before deleting files or directories.
|
|
|
299
299
|
|
|
300
300
|
Publish and manage Apps from a Space workspace. Public App URLs require a username and a Space slug.
|
|
301
301
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Space-side path.
|
|
302
|
+
`apps publish` detects the source from the runtime by default: local CLI runs use local
|
|
303
|
+
filesystem paths, while Cohub Sandbox runs use Space workspace paths. Pass
|
|
304
|
+
`--source workspace` or `--source local` to override the default explicitly.
|
|
306
305
|
|
|
307
306
|
```bash
|
|
308
307
|
cohub profile update --username <username>
|
|
@@ -311,8 +310,9 @@ cohub -s <spaceId> apps ls --json
|
|
|
311
310
|
cohub apps get <appId|url|username/space/app> --json
|
|
312
311
|
cohub apps stats <appId|url|username/space/app>
|
|
313
312
|
cohub apps download <appId|url|username/space/app> --output <path>
|
|
314
|
-
cohub -s <spaceId> apps publish demo --file dist/index.html
|
|
315
|
-
cohub -s <spaceId> apps publish site --dir dist
|
|
313
|
+
cohub -s <spaceId> apps publish demo --file ./dist/index.html
|
|
314
|
+
cohub -s <spaceId> apps publish site --dir ./dist
|
|
315
|
+
cohub -s <spaceId> apps publish site --source workspace --dir dist
|
|
316
316
|
cohub -s <spaceId> apps publish app --port 3000
|
|
317
317
|
cohub apps publish-version <appId>
|
|
318
318
|
cohub apps versions <appId> --json
|
package/dist/commands/apps.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
|
-
import { HttpError } from "@neta-art/cohub";
|
|
4
|
+
import { getCohubContext, HttpError } from "@neta-art/cohub";
|
|
5
5
|
import { createClient, createClientWithAccessToken } from "../client.js";
|
|
6
6
|
import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
|
|
7
7
|
import { resolveSpace } from "../space.js";
|
|
@@ -124,6 +124,9 @@ async function uploadLocalAppSource(client, spaceId, source) {
|
|
|
124
124
|
const manifestAsset = manifestPlan.asset;
|
|
125
125
|
return { sourceRef: manifestAsset.objectKey, targetRef: source.targetType === "file" ? files[0]?.path ?? "" : "." };
|
|
126
126
|
}
|
|
127
|
+
function resolveDefaultAppSource() {
|
|
128
|
+
return getCohubContext().runtime.kind === "sandbox" ? "workspace" : "local";
|
|
129
|
+
}
|
|
127
130
|
function resolveTarget(opts) {
|
|
128
131
|
const targets = [
|
|
129
132
|
opts.file ? { targetType: "file", targetRef: opts.file } : null,
|
|
@@ -264,7 +267,7 @@ export function registerApps(program) {
|
|
|
264
267
|
.description("List apps in the target space")
|
|
265
268
|
.option("--json", "Output as JSON")
|
|
266
269
|
.action(async (opts) => {
|
|
267
|
-
const spaceId = resolveSpace(appsCmd);
|
|
270
|
+
const spaceId = await resolveSpace(appsCmd);
|
|
268
271
|
const client = createClient();
|
|
269
272
|
try {
|
|
270
273
|
const result = await client.apps.listBySpace(spaceId);
|
|
@@ -388,7 +391,7 @@ export function registerApps(program) {
|
|
|
388
391
|
appsCmd
|
|
389
392
|
.command("publish <slug>")
|
|
390
393
|
.description("Create or publish an app in the target space")
|
|
391
|
-
.option("--source <source>", "Source: workspace (default)
|
|
394
|
+
.option("--source <source>", "Source: workspace or local (auto-detected from runtime by default)")
|
|
392
395
|
.option("--file <path>", "Publish a file from the selected source")
|
|
393
396
|
.option("--dir <path>", "Publish a directory site from the selected source")
|
|
394
397
|
.option("--port <port>", "Publish a public sandbox port")
|
|
@@ -407,10 +410,10 @@ export function registerApps(program) {
|
|
|
407
410
|
const target = resolveTarget(opts);
|
|
408
411
|
if (!target)
|
|
409
412
|
return error("Missing target", "Use one of --file, --dir, or --port.");
|
|
410
|
-
const source = opts.source ? parseChoice(opts.source, "source", ["workspace", "local"]) :
|
|
413
|
+
const source = opts.source ? parseChoice(opts.source, "source", ["workspace", "local"]) : resolveDefaultAppSource();
|
|
411
414
|
if (target.targetType === "port" && opts.source)
|
|
412
415
|
return error("Invalid source", "--source applies only to --file and --dir.");
|
|
413
|
-
const spaceId = resolveSpace(appsCmd);
|
|
416
|
+
const spaceId = await resolveSpace(appsCmd);
|
|
414
417
|
const client = createClient();
|
|
415
418
|
let { targetType, targetRef } = target;
|
|
416
419
|
let sourceRef = null;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { authSource, loginWithDeviceFlow, readAuthSession, refreshAccessToken, requestDeviceCode, revokeAndClearAuthSession, verifyDeviceCode } from "../auth.js";
|
|
2
|
+
import { clearDefaultSpaceCache } from "../space.js";
|
|
2
3
|
import { createClient } from "../client.js";
|
|
3
4
|
import { table, json as outJson, jsonRequested, ok, error, spinner, handleHttp } from "../output.js";
|
|
4
5
|
export function registerAuth(program) {
|
|
@@ -67,6 +68,7 @@ export function registerAuth(program) {
|
|
|
67
68
|
.description("Clear stored Logto session")
|
|
68
69
|
.action(async () => {
|
|
69
70
|
await revokeAndClearAuthSession();
|
|
71
|
+
clearDefaultSpaceCache();
|
|
70
72
|
if (process.env.COHUB_EXECUTION_TOKEN?.trim()) {
|
|
71
73
|
ok("Local session cleared. COHUB_EXECUTION_TOKEN is still set.");
|
|
72
74
|
}
|
|
@@ -24,7 +24,7 @@ export function readOptions(command, label = "Comma-separated resource IDs") {
|
|
|
24
24
|
return command.option("--ids <ids>", label);
|
|
25
25
|
}
|
|
26
26
|
export async function resolvedBoard(boards, target) {
|
|
27
|
-
const spaceId = resolveSpace(boards);
|
|
27
|
+
const spaceId = await resolveSpace(boards);
|
|
28
28
|
const boardId = await resolveBoardId(spaceId, target);
|
|
29
29
|
return createClient().space(spaceId).board(boardId);
|
|
30
30
|
}
|
package/dist/commands/boards.js
CHANGED
|
@@ -171,7 +171,7 @@ function registerExportCommand(boards) {
|
|
|
171
171
|
if (!out)
|
|
172
172
|
throw new Error("--out is required");
|
|
173
173
|
const result = await runBoardExport({
|
|
174
|
-
spaceId: resolveSpace(boards),
|
|
174
|
+
spaceId: await resolveSpace(boards),
|
|
175
175
|
target: board,
|
|
176
176
|
region: parseExportRegion(options),
|
|
177
177
|
scale: parseNumber(options.scale ?? "2", "scale", { min: 0.01, max: 16 }),
|
|
@@ -213,7 +213,7 @@ export function registerBoards(program) {
|
|
|
213
213
|
const boards = program
|
|
214
214
|
.command("boards")
|
|
215
215
|
.description("Inspect and update Boards by ID or .board path")
|
|
216
|
-
.hook("preAction", () => { resolveSpace(boards); });
|
|
216
|
+
.hook("preAction", async () => { await resolveSpace(boards); });
|
|
217
217
|
withJson(boards.command("create <path>")
|
|
218
218
|
.description("Create a Board")
|
|
219
219
|
.option("--title <title>", "Board title")
|
|
@@ -239,7 +239,7 @@ Generate an editable seed:
|
|
|
239
239
|
(typeof content.mutationId === "string" ? content.mutationId : randomUUID()),
|
|
240
240
|
...(options.title ? { title: options.title } : {}),
|
|
241
241
|
};
|
|
242
|
-
const result = await createClient().space(resolveSpace(boards)).boards.create(input);
|
|
242
|
+
const result = await createClient().space(await resolveSpace(boards)).boards.create(input);
|
|
243
243
|
if (jsonRequested(options))
|
|
244
244
|
return outJson({ ...result, path });
|
|
245
245
|
ok(`Board created: ${path}`);
|
|
@@ -262,7 +262,7 @@ Apply multiple changes atomically:
|
|
|
262
262
|
cohub boards batch <board> --input changes.json --dry-run`))
|
|
263
263
|
.action(async (target, options) => {
|
|
264
264
|
try {
|
|
265
|
-
const spaceId = resolveSpace(boards);
|
|
265
|
+
const spaceId = await resolveSpace(boards);
|
|
266
266
|
const boardId = await resolveBoardId(spaceId, target);
|
|
267
267
|
const result = await createClient().space(spaceId).board(boardId).summary();
|
|
268
268
|
if (jsonRequested(options))
|
|
@@ -283,7 +283,7 @@ Coordinates:
|
|
|
283
283
|
Use boards examples for editable starter JSON.`))
|
|
284
284
|
.action(async (target, options) => {
|
|
285
285
|
try {
|
|
286
|
-
const spaceId = resolveSpace(boards);
|
|
286
|
+
const spaceId = await resolveSpace(boards);
|
|
287
287
|
const boardId = await resolveBoardId(spaceId, target);
|
|
288
288
|
const result = await createClient().space(spaceId).board(boardId).capabilities();
|
|
289
289
|
if (jsonRequested(options))
|
|
@@ -330,7 +330,7 @@ Use boards examples for editable starter JSON.`))
|
|
|
330
330
|
.option("--command-id <id>", "Idempotency command ID"))
|
|
331
331
|
.action(async (target, compositionId, options) => {
|
|
332
332
|
try {
|
|
333
|
-
const spaceId = resolveSpace(boards);
|
|
333
|
+
const spaceId = await resolveSpace(boards);
|
|
334
334
|
const boardId = await resolveBoardId(spaceId, target);
|
|
335
335
|
const result = await createClient().space(spaceId).board(boardId).play({
|
|
336
336
|
commandId: commandId(options),
|
|
@@ -351,7 +351,7 @@ Use boards examples for editable starter JSON.`))
|
|
|
351
351
|
});
|
|
352
352
|
const playbackAction = (type) => async (target, playbackId, options) => {
|
|
353
353
|
try {
|
|
354
|
-
const spaceId = resolveSpace(boards);
|
|
354
|
+
const spaceId = await resolveSpace(boards);
|
|
355
355
|
const boardId = await resolveBoardId(spaceId, target);
|
|
356
356
|
const board = createClient().space(spaceId).board(boardId);
|
|
357
357
|
const id = commandId(options);
|
|
@@ -375,7 +375,7 @@ Use boards examples for editable starter JSON.`))
|
|
|
375
375
|
.option("--command-id <id>", "Idempotency command ID"))
|
|
376
376
|
.action(async (target, playbackId, position, options) => {
|
|
377
377
|
try {
|
|
378
|
-
const spaceId = resolveSpace(boards);
|
|
378
|
+
const spaceId = await resolveSpace(boards);
|
|
379
379
|
const boardId = await resolveBoardId(spaceId, target);
|
|
380
380
|
const result = await createClient().space(spaceId).board(boardId).seek({
|
|
381
381
|
commandId: commandId(options),
|
|
@@ -399,7 +399,7 @@ Use boards examples for editable starter JSON.`))
|
|
|
399
399
|
.description("Stream Board events"))
|
|
400
400
|
.action(async (target, options) => {
|
|
401
401
|
try {
|
|
402
|
-
const spaceId = resolveSpace(boards);
|
|
402
|
+
const spaceId = await resolveSpace(boards);
|
|
403
403
|
const boardId = await resolveBoardId(spaceId, target);
|
|
404
404
|
const client = createRealtimeClient();
|
|
405
405
|
const board = client.space(spaceId).board(boardId);
|
package/dist/commands/desktop.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getAppByRef } from "../app-ref.js";
|
|
|
7
7
|
const FILE_SCHEME = "file://";
|
|
8
8
|
const APP_SCHEME = "app://";
|
|
9
9
|
const LEGACY_WORK_SCHEME = "work://";
|
|
10
|
+
/** Optional disambiguation for file:// vs app:// — do not fall back to Home. */
|
|
10
11
|
function optionalSpaceId(command) {
|
|
11
12
|
let current = command;
|
|
12
13
|
while (current) {
|
|
@@ -257,7 +257,7 @@ Examples:
|
|
|
257
257
|
`)
|
|
258
258
|
.action(async (prompt, opts) => {
|
|
259
259
|
try {
|
|
260
|
-
const spaceId = resolveSpace(program);
|
|
260
|
+
const spaceId = await resolveSpace(program);
|
|
261
261
|
const content = [{ type: "text", text: prompt }];
|
|
262
262
|
content.push(...await Promise.all(opts.image.map((value) => contentFromPathOrUrl("image", value))));
|
|
263
263
|
content.push(...await Promise.all(opts.video.map((value) => contentFromPathOrUrl("video", value))));
|
package/dist/commands/public.js
CHANGED
|
@@ -179,7 +179,7 @@ function publicUrlPrefix(destination, entry) {
|
|
|
179
179
|
}
|
|
180
180
|
async function uploadPublic(command, source, destination, opts, deps) {
|
|
181
181
|
const client = deps.createClient?.() ?? createClient();
|
|
182
|
-
const spaceId = resolveSpace(command);
|
|
182
|
+
const spaceId = await resolveSpace(command);
|
|
183
183
|
try {
|
|
184
184
|
const upload = await collectPublicUpload(source, destination);
|
|
185
185
|
const plan = await client.space(spaceId).publicFiles.createUpload({
|
|
@@ -244,7 +244,7 @@ async function uploadPublic(command, source, destination, opts, deps) {
|
|
|
244
244
|
async function listPublic(command, path, opts, deps) {
|
|
245
245
|
const client = deps.createClient?.() ?? createClient();
|
|
246
246
|
try {
|
|
247
|
-
const publicFiles = client.space(resolveSpace(command)).publicFiles;
|
|
247
|
+
const publicFiles = client.space(await resolveSpace(command)).publicFiles;
|
|
248
248
|
const entries = [];
|
|
249
249
|
let cursor;
|
|
250
250
|
do {
|
|
@@ -271,7 +271,7 @@ async function listPublic(command, path, opts, deps) {
|
|
|
271
271
|
async function printPublicUrl(command, path, deps) {
|
|
272
272
|
const client = deps.createClient?.() ?? createClient();
|
|
273
273
|
try {
|
|
274
|
-
const result = await client.space(resolveSpace(command)).publicFiles.url(path);
|
|
274
|
+
const result = await client.space(await resolveSpace(command)).publicFiles.url(path);
|
|
275
275
|
console.log(result.url);
|
|
276
276
|
}
|
|
277
277
|
catch (exception) {
|
package/dist/commands/run.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
2
|
import { error, handleHttp, json as outJson, spinner } from "../output.js";
|
|
3
|
+
import { missingSpaceError, resolveDefaultSpace } from "../space.js";
|
|
3
4
|
const DEFAULT_WAIT_TIMEOUT_MS = (6 * 60 * 60 + 60) * 1000;
|
|
4
5
|
const DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
5
6
|
function shellQuote(value) {
|
|
@@ -43,13 +44,13 @@ function parseSpaceId(tokens) {
|
|
|
43
44
|
}
|
|
44
45
|
return undefined;
|
|
45
46
|
}
|
|
46
|
-
function parseRunCliOptions(argv) {
|
|
47
|
+
async function parseRunCliOptions(argv) {
|
|
47
48
|
const runIndex = topLevelRunIndex(argv);
|
|
48
49
|
if (runIndex < 0)
|
|
49
50
|
return error("Invalid invocation", "Use `cohub run [options] <command>`");
|
|
50
51
|
const beforeRun = argv.slice(0, runIndex);
|
|
51
52
|
const afterRun = argv.slice(runIndex + 1);
|
|
52
|
-
|
|
53
|
+
const explicitSpaceId = parseSpaceId(beforeRun) ?? process.env.COHUB_SPACE_ID?.trim() ?? "";
|
|
53
54
|
let json = beforeRun.includes("--json");
|
|
54
55
|
let async = false;
|
|
55
56
|
let commandOption = null;
|
|
@@ -103,9 +104,7 @@ function parseRunCliOptions(argv) {
|
|
|
103
104
|
if (!command) {
|
|
104
105
|
return error("No command", "Pass --command <shell command>, or use `--` followed by the command.");
|
|
105
106
|
}
|
|
106
|
-
|
|
107
|
-
return error("Missing required space", "Add -s, --space <id> before `run` or set COHUB_SPACE_ID.");
|
|
108
|
-
}
|
|
107
|
+
const spaceId = explicitSpaceId || (await resolveDefaultSpace().catch(handleHttp)) || missingSpaceError();
|
|
109
108
|
return { spaceId, json, async, command };
|
|
110
109
|
}
|
|
111
110
|
function printRunHelp() {
|
|
@@ -126,6 +125,7 @@ Examples:
|
|
|
126
125
|
cohub -s <spaceId> run -- git status -sb
|
|
127
126
|
|
|
128
127
|
Notes:
|
|
128
|
+
- Without -s or COHUB_SPACE_ID, the command targets your Home space.
|
|
129
129
|
- Use --command for commands that contain leading flags, or use -- before the shell command.
|
|
130
130
|
- The command runs in /workspace.
|
|
131
131
|
`);
|
|
@@ -199,7 +199,7 @@ async function waitForRunCompletion(taskRunId, showSpinner) {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
async function handleRunCli(argv) {
|
|
202
|
-
const opts = parseRunCliOptions(argv);
|
|
202
|
+
const opts = await parseRunCliOptions(argv);
|
|
203
203
|
const client = createClient();
|
|
204
204
|
try {
|
|
205
205
|
const { taskRunId } = await client.space(opts.spaceId).runCommand({ command: opts.command });
|
package/dist/commands/sandbox.js
CHANGED
|
@@ -5,7 +5,7 @@ import { basename, resolve } from "node:path";
|
|
|
5
5
|
import { resolveCohubEnvironment, resolveWebsocketUrl } from "@neta-art/cohub";
|
|
6
6
|
import { requireAccessToken } from "../auth.js";
|
|
7
7
|
import { createClient } from "../client.js";
|
|
8
|
-
import { error, json as outJson, jsonRequested, ok, spinner } from "../output.js";
|
|
8
|
+
import { error, handleHttp, json as outJson, jsonRequested, ok, spinner } from "../output.js";
|
|
9
9
|
import { resolveSpace } from "../space.js";
|
|
10
10
|
import { ensureSandboxdBinary, SandboxdDownloadError } from "./sandboxd-binary.js";
|
|
11
11
|
// Derive the gateway relay control endpoint from the realtime websocket URL,
|
|
@@ -154,18 +154,22 @@ export function registerSandbox(program) {
|
|
|
154
154
|
.option("-s, --space <id>", "Target space ID")
|
|
155
155
|
.option("--json", "Output as JSON")
|
|
156
156
|
.action(async (opts) => {
|
|
157
|
-
const spaceId = opts.space?.trim() || resolveSpace(program);
|
|
157
|
+
const spaceId = opts.space?.trim() || await resolveSpace(program);
|
|
158
158
|
const client = createClient();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
try {
|
|
160
|
+
const sandbox = (await client.space(spaceId).sandbox.get()).sandbox ?? null;
|
|
161
|
+
if (jsonRequested(opts))
|
|
162
|
+
return outJson({ spaceId, sandbox });
|
|
163
|
+
if (!sandbox) {
|
|
164
|
+
console.log(" (no sandbox)");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
console.log(` space: ${spaceId}`);
|
|
168
|
+
console.log(` provider: ${sandbox.provider ?? "cloud"}`);
|
|
169
|
+
console.log(` status: ${sandbox.status ?? "unknown"}`);
|
|
170
|
+
}
|
|
171
|
+
catch (cause) {
|
|
172
|
+
handleHttp(cause);
|
|
166
173
|
}
|
|
167
|
-
console.log(` space: ${spaceId}`);
|
|
168
|
-
console.log(` provider: ${sandbox.provider ?? "cloud"}`);
|
|
169
|
-
console.log(` status: ${sandbox.status ?? "unknown"}`);
|
|
170
174
|
});
|
|
171
175
|
}
|
|
@@ -19,7 +19,7 @@ const BINARY_NAME = "cohub-sandboxd";
|
|
|
19
19
|
// Public CDN prefix hosting the release archives (the repo is private, so the
|
|
20
20
|
// GitHub Release assets are not publicly downloadable). Overridable for staging
|
|
21
21
|
// or self-hosting.
|
|
22
|
-
const cdnBaseUrl = () => (process.env.COHUB_SANDBOXD_CDN_BASE_URL?.trim() || "https://public.cohub.
|
|
22
|
+
const cdnBaseUrl = () => (process.env.COHUB_SANDBOXD_CDN_BASE_URL?.trim() || "https://public.cohub.live/sandboxd").replace(/\/+$/, "");
|
|
23
23
|
const DOWNLOAD_TIMEOUT_MS = 120_000;
|
|
24
24
|
const LOCK_STALE_MS = 5 * 60 * 1000;
|
|
25
25
|
// Map Node's platform/arch to the Go GOOS/GOARCH used in release asset names.
|
|
@@ -124,7 +124,7 @@ export function registerSpaceActivity(spacesCmd, dependencies = {}) {
|
|
|
124
124
|
.description("Space activity overview: usage, contributors, rankings")
|
|
125
125
|
.option("--json", "Output as JSON")
|
|
126
126
|
.action(async (days, opts) => {
|
|
127
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
127
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
128
128
|
let parsedDays;
|
|
129
129
|
try {
|
|
130
130
|
parsedDays = parseActivityDays(days);
|
|
@@ -153,8 +153,8 @@ async function confirmDanger(opts, detail) {
|
|
|
153
153
|
if (answer !== "y" && answer !== "yes")
|
|
154
154
|
return error("Cancelled");
|
|
155
155
|
}
|
|
156
|
-
function commerceClient(spacesCmd) {
|
|
157
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
156
|
+
async function commerceClient(spacesCmd) {
|
|
157
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
158
158
|
return { spaceId, commerce: createClient().space(spaceId).commerce };
|
|
159
159
|
}
|
|
160
160
|
export function registerSpaceCommerce(spacesCmd) {
|
|
@@ -172,7 +172,7 @@ Examples:
|
|
|
172
172
|
.description("Initialize commerce for the target space")
|
|
173
173
|
.option("--json", "Output as JSON")
|
|
174
174
|
.action(async (opts) => {
|
|
175
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
175
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
176
176
|
try {
|
|
177
177
|
const result = await commerce.setup();
|
|
178
178
|
if (jsonRequested(opts))
|
|
@@ -196,7 +196,7 @@ Examples:
|
|
|
196
196
|
.description("List products")
|
|
197
197
|
.option("--json", "Output as JSON")
|
|
198
198
|
.action(async (opts) => {
|
|
199
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
199
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
200
200
|
try {
|
|
201
201
|
const result = await commerce.listProducts();
|
|
202
202
|
if (jsonRequested(opts))
|
|
@@ -235,7 +235,7 @@ Examples:
|
|
|
235
235
|
status: parseChoice(opts.status, "status", PRODUCT_STATUSES),
|
|
236
236
|
visibility: parseChoice(opts.visibility, "visibility", PRODUCT_VISIBILITIES),
|
|
237
237
|
};
|
|
238
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
238
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
239
239
|
try {
|
|
240
240
|
const result = await commerce.createProduct(input);
|
|
241
241
|
if (jsonRequested(opts))
|
|
@@ -269,7 +269,7 @@ Examples:
|
|
|
269
269
|
});
|
|
270
270
|
if (Object.keys(input).length === 0)
|
|
271
271
|
return error("Nothing to update", "Pass --name, --description, --clear-description, --status, or --visibility.");
|
|
272
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
272
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
273
273
|
try {
|
|
274
274
|
const result = await commerce.updateProduct(productKey, input);
|
|
275
275
|
if (jsonRequested(opts))
|
|
@@ -290,7 +290,7 @@ Examples:
|
|
|
290
290
|
.action(async (opts) => {
|
|
291
291
|
const productKey = requireText(opts.productKey, "product key", "--product-key <key>");
|
|
292
292
|
await confirmDanger(opts, `archive product "${productKey}"`);
|
|
293
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
293
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
294
294
|
try {
|
|
295
295
|
const result = await commerce.updateProduct(productKey, { status: "archived" });
|
|
296
296
|
if (jsonRequested(opts))
|
|
@@ -314,7 +314,7 @@ Examples:
|
|
|
314
314
|
.description("List benefits")
|
|
315
315
|
.option("--json", "Output as JSON")
|
|
316
316
|
.action(async (opts) => {
|
|
317
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
317
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
318
318
|
try {
|
|
319
319
|
const result = await commerce.listBenefits();
|
|
320
320
|
if (jsonRequested(opts))
|
|
@@ -353,7 +353,7 @@ Examples:
|
|
|
353
353
|
? parseInteger(opts.expiresInDays, "expires-in-days", { fallback: 0, min: 1 })
|
|
354
354
|
: undefined,
|
|
355
355
|
};
|
|
356
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
356
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
357
357
|
try {
|
|
358
358
|
const result = await commerce.createBenefit(input);
|
|
359
359
|
if (jsonRequested(opts))
|
|
@@ -373,7 +373,7 @@ Examples:
|
|
|
373
373
|
type: "feature",
|
|
374
374
|
metadata: parseMetadataJson(opts.metadataJson),
|
|
375
375
|
};
|
|
376
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
376
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
377
377
|
try {
|
|
378
378
|
const result = await commerce.createBenefit(input);
|
|
379
379
|
if (jsonRequested(opts))
|
|
@@ -407,7 +407,7 @@ Examples:
|
|
|
407
407
|
});
|
|
408
408
|
if (Object.keys(input).length === 0)
|
|
409
409
|
return error("Nothing to update", "Pass --name, --description, --clear-description, --status, or --metadata-json.");
|
|
410
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
410
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
411
411
|
try {
|
|
412
412
|
const result = await commerce.updateBenefit(benefitKey, input);
|
|
413
413
|
if (jsonRequested(opts))
|
|
@@ -428,7 +428,7 @@ Examples:
|
|
|
428
428
|
.action(async (opts) => {
|
|
429
429
|
const benefitKey = requireText(opts.benefitKey, "benefit key", "--benefit-key <key>");
|
|
430
430
|
await confirmDanger(opts, `archive benefit "${benefitKey}"`);
|
|
431
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
431
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
432
432
|
try {
|
|
433
433
|
const result = await commerce.updateBenefit(benefitKey, { status: "archived" });
|
|
434
434
|
if (jsonRequested(opts))
|
|
@@ -450,7 +450,7 @@ Examples:
|
|
|
450
450
|
productKey: requireText(opts.productKey, "product key", "--product-key <key>"),
|
|
451
451
|
benefitKey: requireText(opts.benefitKey, "benefit key", "--benefit-key <key>"),
|
|
452
452
|
};
|
|
453
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
453
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
454
454
|
try {
|
|
455
455
|
const result = await commerce.bindProductBenefit(input);
|
|
456
456
|
if (jsonRequested(opts))
|
|
@@ -474,7 +474,7 @@ Examples:
|
|
|
474
474
|
benefitKey: requireText(opts.benefitKey, "benefit key", "--benefit-key <key>"),
|
|
475
475
|
};
|
|
476
476
|
await confirmDanger(opts, `unbind benefit "${input.benefitKey}" from product "${input.productKey}"`);
|
|
477
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
477
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
478
478
|
try {
|
|
479
479
|
const result = await commerce.unbindProductBenefit(input);
|
|
480
480
|
if (jsonRequested(opts))
|
|
@@ -495,7 +495,7 @@ Examples:
|
|
|
495
495
|
.option("--limit <limit>", "Page size, max 50")
|
|
496
496
|
.option("--json", "Output as JSON")
|
|
497
497
|
.action(async (opts) => {
|
|
498
|
-
const { commerce } = commerceClient(spacesCmd);
|
|
498
|
+
const { commerce } = await commerceClient(spacesCmd);
|
|
499
499
|
try {
|
|
500
500
|
const result = await commerce.listOrders({
|
|
501
501
|
page: parseInteger(opts.page, "page", { fallback: 1, min: 1 }),
|
|
@@ -72,7 +72,7 @@ export function registerSpaceInvitations(spacesCommand, dependencies = {
|
|
|
72
72
|
.option("--max-uses <count>", "Usage limit, or 0 for unlimited", "0")
|
|
73
73
|
.option("--json", "Output as JSON")
|
|
74
74
|
.action(async (options) => {
|
|
75
|
-
const spaceId = resolveSpace(spacesCommand);
|
|
75
|
+
const spaceId = await resolveSpace(spacesCommand);
|
|
76
76
|
let input;
|
|
77
77
|
try {
|
|
78
78
|
input = parseSpaceInvitationCreateOptions(options);
|
|
@@ -111,7 +111,7 @@ export function registerSpaceInvitations(spacesCommand, dependencies = {
|
|
|
111
111
|
.description("List invite links")
|
|
112
112
|
.option("--json", "Output as JSON")
|
|
113
113
|
.action(async (options) => {
|
|
114
|
-
const spaceId = resolveSpace(spacesCommand);
|
|
114
|
+
const spaceId = await resolveSpace(spacesCommand);
|
|
115
115
|
try {
|
|
116
116
|
const result = await dependencies
|
|
117
117
|
.createClient()
|
|
@@ -150,7 +150,7 @@ export function registerSpaceInvitations(spacesCommand, dependencies = {
|
|
|
150
150
|
.option("--json", "Output as JSON")
|
|
151
151
|
.action(async (code, options) => {
|
|
152
152
|
await confirmRevoke(options);
|
|
153
|
-
const spaceId = resolveSpace(spacesCommand);
|
|
153
|
+
const spaceId = await resolveSpace(spacesCommand);
|
|
154
154
|
try {
|
|
155
155
|
const result = await dependencies
|
|
156
156
|
.createClient()
|
|
@@ -82,8 +82,8 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
82
82
|
const turnsCmd = spacesCmd
|
|
83
83
|
.command("turns")
|
|
84
84
|
.description("Browse turns across the space")
|
|
85
|
-
.hook("preAction", () => {
|
|
86
|
-
resolveSpace(spacesCmd);
|
|
85
|
+
.hook("preAction", async () => {
|
|
86
|
+
await resolveSpace(spacesCmd);
|
|
87
87
|
});
|
|
88
88
|
turnsCmd
|
|
89
89
|
.command("ls")
|
|
@@ -108,7 +108,7 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
108
108
|
}
|
|
109
109
|
throw cause;
|
|
110
110
|
}
|
|
111
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
111
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
112
112
|
const client = dependencies.createClient?.() ?? createClient();
|
|
113
113
|
try {
|
|
114
114
|
try {
|
|
@@ -179,7 +179,7 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
179
179
|
.description("Read persisted intermediate messages from the CDN archive")
|
|
180
180
|
.option("--json", "Output as JSON")
|
|
181
181
|
.action(async (sessionId, turnId, options) => {
|
|
182
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
182
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
183
183
|
const client = dependencies.createClient?.() ?? createClient();
|
|
184
184
|
try {
|
|
185
185
|
const archive = await client.space(spaceId).session(sessionId).turns.intermediate.get(turnId);
|
package/dist/commands/spaces.js
CHANGED
|
@@ -168,7 +168,7 @@ async function putUploadEntry(entry, uploadUrl, headers) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
async function uploadFiles(command, paths, opts) {
|
|
171
|
-
const spaceId = resolveSpace(command);
|
|
171
|
+
const spaceId = await resolveSpace(command);
|
|
172
172
|
const client = createClient();
|
|
173
173
|
try {
|
|
174
174
|
const files = await collectUploadFiles(paths);
|
|
@@ -242,7 +242,7 @@ async function sendPrompt(command, words, opts) {
|
|
|
242
242
|
&& !new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]).has(thinkingLevel)) {
|
|
243
243
|
return error("Invalid thinking level", "Use off|minimal|low|medium|high|xhigh|max");
|
|
244
244
|
}
|
|
245
|
-
const spaceId = resolveSpace(command);
|
|
245
|
+
const spaceId = await resolveSpace(command);
|
|
246
246
|
const client = createClient();
|
|
247
247
|
try {
|
|
248
248
|
const schedule = opts.delayMs
|
|
@@ -304,7 +304,7 @@ async function sendPrompt(command, words, opts) {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
async function runCompletionCommand(command, words, opts) {
|
|
307
|
-
const spaceId = resolveSpace(command);
|
|
307
|
+
const spaceId = await resolveSpace(command);
|
|
308
308
|
const content = words.join(" ").trim();
|
|
309
309
|
if (!content && process.stdin.isTTY) {
|
|
310
310
|
return error("Message required", "Pass content args or pipe via stdin");
|
|
@@ -490,7 +490,7 @@ export function registerSpaces(program) {
|
|
|
490
490
|
.description("Show space details")
|
|
491
491
|
.option("--json", "Output as JSON")
|
|
492
492
|
.action(async (id, opts) => {
|
|
493
|
-
const spaceId = id?.trim() || resolveSpace(spacesCmd);
|
|
493
|
+
const spaceId = id?.trim() || await resolveSpace(spacesCmd);
|
|
494
494
|
const client = createClient();
|
|
495
495
|
try {
|
|
496
496
|
const space = await client.spaces.get(spaceId);
|
|
@@ -562,7 +562,7 @@ export function registerSpaces(program) {
|
|
|
562
562
|
.description("Upload the space avatar")
|
|
563
563
|
.option("--json", "Output as JSON")
|
|
564
564
|
.action(async (path, opts) => {
|
|
565
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
565
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
566
566
|
const client = createClient();
|
|
567
567
|
try {
|
|
568
568
|
const asset = await uploadAvatarAsset({ client, purpose: "space_avatar", spaceId, path });
|
|
@@ -699,7 +699,7 @@ export function registerSpaces(program) {
|
|
|
699
699
|
.description("Space usage statistics (default: 30 days)")
|
|
700
700
|
.option("--json", "Output as JSON")
|
|
701
701
|
.action(async (days, opts) => {
|
|
702
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
702
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
703
703
|
const client = createClient();
|
|
704
704
|
try {
|
|
705
705
|
const usage = await client.space(spaceId).usage.get(parseInteger(days ?? "30", "days", { min: 1 }));
|
|
@@ -737,14 +737,14 @@ function registerLabels(spacesCmd) {
|
|
|
737
737
|
const labelsCmd = spacesCmd
|
|
738
738
|
.command("labels")
|
|
739
739
|
.description("Manage labels")
|
|
740
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
740
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
741
741
|
labelsCmd
|
|
742
742
|
.command("ls")
|
|
743
743
|
.alias("list")
|
|
744
744
|
.description("List labels")
|
|
745
745
|
.option("--json", "Output as JSON")
|
|
746
746
|
.action(async (opts) => {
|
|
747
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
747
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
748
748
|
const client = createClient();
|
|
749
749
|
try {
|
|
750
750
|
const result = await client.space(spaceId).labels.list();
|
|
@@ -764,7 +764,7 @@ function registerLabels(spacesCmd) {
|
|
|
764
764
|
.description("Create a label")
|
|
765
765
|
.option("--json", "Output as JSON")
|
|
766
766
|
.action(async (labelRef, opts) => {
|
|
767
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
767
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
768
768
|
const client = createClient();
|
|
769
769
|
try {
|
|
770
770
|
const result = await client.space(spaceId).labels.create(labelRef);
|
|
@@ -784,7 +784,7 @@ function registerLabels(spacesCmd) {
|
|
|
784
784
|
.option("--rank <n>", "Sort rank")
|
|
785
785
|
.option("--json", "Output as JSON")
|
|
786
786
|
.action(async (labelRef, opts) => {
|
|
787
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
787
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
788
788
|
const client = createClient();
|
|
789
789
|
try {
|
|
790
790
|
const result = await client.space(spaceId).labels.update(labelRef, {
|
|
@@ -805,7 +805,7 @@ function registerLabels(spacesCmd) {
|
|
|
805
805
|
.alias("delete")
|
|
806
806
|
.description("Delete a label")
|
|
807
807
|
.action(async (labelRef) => {
|
|
808
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
808
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
809
809
|
const client = createClient();
|
|
810
810
|
try {
|
|
811
811
|
await client.space(spaceId).labels.delete(labelRef);
|
|
@@ -820,7 +820,7 @@ function registerLabels(spacesCmd) {
|
|
|
820
820
|
.description("Reorder labels")
|
|
821
821
|
.option("--json", "Output as JSON")
|
|
822
822
|
.action(async (labelRefs, opts) => {
|
|
823
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
823
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
824
824
|
const client = createClient();
|
|
825
825
|
try {
|
|
826
826
|
const result = await client.space(spaceId).labels.reorder(labelRefs);
|
|
@@ -839,7 +839,7 @@ function registerLabels(spacesCmd) {
|
|
|
839
839
|
.option("--cursor <cursor>", "Page cursor")
|
|
840
840
|
.option("--json", "Output as JSON")
|
|
841
841
|
.action(async (labelRef, opts) => {
|
|
842
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
842
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
843
843
|
const client = createClient();
|
|
844
844
|
try {
|
|
845
845
|
const result = await client.space(spaceId).labels.listItems(labelRef, {
|
|
@@ -864,7 +864,7 @@ function registerLabels(spacesCmd) {
|
|
|
864
864
|
.description("Attach a label")
|
|
865
865
|
.option("--json", "Output as JSON")
|
|
866
866
|
.action(async (labelRef, resourceType, resourceRef, opts) => {
|
|
867
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
867
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
868
868
|
const client = createClient();
|
|
869
869
|
try {
|
|
870
870
|
const result = await client.space(spaceId).labels.attach(labelRef, { resourceType: parseLabelResourceType(resourceType), resourceRef });
|
|
@@ -880,7 +880,7 @@ function registerLabels(spacesCmd) {
|
|
|
880
880
|
.command("detach <labelRef> <resourceType> <resourceRef>")
|
|
881
881
|
.description("Detach a label")
|
|
882
882
|
.action(async (labelRef, resourceType, resourceRef) => {
|
|
883
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
883
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
884
884
|
const client = createClient();
|
|
885
885
|
try {
|
|
886
886
|
await client.space(spaceId).labels.detach(labelRef, { resourceType: parseLabelResourceType(resourceType), resourceRef });
|
|
@@ -897,7 +897,7 @@ function registerLabels(spacesCmd) {
|
|
|
897
897
|
.option("--remove <refs>", "Comma-separated label refs to remove")
|
|
898
898
|
.option("--json", "Output as JSON")
|
|
899
899
|
.action(async (resourceType, resourceRef, opts) => {
|
|
900
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
900
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
901
901
|
const client = createClient();
|
|
902
902
|
try {
|
|
903
903
|
const result = await client.space(spaceId).labels.patchResourceLabels(parseLabelResourceType(resourceType), resourceRef, {
|
|
@@ -918,7 +918,7 @@ function registerLabels(spacesCmd) {
|
|
|
918
918
|
.option("--labels <refs>", "Comma-separated label refs")
|
|
919
919
|
.option("--json", "Output as JSON")
|
|
920
920
|
.action(async (resourceType, resourceRef, labelRefs, opts) => {
|
|
921
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
921
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
922
922
|
const client = createClient();
|
|
923
923
|
try {
|
|
924
924
|
const refs = [...parseLabelRefs(opts.labels), ...labelRefs];
|
|
@@ -936,14 +936,14 @@ function registerMods(spacesCmd) {
|
|
|
936
936
|
const modsCmd = spacesCmd
|
|
937
937
|
.command("mods")
|
|
938
938
|
.description("Manage space mods")
|
|
939
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
939
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
940
940
|
modsCmd
|
|
941
941
|
.command("ls")
|
|
942
942
|
.alias("list")
|
|
943
943
|
.description("List mods")
|
|
944
944
|
.option("--json", "Output as JSON")
|
|
945
945
|
.action(async (opts) => {
|
|
946
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
946
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
947
947
|
const client = createClient();
|
|
948
948
|
try {
|
|
949
949
|
const result = await client.space(spaceId).mods.list();
|
|
@@ -969,7 +969,7 @@ function registerMods(spacesCmd) {
|
|
|
969
969
|
.option("--json", "Output as JSON")
|
|
970
970
|
.action(async (modSpaceId, opts) => {
|
|
971
971
|
await confirmRestart(opts);
|
|
972
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
972
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
973
973
|
const client = createClient();
|
|
974
974
|
try {
|
|
975
975
|
const result = await client.space(spaceId).mods.create({ modSpaceId, name: opts.name, mountSlug: opts.slug });
|
|
@@ -988,7 +988,7 @@ function registerMods(spacesCmd) {
|
|
|
988
988
|
.option("--json", "Output as JSON")
|
|
989
989
|
.action(async (modId, opts) => {
|
|
990
990
|
await confirmRestart(opts);
|
|
991
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
991
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
992
992
|
const client = createClient();
|
|
993
993
|
try {
|
|
994
994
|
const result = await client.space(spaceId).mods.update(modId, { enabled: true });
|
|
@@ -1007,7 +1007,7 @@ function registerMods(spacesCmd) {
|
|
|
1007
1007
|
.option("--json", "Output as JSON")
|
|
1008
1008
|
.action(async (modId, opts) => {
|
|
1009
1009
|
await confirmRestart(opts);
|
|
1010
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1010
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1011
1011
|
const client = createClient();
|
|
1012
1012
|
try {
|
|
1013
1013
|
const result = await client.space(spaceId).mods.update(modId, { enabled: false });
|
|
@@ -1027,7 +1027,7 @@ function registerMods(spacesCmd) {
|
|
|
1027
1027
|
.option("--json", "Output as JSON")
|
|
1028
1028
|
.action(async (modId, opts) => {
|
|
1029
1029
|
await confirmRestart(opts);
|
|
1030
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1030
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1031
1031
|
const client = createClient();
|
|
1032
1032
|
try {
|
|
1033
1033
|
const result = await client.space(spaceId).mods.remove(modId);
|
|
@@ -1111,14 +1111,14 @@ function registerFiles(spacesCmd) {
|
|
|
1111
1111
|
const filesCmd = spacesCmd
|
|
1112
1112
|
.command("files")
|
|
1113
1113
|
.description("File operations")
|
|
1114
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1114
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
1115
1115
|
filesCmd
|
|
1116
1116
|
.command("ls [path]")
|
|
1117
1117
|
.alias("list")
|
|
1118
1118
|
.description("List directory tree")
|
|
1119
1119
|
.option("--json", "Output as JSON")
|
|
1120
1120
|
.action(async (path, opts) => {
|
|
1121
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1121
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1122
1122
|
const client = createClient();
|
|
1123
1123
|
try {
|
|
1124
1124
|
const tree = await client.space(spaceId).files.list(path ?? "");
|
|
@@ -1143,7 +1143,7 @@ function registerFiles(spacesCmd) {
|
|
|
1143
1143
|
.command("cat <path>")
|
|
1144
1144
|
.description("Read file content")
|
|
1145
1145
|
.action(async (path) => {
|
|
1146
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1146
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1147
1147
|
const client = createClient();
|
|
1148
1148
|
try {
|
|
1149
1149
|
const file = await client.space(spaceId).files.read(path);
|
|
@@ -1173,7 +1173,7 @@ function registerFiles(spacesCmd) {
|
|
|
1173
1173
|
}
|
|
1174
1174
|
if (!content)
|
|
1175
1175
|
return error("No content provided", "Use -c or pipe via stdin");
|
|
1176
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1176
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1177
1177
|
const client = createClient();
|
|
1178
1178
|
try {
|
|
1179
1179
|
const result = await client.space(spaceId).files.write({
|
|
@@ -1197,7 +1197,7 @@ function registerFiles(spacesCmd) {
|
|
|
1197
1197
|
.command("mkdir <path>")
|
|
1198
1198
|
.description("Create a directory")
|
|
1199
1199
|
.action(async (path) => {
|
|
1200
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1200
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1201
1201
|
const client = createClient();
|
|
1202
1202
|
try {
|
|
1203
1203
|
await client.space(spaceId).files.createDir(path);
|
|
@@ -1212,7 +1212,7 @@ function registerFiles(spacesCmd) {
|
|
|
1212
1212
|
.description("Delete a file or directory")
|
|
1213
1213
|
.option("-r, --recursive", "Delete recursively")
|
|
1214
1214
|
.action(async (path, opts) => {
|
|
1215
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1215
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1216
1216
|
const client = createClient();
|
|
1217
1217
|
try {
|
|
1218
1218
|
await client.space(spaceId).files.delete(path, opts.recursive ?? false);
|
|
@@ -1226,7 +1226,7 @@ function registerFiles(spacesCmd) {
|
|
|
1226
1226
|
.command("mv <from> <to>")
|
|
1227
1227
|
.description("Move or rename")
|
|
1228
1228
|
.action(async (from, to) => {
|
|
1229
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1229
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1230
1230
|
const client = createClient();
|
|
1231
1231
|
try {
|
|
1232
1232
|
await client.space(spaceId).files.move({ fromPath: from, toPath: to });
|
|
@@ -1241,7 +1241,7 @@ function registerFiles(spacesCmd) {
|
|
|
1241
1241
|
.description("Show pending workspace changes vs last checkpoint")
|
|
1242
1242
|
.option("--json", "Output as JSON")
|
|
1243
1243
|
.action(async (path, opts) => {
|
|
1244
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1244
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1245
1245
|
const client = createClient();
|
|
1246
1246
|
try {
|
|
1247
1247
|
if (path) {
|
|
@@ -1272,14 +1272,14 @@ function registerSessions(spacesCmd) {
|
|
|
1272
1272
|
const sessionsCmd = spacesCmd
|
|
1273
1273
|
.command("sessions")
|
|
1274
1274
|
.description("Browse sessions and turns")
|
|
1275
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1275
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
1276
1276
|
sessionsCmd
|
|
1277
1277
|
.command("ls")
|
|
1278
1278
|
.alias("list")
|
|
1279
1279
|
.description("List sessions")
|
|
1280
1280
|
.option("--json", "Output as JSON")
|
|
1281
1281
|
.action(async (opts) => {
|
|
1282
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1282
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1283
1283
|
const client = createClient();
|
|
1284
1284
|
try {
|
|
1285
1285
|
const result = await client.space(spaceId).sessions.list();
|
|
@@ -1306,7 +1306,7 @@ function registerSessions(spacesCmd) {
|
|
|
1306
1306
|
.option("--label <ref>", "Attach a label, e.g. Bug or Area/Frontend", collectOption, [])
|
|
1307
1307
|
.option("--json", "Output as JSON")
|
|
1308
1308
|
.action(async (title, opts) => {
|
|
1309
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1309
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1310
1310
|
const client = createClient();
|
|
1311
1311
|
try {
|
|
1312
1312
|
const result = await client.space(spaceId).sessions.create({
|
|
@@ -1330,7 +1330,7 @@ function registerSessions(spacesCmd) {
|
|
|
1330
1330
|
.description("Session details")
|
|
1331
1331
|
.option("--json", "Output as JSON")
|
|
1332
1332
|
.action(async (id, opts) => {
|
|
1333
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1333
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1334
1334
|
const client = createClient();
|
|
1335
1335
|
try {
|
|
1336
1336
|
const result = await client.space(spaceId).session(id).get();
|
|
@@ -1352,7 +1352,7 @@ function registerSessions(spacesCmd) {
|
|
|
1352
1352
|
.command("rename <id> <name>")
|
|
1353
1353
|
.description("Rename a session")
|
|
1354
1354
|
.action(async (id, name) => {
|
|
1355
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1355
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1356
1356
|
const client = createClient();
|
|
1357
1357
|
try {
|
|
1358
1358
|
await client.space(spaceId).session(id).rename(name);
|
|
@@ -1368,7 +1368,7 @@ function registerSessions(spacesCmd) {
|
|
|
1368
1368
|
.description("Stream realtime session events")
|
|
1369
1369
|
.option("--json", "Output as JSON")
|
|
1370
1370
|
.action(async (id, opts) => {
|
|
1371
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1371
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1372
1372
|
const client = createClient();
|
|
1373
1373
|
const session = client.space(spaceId).session(id);
|
|
1374
1374
|
process.stdout.write(" Listening for events...\n\n");
|
|
@@ -1423,7 +1423,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1423
1423
|
.option("--limit <n>", "Page size", "30")
|
|
1424
1424
|
.option("--json", "Output as JSON")
|
|
1425
1425
|
.action(async (sessionId, opts) => {
|
|
1426
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1426
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1427
1427
|
const client = createClient();
|
|
1428
1428
|
try {
|
|
1429
1429
|
const result = await client.space(spaceId).session(sessionId).turns.listPaginated({
|
|
@@ -1455,7 +1455,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1455
1455
|
.description("Show turn details")
|
|
1456
1456
|
.option("--json", "Output as JSON")
|
|
1457
1457
|
.action(async (sessionId, turnId, opts) => {
|
|
1458
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1458
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1459
1459
|
const client = createClient();
|
|
1460
1460
|
try {
|
|
1461
1461
|
const result = await client.space(spaceId).session(sessionId).turns.get(turnId);
|
|
@@ -1484,7 +1484,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1484
1484
|
.description("Run a queued follow-up now")
|
|
1485
1485
|
.option("--json", "Output as JSON")
|
|
1486
1486
|
.action(async (sessionId, turnId, opts) => {
|
|
1487
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1487
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1488
1488
|
const client = createClient();
|
|
1489
1489
|
try {
|
|
1490
1490
|
const result = await client.space(spaceId).session(sessionId).steerTurn(turnId);
|
|
@@ -1501,7 +1501,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1501
1501
|
.description("Cancel a queued follow-up")
|
|
1502
1502
|
.option("--json", "Output as JSON")
|
|
1503
1503
|
.action(async (sessionId, turnId, opts) => {
|
|
1504
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1504
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1505
1505
|
const client = createClient();
|
|
1506
1506
|
try {
|
|
1507
1507
|
const result = await client.space(spaceId).session(sessionId).cancelTurn(turnId);
|
|
@@ -1520,7 +1520,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1520
1520
|
.option("--limit <n>", "Page size", "100")
|
|
1521
1521
|
.option("--json", "Output as JSON")
|
|
1522
1522
|
.action(async (sessionId, opts) => {
|
|
1523
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1523
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1524
1524
|
const client = createClient();
|
|
1525
1525
|
try {
|
|
1526
1526
|
const result = await client.space(spaceId).session(sessionId).turns.index({
|
|
@@ -1554,7 +1554,7 @@ function registerTurns(sessionsCmd) {
|
|
|
1554
1554
|
.option("--after <n>", "Turns after anchor", "20")
|
|
1555
1555
|
.option("--json", "Output as JSON")
|
|
1556
1556
|
.action(async (sessionId, opts) => {
|
|
1557
|
-
const spaceId = resolveSpace(sessionsCmd);
|
|
1557
|
+
const spaceId = await resolveSpace(sessionsCmd);
|
|
1558
1558
|
if (!opts.sequence && !opts.turn)
|
|
1559
1559
|
return error("Missing anchor", "Use --sequence <n> or --turn <id>");
|
|
1560
1560
|
const client = createClient();
|
|
@@ -1647,14 +1647,14 @@ function registerMembers(spacesCmd) {
|
|
|
1647
1647
|
const memCmd = spacesCmd
|
|
1648
1648
|
.command("members")
|
|
1649
1649
|
.description("Member management")
|
|
1650
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1650
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
1651
1651
|
memCmd
|
|
1652
1652
|
.command("ls")
|
|
1653
1653
|
.alias("list")
|
|
1654
1654
|
.description("List space members")
|
|
1655
1655
|
.option("--json", "Output as JSON")
|
|
1656
1656
|
.action(async (opts) => {
|
|
1657
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1657
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1658
1658
|
const client = createClient();
|
|
1659
1659
|
try {
|
|
1660
1660
|
const result = await client.space(spaceId).members.list();
|
|
@@ -1678,7 +1678,7 @@ function registerMembers(spacesCmd) {
|
|
|
1678
1678
|
.command("update <userId> <role>")
|
|
1679
1679
|
.description("Change member role (host | builder | guest)")
|
|
1680
1680
|
.action(async (userId, role) => {
|
|
1681
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1681
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1682
1682
|
const client = createClient();
|
|
1683
1683
|
try {
|
|
1684
1684
|
await client.space(spaceId).members.update(userId, parseChoice(role, "role", SPACE_ROLES));
|
|
@@ -1692,7 +1692,7 @@ function registerMembers(spacesCmd) {
|
|
|
1692
1692
|
.command("remove <userId>")
|
|
1693
1693
|
.description("Remove a member")
|
|
1694
1694
|
.action(async (userId) => {
|
|
1695
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1695
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1696
1696
|
const client = createClient();
|
|
1697
1697
|
try {
|
|
1698
1698
|
await client.space(spaceId).members.remove(userId);
|
|
@@ -1708,13 +1708,13 @@ function registerAccess(spacesCmd) {
|
|
|
1708
1708
|
const accCmd = spacesCmd
|
|
1709
1709
|
.command("access")
|
|
1710
1710
|
.description("Access control")
|
|
1711
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1711
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
1712
1712
|
accCmd
|
|
1713
1713
|
.command("get")
|
|
1714
1714
|
.description("Get access policy")
|
|
1715
1715
|
.option("--json", "Output as JSON")
|
|
1716
1716
|
.action(async (opts) => {
|
|
1717
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1717
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1718
1718
|
const client = createClient();
|
|
1719
1719
|
try {
|
|
1720
1720
|
const policy = await client.space(spaceId).access.get();
|
|
@@ -1736,7 +1736,7 @@ function registerAccess(spacesCmd) {
|
|
|
1736
1736
|
.option("--anonymous <role>", "Role for anonymous users (host|builder|guest|null)")
|
|
1737
1737
|
.option("--json", "Output as JSON")
|
|
1738
1738
|
.action(async (opts) => {
|
|
1739
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1739
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1740
1740
|
const client = createClient();
|
|
1741
1741
|
try {
|
|
1742
1742
|
const policy = await client.space(spaceId).access.set({
|
|
@@ -1761,7 +1761,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1761
1761
|
const cpCmd = spacesCmd
|
|
1762
1762
|
.command("checkpoints")
|
|
1763
1763
|
.description("Checkpoint management")
|
|
1764
|
-
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1764
|
+
.hook("preAction", async () => { await resolveSpace(spacesCmd); });
|
|
1765
1765
|
cpCmd
|
|
1766
1766
|
.command("ls")
|
|
1767
1767
|
.alias("list")
|
|
@@ -1770,7 +1770,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1770
1770
|
.option("--cursor <cursor>", "Pagination cursor")
|
|
1771
1771
|
.option("--json", "Output as JSON")
|
|
1772
1772
|
.action(async (opts) => {
|
|
1773
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1773
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1774
1774
|
const client = createClient();
|
|
1775
1775
|
try {
|
|
1776
1776
|
const result = await client.space(spaceId).checkpoints.list({
|
|
@@ -1799,7 +1799,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1799
1799
|
.description("Checkpoint details")
|
|
1800
1800
|
.option("--json", "Output as JSON")
|
|
1801
1801
|
.action(async (id, opts) => {
|
|
1802
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1802
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1803
1803
|
const client = createClient();
|
|
1804
1804
|
try {
|
|
1805
1805
|
const result = await client.space(spaceId).checkpoints.get(id);
|
|
@@ -1822,7 +1822,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1822
1822
|
.description("Create a checkpoint")
|
|
1823
1823
|
.option("--json", "Output as JSON")
|
|
1824
1824
|
.action(async (description, opts) => {
|
|
1825
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1825
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1826
1826
|
const client = createClient();
|
|
1827
1827
|
try {
|
|
1828
1828
|
const result = await client.space(spaceId).checkpoints.create(description ?? null);
|
|
@@ -1839,7 +1839,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1839
1839
|
.description("List checkpoint tree")
|
|
1840
1840
|
.option("--json", "Output as JSON")
|
|
1841
1841
|
.action(async (checkpointId, path, opts) => {
|
|
1842
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1842
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1843
1843
|
const client = createClient();
|
|
1844
1844
|
try {
|
|
1845
1845
|
const tree = await client.space(spaceId).checkpoints(checkpointId).files.list(path ?? "");
|
|
@@ -1866,7 +1866,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1866
1866
|
.description("Show checkpoint file content")
|
|
1867
1867
|
.option("--json", "Output as JSON")
|
|
1868
1868
|
.action(async (checkpointId, path, opts) => {
|
|
1869
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1869
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1870
1870
|
const client = createClient();
|
|
1871
1871
|
try {
|
|
1872
1872
|
const file = await client.space(spaceId).checkpoints(checkpointId).files.read(path);
|
|
@@ -1888,7 +1888,7 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1888
1888
|
.option("--base <checkpointId>", "Compare against another checkpoint")
|
|
1889
1889
|
.option("--json", "Output as JSON")
|
|
1890
1890
|
.action(async (checkpointId, path, opts) => {
|
|
1891
|
-
const spaceId = resolveSpace(spacesCmd);
|
|
1891
|
+
const spaceId = await resolveSpace(spacesCmd);
|
|
1892
1892
|
const client = createClient();
|
|
1893
1893
|
try {
|
|
1894
1894
|
if (path) {
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ program
|
|
|
36
36
|
.summary("Work with Cohub from your terminal")
|
|
37
37
|
.description("Send prompts, manage Space files, and publish public output.")
|
|
38
38
|
.version(VERSION, "-v, --version", "Show version")
|
|
39
|
-
.option("-s, --space <id>", "Target Space ID")
|
|
39
|
+
.option("-s, --space <id>", "Target Space ID (defaults to your Home space)")
|
|
40
40
|
.option("--json", "Print machine-readable JSON when supported")
|
|
41
41
|
.helpOption("-h, --help", "Show help")
|
|
42
42
|
.addHelpText("after", `
|
|
@@ -45,9 +45,9 @@ Common commands:
|
|
|
45
45
|
cohub auth login
|
|
46
46
|
cohub profile avatar ./avatar.png
|
|
47
47
|
cohub spaces ls
|
|
48
|
-
cohub
|
|
49
|
-
cohub
|
|
50
|
-
cohub
|
|
48
|
+
cohub prompt "Fix the failing tests"
|
|
49
|
+
cohub completion "Summarize AGENTS.md" --system-prompt AGENTS.md --stream
|
|
50
|
+
cohub run -- git status
|
|
51
51
|
cohub sandbox up ./my-project
|
|
52
52
|
cohub search "release notes"
|
|
53
53
|
cohub -s <space-id> boards inspect <board-id>
|
|
@@ -62,7 +62,11 @@ Common commands:
|
|
|
62
62
|
cohub models ls --model-type multimodal
|
|
63
63
|
cohub generate "A calm lake at sunrise" --model <model> --output lake.png
|
|
64
64
|
|
|
65
|
+
Target space:
|
|
66
|
+
-s <space-id>, then COHUB_SPACE_ID, then your Home space
|
|
67
|
+
|
|
65
68
|
Environment:
|
|
69
|
+
COHUB_SPACE_ID Target Space ID when -s is omitted
|
|
66
70
|
COHUB_EXECUTION_TOKEN Use this token instead of the stored Logto session
|
|
67
71
|
ENV=dev Use the development Cohub environment
|
|
68
72
|
`);
|
package/dist/space.d.ts
CHANGED
|
@@ -1,2 +1,32 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Cache key aligned with auth: execution token is exclusive (same as
|
|
4
|
+
* `resolveAccessToken`) and never falls back to a local Logto session.
|
|
5
|
+
* Execution grants identify the actor as `actorUserId`, not `sub`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function identityKeyFrom(input: {
|
|
8
|
+
env: string;
|
|
9
|
+
executionToken?: string | null;
|
|
10
|
+
idToken?: string | null;
|
|
11
|
+
accessToken?: string | null;
|
|
12
|
+
}): string | null;
|
|
13
|
+
/** Exported for tests; production always uses `CACHE_PATH`. */
|
|
14
|
+
export declare function readDefaultSpaceCache(path: string, key: string, now?: number): string | null;
|
|
15
|
+
export declare function clearDefaultSpaceCache(): void;
|
|
16
|
+
/** Explicit target from `-s/--space` (any ancestor) or `COHUB_SPACE_ID`, else null. */
|
|
17
|
+
export declare function explicitSpace(program: Command): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the user's home space when no target is given. Cached locally per
|
|
20
|
+
* identity so repeated invocations skip the network entirely, and memoized
|
|
21
|
+
* in-process so preAction hooks and actions share a single lookup.
|
|
22
|
+
* Network and auth failures propagate so callers can report them faithfully.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveDefaultSpace(): Promise<string | null>;
|
|
25
|
+
/** Shared exit for commands that need a space but resolved none. */
|
|
26
|
+
export declare function missingSpaceError(): never;
|
|
27
|
+
/**
|
|
28
|
+
* Target space for a command: explicit `-s`/`COHUB_SPACE_ID` first, then the
|
|
29
|
+
* user's home space. Exits with guidance when neither is available; request
|
|
30
|
+
* failures go through the shared HTTP error handler.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveSpace(program: Command): Promise<string>;
|
package/dist/space.js
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { resolveCohubEnvironment } from "@neta-art/cohub";
|
|
5
|
+
import { readAuthSession } from "./auth.js";
|
|
6
|
+
import { createClient } from "./client.js";
|
|
7
|
+
import { error, handleHttp } from "./output.js";
|
|
8
|
+
const CONFIG_DIR = join(homedir(), ".config", "cohub");
|
|
9
|
+
const CACHE_PATH = join(CONFIG_DIR, "default-space.json");
|
|
10
|
+
/** Home space is stable; a one-day TTL bounds how long a stale hit survives. */
|
|
11
|
+
const CACHE_TTL_MS = 86_400_000;
|
|
12
|
+
function jwtClaim(token, key) {
|
|
13
|
+
const payload = token?.split(".")[1];
|
|
14
|
+
if (!payload)
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
18
|
+
const value = parsed[key];
|
|
19
|
+
return typeof value === "string" && value ? value : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Cache key aligned with auth: execution token is exclusive (same as
|
|
27
|
+
* `resolveAccessToken`) and never falls back to a local Logto session.
|
|
28
|
+
* Execution grants identify the actor as `actorUserId`, not `sub`.
|
|
29
|
+
*/
|
|
30
|
+
export function identityKeyFrom(input) {
|
|
31
|
+
if (input.executionToken) {
|
|
32
|
+
const actor = jwtClaim(input.executionToken, "actorUserId") ?? jwtClaim(input.executionToken, "sub");
|
|
33
|
+
return actor ? `${input.env}:${actor}` : null;
|
|
34
|
+
}
|
|
35
|
+
const sub = jwtClaim(input.idToken, "sub") ?? jwtClaim(input.accessToken, "sub");
|
|
36
|
+
return sub ? `${input.env}:${sub}` : null;
|
|
37
|
+
}
|
|
38
|
+
function identityKey() {
|
|
39
|
+
const session = readAuthSession();
|
|
40
|
+
return identityKeyFrom({
|
|
41
|
+
env: resolveCohubEnvironment(),
|
|
42
|
+
executionToken: process.env.COHUB_EXECUTION_TOKEN?.trim(),
|
|
43
|
+
idToken: session?.idToken,
|
|
44
|
+
accessToken: session?.accessToken,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Exported for tests; production always uses `CACHE_PATH`. */
|
|
48
|
+
export function readDefaultSpaceCache(path, key, now = Date.now()) {
|
|
49
|
+
try {
|
|
50
|
+
const cache = JSON.parse(readFileSync(path, "utf-8"));
|
|
51
|
+
if (cache.key !== key || typeof cache.spaceId !== "string" || typeof cache.cachedAt !== "number")
|
|
52
|
+
return null;
|
|
53
|
+
if (now - cache.cachedAt > CACHE_TTL_MS)
|
|
54
|
+
return null;
|
|
55
|
+
return cache.spaceId;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function writeCachedDefaultSpace(key, spaceId) {
|
|
62
|
+
try {
|
|
63
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
64
|
+
const cache = { key, spaceId, cachedAt: Date.now() };
|
|
65
|
+
writeFileSync(CACHE_PATH, `${JSON.stringify(cache, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Cache is best-effort; never fail the command over it.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let defaultSpacePromise = null;
|
|
72
|
+
export function clearDefaultSpaceCache() {
|
|
73
|
+
defaultSpacePromise = null;
|
|
74
|
+
try {
|
|
75
|
+
rmSync(CACHE_PATH, { force: true });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Best-effort, same as writes.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Explicit target from `-s/--space` (any ancestor) or `COHUB_SPACE_ID`, else null. */
|
|
82
|
+
export function explicitSpace(program) {
|
|
3
83
|
let current = program;
|
|
4
84
|
while (current) {
|
|
5
85
|
const opts = current.opts();
|
|
@@ -7,8 +87,39 @@ export function resolveSpace(program) {
|
|
|
7
87
|
return opts.space.trim();
|
|
8
88
|
current = current.parent ?? null;
|
|
9
89
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
90
|
+
return process.env.COHUB_SPACE_ID?.trim() || null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the user's home space when no target is given. Cached locally per
|
|
94
|
+
* identity so repeated invocations skip the network entirely, and memoized
|
|
95
|
+
* in-process so preAction hooks and actions share a single lookup.
|
|
96
|
+
* Network and auth failures propagate so callers can report them faithfully.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveDefaultSpace() {
|
|
99
|
+
defaultSpacePromise ??= (async () => {
|
|
100
|
+
const key = identityKey();
|
|
101
|
+
if (key) {
|
|
102
|
+
const cached = readDefaultSpaceCache(CACHE_PATH, key);
|
|
103
|
+
if (cached)
|
|
104
|
+
return cached;
|
|
105
|
+
}
|
|
106
|
+
const space = (await createClient().spaces.getDefault()).space ?? null;
|
|
107
|
+
// Recent-space fallback from getDefault() is not stable enough to cache.
|
|
108
|
+
if (space?.id && space.slug === "home" && key)
|
|
109
|
+
writeCachedDefaultSpace(key, space.id);
|
|
110
|
+
return space?.id ?? null;
|
|
111
|
+
})();
|
|
112
|
+
return defaultSpacePromise;
|
|
113
|
+
}
|
|
114
|
+
/** Shared exit for commands that need a space but resolved none. */
|
|
115
|
+
export function missingSpaceError() {
|
|
116
|
+
return error("No target space", "Add -s, --space <id> or set COHUB_SPACE_ID. Run `cohub auth login` to use your home space.");
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Target space for a command: explicit `-s`/`COHUB_SPACE_ID` first, then the
|
|
120
|
+
* user's home space. Exits with guidance when neither is available; request
|
|
121
|
+
* failures go through the shared HTTP error handler.
|
|
122
|
+
*/
|
|
123
|
+
export async function resolveSpace(program) {
|
|
124
|
+
return explicitSpace(program) ?? (await resolveDefaultSpace().catch(handleHttp)) ?? missingSpaceError();
|
|
14
125
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"commander": "^15.0.0",
|
|
20
20
|
"pixi.js": "^8.20.1",
|
|
21
21
|
"sharp": "^0.35.4",
|
|
22
|
-
"@neta-art/cohub": "8.10.
|
|
22
|
+
"@neta-art/cohub": "8.10.1"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|