@gaia-ai/conductor 0.5.4 → 0.6.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.
- package/dist/src/cli/conductor-registry.d.ts +7 -0
- package/dist/src/cli/conductor-registry.js +6 -0
- package/dist/src/cli/config-schema.d.ts +54 -0
- package/dist/src/cli/config-schema.js +91 -0
- package/dist/src/cli/deployment.d.ts +34 -0
- package/dist/src/cli/deployment.js +63 -0
- package/dist/src/cli/init.d.ts +31 -33
- package/dist/src/cli/init.js +96 -80
- package/dist/src/cli/upgrade.d.ts +38 -0
- package/dist/src/cli/upgrade.js +143 -0
- package/dist/src/cli/version-check.js +5 -1
- package/dist/src/commands/conductor.d.ts +33 -0
- package/dist/src/{cli/gaia.js → commands/conductor.js} +115 -210
- package/dist/src/config.d.ts +50 -23
- package/dist/src/config.js +127 -153
- package/dist/src/core/conductor.js +14 -11
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.js +11 -2
- package/dist/src/preset.d.ts +2 -0
- package/dist/src/preset.js +8 -0
- package/package.json +7 -5
- package/dist/src/cli/gaia.d.ts +0 -23
- package/dist/src/cli/local-registry.d.ts +0 -14
- package/dist/src/cli/local-registry.js +0 -56
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { applyMigrations, CONNECTION_MIGRATIONS, GAIA_CONFIG_SCHEMA_VERSION, readConfigSchemaVersion, stampVersion, stripVersionHeader, } from './config-schema.js';
|
|
6
|
+
import { renderGaiaConfig } from './init.js';
|
|
7
|
+
const GAIA_DIR = '.gaia';
|
|
8
|
+
const DEFAULT_ENGINE = 'conductor.config.js';
|
|
9
|
+
const VARIANT_SUFFIX = '.conductor.config.js';
|
|
10
|
+
const CONNECTION = 'gaia.config.js';
|
|
11
|
+
/** Walk from cwd root-ward to the nearest `.gaia/` dir. */
|
|
12
|
+
function findGaiaDir(cwd) {
|
|
13
|
+
let dir = resolve(cwd);
|
|
14
|
+
for (;;) {
|
|
15
|
+
const g = join(dir, GAIA_DIR);
|
|
16
|
+
if (existsSync(g))
|
|
17
|
+
return g;
|
|
18
|
+
const parent = dirname(dir);
|
|
19
|
+
if (parent === dir)
|
|
20
|
+
return undefined;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function hasEngineConfig(gaiaDir) {
|
|
25
|
+
return readdirSync(gaiaDir).some((f) => f === DEFAULT_ENGINE || f.endsWith(VARIANT_SUFFIX));
|
|
26
|
+
}
|
|
27
|
+
function legacyMachinePath(home) {
|
|
28
|
+
return join(home, '.config', 'conductor', 'conductor.config.machine.js');
|
|
29
|
+
}
|
|
30
|
+
function canonicalMachinePath(home) {
|
|
31
|
+
return join(home, GAIA_DIR, 'machine.config.js');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Route a single connection-config `path` against the schema version (GAIA-216).
|
|
35
|
+
* `label` names the file for the report; `current`/`chain` are injectable for
|
|
36
|
+
* tests (default to the shipped registry). Never executes the config module —
|
|
37
|
+
* the decision is a regex read of the header marker.
|
|
38
|
+
*
|
|
39
|
+
* | existing marker | action |
|
|
40
|
+
* | absent (no file) | seed: render current template, stamp CURRENT |
|
|
41
|
+
* | `== CURRENT` | kept (byte-identical) |
|
|
42
|
+
* | `1..CURRENT-1` (stale) | back up `.v<old>.bak`, run chain → stamp CURRENT |
|
|
43
|
+
* | `0` (unversioned) | kept (do not clobber hand edits) |
|
|
44
|
+
* | `> CURRENT` (newer) | kept (refuse to downgrade) |
|
|
45
|
+
*/
|
|
46
|
+
export function runConnectionUpgrade(path, opts = {}) {
|
|
47
|
+
const dry = opts.dryRun ?? false;
|
|
48
|
+
const label = opts.label ?? 'connection config';
|
|
49
|
+
const current = opts.current ?? GAIA_CONFIG_SCHEMA_VERSION;
|
|
50
|
+
const chain = opts.chain ?? CONNECTION_MIGRATIONS;
|
|
51
|
+
if (!existsSync(path)) {
|
|
52
|
+
if (!dry) {
|
|
53
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
54
|
+
writeFileSync(path, renderGaiaConfig(), 'utf8');
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
action: `${dry ? 'would write' : 'wrote'} ${path} (${label}, schema v${current})`,
|
|
58
|
+
changed: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const version = readConfigSchemaVersion(path);
|
|
62
|
+
if (version === current) {
|
|
63
|
+
return {
|
|
64
|
+
action: `kept ${path} (${label}, schema v${current} — up to date)`,
|
|
65
|
+
changed: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (version === 0) {
|
|
69
|
+
return {
|
|
70
|
+
action: `kept ${path} (${label}, unversioned — pass --force to regenerate)`,
|
|
71
|
+
changed: false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (version > current) {
|
|
75
|
+
return {
|
|
76
|
+
action: `kept ${path} (${label}, schema v${version} newer than gaia's v${current} — not downgrading)`,
|
|
77
|
+
changed: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// 1..current-1: migrate via the per-version chain.
|
|
81
|
+
const steps = chain
|
|
82
|
+
.filter((m) => m.from >= version && m.to <= current)
|
|
83
|
+
.map((m) => m.description)
|
|
84
|
+
.join('; ') || 'regenerate';
|
|
85
|
+
if (dry) {
|
|
86
|
+
return {
|
|
87
|
+
action: `would migrate ${path} schema v${version}→v${current} (steps: ${steps})`,
|
|
88
|
+
changed: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const original = readFileSync(path, 'utf8');
|
|
92
|
+
const bak = `${path}.v${version}.bak`;
|
|
93
|
+
writeFileSync(bak, original, 'utf8');
|
|
94
|
+
const migrated = applyMigrations(stripVersionHeader(original), version, current, chain);
|
|
95
|
+
writeFileSync(path, stampVersion(migrated, current), 'utf8');
|
|
96
|
+
return {
|
|
97
|
+
action: `migrated ${path} schema v${version}→v${current} (steps: ${steps}; backup ${bak})`,
|
|
98
|
+
changed: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Run the migration. Pure w.r.t. injected `cwd`/`home`; `dryRun` suppresses writes. */
|
|
102
|
+
export function runUpgrade(opts = {}) {
|
|
103
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
104
|
+
const home = opts.home ?? homedir();
|
|
105
|
+
const dry = opts.dryRun ?? false;
|
|
106
|
+
const actions = [];
|
|
107
|
+
let changed = false;
|
|
108
|
+
// 1. Project connection config (schema-versioned seed/migrate).
|
|
109
|
+
const gaiaDir = findGaiaDir(cwd);
|
|
110
|
+
if (gaiaDir !== undefined && hasEngineConfig(gaiaDir)) {
|
|
111
|
+
const res = runConnectionUpgrade(join(gaiaDir, CONNECTION), {
|
|
112
|
+
dryRun: dry,
|
|
113
|
+
label: 'project connection config',
|
|
114
|
+
});
|
|
115
|
+
actions.push(res.action);
|
|
116
|
+
changed = changed || res.changed;
|
|
117
|
+
}
|
|
118
|
+
// 2. Machine context move (secret-bearing — never schema-migrated).
|
|
119
|
+
const legacy = legacyMachinePath(home);
|
|
120
|
+
const canonical = canonicalMachinePath(home);
|
|
121
|
+
if (existsSync(canonical)) {
|
|
122
|
+
actions.push(`kept ${canonical} (machine context already migrated)`);
|
|
123
|
+
}
|
|
124
|
+
else if (existsSync(legacy)) {
|
|
125
|
+
if (!dry) {
|
|
126
|
+
mkdirSync(dirname(canonical), { recursive: true });
|
|
127
|
+
copyFileSync(legacy, canonical);
|
|
128
|
+
chmodSync(canonical, 0o600);
|
|
129
|
+
// Leave a re-export shim at the legacy path so an un-upgraded config still resolves.
|
|
130
|
+
writeFileSync(legacy, `// Moved to ${canonical} by \`gaia upgrade\` (GAIA-201).\nexport { default } from '${pathToFileURL(canonical).href}';\n`, 'utf8');
|
|
131
|
+
}
|
|
132
|
+
actions.push(`${dry ? 'would move' : 'moved'} ${legacy} → ${canonical} (machine context, 0600 + shim)`);
|
|
133
|
+
changed = true;
|
|
134
|
+
}
|
|
135
|
+
// 3. Home connection config (schema-versioned seed/migrate).
|
|
136
|
+
const homeRes = runConnectionUpgrade(join(home, GAIA_DIR, CONNECTION), {
|
|
137
|
+
dryRun: dry,
|
|
138
|
+
label: 'home connection config',
|
|
139
|
+
});
|
|
140
|
+
actions.push(homeRes.action);
|
|
141
|
+
changed = changed || homeRes.changed;
|
|
142
|
+
return { actions, alreadyCurrent: !changed };
|
|
143
|
+
}
|
|
@@ -81,8 +81,12 @@ export function updateNotice(current, latest) {
|
|
|
81
81
|
return null;
|
|
82
82
|
if (!semver.gt(latest, current))
|
|
83
83
|
return null;
|
|
84
|
+
// Two distinct steps, apt-style: first get the newer CODE, then migrate the
|
|
85
|
+
// CONFIG. There is no `gaia update` command — the CLI is a global npm package,
|
|
86
|
+
// so code is updated via npm; `gaia upgrade` then migrates the config shape.
|
|
84
87
|
return (`A new gaia version is available: ${current} → ${latest}\n` +
|
|
85
|
-
'Run `gaia
|
|
88
|
+
'Run `npm install -g @gaia-ai/gaia@latest` to update the CLI, ' +
|
|
89
|
+
'then `gaia upgrade` to migrate your config.');
|
|
86
90
|
}
|
|
87
91
|
const defaultSpawn = (cmd, args) => new Promise((resolve) => {
|
|
88
92
|
const child = spawn(cmd, args, { stdio: 'inherit', shell: false });
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ConductorFileConfig, type ConductorLogger, type GaiaCommandHost, type GaiaCommandPlugin, type GaiaExecutor, type GaiaRemote, type GaiaWorkspace, type ResolvedAgent } from '@gaia-ai/core';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
/** Test seam: inject any subset of dependencies. */
|
|
4
|
+
export interface GaiaCliDeps {
|
|
5
|
+
remote?: GaiaRemote;
|
|
6
|
+
executor?: GaiaExecutor;
|
|
7
|
+
workspace?: GaiaWorkspace;
|
|
8
|
+
agents?: ResolvedAgent[];
|
|
9
|
+
config?: ConductorFileConfig;
|
|
10
|
+
/** Injectable registry fetch for the start-time version check (tests). */
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
/** Host contract (resolveBases); default: this install + cwd. */
|
|
13
|
+
host?: GaiaCommandHost;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseHerdrJson(output: string, command: string): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Start-time auth gate. Returns true if authenticated (session or session-less
|
|
18
|
+
* provider); otherwise logs a single clear line and returns false.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensureAuthenticated(config: ConductorFileConfig, logger: ConductorLogger): Promise<boolean>;
|
|
21
|
+
/** Build the `conductor` subcommand tree (lifecycle + registry + init). */
|
|
22
|
+
export declare function createConductorCommand(deps: GaiaCliDeps): Command;
|
|
23
|
+
/** The `conductor` command plugin the host mounts (GAIA-201). It also registers
|
|
24
|
+
* the `gaia deployment` batch helper (conductor-scoped: needs remote + project). */
|
|
25
|
+
declare const conductorCommandPlugin: GaiaCommandPlugin;
|
|
26
|
+
export default conductorCommandPlugin;
|
|
27
|
+
/**
|
|
28
|
+
* Build a standalone program with the conductor command mounted — the test entry
|
|
29
|
+
* (mirrors what the host does for `gaia conductor …`). `deps` inject fakes.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildConductorProgram(deps?: GaiaCliDeps): Command;
|
|
32
|
+
/** Parse argv against the conductor program (test entry). */
|
|
33
|
+
export declare function runConductorCli(argv: string[], deps?: GaiaCliDeps): Promise<void>;
|