@gaia-ai/ui 0.6.4 → 0.7.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.
@@ -3,6 +3,6 @@ export const commands = (acc) => [
3
3
  {
4
4
  name: 'ui',
5
5
  plugin: '@gaia-ai/ui',
6
- describe: 'interactive terminal UI: project dashboard, ticket list, ticket detail',
6
+ describe: 'interactive terminal UI: dashboard, projects, tickets',
7
7
  },
8
8
  ];
package/dist/src/route.js CHANGED
@@ -253,7 +253,7 @@ export async function resolveHere(ctx) {
253
253
  const got = remote === '' ? '' : `, got '${remote}'`;
254
254
  throw new Error(`gaia ui --here: could not derive a project from '${ctx.cwd}' — ` +
255
255
  `no usable git remote (looked at 'origin'${got}).\n` +
256
- " Run 'gaia ui' for the dashboard, or 'gaia ui project:<name>' to open one directly.");
256
+ " Run 'gaia ui' and press Tab for the project list, or 'gaia ui project:<name>' to open one directly.");
257
257
  }
258
258
  return {
259
259
  kind: 'project',
@@ -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) {
@@ -287,11 +343,11 @@ export async function cmdUi(deps = {}) {
287
343
  const uiCommandPlugin = {
288
344
  kind: 'command',
289
345
  name: 'ui',
290
- describe: 'interactive terminal UI: project dashboard, ticket list, ticket detail',
346
+ describe: 'interactive terminal UI: dashboard, projects, tickets',
291
347
  register(program, host) {
292
348
  program
293
349
  .command('ui')
294
- .description('interactive terminal UI: project dashboard, ticket list, ticket detail')
350
+ .description('interactive terminal UI: dashboard, projects, tickets')
295
351
  .argument('[target]', 'deep-link target: GAIA-221 · ticket:<id|uuid> · project:<name|uuid> · run:<uuid>')
296
352
  .option('--ticket <id>', 'open a ticket by identifier or uuid')
297
353
  .option('--project <id>', 'open a project by name or uuid')
@@ -302,7 +358,7 @@ const uiCommandPlugin = {
302
358
  .option('--print-config', 'print the resolved connection config and exit, without launching the UI')
303
359
  .addHelpText('after', `
304
360
  Targets (at most ONE of the positional, --ticket, --project, --run, --here):
305
- gaia ui the project dashboard
361
+ gaia ui the dashboard
306
362
  gaia ui GAIA-221 a ticket by identifier (case-insensitive)
307
363
  gaia ui ticket:GAIA-221 the same, said explicitly
308
364
  gaia ui ticket:<uuid> a ticket by uuid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/ui",
3
- "version": "0.6.4",
3
+ "version": "0.7.0",
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.7.0",
30
+ "@gaia-ai/addon-herdr": "^0.7.0",
31
31
  "commander": "^12.1.0",
32
32
  "dropsh": "^0.5.8"
33
33
  }