@gaia-ai/ui 0.6.4 → 0.6.5

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.
@@ -10,7 +10,15 @@ export interface HomeConnection {
10
10
  jsonapiPrefix?: string | undefined;
11
11
  authPlugins: unknown[];
12
12
  configPath?: string | undefined;
13
+ /** The dropsh auth profile the cockpit READS as (`gaia_run` is session-only). */
13
14
  authProfile?: string | undefined;
15
+ /**
16
+ * GAIA-233: the dropsh auth profile the cockpit WRITES as, named beside the
17
+ * read profile so both scopes are declared in one place. `session` holds no
18
+ * `update gaia_ticket`, and `conductor_id` is additionally field-gated on
19
+ * `assign gaia conductor`.
20
+ */
21
+ writeProfile?: string | undefined;
14
22
  }
15
23
  /**
16
24
  * Resolve the default project (opaque group) from the conductor registry: the
package/dist/src/ui.d.ts CHANGED
@@ -47,6 +47,16 @@ export interface RunGaiaUiOptions {
47
47
  /** GAIA-223: the deep-link target, still UNRESOLVED (the entry has no JSON:API
48
48
  * client — Finding 1). Absent ⇒ the renderer boots its dashboard (AC-4). */
49
49
  initialRoute?: UiInitialRoute | undefined;
50
+ /** GAIA-233: the write-scoped JSON:API client. Absent ⇒ the renderer writes
51
+ * through the read client and the server decides (spec Decision 1). */
52
+ writeClient?: unknown;
53
+ }
54
+ /** What a write-client builder needs — everything a connection already carries. */
55
+ export interface BuildWriteClientOptions {
56
+ baseUrl: string;
57
+ jsonapiPrefix: string;
58
+ plugins: GaiaConnectionConfig['plugins'];
59
+ profile: string;
50
60
  }
51
61
  /** Test/composition seams for `gaia ui`. */
52
62
  export interface UiDeps {
@@ -91,6 +101,9 @@ export interface UiDeps {
91
101
  readRemoteUrl?: (cwd: string) => Promise<string>;
92
102
  /** Where user-facing grammar errors go; default `process.stderr`. */
93
103
  stderr?: (line: string) => void;
104
+ /** GAIA-233: builds the write-scoped client; default: dropsh under `pm`.
105
+ * Injected so the write seam is testable without a live token endpoint. */
106
+ buildWriteClient?: (o: BuildWriteClientOptions) => Promise<unknown>;
94
107
  }
95
108
  /**
96
109
  * `gaia ui` — launch the interactive terminal UI. Resolves the connection
package/dist/src/ui.js CHANGED
@@ -3,9 +3,38 @@ import { homedir } from 'node:os';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { herdrAgentHost } from '@gaia-ai/addon-herdr';
5
5
  import { CommandRunner, createLogger, exec, listRegisteredConductors, loadGaiaConfig, machineContextPath, readMachineContext, setDefaultCommandRunner, } from '@gaia-ai/core';
6
- import { buildProgram as buildDropshProgram } from 'dropsh';
6
+ import { buildProgram as buildDropshProgram, resolveAuth } from 'dropsh';
7
+ import { createHttpClient, createJsonApiClient } from 'dropsh/plugin';
7
8
  import { parseTarget, resolveHere, UI_VIEW_MODES, } from './route.js';
8
9
  import { resolveAgentCommand, resolveProjectRooting, } from './ui-home.js';
10
+ /** The auth profile `gaia ui` writes as. Reads stay on `session`. */
11
+ const WRITE_PROFILE = 'pm';
12
+ /**
13
+ * Build a second JSON:API client under a different auth profile, from the
14
+ * plugins core already constructed. `@dropsh/plugin-oauth2@0.5.7` derives its
15
+ * plugin id from the profile id, so `session` and `pm` are distinct plugins and
16
+ * BOTH resolve in one process — the old profile-dedupe that collapsed them is
17
+ * fixed (measured: `providers: … session, pm`).
18
+ *
19
+ * Throws a catchable error when the connection declares no such profile; the
20
+ * caller degrades explicitly rather than aborting the cockpit.
21
+ */
22
+ async function buildWriteClientDefault(o) {
23
+ const http = createHttpClient();
24
+ const auth = await resolveAuth({
25
+ baseUrl: o.baseUrl,
26
+ plugins: o.plugins,
27
+ http,
28
+ now: () => Date.now(),
29
+ profile: o.profile,
30
+ });
31
+ return createJsonApiClient({
32
+ baseUrl: o.baseUrl,
33
+ prefix: o.jsonapiPrefix,
34
+ http,
35
+ auth,
36
+ });
37
+ }
9
38
  /**
10
39
  * The current branch of `cwd`. Git resolves this PER WORKTREE, so
11
40
  * worktree-awareness for `--here` costs nothing (Decision 3). Never throws:
@@ -186,7 +215,33 @@ export async function cmdUi(deps = {}) {
186
215
  authPlugins: resolved.plugins,
187
216
  configPath: resolved.config_path,
188
217
  authProfile: 'session',
218
+ writeProfile: WRITE_PROFILE,
189
219
  };
220
+ // GAIA-233: the write client, built here because this package already holds
221
+ // the constructed plugins and imports dropsh — the renderer stays
222
+ // dropsh-decoupled and receives a finished object.
223
+ //
224
+ // A connection declaring only `session` is a supported configuration, not an
225
+ // error: the cockpit still reads everything. So the failure is EXPLICIT
226
+ // rather than fatal — no write client is injected, `writeProfile` names the
227
+ // profile the write will really use, and the popup says so. The server's own
228
+ // 403 then surfaces verbatim; there is one failure path for "wrong scope" and
229
+ // "no scope" alike.
230
+ let writeClient;
231
+ try {
232
+ writeClient = await (deps.buildWriteClient ?? buildWriteClientDefault)({
233
+ baseUrl: resolved.site.base_url,
234
+ jsonapiPrefix: resolved.site.jsonapi_prefix ?? '/jsonapi',
235
+ plugins: resolved.plugins,
236
+ profile: WRITE_PROFILE,
237
+ });
238
+ }
239
+ catch (err) {
240
+ connection.writeProfile = connection.authProfile;
241
+ logger.warn({ profile: WRITE_PROFILE }, `gaia ui: no '${WRITE_PROFILE}'-scoped write client ` +
242
+ `(${err instanceof Error ? err.message : String(err)}); ` +
243
+ `edits will be attempted as '${connection.authProfile}'.`);
244
+ }
190
245
  const rooting = resolveProjectRooting(cwd, entries, homedir());
191
246
  const ownConductorMachineId = rooting.ownConductorMachineId;
192
247
  const project = rooting.project;
@@ -277,6 +332,7 @@ export async function cmdUi(deps = {}) {
277
332
  }),
278
333
  ...(renderer ? { renderer } : {}),
279
334
  ...(initialRoute ? { initialRoute } : {}),
335
+ ...(writeClient !== undefined ? { writeClient } : {}),
280
336
  });
281
337
  }
282
338
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/ui",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "GAIA project-first cockpit: the `gaia ui` command plugin (renderer resolved dynamically).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,8 +26,8 @@
26
26
  "directory": "gaia-cli/ui"
27
27
  },
28
28
  "dependencies": {
29
- "@gaia-ai/core": "^0.6.4",
30
- "@gaia-ai/addon-herdr": "^0.6.4",
29
+ "@gaia-ai/core": "^0.6.5",
30
+ "@gaia-ai/addon-herdr": "^0.6.5",
31
31
  "commander": "^12.1.0",
32
32
  "dropsh": "^0.5.8"
33
33
  }