@neta-art/cohub-cli 10.0.0 → 10.1.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.
@@ -1,3 +1,4 @@
1
+ import { consumeBaseUrlOption } from "../endpoint-options.js";
1
2
  import { createClient } from "../client.js";
2
3
  import { error, handleHttp, json as outJson, spinner } from "../output.js";
3
4
  import { resolveSpaceTarget } from "../space.js";
@@ -18,7 +19,7 @@ function topLevelRunIndex(argv) {
18
19
  const token = argv[index] ?? "";
19
20
  if (token === "--")
20
21
  return -1;
21
- if (token === "-s" || token === "--space") {
22
+ if (token === "-s" || token === "--space" || token === "--base-url") {
22
23
  index += 1;
23
24
  continue;
24
25
  }
@@ -50,6 +51,11 @@ export async function parseRunCliOptions(argv, spaceOptions = {}) {
50
51
  return error("Invalid invocation", "Use `cohub run [options] <command>`");
51
52
  const beforeRun = argv.slice(0, runIndex);
52
53
  const afterRun = argv.slice(runIndex + 1);
54
+ for (let index = 0; index < beforeRun.length; index += 1) {
55
+ index = consumeBaseUrlOption(beforeRun, index) ?? index;
56
+ if (beforeRun[index] === "-s" || beforeRun[index] === "--space")
57
+ index += 1;
58
+ }
53
59
  const explicitSpaceId = parseSpaceId(beforeRun) ?? process.env.COHUB_SPACE_ID?.trim() ?? "";
54
60
  let json = beforeRun.includes("--json");
55
61
  let async = false;
@@ -57,6 +63,11 @@ export async function parseRunCliOptions(argv, spaceOptions = {}) {
57
63
  let positionalTokens = [];
58
64
  for (let index = 0; index < afterRun.length; index += 1) {
59
65
  const token = afterRun[index] ?? "";
66
+ const endpointIndex = consumeBaseUrlOption(afterRun, index);
67
+ if (endpointIndex !== null) {
68
+ index = endpointIndex;
69
+ continue;
70
+ }
60
71
  if (token === "-h" || token === "--help") {
61
72
  printRunHelp();
62
73
  process.exit(0);
@@ -110,10 +121,11 @@ export async function parseRunCliOptions(argv, spaceOptions = {}) {
110
121
  export function printRunHelp() {
111
122
  process.stdout.write(`
112
123
  Usage:
113
- cohub [-s <space>] run [options] -- <shell command>
124
+ cohub [--base-url <url>] [-s <space>] run [options] -- <shell command>
114
125
  cohub [-s <space>] run --command <shell command>
115
126
 
116
127
  Options:
128
+ --base-url <url> API base URL (overrides COHUB_BASE_URL)
117
129
  -c, --command <command> Shell command to execute in the space workspace
118
130
  --async Queue the command and return immediately
119
131
  --json Output as JSON
@@ -0,0 +1,4 @@
1
+ import { type Command } from "commander";
2
+ export declare function setApiBaseUrl(value: string): string;
3
+ export declare function registerEndpointOptions(program: Command): Command;
4
+ export declare function consumeBaseUrlOption(tokens: string[], index: number): number | null;
@@ -0,0 +1,26 @@
1
+ import { normalizeBaseUrl } from "@neta-art/cohub";
2
+ import { InvalidArgumentError } from "commander";
3
+ export function setApiBaseUrl(value) {
4
+ const url = normalizeBaseUrl(value);
5
+ if (!url)
6
+ throw new InvalidArgumentError("API base URL must not be empty");
7
+ process.env.COHUB_BASE_URL = url;
8
+ return url;
9
+ }
10
+ export function registerEndpointOptions(program) {
11
+ return program.option("--base-url <url>", "API base URL (overrides COHUB_BASE_URL)", setApiBaseUrl);
12
+ }
13
+ export function consumeBaseUrlOption(tokens, index) {
14
+ const token = tokens[index];
15
+ if (token?.startsWith("--base-url=")) {
16
+ setApiBaseUrl(token.slice("--base-url=".length));
17
+ return index;
18
+ }
19
+ if (token !== "--base-url")
20
+ return null;
21
+ const value = tokens[index + 1];
22
+ if (value === undefined)
23
+ throw new InvalidArgumentError("--base-url requires a value");
24
+ setApiBaseUrl(value);
25
+ return index + 1;
26
+ }
package/dist/help-path.js CHANGED
@@ -73,11 +73,11 @@ function runRemainderRequestsHelp(argv) {
73
73
  return true;
74
74
  if (token === "--async" || token === "--json")
75
75
  continue;
76
- if (token === "-c" || token === "--command") {
76
+ if (token === "-c" || token === "--command" || token === "--base-url") {
77
77
  index += 1;
78
78
  continue;
79
79
  }
80
- if (token.startsWith("--command="))
80
+ if (token.startsWith("--command=") || token.startsWith("--base-url="))
81
81
  continue;
82
82
  return false;
83
83
  }
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import { readFileSync } from "node:fs";
4
+ import { registerEndpointOptions } from "./endpoint-options.js";
4
5
  import { registerAuth } from "./commands/auth.js";
5
6
  import { registerBoards } from "./commands/boards.js";
6
7
  import { registerChannels } from "./commands/channels.js";
@@ -32,7 +33,7 @@ const VERSION = (() => {
32
33
  return "1.0.0";
33
34
  }
34
35
  })();
35
- const program = new Command("cohub");
36
+ const program = registerEndpointOptions(new Command("cohub"));
36
37
  program
37
38
  .name("cohub")
38
39
  .summary("Work with Cohub from your terminal")
@@ -79,6 +80,9 @@ Target space:
79
80
  Environment:
80
81
  COHUB_SPACE_ID Target Space when -s is omitted
81
82
  COHUB_EXECUTION_TOKEN Use this token instead of the stored Logto session
83
+ COHUB_BASE_URL Override the API base URL
84
+ COHUB_WS_URL Override the realtime WebSocket URL
85
+ COHUB_WEB_URL Override the web URL used in links
82
86
  ENV=dev Use the development Cohub environment
83
87
  HTTPS_PROXY Honored for API and uploads (also HTTP_PROXY, NO_PROXY)
84
88
  Runtime logs ~/.local/state/cohub/runtime/<space-id>/diagnostics
@@ -102,10 +102,10 @@ export async function runRuntime(config, onState, externalSignal, onDiagnostic)
102
102
  config.capabilities ?? discoverHarnesses(config.harnesses, config.executables, config.root),
103
103
  ]);
104
104
  signal.throwIfAborted();
105
- const url = new URL(resolveWebsocketUrl({ url: process.env.COHUB_WS_URL }));
106
- url.pathname = "/runtime/relay";
105
+ const url = new URL(resolveWebsocketUrl());
106
+ url.pathname = url.pathname.replace(/\/ws$/, "/runtime/relay");
107
107
  const relay = new URL(url);
108
- relay.pathname = "/sandbox/relay";
108
+ relay.pathname = relay.pathname.replace(/\/runtime\/relay$/, "/sandbox/relay");
109
109
  const runBridge = async () => {
110
110
  let attempts = 0;
111
111
  while (!signal.aborted) {
package/dist/space.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare class SpaceTargetError extends Error {
11
11
  */
12
12
  export declare function identityKeyFrom(input: {
13
13
  env: string;
14
+ baseUrl?: string;
14
15
  executionToken?: string | null;
15
16
  idToken?: string | null;
16
17
  accessToken?: string | null;
package/dist/space.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
- import { HOME_SPACE_SLUG, parseSpaceRef, resolveCohubEnvironment } from "@neta-art/cohub";
4
+ import { COHUB_ENVIRONMENTS, HOME_SPACE_SLUG, normalizeBaseUrl, parseSpaceRef, resolveApiBaseUrl, resolveCohubEnvironment } from "@neta-art/cohub";
5
5
  import { readAuthSession } from "./auth.js";
6
6
  import { createClient } from "./client.js";
7
7
  import { getRuntimeSpaceBinding } from "./runtime/space-binding.js";
@@ -40,17 +40,21 @@ function jwtClaim(token, key) {
40
40
  * Execution grants identify the actor as `actorUserId`, not `sub`.
41
41
  */
42
42
  export function identityKeyFrom(input) {
43
+ const baseUrl = input.baseUrl ? normalizeBaseUrl(input.baseUrl) : "";
44
+ const defaultBaseUrl = COHUB_ENVIRONMENTS[input.env === "dev" ? "dev" : "prod"].apiBaseUrl;
45
+ const namespace = baseUrl && baseUrl !== defaultBaseUrl ? `${input.env}:${baseUrl}` : input.env;
43
46
  if (input.executionToken) {
44
47
  const actor = jwtClaim(input.executionToken, "actorUserId") ?? jwtClaim(input.executionToken, "sub");
45
- return actor ? `${input.env}:${actor}` : null;
48
+ return actor ? `${namespace}:${actor}` : null;
46
49
  }
47
50
  const sub = jwtClaim(input.idToken, "sub") ?? jwtClaim(input.accessToken, "sub");
48
- return sub ? `${input.env}:${sub}` : null;
51
+ return sub ? `${namespace}:${sub}` : null;
49
52
  }
50
53
  export function currentIdentityKey() {
51
54
  const session = readAuthSession();
52
55
  return identityKeyFrom({
53
56
  env: resolveCohubEnvironment(),
57
+ baseUrl: resolveApiBaseUrl(),
54
58
  executionToken: process.env.COHUB_EXECUTION_TOKEN?.trim(),
55
59
  idToken: session?.idToken,
56
60
  accessToken: session?.accessToken,
@@ -81,10 +85,10 @@ function writeHomeSpaceCache(key, spaceId) {
81
85
  // Cache is best-effort; never fail the command over it.
82
86
  }
83
87
  }
84
- let homeSpacePromise = null;
88
+ const homeSpaces = new Map();
85
89
  const spaceRefs = new Map();
86
90
  export function clearHomeSpaceCache() {
87
- homeSpacePromise = null;
91
+ homeSpaces.clear();
88
92
  spaceRefs.clear();
89
93
  try {
90
94
  rmSync(CACHE_PATH, { force: true });
@@ -107,17 +111,21 @@ export function explicitSpace(program) {
107
111
  }
108
112
  /** Cached per identity and memoized per process. */
109
113
  export function resolveHomeSpace() {
110
- homeSpacePromise ??= (async () => {
111
- const key = currentIdentityKey();
112
- const cached = key ? readHomeSpaceCache(CACHE_PATH, key) : null;
113
- if (cached)
114
- return cached;
115
- const space = await createClient().spaces.ensureHome();
116
- if (key)
117
- writeHomeSpaceCache(key, space.id);
118
- return space.id;
119
- })();
120
- return homeSpacePromise;
114
+ const key = currentIdentityKey();
115
+ const memoKey = key ?? resolveApiBaseUrl();
116
+ let resolved = homeSpaces.get(memoKey);
117
+ if (!resolved)
118
+ resolved = (async () => {
119
+ const cached = key ? readHomeSpaceCache(CACHE_PATH, key) : null;
120
+ if (cached)
121
+ return cached;
122
+ const space = await createClient().spaces.ensureHome();
123
+ if (key)
124
+ writeHomeSpaceCache(key, space.id);
125
+ return space.id;
126
+ })();
127
+ homeSpaces.set(memoKey, resolved);
128
+ return resolved;
121
129
  }
122
130
  async function lookupSpaceRef(value) {
123
131
  const ref = parseSpaceRef(value);
@@ -141,10 +149,10 @@ async function lookupSpaceRef(value) {
141
149
  }
142
150
  }
143
151
  export function resolveSpaceRef(value) {
144
- const key = value.trim();
152
+ const key = `${currentIdentityKey() ?? resolveApiBaseUrl()}:${value.trim()}`;
145
153
  let resolved = spaceRefs.get(key);
146
154
  if (!resolved) {
147
- resolved = lookupSpaceRef(key);
155
+ resolved = lookupSpaceRef(value.trim());
148
156
  spaceRefs.set(key, resolved);
149
157
  }
150
158
  return resolved;
package/dist/web.js CHANGED
@@ -1,6 +1,4 @@
1
- import { resolveCohubEnvironment } from "@neta-art/cohub";
1
+ import { resolveWebBaseUrl } from "@neta-art/cohub";
2
2
  export function webUrl(path) {
3
- const origin = process.env.COHUB_WEB_URL?.replace(/\/+$/, "") ??
4
- `https://${resolveCohubEnvironment() === "prod" ? "" : "dev."}cohub.live`;
5
- return `${origin}${path}`;
3
+ return `${resolveWebBaseUrl()}${path}`;
6
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "10.0.0",
3
+ "version": "10.1.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -18,7 +18,7 @@
18
18
  "NOTICE"
19
19
  ],
20
20
  "dependencies": {
21
- "@neta-art/cohub": "11.0.0",
21
+ "@neta-art/cohub": "11.1.0",
22
22
  "@neta-art/generation": "^0.1.33",
23
23
  "commander": "^15.0.0",
24
24
  "sharp": "^0.35.4",