@gaia-ai/gaia 0.14.1 → 0.15.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.
Files changed (2) hide show
  1. package/dist/src/config.js +83 -16
  2. package/package.json +24 -24
@@ -60,18 +60,25 @@ export function renderConfigReport(report, dryRun = false) {
60
60
  lines.push('Connection readiness is unverified; successful migration does not verify server access.');
61
61
  return `${lines.join('\n')}\n`;
62
62
  }
63
- export function registerConfig(program, deps = {}) {
64
- const group = program
65
- .command('config')
66
- .description('initialize, migrate and validate scoped GAIA configuration');
67
- for (const operation of ['init', 'upgrade', 'check']) {
68
- const command = group
63
+ /**
64
+ * Attaches one `config <operation>` command to `parent` (either the `config`
65
+ * group or, for the `gaia init` shortcut, `program` itself). `hiddenScope`
66
+ * exists only for that shortcut: `gaia init` resolves to machine + connection
67
+ * without ever showing `--scope` or its value anywhere (not in `--help`, not
68
+ * in a default), since a human running `gaia init` never chooses or sees a
69
+ * scope at all — while `gaia config init` keeps asking for one (or requiring
70
+ * `--scope` explicitly) exactly as before.
71
+ */
72
+ function buildConfigCommand(parent, operation, deps, hiddenScope, description) {
73
+ {
74
+ const command = parent
69
75
  .command(operation)
70
- .description(operation === 'init'
71
- ? 'initialize or repair selected configuration and verify readiness'
72
- : operation === 'upgrade'
73
- ? 'migrate selected configuration without changing existing values'
74
- : 'validate configuration offline; optionally verify online access')
76
+ .description(description ??
77
+ (operation === 'init'
78
+ ? 'initialize or repair selected configuration and verify readiness'
79
+ : operation === 'upgrade'
80
+ ? 'migrate selected configuration without changing existing values'
81
+ : 'validate configuration offline; optionally verify online access'))
75
82
  .option('--scope <scope>', 'machine | connection | project | all; init requires a scope')
76
83
  .addOption(new Option('--format <format>', 'report format; JSON disables prompts')
77
84
  .choices(['text', 'json'])
@@ -94,8 +101,11 @@ export function registerConfig(program, deps = {}) {
94
101
  .option('--client-id <id>', 'OAuth client identifier')
95
102
  .option('--workspace-id <uuid>', 'explicit workspace UUID')
96
103
  .option('--workspace <name>', 'committed project workspace name')
97
- .option('--secret-env <name>', 'environment variable containing the OAuth secret');
104
+ .option('--secret-env <name>', 'environment variable containing the OAuth secret')
105
+ .addOption(new Option('--auth-type <type>', 'sign in with device code (default, no secret) or a client secret (internal/CI)').choices(['client_credentials', 'device']));
98
106
  command.action(async (options) => {
107
+ if (hiddenScope !== undefined && options.scope === undefined)
108
+ options.scope = hiddenScope;
99
109
  const { applyConfigPlan, planConfig } = await import('@gaia-ai/conductor/config');
100
110
  if (options.projectConnection !== undefined)
101
111
  throw new Error('--project-connection is retired; use --connection <id> and .gaia/connection.config.local.js.');
@@ -134,11 +144,19 @@ export function registerConfig(program, deps = {}) {
134
144
  const secret = options.secretEnv ? env[options.secretEnv] : undefined;
135
145
  if (secret !== undefined)
136
146
  inputs.secret = secret;
147
+ if (options.authType)
148
+ inputs.authType =
149
+ options.authType === 'client_credentials'
150
+ ? 'oauth2_client_credentials'
151
+ : 'oauth2_device_code';
137
152
  const planning = {
138
153
  operation,
139
154
  cwd,
140
155
  home: deps.home ?? homedir(),
141
156
  inputs,
157
+ // `gaia init` (hiddenScope !== undefined) never pulls the project
158
+ // layer in, even inside a project checkout (GAIA-496 finding 1).
159
+ ...(hiddenScope !== undefined ? { excludeProjectLayer: true } : {}),
142
160
  };
143
161
  for (const key of ['scope', 'path', 'project', 'machinePath']) {
144
162
  const value = options[key];
@@ -180,10 +198,25 @@ export function registerConfig(program, deps = {}) {
180
198
  plan.report.files.some((file) => file.kind === 'connection' &&
181
199
  file.selected &&
182
200
  file.state === 'absent');
201
+ // No sign-in question: device code is the only way `gaia config init`
202
+ // offers (GAIA-496). `--auth-type client_credentials` remains for the
203
+ // internal/CI path but is never interviewed for or invented outside
204
+ // it — this default applies whether or not a terminal is attached, so
205
+ // a non-interactive `gaia init --yes` gets the same secret-free
206
+ // connection an interactive run does. A caller who already supplied a
207
+ // secret (`--secret-env`) evidently wants client_credentials and is
208
+ // left alone rather than silently switched to device code.
209
+ if (freshConnection &&
210
+ inputs.authType === undefined &&
211
+ inputs.secret === undefined) {
212
+ inputs.authType = 'oauth2_device_code';
213
+ plan = planConfig(planning);
214
+ }
183
215
  if (interactive &&
184
216
  operation !== 'check' &&
185
217
  !cancelled &&
186
218
  !plan.report.findings.some((f) => f.stage === 'scope')) {
219
+ const deviceCode = inputs.authType === 'oauth2_device_code';
187
220
  const fields = [
188
221
  ['machineId', 'machine_id', 'Machine identity'],
189
222
  ['userId', 'user_id', 'Local operator identifier'],
@@ -192,10 +225,12 @@ export function registerConfig(program, deps = {}) {
192
225
  ['secret', 'client_secret', 'OAuth client secret'],
193
226
  ['workspaceId', 'workspace_id', 'Workspace UUID'],
194
227
  ['workspace', 'gaia.workspace', 'Workspace name'],
195
- ];
228
+ ].filter(([input]) => !(deviceCode && input === 'secret'));
196
229
  for (const [input, key, label] of fields) {
197
230
  const requiredForFreshConnection = freshConnection &&
198
- ['baseUrl', 'clientId', 'secret'].includes(input);
231
+ (deviceCode
232
+ ? ['baseUrl', 'clientId']
233
+ : ['baseUrl', 'clientId', 'secret']).includes(input);
199
234
  if (inputs[input] !== undefined ||
200
235
  (!requiredForFreshConnection &&
201
236
  !plan.report.findings.some((f) => plan.report.resolved_scopes.includes(f.owner) &&
@@ -203,7 +238,17 @@ export function registerConfig(program, deps = {}) {
203
238
  (f.key === key || f.key?.endsWith(`.${key}`)))))
204
239
  continue;
205
240
  try {
206
- const suggested = input === 'machineId' ? hostname().split('.')[0] : undefined;
241
+ const suggested = input === 'machineId'
242
+ ? hostname().split('.')[0]
243
+ : input === 'baseUrl'
244
+ ? // Suggests keytec's own fleet instance so pressing Enter
245
+ // reaches a working setup with no site to look up first;
246
+ // it is a default, not a requirement — any base URL
247
+ // typed over it is used verbatim (GAIA-496).
248
+ 'https://gaia.keytec.de'
249
+ : deviceCode && input === 'clientId'
250
+ ? 'gaia-cli'
251
+ : undefined;
207
252
  const answer = (await prompt(`${label}${suggested ? ` [${suggested}]` : ''}`, input === 'secret')) || suggested;
208
253
  if (answer !== undefined)
209
254
  inputs[input] = answer;
@@ -257,7 +302,13 @@ export function registerConfig(program, deps = {}) {
257
302
  resolveBases: [import.meta.url, pathToFileURL(`${cwd}/`).href],
258
303
  },
259
304
  ...(deps.stateDir ? { stateDir: deps.stateDir } : {}),
260
- ...(freshConnection ? { ephemeralClientCredentials: true } : {}),
305
+ // A fresh device-code connection cannot be verified without a
306
+ // human confirming the code, so it is left to the ordinary
307
+ // session-based check (which fails fast rather than polling
308
+ // forever for a confirmation that never arrives).
309
+ ...(freshConnection && inputs.authType !== 'oauth2_device_code'
310
+ ? { ephemeralClientCredentials: true }
311
+ : {}),
261
312
  });
262
313
  }
263
314
  else if (!options.dryRun &&
@@ -304,3 +355,19 @@ export function registerConfig(program, deps = {}) {
304
355
  });
305
356
  }
306
357
  }
358
+ export function registerConfig(program, deps = {}) {
359
+ const group = program
360
+ .command('config')
361
+ .description('initialize, migrate and validate scoped GAIA configuration');
362
+ for (const operation of ['init', 'upgrade', 'check'])
363
+ buildConfigCommand(group, operation, deps);
364
+ // `gaia init` is the user-facing shortcut that sets up machine identity AND
365
+ // the sign-in connection — the two things a human actually needs before
366
+ // running anything else. There is no `--scope` for it at all: it silently
367
+ // resolves both layers (via the same `all` inventory scope `gaia config
368
+ // init --scope all` uses outside a project — config-inventory.ts — but
369
+ // that value is never shown, defaulted, or documented here). The project
370
+ // scope stays reachable only through the full `gaia config init --scope
371
+ // project` command.
372
+ buildConfigCommand(program, 'init', deps, 'all', 'sign in: sets up machine identity and the connection (not project)');
373
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/gaia",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "GAIA meta package: the `gaia` plugin-host CLI + all runtime plugins. Global install target.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,29 +38,29 @@
38
38
  "jsonapi"
39
39
  ],
40
40
  "dependencies": {
41
- "@gaia-ai/addon-agents": "^0.14.1",
42
- "@gaia-ai/addon-auth-basic": "^0.14.1",
43
- "@gaia-ai/addon-claude": "^0.14.1",
44
- "@gaia-ai/addon-codex": "^0.14.1",
45
- "@gaia-ai/addon-deployment": "^0.14.1",
46
- "@gaia-ai/addon-dropsh": "^0.14.1",
47
- "@gaia-ai/addon-essentials": "^0.14.1",
48
- "@gaia-ai/addon-gaia-ui": "^0.14.1",
49
- "@gaia-ai/addon-gaia-ui-artifacts": "^0.14.1",
50
- "@gaia-ai/addon-grok": "^0.14.1",
51
- "@gaia-ai/addon-herdr": "^0.14.1",
52
- "@gaia-ai/addon-info": "^0.14.1",
53
- "@gaia-ai/addon-kimi": "^0.14.1",
54
- "@gaia-ai/addon-opencode": "^0.14.1",
55
- "@gaia-ai/addon-pi": "^0.14.1",
56
- "@gaia-ai/addon-ports": "^0.14.1",
57
- "@gaia-ai/addon-ports-ui": "^0.14.1",
58
- "@gaia-ai/addon-remote-drupal": "^0.14.1",
59
- "@gaia-ai/addon-workspace": "^0.14.1",
60
- "@gaia-ai/addon-workspace-git": "^0.14.1",
61
- "@gaia-ai/conductor": "^0.14.1",
62
- "@gaia-ai/core": "^0.14.1",
63
- "@gaia-ai/ui": "^0.14.1",
41
+ "@gaia-ai/addon-agents": "^0.15.0",
42
+ "@gaia-ai/addon-auth-basic": "^0.15.0",
43
+ "@gaia-ai/addon-claude": "^0.15.0",
44
+ "@gaia-ai/addon-codex": "^0.15.0",
45
+ "@gaia-ai/addon-deployment": "^0.15.0",
46
+ "@gaia-ai/addon-dropsh": "^0.15.0",
47
+ "@gaia-ai/addon-essentials": "^0.15.0",
48
+ "@gaia-ai/addon-gaia-ui": "^0.15.0",
49
+ "@gaia-ai/addon-gaia-ui-artifacts": "^0.15.0",
50
+ "@gaia-ai/addon-grok": "^0.15.0",
51
+ "@gaia-ai/addon-herdr": "^0.15.0",
52
+ "@gaia-ai/addon-info": "^0.15.0",
53
+ "@gaia-ai/addon-kimi": "^0.15.0",
54
+ "@gaia-ai/addon-opencode": "^0.15.0",
55
+ "@gaia-ai/addon-pi": "^0.15.0",
56
+ "@gaia-ai/addon-ports": "^0.15.0",
57
+ "@gaia-ai/addon-ports-ui": "^0.15.0",
58
+ "@gaia-ai/addon-remote-drupal": "^0.15.0",
59
+ "@gaia-ai/addon-workspace": "^0.15.0",
60
+ "@gaia-ai/addon-workspace-git": "^0.15.0",
61
+ "@gaia-ai/conductor": "^0.15.0",
62
+ "@gaia-ai/core": "^0.15.0",
63
+ "@gaia-ai/ui": "^0.15.0",
64
64
  "commander": "^12.1.0"
65
65
  }
66
66
  }