@gaia-ai/gaia 0.2.0 → 0.3.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 { existsSync } from 'node:fs';
1
2
  import { dirname } from 'node:path';
2
3
  import { createInterface } from 'node:readline';
3
4
  import { Command } from 'commander';
@@ -416,8 +417,8 @@ Examples:
416
417
  conductor
417
418
  .command('init')
418
419
  .description('scaffold the committed .gaia/conductor.config.js for this repo plus the user-global conductor.config.machine.js context (identity + connection incl. secret)')
419
- .requiredOption('--base-url <url>', 'control-plane base URL (site.base_url)')
420
- .requiredOption('--project <name>', 'GAIA project name')
420
+ .option('--base-url <url>', 'control-plane base URL (site.base_url) — required only when onboarding this machine')
421
+ .option('--project <name>', 'GAIA project name — required only to scaffold the committed repo config; omit for machine-only onboarding')
421
422
  .option('--secret-env <VAR>', 'env var name to read the oauth client secret from (else TTY prompt)')
422
423
  .option('--client-id <id>', 'oauth consumer id', 'gaia-agent')
423
424
  .option('--machine-id <id>', 'machine host token for the context (defaults to hostname())')
@@ -425,40 +426,64 @@ Examples:
425
426
  .option('--machine-path <path>', 'user-global machine context path (defaults to ~/.config/conductor/conductor.config.machine.js)')
426
427
  .option('--config <path>', 'target committed config path', './.gaia/conductor.config.js')
427
428
  .option('--force', 'overwrite an existing committed config', false)
429
+ .option('--reonboard', 'force machine-context onboarding even if a context file exists', false)
428
430
  .action(async (opts) => {
429
- let userId = opts.userId;
430
- if (userId === undefined || userId.trim() === '') {
431
- if (process.stdin.isTTY) {
432
- userId = await promptUserId();
433
- }
434
- if (userId === undefined || userId.trim() === '') {
435
- throw new Error('--user-id is required (or run in a TTY to be prompted)');
436
- }
437
- }
438
- // The secret lives in the machine context; resolve it only when that
439
- // context does not already carry one (existing values are never
440
- // overwritten). Source: --secret-env value, else a TTY prompt.
431
+ // Two independent axes decide what init writes:
432
+ // - machine axis: an existing context means project-only; --reonboard
433
+ // (or an absent context) forces machine-context (re)scaffolding.
434
+ // - repo axis: --project scaffolds the committed repo config; omitting
435
+ // it means machine-only (no repo). The 4 quadrants:
436
+ // context absent + project both files
437
+ // context absent + no proj machine-only onboarding
438
+ // context present + project → project-only
439
+ // context present + no proj → no-op (machine already onboarded)
441
440
  const machinePath = opts.machinePath ?? machineContextPath();
442
441
  const existing = await readMachineContext(machinePath);
442
+ const contextPresent = existsSync(machinePath);
443
+ const onboarding = !contextPresent || opts.reonboard;
444
+ const project = opts.project ?? '';
445
+ const hasProject = project.trim() !== '';
446
+ // Context present and nothing repo-scoped to do → no-op with guidance.
447
+ if (!onboarding && !hasProject) {
448
+ console.log(`machine already onboarded (${machinePath}) — pass --project to set up a repo`);
449
+ return;
450
+ }
451
+ let userId = opts.userId ?? '';
443
452
  let secret = '';
444
- if (typeof existing.client_secret !== 'string' ||
445
- existing.client_secret.trim() === '') {
446
- secret =
447
- opts.secretEnv !== undefined
448
- ? (process.env[opts.secretEnv] ?? '')
449
- : '';
450
- if (secret.trim() === '') {
451
- if (process.stdin.isTTY) {
452
- secret = await promptSecret();
453
+ const baseUrl = opts.baseUrl ?? '';
454
+ if (onboarding) {
455
+ // Full onboarding: base-url + user-id + secret are required here.
456
+ if (baseUrl.trim() === '') {
457
+ throw new Error('--base-url is required when onboarding this machine (no machine context yet, or --reonboard)');
458
+ }
459
+ if (userId.trim() === '') {
460
+ if (process.stdin.isTTY)
461
+ userId = await promptUserId();
462
+ if (userId.trim() === '') {
463
+ throw new Error('--user-id is required (or run in a TTY to be prompted)');
453
464
  }
465
+ }
466
+ // The secret lives in the machine context; resolve it only when that
467
+ // context does not already carry one (existing values are never
468
+ // overwritten). Source: --secret-env value, else a TTY prompt.
469
+ if (typeof existing.client_secret !== 'string' ||
470
+ existing.client_secret.trim() === '') {
471
+ secret =
472
+ opts.secretEnv !== undefined
473
+ ? (process.env[opts.secretEnv] ?? '')
474
+ : '';
454
475
  if (secret.trim() === '') {
455
- throw new Error('OAuth client secret required: pass --secret-env <VAR> (exported) or run in a TTY to be prompted');
476
+ if (process.stdin.isTTY)
477
+ secret = await promptSecret();
478
+ if (secret.trim() === '') {
479
+ throw new Error('OAuth client secret required: pass --secret-env <VAR> (exported) or run in a TTY to be prompted');
480
+ }
456
481
  }
457
482
  }
458
483
  }
459
484
  const inputs = {
460
- baseUrl: opts.baseUrl,
461
- project: opts.project,
485
+ baseUrl,
486
+ project,
462
487
  clientId: opts.clientId,
463
488
  secret,
464
489
  userId,
@@ -470,16 +495,39 @@ Examples:
470
495
  configPath: opts.config,
471
496
  force: opts.force,
472
497
  machinePath,
498
+ skipMachine: !onboarding,
499
+ skipCommitted: !hasProject,
473
500
  });
474
- console.log(res.wroteCommitted
475
- ? `wrote ${res.committedPath}`
476
- : `kept ${res.committedPath} (exists pass --force to replace)`);
501
+ // Mode banner.
502
+ if (!onboarding) {
503
+ console.log(`machine context found (${machinePath}) setting up project only`);
504
+ if (typeof existing.base_url !== 'string' ||
505
+ existing.base_url.trim() === '') {
506
+ console.log(`warning: ${machinePath} exists but looks incomplete (no base_url) — ` +
507
+ `run with --reonboard to fill the machine context`);
508
+ }
509
+ }
510
+ else if (!hasProject) {
511
+ console.log(`onboarding this machine (no project) → run again with --project inside a repo to set it up`);
512
+ }
513
+ else {
514
+ console.log(opts.reonboard
515
+ ? `re-onboarding this machine (--reonboard)`
516
+ : `no machine context → onboarding this machine`);
517
+ }
518
+ if (hasProject) {
519
+ console.log(res.wroteCommitted
520
+ ? `wrote ${res.committedPath}`
521
+ : `kept ${res.committedPath} (exists — pass --force to replace)`);
522
+ }
477
523
  const m = res.machine;
478
- console.log(m.created
479
- ? `wrote ${m.path} (user-global machine context)`
480
- : m.filledKeys.length > 0
481
- ? `updated ${m.path} (filled: ${m.filledKeys.join(', ')})`
482
- : `kept ${m.path} (already complete)`);
524
+ if (onboarding) {
525
+ console.log(m.created
526
+ ? `wrote ${m.path} (user-global machine context)`
527
+ : m.filledKeys.length > 0
528
+ ? `updated ${m.path} (filled: ${m.filledKeys.join(', ')})`
529
+ : `kept ${m.path} (already complete)`);
530
+ }
483
531
  console.log(`\nNext steps:\n` +
484
532
  ` gaia dropsh auth login --provider session\n` +
485
533
  ` gaia dropsh auth login --provider pm\n` +
@@ -11,6 +11,10 @@ export interface ScaffoldOptions {
11
11
  configPath: string;
12
12
  force: boolean;
13
13
  machinePath?: string;
14
+ /** When true, scaffold the committed config only — never read/write the machine context. */
15
+ skipMachine?: boolean;
16
+ /** When true, scaffold the machine context only — never write the committed config. */
17
+ skipCommitted?: boolean;
14
18
  }
15
19
  export interface ScaffoldResult {
16
20
  committedPath: string;
@@ -70,5 +74,9 @@ export declare function scaffoldMachineContext(opts: MachineContextOptions): Pro
70
74
  * (created only if missing — never overwritten unless `force`) and the
71
75
  * user-global machine context (create-if-missing / fill-only-missing). The
72
76
  * optional per-project conductor.config.local.js is NOT generated.
77
+ *
78
+ * `skipMachine` writes the committed config only (project-only setup); its
79
+ * mirror `skipCommitted` writes the machine context only (machine-only
80
+ * onboarding, no repo). Setting both is a no-op.
73
81
  */
74
82
  export declare function scaffold(inputs: InitInputs, opts: ScaffoldOptions): Promise<ScaffoldResult>;
@@ -199,22 +199,33 @@ export async function scaffoldMachineContext(opts) {
199
199
  * (created only if missing — never overwritten unless `force`) and the
200
200
  * user-global machine context (create-if-missing / fill-only-missing). The
201
201
  * optional per-project conductor.config.local.js is NOT generated.
202
+ *
203
+ * `skipMachine` writes the committed config only (project-only setup); its
204
+ * mirror `skipCommitted` writes the machine context only (machine-only
205
+ * onboarding, no repo). Setting both is a no-op.
202
206
  */
203
207
  export async function scaffold(inputs, opts) {
204
208
  const committedPath = opts.configPath;
205
- mkdirSync(dirname(committedPath), { recursive: true });
206
209
  let wroteCommitted = false;
207
- if (!existsSync(committedPath) || opts.force) {
208
- writeFileSync(committedPath, renderCommittedConfig(inputs), 'utf8');
209
- wroteCommitted = true;
210
+ if (!opts.skipCommitted) {
211
+ mkdirSync(dirname(committedPath), { recursive: true });
212
+ if (!existsSync(committedPath) || opts.force) {
213
+ writeFileSync(committedPath, renderCommittedConfig(inputs), 'utf8');
214
+ wroteCommitted = true;
215
+ }
210
216
  }
211
- const machine = await scaffoldMachineContext({
212
- path: opts.machinePath ?? machineContextPath(),
213
- userId: inputs.userId ?? '',
214
- baseUrl: inputs.baseUrl,
215
- clientId: inputs.clientId,
216
- secret: inputs.secret,
217
- ...(inputs.machineId !== undefined ? { machineId: inputs.machineId } : {}),
218
- });
217
+ const machinePath = opts.machinePath ?? machineContextPath();
218
+ const machine = opts.skipMachine
219
+ ? { path: machinePath, created: false, filledKeys: [] }
220
+ : await scaffoldMachineContext({
221
+ path: machinePath,
222
+ userId: inputs.userId ?? '',
223
+ baseUrl: inputs.baseUrl,
224
+ clientId: inputs.clientId,
225
+ secret: inputs.secret,
226
+ ...(inputs.machineId !== undefined
227
+ ? { machineId: inputs.machineId }
228
+ : {}),
229
+ });
219
230
  return { committedPath, wroteCommitted, machine };
220
231
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/gaia",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "GAIA Network conductor node: registers, claims tickets via JSON:API, runs agents through spawn plugins.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,10 +41,10 @@
41
41
  "dependencies": {
42
42
  "@dropsh/plugin-oauth2": "^0.4.1",
43
43
  "@dropsh/plugin-schemata": "^0.4.1",
44
- "@gaia-ai/plugin-claude": "^0.2.0",
45
- "@gaia-ai/plugin-codex": "^0.2.0",
46
- "@gaia-ai/plugin-herdr": "^0.2.0",
47
- "@gaia-ai/plugin-herdr-workspace": "^0.2.0",
44
+ "@gaia-ai/plugin-claude": "^0.3.0",
45
+ "@gaia-ai/plugin-codex": "^0.3.0",
46
+ "@gaia-ai/plugin-herdr": "^0.3.0",
47
+ "@gaia-ai/plugin-herdr-workspace": "^0.3.0",
48
48
  "commander": "^12.1.0",
49
49
  "dropsh": "^0.4.1",
50
50
  "pino": "^9.6.0",