@ekanos/cli 0.1.4 → 0.1.6
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/README.md +132 -6
- package/dist/bin.js +14 -1
- package/dist/bin.js.map +1 -1
- package/dist/commands/dev.d.ts +4 -0
- package/dist/commands/dev.js +6 -20
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts +25 -0
- package/dist/commands/init.js +19 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.d.ts +59 -1
- package/dist/commands/publish.js +209 -25
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/sources.d.ts +17 -0
- package/dist/commands/sources.js +75 -0
- package/dist/commands/sources.js.map +1 -0
- package/dist/commands/status.js +9 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts +47 -0
- package/dist/commands/upgrade.js +445 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/use.d.ts +21 -0
- package/dist/commands/use.js +62 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/commands/validate.d.ts +5 -0
- package/dist/commands/validate.js +20 -6
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.js +26 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/context.d.ts +9 -0
- package/dist/context.js +9 -0
- package/dist/context.js.map +1 -1
- package/dist/delegate.d.ts +28 -0
- package/dist/delegate.js +136 -0
- package/dist/delegate.js.map +1 -0
- package/dist/harness-scaffold.d.ts +21 -6
- package/dist/harness-scaffold.js +8 -5
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +81 -4
- package/dist/index.js.map +1 -1
- package/dist/package-manager.d.ts +10 -0
- package/dist/package-manager.js +32 -0
- package/dist/package-manager.js.map +1 -1
- package/dist/schema-skew.d.ts +77 -0
- package/dist/schema-skew.js +163 -0
- package/dist/schema-skew.js.map +1 -0
- package/dist/seats.d.ts +21 -0
- package/dist/seats.js +15 -0
- package/dist/seats.js.map +1 -0
- package/dist/sources-api.d.ts +44 -0
- package/dist/sources-api.js +69 -0
- package/dist/sources-api.js.map +1 -0
- package/dist/toolchain-api.d.ts +54 -0
- package/dist/toolchain-api.js +58 -0
- package/dist/toolchain-api.js.map +1 -0
- package/dist/toolchain-resolve.d.ts +26 -0
- package/dist/toolchain-resolve.js +100 -0
- package/dist/toolchain-resolve.js.map +1 -0
- package/dist/toolchain.d.ts +21 -0
- package/dist/toolchain.js +16 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/update-notice.d.ts +32 -0
- package/dist/update-notice.js +180 -0
- package/dist/update-notice.js.map +1 -0
- package/dist/validate-findings.d.ts +12 -0
- package/dist/validate-findings.js +12 -0
- package/dist/validate-findings.js.map +1 -1
- package/package.json +1 -1
- package/templates/AGENTS.md.tmpl +81 -18
- package/templates/CLAUDE.md.tmpl +2 -1
- package/templates/claude-skill.md.tmpl +45 -13
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Finding } from '@ekanos/integration-schema';
|
|
2
|
+
import type { CliContext } from '../context.js';
|
|
3
|
+
import { type PackageManager } from '../package-manager.js';
|
|
4
|
+
import { resolveToolchainTarget } from '../toolchain-resolve.js';
|
|
5
|
+
/**
|
|
6
|
+
* `ekanos upgrade` — move a project's toolchain to a coherent target version
|
|
7
|
+
* and VERIFY it before calling the job done.
|
|
8
|
+
*
|
|
9
|
+
* The whole point of this verb is that it cannot leave a project worse off
|
|
10
|
+
* than it found it: `package.json` and the lockfile are snapshotted before
|
|
11
|
+
* anything installs, the target set is only ever a coherence-checked whole
|
|
12
|
+
* (never "latest of each, independently" — see `toolchain-resolve.ts`), and
|
|
13
|
+
* any failure from install through validate through the project's own test
|
|
14
|
+
* script rolls everything back to the snapshot and says exactly which step
|
|
15
|
+
* failed. A partner (or an agent) running this unattended should never end up
|
|
16
|
+
* with a half-upgraded, uninstallable project.
|
|
17
|
+
*/
|
|
18
|
+
export interface UpgradeArgs {
|
|
19
|
+
dir: string;
|
|
20
|
+
host?: string;
|
|
21
|
+
env: Record<string, string | undefined>;
|
|
22
|
+
/** Report current vs. target and exit 0. Never mutates anything. */
|
|
23
|
+
check: boolean;
|
|
24
|
+
/** Skip the interactive confirm; required to mutate outside a TTY. */
|
|
25
|
+
yes: boolean;
|
|
26
|
+
/** Skip the post-install validate + test-script verification. */
|
|
27
|
+
noVerify: boolean;
|
|
28
|
+
stdinIsTTY?: boolean;
|
|
29
|
+
confirm?: (question: string) => Promise<boolean>;
|
|
30
|
+
}
|
|
31
|
+
/** Injectable seams so tests can exercise install/verify/rollback without shelling out. */
|
|
32
|
+
export interface UpgradeDeps {
|
|
33
|
+
resolveTarget?: typeof resolveToolchainTarget;
|
|
34
|
+
runInstall?: (argv: string[], bin: string, cwd: string) => Promise<{
|
|
35
|
+
code: number;
|
|
36
|
+
output: string;
|
|
37
|
+
}>;
|
|
38
|
+
runValidateStep?: (projectDir: string) => Promise<{
|
|
39
|
+
ok: boolean;
|
|
40
|
+
findings?: Finding[];
|
|
41
|
+
}>;
|
|
42
|
+
runTestStep?: (projectDir: string, pm: PackageManager) => Promise<{
|
|
43
|
+
code: number;
|
|
44
|
+
output: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export declare function runUpgrade(ctx: CliContext, args: UpgradeArgs, deps?: UpgradeDeps): Promise<number>;
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { resolveAuthEnvironment } from '../auth/session.js';
|
|
6
|
+
import { cliVersion } from '../compatibility.js';
|
|
7
|
+
import { networkError, preconditionError, validationError } from '../errors.js';
|
|
8
|
+
import { EXIT_CODES } from '../exit-codes.js';
|
|
9
|
+
import { readInstalledPackageManifest } from '../harness-scaffold.js';
|
|
10
|
+
import { detectPackageManager, installExactArgs, lockfileName, } from '../package-manager.js';
|
|
11
|
+
import { loadProject } from '../project.js';
|
|
12
|
+
import { renderTemplate, toDisplayName } from '../templates.js';
|
|
13
|
+
import { TOOLCHAIN_PACKAGES, } from '../toolchain.js';
|
|
14
|
+
import { resolveToolchainTarget } from '../toolchain-resolve.js';
|
|
15
|
+
import { collectAllFindings } from '../validate-findings.js';
|
|
16
|
+
import { AGENT_CONTEXT_FILES } from './init.js';
|
|
17
|
+
import { promptYesNo } from './publish.js';
|
|
18
|
+
export async function runUpgrade(ctx, args, deps = {}) {
|
|
19
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
20
|
+
const loaded = loadProject(args.dir);
|
|
21
|
+
const pm = detectPackageManager(loaded.projectDir);
|
|
22
|
+
// Captured BEFORE anything installs: the templates the project's CURRENT
|
|
23
|
+
// (pre-upgrade) local `@ekanos/cli` ships, which is what actually
|
|
24
|
+
// generated the agent-context files on disk — not necessarily this
|
|
25
|
+
// process's own bundled templates, since `upgrade` is a machine-level verb
|
|
26
|
+
// that never delegates and is routinely invoked via a DIFFERENT (global)
|
|
27
|
+
// install. See `regenerateAgentContextFiles`.
|
|
28
|
+
const oldAgentTemplates = captureAgentTemplates(loaded.projectDir);
|
|
29
|
+
const current = readCurrentToolchain(loaded.projectDir);
|
|
30
|
+
const session = resolveSessionQuietly(ctx, args);
|
|
31
|
+
const resolveTarget = (_a = deps.resolveTarget) !== null && _a !== void 0 ? _a : resolveToolchainTarget;
|
|
32
|
+
const resolution = await resolveTarget({
|
|
33
|
+
host: session === null || session === void 0 ? void 0 : session.host,
|
|
34
|
+
session: session === null || session === void 0 ? void 0 : session.session,
|
|
35
|
+
});
|
|
36
|
+
if (resolution.status === 'unavailable') {
|
|
37
|
+
throw networkError('Could not resolve a toolchain target from the Fusion host or the ' +
|
|
38
|
+
'npm registry.', 'Check connectivity and retry. If this deployment has no ' +
|
|
39
|
+
'"/api/partner/toolchain" route yet, the npm registry is the ' +
|
|
40
|
+
'fallback and a failure there means npm itself is unreachable.');
|
|
41
|
+
}
|
|
42
|
+
if (resolution.status === 'incoherent') {
|
|
43
|
+
throw preconditionError(`The resolved toolchain versions do not form an installable set: ` +
|
|
44
|
+
resolution.detail, 'Retry in a moment — this is almost always a multi-package publish ' +
|
|
45
|
+
'still in flight, and it self-resolves once every package finishes ' +
|
|
46
|
+
'publishing.');
|
|
47
|
+
}
|
|
48
|
+
const target = resolution.target;
|
|
49
|
+
const deltas = computeDeltas(current, target);
|
|
50
|
+
if (args.check) {
|
|
51
|
+
return ctx.succeed(Object.assign({ source: resolution.source, current,
|
|
52
|
+
target,
|
|
53
|
+
deltas }, (resolution.source === 'host'
|
|
54
|
+
? { minimum: resolution.minimum, notes: resolution.notes }
|
|
55
|
+
: {})), checkSummary(resolution.source, deltas));
|
|
56
|
+
}
|
|
57
|
+
const changedKeys = Object.keys(deltas).filter((key) => deltas[key].changed);
|
|
58
|
+
if (changedKeys.length === 0) {
|
|
59
|
+
return ctx.succeed({ source: resolution.source, current, target, deltas }, `upgrade: already at the target toolchain (source: ${resolution.source}).`);
|
|
60
|
+
}
|
|
61
|
+
const isHuman = !ctx.jsonMode;
|
|
62
|
+
const stdinIsTTY = (_b = args.stdinIsTTY) !== null && _b !== void 0 ? _b : process.stdin.isTTY === true;
|
|
63
|
+
const canPrompt = isHuman && stdinIsTTY;
|
|
64
|
+
if (!args.yes) {
|
|
65
|
+
if (!canPrompt) {
|
|
66
|
+
throw validationError('Refusing to modify the project non-interactively without --yes.', 'Pass "ekanos upgrade --yes" to proceed, or "ekanos upgrade --check" ' +
|
|
67
|
+
'to preview the target with no changes.');
|
|
68
|
+
}
|
|
69
|
+
ctx.log(planSummary(resolution.source, deltas));
|
|
70
|
+
const confirm = (_c = args.confirm) !== null && _c !== void 0 ? _c : promptYesNo;
|
|
71
|
+
const confirmed = await confirm('Upgrade now? [y/N]');
|
|
72
|
+
if (!confirmed) {
|
|
73
|
+
throw validationError('Upgrade was not confirmed.', 'Re-run "ekanos upgrade --yes" to proceed non-interactively, or ' +
|
|
74
|
+
'"ekanos upgrade --check" to preview with no changes. Nothing was ' +
|
|
75
|
+
'modified.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const packageJsonPath = path.join(loaded.projectDir, 'package.json');
|
|
79
|
+
const manifest = readManifest(packageJsonPath);
|
|
80
|
+
const specs = buildInstallSpecs(manifest, target, changedKeys);
|
|
81
|
+
if (specs.length === 0) {
|
|
82
|
+
return ctx.succeed({ source: resolution.source, current, target, deltas }, 'upgrade: none of the changed packages are direct dependencies of ' +
|
|
83
|
+
'this project — nothing to install directly. Their versions follow ' +
|
|
84
|
+
'from whichever direct dependency pins them.');
|
|
85
|
+
}
|
|
86
|
+
const snapshot = snapshotProject(loaded.projectDir, pm);
|
|
87
|
+
try {
|
|
88
|
+
const runInstall = (_d = deps.runInstall) !== null && _d !== void 0 ? _d : defaultRunInstall;
|
|
89
|
+
for (const group of [
|
|
90
|
+
specs.filter((s) => !s.dev),
|
|
91
|
+
specs.filter((s) => s.dev),
|
|
92
|
+
]) {
|
|
93
|
+
if (group.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
const argv = installExactArgs(pm, group.map((s) => `${s.name}@${s.version}`), group[0].dev);
|
|
96
|
+
ctx.log(`Running "${pm.bin} ${argv.join(' ')}" in ${loaded.projectDir}…`);
|
|
97
|
+
const result = await runInstall(argv, pm.bin, loaded.projectDir);
|
|
98
|
+
if (result.code !== 0) {
|
|
99
|
+
return await failAndRollback(ctx, snapshot, pm, loaded.projectDir, runInstall, {
|
|
100
|
+
step: 'install',
|
|
101
|
+
exitCode: EXIT_CODES.PRECONDITION_FAILED,
|
|
102
|
+
message: `"${pm.bin} ${argv.join(' ')}" exited with code ${result.code}.`,
|
|
103
|
+
output: result.output,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!args.noVerify) {
|
|
108
|
+
const runValidateStep = (_e = deps.runValidateStep) !== null && _e !== void 0 ? _e : defaultRunValidateStep;
|
|
109
|
+
const validateResult = await runValidateStep(loaded.projectDir);
|
|
110
|
+
if (!validateResult.ok) {
|
|
111
|
+
return await failAndRollback(ctx, snapshot, pm, loaded.projectDir, runInstall, {
|
|
112
|
+
step: 'validate',
|
|
113
|
+
exitCode: EXIT_CODES.VALIDATION,
|
|
114
|
+
message: 'The upgraded toolchain fails "ekanos validate" against this ' +
|
|
115
|
+
"project's own integration definition.",
|
|
116
|
+
output: JSON.stringify((_f = validateResult.findings) !== null && _f !== void 0 ? _f : [], null, 2),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const runTestStep = (_g = deps.runTestStep) !== null && _g !== void 0 ? _g : defaultRunTestStep;
|
|
120
|
+
const testResult = await runTestStep(loaded.projectDir, pm);
|
|
121
|
+
if (testResult.code !== 0) {
|
|
122
|
+
return await failAndRollback(ctx, snapshot, pm, loaded.projectDir, runInstall, {
|
|
123
|
+
step: 'test',
|
|
124
|
+
exitCode: testResult.code,
|
|
125
|
+
message: `The project's test script exited with code ${testResult.code} on the upgraded toolchain.`,
|
|
126
|
+
output: testResult.output,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
snapshot.dispose();
|
|
133
|
+
}
|
|
134
|
+
const regenerated = regenerateAgentContextFiles({
|
|
135
|
+
projectDir: loaded.projectDir,
|
|
136
|
+
slug: loaded.primary.slug,
|
|
137
|
+
oldTemplates: oldAgentTemplates,
|
|
138
|
+
newTemplatesDir: path.join(loaded.projectDir, 'node_modules', '@ekanos', 'cli', 'templates'),
|
|
139
|
+
});
|
|
140
|
+
// `specs` only ever covers DIRECT dependencies (see `buildInstallSpecs`) —
|
|
141
|
+
// a changed key with no spec is transitive and was never actually
|
|
142
|
+
// installed here, so the report (and the human summary) must say so
|
|
143
|
+
// explicitly rather than implying every changed key was upgraded. This
|
|
144
|
+
// matters most for exactly the scenario this PR's own delegation feature
|
|
145
|
+
// targets: a globally-installed `ekanos` with no local `@ekanos/cli`
|
|
146
|
+
// devDependency, where `cli` changes in `deltas` but the running global
|
|
147
|
+
// binary itself is untouched by this command.
|
|
148
|
+
const installedKeys = specs.map((spec) => spec.key);
|
|
149
|
+
const skippedKeys = changedKeys.filter((key) => !installedKeys.includes(key));
|
|
150
|
+
return ctx.succeed({
|
|
151
|
+
source: resolution.source,
|
|
152
|
+
current,
|
|
153
|
+
target,
|
|
154
|
+
deltas,
|
|
155
|
+
installed: installedKeys,
|
|
156
|
+
skippedTransitive: skippedKeys,
|
|
157
|
+
regenerated,
|
|
158
|
+
}, successSummary(installedKeys, skippedKeys, deltas, regenerated));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Best-effort host/session resolution — NEVER throws. `upgrade` must work for
|
|
162
|
+
* a partner who has never logged in at all (pure npm-registry flow), so the
|
|
163
|
+
* throwing `resolveAuthEnvironment`/`requireSession` machinery is unusable
|
|
164
|
+
* here; this mirrors the tolerant lookup `update-notice.ts` uses.
|
|
165
|
+
*/
|
|
166
|
+
function resolveSessionQuietly(ctx, args) {
|
|
167
|
+
try {
|
|
168
|
+
const env = resolveAuthEnvironment(ctx, args.host, args.env, {
|
|
169
|
+
projectDir: args.dir,
|
|
170
|
+
});
|
|
171
|
+
const session = env.store.read(env.host);
|
|
172
|
+
return session ? { host: env.host, session } : null;
|
|
173
|
+
}
|
|
174
|
+
catch (_a) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function readCurrentToolchain(projectDir) {
|
|
179
|
+
var _a;
|
|
180
|
+
const result = {};
|
|
181
|
+
for (const [key, pkg] of Object.entries(TOOLCHAIN_PACKAGES)) {
|
|
182
|
+
const installed = readInstalledPackageManifest(projectDir, pkg);
|
|
183
|
+
result[key] = (_a = installed === null || installed === void 0 ? void 0 : installed.version) !== null && _a !== void 0 ? _a : (key === 'cli' ? cliVersion() : null);
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
function computeDeltas(current, target) {
|
|
188
|
+
const keys = Object.keys(TOOLCHAIN_PACKAGES);
|
|
189
|
+
const result = {};
|
|
190
|
+
for (const key of keys) {
|
|
191
|
+
result[key] = {
|
|
192
|
+
current: current[key],
|
|
193
|
+
target: target[key],
|
|
194
|
+
changed: current[key] !== target[key],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Only packages the project depends on DIRECTLY are installed explicitly —
|
|
201
|
+
* `@ekanos/integration-schema` and often `@ekanos/harness` are transitive
|
|
202
|
+
* (pulled in by `@ekanos/sdk`), and forcing an exact transitive version would
|
|
203
|
+
* need a package-manager-specific override mechanism this CLI does not
|
|
204
|
+
* manage. Their versions follow from whichever direct dependency pins them,
|
|
205
|
+
* which is exactly what the coherent target guarantees is already correct.
|
|
206
|
+
*/
|
|
207
|
+
function buildInstallSpecs(manifest, target, changedKeys) {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
const deps = ((_a = manifest.dependencies) !== null && _a !== void 0 ? _a : {});
|
|
210
|
+
const devDeps = ((_b = manifest.devDependencies) !== null && _b !== void 0 ? _b : {});
|
|
211
|
+
const specs = [];
|
|
212
|
+
for (const key of changedKeys) {
|
|
213
|
+
const pkg = TOOLCHAIN_PACKAGES[key];
|
|
214
|
+
if (pkg in deps) {
|
|
215
|
+
specs.push({ key, name: pkg, version: target[key], dev: false });
|
|
216
|
+
}
|
|
217
|
+
else if (pkg in devDeps) {
|
|
218
|
+
specs.push({ key, name: pkg, version: target[key], dev: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return specs;
|
|
222
|
+
}
|
|
223
|
+
function readManifest(packageJsonPath) {
|
|
224
|
+
let parsed;
|
|
225
|
+
try {
|
|
226
|
+
parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
throw preconditionError(`Could not read ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`, 'Ensure the project has a valid package.json, then re-run "ekanos upgrade".');
|
|
230
|
+
}
|
|
231
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
232
|
+
throw preconditionError(`${packageJsonPath} does not contain a JSON object.`, 'Fix package.json, then re-run "ekanos upgrade".');
|
|
233
|
+
}
|
|
234
|
+
return parsed;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Copy `package.json` and the lockfile to a temp directory before anything
|
|
238
|
+
* installs. `restore()` copies them back (and removes a lockfile that did not
|
|
239
|
+
* exist before, if the failed install created one); `dispose()` cleans up the
|
|
240
|
+
* temp copies once the run is over, success or failure.
|
|
241
|
+
*/
|
|
242
|
+
function snapshotProject(projectDir, pm) {
|
|
243
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ekanos-upgrade-'));
|
|
244
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
245
|
+
// bun has shipped both a text (`bun.lock`) and a legacy binary
|
|
246
|
+
// (`bun.lockb`) lockfile; snapshot whichever is actually present.
|
|
247
|
+
const lockCandidates = pm.name === 'bun' ? ['bun.lock', 'bun.lockb'] : [lockfileName(pm)];
|
|
248
|
+
const lockName = lockCandidates.find((name) => fs.existsSync(path.join(projectDir, name)));
|
|
249
|
+
const lockfilePath = lockName
|
|
250
|
+
? path.join(projectDir, lockName)
|
|
251
|
+
: path.join(projectDir, lockfileName(pm));
|
|
252
|
+
const lockfileExisted = lockName !== undefined;
|
|
253
|
+
fs.copyFileSync(packageJsonPath, path.join(tmpDir, 'package.json'));
|
|
254
|
+
if (lockfileExisted) {
|
|
255
|
+
fs.copyFileSync(lockfilePath, path.join(tmpDir, 'lockfile.snapshot'));
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
restore: () => {
|
|
259
|
+
fs.copyFileSync(path.join(tmpDir, 'package.json'), packageJsonPath);
|
|
260
|
+
if (lockfileExisted) {
|
|
261
|
+
fs.copyFileSync(path.join(tmpDir, 'lockfile.snapshot'), lockfilePath);
|
|
262
|
+
}
|
|
263
|
+
else if (fs.existsSync(lockfilePath)) {
|
|
264
|
+
fs.rmSync(lockfilePath);
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
dispose: () => {
|
|
268
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The one rollback path every failure funnels through: restore the snapshot,
|
|
274
|
+
* reinstall to put `node_modules` back in sync with the restored
|
|
275
|
+
* `package.json`/lockfile, and report exactly which step failed and what it
|
|
276
|
+
* said — with an explicit statement that the project was restored, so a
|
|
277
|
+
* partner (or an agent) never has to wonder whether they are looking at a
|
|
278
|
+
* half-upgraded tree.
|
|
279
|
+
*/
|
|
280
|
+
async function failAndRollback(ctx, snapshot, pm, projectDir, runInstall, failure) {
|
|
281
|
+
ctx.warn(`ekanos: the "${failure.step}" step failed — rolling back package.json ` +
|
|
282
|
+
'and the lockfile, then reinstalling to restore node_modules…');
|
|
283
|
+
snapshot.restore();
|
|
284
|
+
// The SAME injectable installer seam used for the forward install — a test
|
|
285
|
+
// that mocks it never shells out for the rollback path either.
|
|
286
|
+
const restore = await runInstall(['install'], pm.bin, projectDir);
|
|
287
|
+
return ctx.failWith({
|
|
288
|
+
code: `upgrade_${failure.step}_failed`,
|
|
289
|
+
message: failure.message,
|
|
290
|
+
hint: `The project HAS BEEN RESTORED to its pre-upgrade state (package.json, ` +
|
|
291
|
+
`lockfile and node_modules all rolled back${restore.code === 0
|
|
292
|
+
? ''
|
|
293
|
+
: ' — though the restore install itself also failed with code ' +
|
|
294
|
+
`${restore.code}; run "${pm.bin} install" by hand to finish ` +
|
|
295
|
+
'repairing node_modules'}). Read the output in data.output, fix the underlying issue, and ` +
|
|
296
|
+
're-run "ekanos upgrade".',
|
|
297
|
+
exitCode: failure.exitCode,
|
|
298
|
+
data: { step: failure.step, output: failure.output },
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async function defaultRunInstall(argv, bin, cwd) {
|
|
302
|
+
return new Promise((resolve, reject) => {
|
|
303
|
+
var _a, _b;
|
|
304
|
+
const child = spawn(bin, argv, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
305
|
+
let output = '';
|
|
306
|
+
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
|
|
307
|
+
output += chunk.toString();
|
|
308
|
+
});
|
|
309
|
+
(_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => {
|
|
310
|
+
output += chunk.toString();
|
|
311
|
+
});
|
|
312
|
+
child.on('error', reject);
|
|
313
|
+
child.on('close', (code) => resolve({ code: code !== null && code !== void 0 ? code : 1, output }));
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
async function defaultRunValidateStep(projectDir) {
|
|
317
|
+
const loaded = loadProject(projectDir);
|
|
318
|
+
const findings = await collectAllFindings(loaded);
|
|
319
|
+
const errorCount = findings.filter((f) => f.severity === 'error').length;
|
|
320
|
+
return { ok: errorCount === 0, findings };
|
|
321
|
+
}
|
|
322
|
+
async function defaultRunTestStep(projectDir, pm) {
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
var _a, _b;
|
|
325
|
+
const child = spawn(pm.bin, ['run', 'test'], {
|
|
326
|
+
cwd: projectDir,
|
|
327
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
328
|
+
});
|
|
329
|
+
let output = '';
|
|
330
|
+
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
|
|
331
|
+
output += chunk.toString();
|
|
332
|
+
});
|
|
333
|
+
(_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => {
|
|
334
|
+
output += chunk.toString();
|
|
335
|
+
});
|
|
336
|
+
child.on('error', reject);
|
|
337
|
+
child.on('close', (code) => resolve({ code: code !== null && code !== void 0 ? code : 1, output }));
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Read every `AGENT_CONTEXT_FILES` template source out of the project's
|
|
342
|
+
* CURRENT (pre-upgrade) local `@ekanos/cli` install, before anything installs
|
|
343
|
+
* over it. Missing entirely (the package isn't a resolvable local dependency)
|
|
344
|
+
* just means every file below is treated conservatively — see
|
|
345
|
+
* `regenerateAgentContextFiles`.
|
|
346
|
+
*/
|
|
347
|
+
function captureAgentTemplates(projectDir) {
|
|
348
|
+
const templatesDir = path.join(projectDir, 'node_modules', '@ekanos', 'cli', 'templates');
|
|
349
|
+
const captured = new Map();
|
|
350
|
+
for (const { template } of AGENT_CONTEXT_FILES) {
|
|
351
|
+
const templatePath = path.join(templatesDir, template);
|
|
352
|
+
try {
|
|
353
|
+
captured.set(template, fs.readFileSync(templatePath, 'utf8'));
|
|
354
|
+
}
|
|
355
|
+
catch (_a) {
|
|
356
|
+
// Not resolvable — left out of the map, handled as "can't verify" below.
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return captured;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Regenerate `AGENTS.md`, `CLAUDE.md` and the Claude Code skill from the
|
|
363
|
+
* NEWLY installed CLI's bundled templates — using the exact byte-comparison
|
|
364
|
+
* discipline `harness-scaffold.ts` applies to the dev harness shell: a file
|
|
365
|
+
* whose current bytes match what the project's PRE-upgrade local CLI would
|
|
366
|
+
* have rendered (captured in `oldTemplates`, before install ran) is
|
|
367
|
+
* untouched by the partner and safe to regenerate; anything else has been
|
|
368
|
+
* edited and is left alone, reported so a human can merge it by hand. A
|
|
369
|
+
* stale AGENTS.md teaches an agent yesterday's contract — that is as harmful
|
|
370
|
+
* as a stale binary, which is why this runs on every successful upgrade
|
|
371
|
+
* rather than only on `init`.
|
|
372
|
+
*/
|
|
373
|
+
function regenerateAgentContextFiles(params) {
|
|
374
|
+
const vars = { SLUG: params.slug, DISPLAY_NAME: toDisplayName(params.slug) };
|
|
375
|
+
const updated = [];
|
|
376
|
+
const unchanged = [];
|
|
377
|
+
const needsAttention = [];
|
|
378
|
+
for (const { template, dest } of AGENT_CONTEXT_FILES) {
|
|
379
|
+
const destPath = path.join(params.projectDir, dest);
|
|
380
|
+
if (!fs.existsSync(destPath))
|
|
381
|
+
continue; // never scaffolded — nothing to upgrade
|
|
382
|
+
const current = fs.readFileSync(destPath, 'utf8');
|
|
383
|
+
const oldSource = params.oldTemplates.get(template);
|
|
384
|
+
const oldRendered = oldSource === undefined ? null : renderTemplate(oldSource, vars);
|
|
385
|
+
if (oldRendered === null || current !== oldRendered) {
|
|
386
|
+
needsAttention.push(dest);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const newTemplatePath = path.join(params.newTemplatesDir, template);
|
|
390
|
+
if (!fs.existsSync(newTemplatePath))
|
|
391
|
+
continue;
|
|
392
|
+
const newRendered = renderTemplate(fs.readFileSync(newTemplatePath, 'utf8'), vars);
|
|
393
|
+
if (newRendered === current) {
|
|
394
|
+
unchanged.push(dest);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
fs.writeFileSync(destPath, newRendered);
|
|
398
|
+
updated.push(dest);
|
|
399
|
+
}
|
|
400
|
+
return { updated, unchanged, needsAttention };
|
|
401
|
+
}
|
|
402
|
+
function checkSummary(source, deltas) {
|
|
403
|
+
var _a;
|
|
404
|
+
const lines = [`upgrade --check: target resolved from ${source}.`];
|
|
405
|
+
for (const [key, delta] of Object.entries(deltas)) {
|
|
406
|
+
lines.push(` ${key}: ${(_a = delta.current) !== null && _a !== void 0 ? _a : '(not installed)'} → ${delta.target}` +
|
|
407
|
+
(delta.changed ? '' : ' (already current)'));
|
|
408
|
+
}
|
|
409
|
+
return lines.join('\n');
|
|
410
|
+
}
|
|
411
|
+
function planSummary(source, deltas) {
|
|
412
|
+
const changing = Object.entries(deltas)
|
|
413
|
+
.filter(([, d]) => d.changed)
|
|
414
|
+
.map(([key, d]) => { var _a; return `${key} ${(_a = d.current) !== null && _a !== void 0 ? _a : '(not installed)'} → ${d.target}`; });
|
|
415
|
+
return (`Upgrading (target resolved from ${source}):\n` +
|
|
416
|
+
changing.map((line) => ` ${line}`).join('\n'));
|
|
417
|
+
}
|
|
418
|
+
function successSummary(installedKeys, skippedKeys, deltas, regenerated) {
|
|
419
|
+
const installed = installedKeys
|
|
420
|
+
.map((key) => `${key} → ${deltas[key].target}`)
|
|
421
|
+
.join(', ');
|
|
422
|
+
const parts = [
|
|
423
|
+
installed.length > 0
|
|
424
|
+
? `upgrade: OK — installed ${installed}.`
|
|
425
|
+
: 'upgrade: OK — nothing was a direct dependency of this project, so nothing was installed here.',
|
|
426
|
+
];
|
|
427
|
+
if (skippedKeys.length > 0) {
|
|
428
|
+
// These changed in the resolved target but are transitive (or, for
|
|
429
|
+
// "cli", the invoked binary was global with no local devDependency) —
|
|
430
|
+
// never touched by the install step above.
|
|
431
|
+
const skipped = skippedKeys
|
|
432
|
+
.map((key) => `${key} → ${deltas[key].target}`)
|
|
433
|
+
.join(', ');
|
|
434
|
+
parts.push(`Not installed directly (transitive or not a local dependency): ${skipped}.`);
|
|
435
|
+
}
|
|
436
|
+
if (regenerated.updated.length > 0) {
|
|
437
|
+
parts.push(`regenerated ${regenerated.updated.join(', ')}.`);
|
|
438
|
+
}
|
|
439
|
+
if (regenerated.needsAttention.length > 0) {
|
|
440
|
+
parts.push(`${regenerated.needsAttention.join(', ')} were locally modified — ` +
|
|
441
|
+
'left untouched, merge the new template by hand.');
|
|
442
|
+
}
|
|
443
|
+
return parts.join(' ');
|
|
444
|
+
}
|
|
445
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,kBAAkB,GAGnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAqDxC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAe,EACf,IAAiB,EACjB,OAAoB,EAAE;;IAEtB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEnD,yEAAyE;IACzE,kEAAkE;IAClE,mEAAmE;IACnE,2EAA2E;IAC3E,yEAAyE;IACzE,8CAA8C;IAC9C,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAExD,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,MAAA,IAAI,CAAC,aAAa,mCAAI,sBAAsB,CAAC;IACnE,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC;QACrC,IAAI,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI;QACnB,OAAO,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO;KAC1B,CAAC,CAAC;IAEH,IAAI,UAAU,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACxC,MAAM,YAAY,CAChB,mEAAmE;YACjE,eAAe,EACjB,0DAA0D;YACxD,8DAA8D;YAC9D,+DAA+D,CAClE,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;QACvC,MAAM,iBAAiB,CACrB,kEAAkE;YAChE,UAAU,CAAC,MAAM,EACnB,oEAAoE;YAClE,oEAAoE;YACpE,aAAa,CAChB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IACjC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE9C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,OAAO,iBAEd,MAAM,EAAE,UAAU,CAAC,MAAM,EACzB,OAAO;YACP,MAAM;YACN,MAAM,IACH,CAAC,UAAU,CAAC,MAAM,KAAK,MAAM;YAC9B,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE;YAC1D,CAAC,CAAC,EAAE,CAAC,GAET,YAAY,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CACxC,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAoB,CAAC,MAAM,CAChE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC,OAAO,CAC9B,CAAC;IAEF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EACtD,qDAAqD,UAAU,CAAC,MAAM,IAAI,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9B,MAAM,UAAU,GAAG,MAAA,IAAI,CAAC,UAAU,mCAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC;IACnE,MAAM,SAAS,GAAG,OAAO,IAAI,UAAU,CAAC;IAExC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,eAAe,CACnB,iEAAiE,EACjE,sEAAsE;gBACpE,wCAAwC,CAC3C,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,MAAA,IAAI,CAAC,OAAO,mCAAI,WAAW,CAAC;QAC5C,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAEtD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,eAAe,CACnB,4BAA4B,EAC5B,iEAAiE;gBAC/D,mEAAmE;gBACnE,WAAW,CACd,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAE/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EACtD,mEAAmE;YACjE,oEAAoE;YACpE,6CAA6C,CAChD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAA,IAAI,CAAC,UAAU,mCAAI,iBAAiB,CAAC;QAExD,KAAK,MAAM,KAAK,IAAI;YAClB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC3B,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;SAC3B,EAAE,CAAC;YACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEjC,MAAM,IAAI,GAAG,gBAAgB,CAC3B,EAAE,EACF,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,EAC1C,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,CACd,CAAC;YACF,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YAEjE,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,MAAM,eAAe,CAC1B,GAAG,EACH,QAAQ,EACR,EAAE,EACF,MAAM,CAAC,UAAU,EACjB,UAAU,EACV;oBACE,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,UAAU,CAAC,mBAAmB;oBACxC,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,IAAI,GAAG;oBACzE,MAAM,EAAE,MAAM,CAAC,MAAM;iBACtB,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,eAAe,GAAG,MAAA,IAAI,CAAC,eAAe,mCAAI,sBAAsB,CAAC;YACvE,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAEhE,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;gBACvB,OAAO,MAAM,eAAe,CAC1B,GAAG,EACH,QAAQ,EACR,EAAE,EACF,MAAM,CAAC,UAAU,EACjB,UAAU,EACV;oBACE,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,UAAU,CAAC,UAAU;oBAC/B,OAAO,EACL,8DAA8D;wBAC9D,uCAAuC;oBACzC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAA,cAAc,CAAC,QAAQ,mCAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;iBAC/D,CACF,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,WAAW,mCAAI,kBAAkB,CAAC;YAC3D,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAE5D,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,MAAM,eAAe,CAC1B,GAAG,EACH,QAAQ,EACR,EAAE,EACF,MAAM,CAAC,UAAU,EACjB,UAAU,EACV;oBACE,IAAI,EAAE,MAAM;oBACZ,QAAQ,EAAE,UAAU,CAAC,IAAI;oBACzB,OAAO,EAAE,8CAA8C,UAAU,CAAC,IAAI,6BAA6B;oBACnG,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CACF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,WAAW,GAAG,2BAA2B,CAAC;QAC9C,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QACzB,YAAY,EAAE,iBAAiB;QAC/B,eAAe,EAAE,IAAI,CAAC,IAAI,CACxB,MAAM,CAAC,UAAU,EACjB,cAAc,EACd,SAAS,EACT,KAAK,EACL,WAAW,CACZ;KACF,CAAC,CAAC;IAEH,2EAA2E;IAC3E,kEAAkE;IAClE,oEAAoE;IACpE,uEAAuE;IACvE,yEAAyE;IACzE,qEAAqE;IACrE,wEAAwE;IACxE,8CAA8C;IAC9C,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAE9E,OAAO,GAAG,CAAC,OAAO,CAChB;QACE,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,OAAO;QACP,MAAM;QACN,MAAM;QACN,SAAS,EAAE,aAAa;QACxB,iBAAiB,EAAE,WAAW;QAC9B,WAAW;KACZ,EACD,cAAc,CAAC,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAChE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,qBAAqB,CAC5B,GAAe,EACf,IAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;YAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;SACrB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,UAAkB;;IAElB,MAAM,MAAM,GAAG,EAAyC,CAAC;IAEzD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAGvD,EAAE,CAAC;QACJ,MAAM,SAAS,GAAG,4BAA4B,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,CAAC,GAAG,CAAC,GAAG,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,OAAO,mCAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CACpB,OAA4C,EAC5C,MAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAmB,CAAC;IAC/D,MAAM,MAAM,GAAG,EAAsC,CAAC;IAEtD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,GAAG;YACZ,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;YACrB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;YACnB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC;SACtC,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AASD;;;;;;;GAOG;AACH,SAAS,iBAAiB,CACxB,QAAiC,EACjC,MAAyB,EACzB,WAAoC;;IAEpC,MAAM,IAAI,GAAG,CAAC,MAAA,QAAQ,CAAC,YAAY,mCAAI,EAAE,CAA4B,CAAC;IACtE,MAAM,OAAO,GAAG,CAAC,MAAA,QAAQ,CAAC,eAAe,mCAAI,EAAE,CAA4B,CAAC;IAE5E,MAAM,KAAK,GAAkB,EAAE,CAAC;IAEhC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;aAAM,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,eAAuB;IAC3C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,iBAAiB,CACrB,kBAAkB,eAAe,KAC/B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,EACF,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,iBAAiB,CACrB,GAAG,eAAe,kCAAkC,EACpD,iDAAiD,CAClD,CAAC;IACJ,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAOD;;;;;GAKG;AACH,SAAS,eAAe,CAAC,UAAkB,EAAE,EAAkB;IAC7D,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACzE,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAE9D,+DAA+D;IAC/D,kEAAkE;IAClE,MAAM,cAAc,GAClB,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAC3C,CAAC;IACF,MAAM,YAAY,GAAG,QAAQ;QAC3B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC;QACjC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,eAAe,GAAG,QAAQ,KAAK,SAAS,CAAC;IAE/C,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IACpE,IAAI,eAAe,EAAE,CAAC;QACpB,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO;QACL,OAAO,EAAE,GAAG,EAAE;YACZ,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,eAAe,CAAC,CAAC;YACpE,IAAI,eAAe,EAAE,CAAC;gBACpB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,YAAY,CAAC,CAAC;YACxE,CAAC;iBAAM,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACvC,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,EAAE;YACZ,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC;AASD;;;;;;;GAOG;AACH,KAAK,UAAU,eAAe,CAC5B,GAAe,EACf,QAAkB,EAClB,EAAkB,EAClB,UAAkB,EAClB,UAI8C,EAC9C,OAAoB;IAEpB,GAAG,CAAC,IAAI,CACN,gBAAgB,OAAO,CAAC,IAAI,4CAA4C;QACtE,8DAA8D,CACjE,CAAC;IAEF,QAAQ,CAAC,OAAO,EAAE,CAAC;IACnB,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAElE,OAAO,GAAG,CAAC,QAAQ,CAAC;QAClB,IAAI,EAAE,WAAW,OAAO,CAAC,IAAI,SAAS;QACtC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EACF,wEAAwE;YACxE,4CACE,OAAO,CAAC,IAAI,KAAK,CAAC;gBAChB,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,6DAA6D;oBAC7D,GAAG,OAAO,CAAC,IAAI,UAAU,EAAE,CAAC,GAAG,8BAA8B;oBAC7D,wBACN,mEAAmE;YACnE,0BAA0B;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;KACrD,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,IAAc,EACd,GAAW,EACX,GAAW;IAEX,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3E,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAA,KAAK,CAAC,MAAM,0CAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,MAAA,KAAK,CAAC,MAAM,0CAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,UAAkB;IAElB,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACzE,OAAO,EAAE,EAAE,EAAE,UAAU,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,UAAkB,EAClB,EAAkB;IAElB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE;YAC3C,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAA,KAAK,CAAC,MAAM,0CAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,MAAA,KAAK,CAAC,MAAM,0CAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC;AAQD;;;;;;GAMG;AACH,SAAS,qBAAqB,CAAC,UAAkB;IAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,UAAU,EACV,cAAc,EACd,SAAS,EACT,KAAK,EACL,WAAW,CACZ,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,mBAAmB,EAAE,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,WAAM,CAAC;YACP,yEAAyE;QAC3E,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,2BAA2B,CAAC,MAKpC;IACC,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IAC7E,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,mBAAmB,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS,CAAC,wCAAwC;QAEhF,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEpD,MAAM,WAAW,GACf,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAEnE,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YACpD,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QAE9C,MAAM,WAAW,GAAG,cAAc,CAChC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,EACxC,IAAI,CACL,CAAC;QAEF,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;YAC5B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,YAAY,CACnB,MAAsB,EACtB,MAAwC;;IAExC,MAAM,KAAK,GAAG,CAAC,yCAAyC,MAAM,GAAG,CAAC,CAAC;IACnE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,KAAK,CAAC,IAAI,CACR,KAAK,GAAG,KAAK,MAAA,KAAK,CAAC,OAAO,mCAAI,iBAAiB,MAAM,KAAK,CAAC,MAAM,EAAE;YACjE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAC9C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,WAAW,CAClB,MAAc,EACd,MAAwC;IAExC,MAAM,QAAQ,GAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAkC;SACtE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;SAC5B,GAAG,CACF,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,GAAG,GAAG,IAAI,MAAA,CAAC,CAAC,OAAO,mCAAI,iBAAiB,MAAM,CAAC,CAAC,MAAM,EAAE,CAAA,EAAA,CACvE,CAAC;IACJ,OAAO,CACL,mCAAmC,MAAM,MAAM;QAC/C,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC/C,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,aAAsC,EACtC,WAAoC,EACpC,MAAwC,EACxC,WAA+B;IAE/B,MAAM,SAAS,GAAG,aAAa;SAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAE,CAAC,MAAM,EAAE,CAAC;SAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,KAAK,GAAG;QACZ,SAAS,CAAC,MAAM,GAAG,CAAC;YAClB,CAAC,CAAC,2BAA2B,SAAS,GAAG;YACzC,CAAC,CAAC,+FAA+F;KACpG,CAAC;IACF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,mEAAmE;QACnE,sEAAsE;QACtE,2CAA2C;QAC3C,MAAM,OAAO,GAAG,WAAW;aACxB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAE,CAAC,MAAM,EAAE,CAAC;aAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,KAAK,CAAC,IAAI,CACR,kEAAkE,OAAO,GAAG,CAC7E,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,WAAW,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,KAAK,CAAC,IAAI,CACR,GAAG,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B;YACjE,iDAAiD,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { resolveAuthEnvironment } from '../auth/session';\nimport { cliVersion } from '../compatibility';\nimport type { CliContext } from '../context';\nimport { networkError, preconditionError, validationError } from '../errors';\nimport { EXIT_CODES } from '../exit-codes';\nimport { readInstalledPackageManifest } from '../harness-scaffold';\nimport {\n type PackageManager,\n detectPackageManager,\n installExactArgs,\n lockfileName,\n} from '../package-manager';\nimport { loadProject } from '../project';\nimport { renderTemplate, toDisplayName } from '../templates';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainKey,\n type ToolchainVersions,\n} from '../toolchain';\nimport { resolveToolchainTarget } from '../toolchain-resolve';\nimport { collectAllFindings } from '../validate-findings';\nimport { AGENT_CONTEXT_FILES } from './init';\nimport { promptYesNo } from './publish';\n\n/**\n * `ekanos upgrade` — move a project's toolchain to a coherent target version\n * and VERIFY it before calling the job done.\n *\n * The whole point of this verb is that it cannot leave a project worse off\n * than it found it: `package.json` and the lockfile are snapshotted before\n * anything installs, the target set is only ever a coherence-checked whole\n * (never \"latest of each, independently\" — see `toolchain-resolve.ts`), and\n * any failure from install through validate through the project's own test\n * script rolls everything back to the snapshot and says exactly which step\n * failed. A partner (or an agent) running this unattended should never end up\n * with a half-upgraded, uninstallable project.\n */\n\nexport interface UpgradeArgs {\n dir: string;\n host?: string;\n env: Record<string, string | undefined>;\n /** Report current vs. target and exit 0. Never mutates anything. */\n check: boolean;\n /** Skip the interactive confirm; required to mutate outside a TTY. */\n yes: boolean;\n /** Skip the post-install validate + test-script verification. */\n noVerify: boolean;\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/** Injectable seams so tests can exercise install/verify/rollback without shelling out. */\nexport interface UpgradeDeps {\n resolveTarget?: typeof resolveToolchainTarget;\n runInstall?: (\n argv: string[],\n bin: string,\n cwd: string,\n ) => Promise<{ code: number; output: string }>;\n runValidateStep?: (\n projectDir: string,\n ) => Promise<{ ok: boolean; findings?: Finding[] }>;\n runTestStep?: (\n projectDir: string,\n pm: PackageManager,\n ) => Promise<{ code: number; output: string }>;\n}\n\ninterface DeltaEntry {\n current: string | null;\n target: string;\n changed: boolean;\n}\n\nexport async function runUpgrade(\n ctx: CliContext,\n args: UpgradeArgs,\n deps: UpgradeDeps = {},\n): Promise<number> {\n const loaded = loadProject(args.dir);\n const pm = detectPackageManager(loaded.projectDir);\n\n // Captured BEFORE anything installs: the templates the project's CURRENT\n // (pre-upgrade) local `@ekanos/cli` ships, which is what actually\n // generated the agent-context files on disk — not necessarily this\n // process's own bundled templates, since `upgrade` is a machine-level verb\n // that never delegates and is routinely invoked via a DIFFERENT (global)\n // install. See `regenerateAgentContextFiles`.\n const oldAgentTemplates = captureAgentTemplates(loaded.projectDir);\n\n const current = readCurrentToolchain(loaded.projectDir);\n\n const session = resolveSessionQuietly(ctx, args);\n const resolveTarget = deps.resolveTarget ?? resolveToolchainTarget;\n const resolution = await resolveTarget({\n host: session?.host,\n session: session?.session,\n });\n\n if (resolution.status === 'unavailable') {\n throw networkError(\n 'Could not resolve a toolchain target from the Fusion host or the ' +\n 'npm registry.',\n 'Check connectivity and retry. If this deployment has no ' +\n '\"/api/partner/toolchain\" route yet, the npm registry is the ' +\n 'fallback and a failure there means npm itself is unreachable.',\n );\n }\n\n if (resolution.status === 'incoherent') {\n throw preconditionError(\n `The resolved toolchain versions do not form an installable set: ` +\n resolution.detail,\n 'Retry in a moment — this is almost always a multi-package publish ' +\n 'still in flight, and it self-resolves once every package finishes ' +\n 'publishing.',\n );\n }\n\n const target = resolution.target;\n const deltas = computeDeltas(current, target);\n\n if (args.check) {\n return ctx.succeed(\n {\n source: resolution.source,\n current,\n target,\n deltas,\n ...(resolution.source === 'host'\n ? { minimum: resolution.minimum, notes: resolution.notes }\n : {}),\n },\n checkSummary(resolution.source, deltas),\n );\n }\n\n const changedKeys = (Object.keys(deltas) as ToolchainKey[]).filter(\n (key) => deltas[key]!.changed,\n );\n\n if (changedKeys.length === 0) {\n return ctx.succeed(\n { source: resolution.source, current, target, deltas },\n `upgrade: already at the target toolchain (source: ${resolution.source}).`,\n );\n }\n\n const isHuman = !ctx.jsonMode;\n const stdinIsTTY = args.stdinIsTTY ?? process.stdin.isTTY === true;\n const canPrompt = isHuman && stdinIsTTY;\n\n if (!args.yes) {\n if (!canPrompt) {\n throw validationError(\n 'Refusing to modify the project non-interactively without --yes.',\n 'Pass \"ekanos upgrade --yes\" to proceed, or \"ekanos upgrade --check\" ' +\n 'to preview the target with no changes.',\n );\n }\n\n ctx.log(planSummary(resolution.source, deltas));\n const confirm = args.confirm ?? promptYesNo;\n const confirmed = await confirm('Upgrade now? [y/N]');\n\n if (!confirmed) {\n throw validationError(\n 'Upgrade was not confirmed.',\n 'Re-run \"ekanos upgrade --yes\" to proceed non-interactively, or ' +\n '\"ekanos upgrade --check\" to preview with no changes. Nothing was ' +\n 'modified.',\n );\n }\n }\n\n const packageJsonPath = path.join(loaded.projectDir, 'package.json');\n const manifest = readManifest(packageJsonPath);\n const specs = buildInstallSpecs(manifest, target, changedKeys);\n\n if (specs.length === 0) {\n return ctx.succeed(\n { source: resolution.source, current, target, deltas },\n 'upgrade: none of the changed packages are direct dependencies of ' +\n 'this project — nothing to install directly. Their versions follow ' +\n 'from whichever direct dependency pins them.',\n );\n }\n\n const snapshot = snapshotProject(loaded.projectDir, pm);\n\n try {\n const runInstall = deps.runInstall ?? defaultRunInstall;\n\n for (const group of [\n specs.filter((s) => !s.dev),\n specs.filter((s) => s.dev),\n ]) {\n if (group.length === 0) continue;\n\n const argv = installExactArgs(\n pm,\n group.map((s) => `${s.name}@${s.version}`),\n group[0]!.dev,\n );\n ctx.log(`Running \"${pm.bin} ${argv.join(' ')}\" in ${loaded.projectDir}…`);\n const result = await runInstall(argv, pm.bin, loaded.projectDir);\n\n if (result.code !== 0) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'install',\n exitCode: EXIT_CODES.PRECONDITION_FAILED,\n message: `\"${pm.bin} ${argv.join(' ')}\" exited with code ${result.code}.`,\n output: result.output,\n },\n );\n }\n }\n\n if (!args.noVerify) {\n const runValidateStep = deps.runValidateStep ?? defaultRunValidateStep;\n const validateResult = await runValidateStep(loaded.projectDir);\n\n if (!validateResult.ok) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'validate',\n exitCode: EXIT_CODES.VALIDATION,\n message:\n 'The upgraded toolchain fails \"ekanos validate\" against this ' +\n \"project's own integration definition.\",\n output: JSON.stringify(validateResult.findings ?? [], null, 2),\n },\n );\n }\n\n const runTestStep = deps.runTestStep ?? defaultRunTestStep;\n const testResult = await runTestStep(loaded.projectDir, pm);\n\n if (testResult.code !== 0) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'test',\n exitCode: testResult.code,\n message: `The project's test script exited with code ${testResult.code} on the upgraded toolchain.`,\n output: testResult.output,\n },\n );\n }\n }\n } finally {\n snapshot.dispose();\n }\n\n const regenerated = regenerateAgentContextFiles({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n oldTemplates: oldAgentTemplates,\n newTemplatesDir: path.join(\n loaded.projectDir,\n 'node_modules',\n '@ekanos',\n 'cli',\n 'templates',\n ),\n });\n\n // `specs` only ever covers DIRECT dependencies (see `buildInstallSpecs`) —\n // a changed key with no spec is transitive and was never actually\n // installed here, so the report (and the human summary) must say so\n // explicitly rather than implying every changed key was upgraded. This\n // matters most for exactly the scenario this PR's own delegation feature\n // targets: a globally-installed `ekanos` with no local `@ekanos/cli`\n // devDependency, where `cli` changes in `deltas` but the running global\n // binary itself is untouched by this command.\n const installedKeys = specs.map((spec) => spec.key);\n const skippedKeys = changedKeys.filter((key) => !installedKeys.includes(key));\n\n return ctx.succeed(\n {\n source: resolution.source,\n current,\n target,\n deltas,\n installed: installedKeys,\n skippedTransitive: skippedKeys,\n regenerated,\n },\n successSummary(installedKeys, skippedKeys, deltas, regenerated),\n );\n}\n\n/**\n * Best-effort host/session resolution — NEVER throws. `upgrade` must work for\n * a partner who has never logged in at all (pure npm-registry flow), so the\n * throwing `resolveAuthEnvironment`/`requireSession` machinery is unusable\n * here; this mirrors the tolerant lookup `update-notice.ts` uses.\n */\nfunction resolveSessionQuietly(\n ctx: CliContext,\n args: UpgradeArgs,\n): { host: string; session: { accessToken: string } } | null {\n try {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = env.store.read(env.host);\n return session ? { host: env.host, session } : null;\n } catch {\n return null;\n }\n}\n\nfunction readCurrentToolchain(\n projectDir: string,\n): Record<ToolchainKey, string | null> {\n const result = {} as Record<ToolchainKey, string | null>;\n\n for (const [key, pkg] of Object.entries(TOOLCHAIN_PACKAGES) as [\n ToolchainKey,\n string,\n ][]) {\n const installed = readInstalledPackageManifest(projectDir, pkg);\n result[key] = installed?.version ?? (key === 'cli' ? cliVersion() : null);\n }\n\n return result;\n}\n\nfunction computeDeltas(\n current: Record<ToolchainKey, string | null>,\n target: ToolchainVersions,\n): Record<ToolchainKey, DeltaEntry> {\n const keys = Object.keys(TOOLCHAIN_PACKAGES) as ToolchainKey[];\n const result = {} as Record<ToolchainKey, DeltaEntry>;\n\n for (const key of keys) {\n result[key] = {\n current: current[key],\n target: target[key],\n changed: current[key] !== target[key],\n };\n }\n\n return result;\n}\n\ninterface InstallSpec {\n key: ToolchainKey;\n name: string;\n version: string;\n dev: boolean;\n}\n\n/**\n * Only packages the project depends on DIRECTLY are installed explicitly —\n * `@ekanos/integration-schema` and often `@ekanos/harness` are transitive\n * (pulled in by `@ekanos/sdk`), and forcing an exact transitive version would\n * need a package-manager-specific override mechanism this CLI does not\n * manage. Their versions follow from whichever direct dependency pins them,\n * which is exactly what the coherent target guarantees is already correct.\n */\nfunction buildInstallSpecs(\n manifest: Record<string, unknown>,\n target: ToolchainVersions,\n changedKeys: readonly ToolchainKey[],\n): InstallSpec[] {\n const deps = (manifest.dependencies ?? {}) as Record<string, unknown>;\n const devDeps = (manifest.devDependencies ?? {}) as Record<string, unknown>;\n\n const specs: InstallSpec[] = [];\n\n for (const key of changedKeys) {\n const pkg = TOOLCHAIN_PACKAGES[key];\n if (pkg in deps) {\n specs.push({ key, name: pkg, version: target[key], dev: false });\n } else if (pkg in devDeps) {\n specs.push({ key, name: pkg, version: target[key], dev: true });\n }\n }\n\n return specs;\n}\n\nfunction readManifest(packageJsonPath: string): Record<string, unknown> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n } catch (error) {\n throw preconditionError(\n `Could not read ${packageJsonPath}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'Ensure the project has a valid package.json, then re-run \"ekanos upgrade\".',\n );\n }\n if (typeof parsed !== 'object' || parsed === null) {\n throw preconditionError(\n `${packageJsonPath} does not contain a JSON object.`,\n 'Fix package.json, then re-run \"ekanos upgrade\".',\n );\n }\n return parsed as Record<string, unknown>;\n}\n\ninterface Snapshot {\n restore: () => void;\n dispose: () => void;\n}\n\n/**\n * Copy `package.json` and the lockfile to a temp directory before anything\n * installs. `restore()` copies them back (and removes a lockfile that did not\n * exist before, if the failed install created one); `dispose()` cleans up the\n * temp copies once the run is over, success or failure.\n */\nfunction snapshotProject(projectDir: string, pm: PackageManager): Snapshot {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ekanos-upgrade-'));\n const packageJsonPath = path.join(projectDir, 'package.json');\n\n // bun has shipped both a text (`bun.lock`) and a legacy binary\n // (`bun.lockb`) lockfile; snapshot whichever is actually present.\n const lockCandidates =\n pm.name === 'bun' ? ['bun.lock', 'bun.lockb'] : [lockfileName(pm)];\n const lockName = lockCandidates.find((name) =>\n fs.existsSync(path.join(projectDir, name)),\n );\n const lockfilePath = lockName\n ? path.join(projectDir, lockName)\n : path.join(projectDir, lockfileName(pm));\n const lockfileExisted = lockName !== undefined;\n\n fs.copyFileSync(packageJsonPath, path.join(tmpDir, 'package.json'));\n if (lockfileExisted) {\n fs.copyFileSync(lockfilePath, path.join(tmpDir, 'lockfile.snapshot'));\n }\n\n return {\n restore: () => {\n fs.copyFileSync(path.join(tmpDir, 'package.json'), packageJsonPath);\n if (lockfileExisted) {\n fs.copyFileSync(path.join(tmpDir, 'lockfile.snapshot'), lockfilePath);\n } else if (fs.existsSync(lockfilePath)) {\n fs.rmSync(lockfilePath);\n }\n },\n dispose: () => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n },\n };\n}\n\ninterface StepFailure {\n step: 'install' | 'validate' | 'test';\n exitCode: number;\n message: string;\n output: string;\n}\n\n/**\n * The one rollback path every failure funnels through: restore the snapshot,\n * reinstall to put `node_modules` back in sync with the restored\n * `package.json`/lockfile, and report exactly which step failed and what it\n * said — with an explicit statement that the project was restored, so a\n * partner (or an agent) never has to wonder whether they are looking at a\n * half-upgraded tree.\n */\nasync function failAndRollback(\n ctx: CliContext,\n snapshot: Snapshot,\n pm: PackageManager,\n projectDir: string,\n runInstall: (\n argv: string[],\n bin: string,\n cwd: string,\n ) => Promise<{ code: number; output: string }>,\n failure: StepFailure,\n): Promise<number> {\n ctx.warn(\n `ekanos: the \"${failure.step}\" step failed — rolling back package.json ` +\n 'and the lockfile, then reinstalling to restore node_modules…',\n );\n\n snapshot.restore();\n // The SAME injectable installer seam used for the forward install — a test\n // that mocks it never shells out for the rollback path either.\n const restore = await runInstall(['install'], pm.bin, projectDir);\n\n return ctx.failWith({\n code: `upgrade_${failure.step}_failed`,\n message: failure.message,\n hint:\n `The project HAS BEEN RESTORED to its pre-upgrade state (package.json, ` +\n `lockfile and node_modules all rolled back${\n restore.code === 0\n ? ''\n : ' — though the restore install itself also failed with code ' +\n `${restore.code}; run \"${pm.bin} install\" by hand to finish ` +\n 'repairing node_modules'\n }). Read the output in data.output, fix the underlying issue, and ` +\n 're-run \"ekanos upgrade\".',\n exitCode: failure.exitCode,\n data: { step: failure.step, output: failure.output },\n });\n}\n\nasync function defaultRunInstall(\n argv: string[],\n bin: string,\n cwd: string,\n): Promise<{ code: number; output: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(bin, argv, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });\n let output = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => resolve({ code: code ?? 1, output }));\n });\n}\n\nasync function defaultRunValidateStep(\n projectDir: string,\n): Promise<{ ok: boolean; findings?: Finding[] }> {\n const loaded = loadProject(projectDir);\n const findings = await collectAllFindings(loaded);\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n return { ok: errorCount === 0, findings };\n}\n\nasync function defaultRunTestStep(\n projectDir: string,\n pm: PackageManager,\n): Promise<{ code: number; output: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(pm.bin, ['run', 'test'], {\n cwd: projectDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let output = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => resolve({ code: code ?? 1, output }));\n });\n}\n\ninterface RegenerationReport {\n updated: string[];\n unchanged: string[];\n needsAttention: string[];\n}\n\n/**\n * Read every `AGENT_CONTEXT_FILES` template source out of the project's\n * CURRENT (pre-upgrade) local `@ekanos/cli` install, before anything installs\n * over it. Missing entirely (the package isn't a resolvable local dependency)\n * just means every file below is treated conservatively — see\n * `regenerateAgentContextFiles`.\n */\nfunction captureAgentTemplates(projectDir: string): Map<string, string> {\n const templatesDir = path.join(\n projectDir,\n 'node_modules',\n '@ekanos',\n 'cli',\n 'templates',\n );\n const captured = new Map<string, string>();\n\n for (const { template } of AGENT_CONTEXT_FILES) {\n const templatePath = path.join(templatesDir, template);\n try {\n captured.set(template, fs.readFileSync(templatePath, 'utf8'));\n } catch {\n // Not resolvable — left out of the map, handled as \"can't verify\" below.\n }\n }\n\n return captured;\n}\n\n/**\n * Regenerate `AGENTS.md`, `CLAUDE.md` and the Claude Code skill from the\n * NEWLY installed CLI's bundled templates — using the exact byte-comparison\n * discipline `harness-scaffold.ts` applies to the dev harness shell: a file\n * whose current bytes match what the project's PRE-upgrade local CLI would\n * have rendered (captured in `oldTemplates`, before install ran) is\n * untouched by the partner and safe to regenerate; anything else has been\n * edited and is left alone, reported so a human can merge it by hand. A\n * stale AGENTS.md teaches an agent yesterday's contract — that is as harmful\n * as a stale binary, which is why this runs on every successful upgrade\n * rather than only on `init`.\n */\nfunction regenerateAgentContextFiles(params: {\n projectDir: string;\n slug: string;\n oldTemplates: Map<string, string>;\n newTemplatesDir: string;\n}): RegenerationReport {\n const vars = { SLUG: params.slug, DISPLAY_NAME: toDisplayName(params.slug) };\n const updated: string[] = [];\n const unchanged: string[] = [];\n const needsAttention: string[] = [];\n\n for (const { template, dest } of AGENT_CONTEXT_FILES) {\n const destPath = path.join(params.projectDir, dest);\n if (!fs.existsSync(destPath)) continue; // never scaffolded — nothing to upgrade\n\n const current = fs.readFileSync(destPath, 'utf8');\n const oldSource = params.oldTemplates.get(template);\n\n const oldRendered =\n oldSource === undefined ? null : renderTemplate(oldSource, vars);\n\n if (oldRendered === null || current !== oldRendered) {\n needsAttention.push(dest);\n continue;\n }\n\n const newTemplatePath = path.join(params.newTemplatesDir, template);\n if (!fs.existsSync(newTemplatePath)) continue;\n\n const newRendered = renderTemplate(\n fs.readFileSync(newTemplatePath, 'utf8'),\n vars,\n );\n\n if (newRendered === current) {\n unchanged.push(dest);\n continue;\n }\n\n fs.writeFileSync(destPath, newRendered);\n updated.push(dest);\n }\n\n return { updated, unchanged, needsAttention };\n}\n\nfunction checkSummary(\n source: 'host' | 'npm',\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n const lines = [`upgrade --check: target resolved from ${source}.`];\n for (const [key, delta] of Object.entries(deltas)) {\n lines.push(\n ` ${key}: ${delta.current ?? '(not installed)'} → ${delta.target}` +\n (delta.changed ? '' : ' (already current)'),\n );\n }\n return lines.join('\\n');\n}\n\nfunction planSummary(\n source: string,\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n const changing = (Object.entries(deltas) as [ToolchainKey, DeltaEntry][])\n .filter(([, d]) => d.changed)\n .map(\n ([key, d]) => `${key} ${d.current ?? '(not installed)'} → ${d.target}`,\n );\n return (\n `Upgrading (target resolved from ${source}):\\n` +\n changing.map((line) => ` ${line}`).join('\\n')\n );\n}\n\nfunction successSummary(\n installedKeys: readonly ToolchainKey[],\n skippedKeys: readonly ToolchainKey[],\n deltas: Record<ToolchainKey, DeltaEntry>,\n regenerated: RegenerationReport,\n): string {\n const installed = installedKeys\n .map((key) => `${key} → ${deltas[key]!.target}`)\n .join(', ');\n const parts = [\n installed.length > 0\n ? `upgrade: OK — installed ${installed}.`\n : 'upgrade: OK — nothing was a direct dependency of this project, so nothing was installed here.',\n ];\n if (skippedKeys.length > 0) {\n // These changed in the resolved target but are transitive (or, for\n // \"cli\", the invoked binary was global with no local devDependency) —\n // never touched by the install step above.\n const skipped = skippedKeys\n .map((key) => `${key} → ${deltas[key]!.target}`)\n .join(', ');\n parts.push(\n `Not installed directly (transitive or not a local dependency): ${skipped}.`,\n );\n }\n if (regenerated.updated.length > 0) {\n parts.push(`regenerated ${regenerated.updated.join(', ')}.`);\n }\n if (regenerated.needsAttention.length > 0) {\n parts.push(\n `${regenerated.needsAttention.join(', ')} were locally modified — ` +\n 'left untouched, merge the new template by hand.',\n );\n }\n return parts.join(' ');\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CliContext } from '../context.js';
|
|
2
|
+
import type { ExitCode } from '../exit-codes.js';
|
|
3
|
+
export interface UseArgs {
|
|
4
|
+
host?: string;
|
|
5
|
+
/** Project directory (contains ekanos.json). Defaults to cwd. */
|
|
6
|
+
dir: string;
|
|
7
|
+
/** The source slug to switch this project's publish target to. */
|
|
8
|
+
slug?: string;
|
|
9
|
+
env: Record<string, string | undefined>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `use <source-slug>` — set this project's publish target, with the same
|
|
13
|
+
* seat verification `publish` runs: the slug must name a source the caller
|
|
14
|
+
* actually holds a seat on, so a project can never be pointed at a source by
|
|
15
|
+
* a typo alone.
|
|
16
|
+
*
|
|
17
|
+
* Persisted via the exact same mechanics `publish` uses after a successful
|
|
18
|
+
* submission (`persistProjectFields`), so `host` also sticks on first write
|
|
19
|
+
* and a later `publish` needs neither flag.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runUse(ctx: CliContext, args: UseArgs): Promise<ExitCode>;
|