@microwiseai/snapshot 0.5.2 → 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/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +37 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +68 -206
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/publish.d.ts +0 -4
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +1 -35
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +52 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/index.js +18 -27
- package/dist/index.js.map +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/gitlab.d.ts.map +1 -1
- package/dist/lib/gitlab.js +8 -6
- package/dist/lib/gitlab.js.map +1 -1
- package/dist/lib/prerequisites.d.ts +33 -0
- package/dist/lib/prerequisites.d.ts.map +1 -0
- package/dist/lib/prerequisites.js +123 -0
- package/dist/lib/prerequisites.js.map +1 -0
- package/dist/lib/transitive-resolver.d.ts +0 -121
- package/dist/lib/transitive-resolver.d.ts.map +1 -1
- package/dist/lib/transitive-resolver.js +0 -190
- package/dist/lib/transitive-resolver.js.map +1 -1
- package/package.json +3 -20
- package/dist/commands/clone-all.d.ts +0 -21
- package/dist/commands/clone-all.d.ts.map +0 -1
- package/dist/commands/clone-all.js +0 -57
- package/dist/commands/clone-all.js.map +0 -1
- package/dist/commands/preflight.d.ts +0 -31
- package/dist/commands/preflight.d.ts.map +0 -1
- package/dist/commands/preflight.js +0 -83
- package/dist/commands/preflight.js.map +0 -1
- package/dist/lib/distkit-closure-bridge.d.ts +0 -102
- package/dist/lib/distkit-closure-bridge.d.ts.map +0 -1
- package/dist/lib/distkit-closure-bridge.js +0 -177
- package/dist/lib/distkit-closure-bridge.js.map +0 -1
- package/dist/lib/distkit-closure-parity.d.ts +0 -110
- package/dist/lib/distkit-closure-parity.d.ts.map +0 -1
- package/dist/lib/distkit-closure-parity.js +0 -134
- package/dist/lib/distkit-closure-parity.js.map +0 -1
- package/dist/lib/distkit-routing-port.d.ts +0 -37
- package/dist/lib/distkit-routing-port.d.ts.map +0 -1
- package/dist/lib/distkit-routing-port.js +0 -72
- package/dist/lib/distkit-routing-port.js.map +0 -1
- package/dist/lib/install-timeline.d.ts +0 -81
- package/dist/lib/install-timeline.d.ts.map +0 -1
- package/dist/lib/install-timeline.js +0 -243
- package/dist/lib/install-timeline.js.map +0 -1
- package/dist/lib/preflight-adapter.d.ts +0 -52
- package/dist/lib/preflight-adapter.d.ts.map +0 -1
- package/dist/lib/preflight-adapter.js +0 -224
- package/dist/lib/preflight-adapter.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/README.md +0 -117
- package/node_modules/@gitdot/clone-lib/dist/bin/gitdot-resolve.d.ts +0 -9
- package/node_modules/@gitdot/clone-lib/dist/bin/gitdot-resolve.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/bin/gitdot-resolve.js +0 -154
- package/node_modules/@gitdot/clone-lib/dist/bin/gitdot-resolve.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/clone.d.ts +0 -21
- package/node_modules/@gitdot/clone-lib/dist/clone.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/clone.js +0 -228
- package/node_modules/@gitdot/clone-lib/dist/clone.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/dotpath.d.ts +0 -10
- package/node_modules/@gitdot/clone-lib/dist/dotpath.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/dotpath.js +0 -27
- package/node_modules/@gitdot/clone-lib/dist/dotpath.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/git.d.ts +0 -23
- package/node_modules/@gitdot/clone-lib/dist/git.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/git.js +0 -134
- package/node_modules/@gitdot/clone-lib/dist/git.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/index.d.ts +0 -17
- package/node_modules/@gitdot/clone-lib/dist/index.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/index.js +0 -35
- package/node_modules/@gitdot/clone-lib/dist/index.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/lock.d.ts +0 -24
- package/node_modules/@gitdot/clone-lib/dist/lock.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/lock.js +0 -97
- package/node_modules/@gitdot/clone-lib/dist/lock.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/gitlab.d.ts +0 -15
- package/node_modules/@gitdot/clone-lib/dist/resolve/gitlab.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/gitlab.js +0 -124
- package/node_modules/@gitdot/clone-lib/dist/resolve/gitlab.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/index.d.ts +0 -12
- package/node_modules/@gitdot/clone-lib/dist/resolve/index.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/index.js +0 -31
- package/node_modules/@gitdot/clone-lib/dist/resolve/index.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/manifest.d.ts +0 -32
- package/node_modules/@gitdot/clone-lib/dist/resolve/manifest.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/manifest.js +0 -159
- package/node_modules/@gitdot/clone-lib/dist/resolve/manifest.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/member.d.ts +0 -44
- package/node_modules/@gitdot/clone-lib/dist/resolve/member.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/member.js +0 -163
- package/node_modules/@gitdot/clone-lib/dist/resolve/member.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/resolve.d.ts +0 -18
- package/node_modules/@gitdot/clone-lib/dist/resolve/resolve.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/resolve/resolve.js +0 -155
- package/node_modules/@gitdot/clone-lib/dist/resolve/resolve.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/types.d.ts +0 -225
- package/node_modules/@gitdot/clone-lib/dist/types.d.ts.map +0 -1
- package/node_modules/@gitdot/clone-lib/dist/types.js +0 -9
- package/node_modules/@gitdot/clone-lib/dist/types.js.map +0 -1
- package/node_modules/@gitdot/clone-lib/package.json +0 -53
- package/node_modules/@microwiseai/install-preflight/dist/index.d.ts +0 -27
- package/node_modules/@microwiseai/install-preflight/dist/index.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/index.js +0 -26
- package/node_modules/@microwiseai/install-preflight/dist/index.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/policy.d.ts +0 -42
- package/node_modules/@microwiseai/install-preflight/dist/policy.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/policy.js +0 -79
- package/node_modules/@microwiseai/install-preflight/dist/policy.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/preflight.d.ts +0 -18
- package/node_modules/@microwiseai/install-preflight/dist/preflight.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/preflight.js +0 -0
- package/node_modules/@microwiseai/install-preflight/dist/preflight.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.d.ts +0 -26
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.js +0 -63
- package/node_modules/@microwiseai/install-preflight/dist/resolvers.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/semver.d.ts +0 -45
- package/node_modules/@microwiseai/install-preflight/dist/semver.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/semver.js +0 -195
- package/node_modules/@microwiseai/install-preflight/dist/semver.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/types.d.ts +0 -136
- package/node_modules/@microwiseai/install-preflight/dist/types.d.ts.map +0 -1
- package/node_modules/@microwiseai/install-preflight/dist/types.js +0 -11
- package/node_modules/@microwiseai/install-preflight/dist/types.js.map +0 -1
- package/node_modules/@microwiseai/install-preflight/package.json +0 -49
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAoCnD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { checkPrerequisites, MINIMAL_PREREQUISITES } from '../lib/prerequisites.js';
|
|
3
|
+
export async function doctorCommand() {
|
|
4
|
+
console.log(chalk.blue('Checking prerequisites...\n'));
|
|
5
|
+
const results = checkPrerequisites(MINIMAL_PREREQUISITES);
|
|
6
|
+
const missing = results.filter(r => !r.installed);
|
|
7
|
+
// Print results
|
|
8
|
+
for (const r of results) {
|
|
9
|
+
const icon = r.installed ? chalk.green('✅') : chalk.red('❌');
|
|
10
|
+
console.log(` ${icon} ${r.name}`);
|
|
11
|
+
if (!r.installed) {
|
|
12
|
+
console.log(chalk.gray(` Required by: ${r.requiredBy.join(', ')}`));
|
|
13
|
+
if (r.installCommand) {
|
|
14
|
+
console.log(chalk.gray(` Fix: ${r.installCommand}`));
|
|
15
|
+
}
|
|
16
|
+
if (r.guide) {
|
|
17
|
+
console.log(chalk.gray(` ${r.guide}`));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
console.log('');
|
|
22
|
+
if (missing.length === 0) {
|
|
23
|
+
console.log(chalk.green('All prerequisites satisfied! ✓'));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
console.log(chalk.yellow(`${missing.length} issue(s) found.`));
|
|
27
|
+
const fixable = missing.filter(r => r.installCommand);
|
|
28
|
+
if (fixable.length > 0) {
|
|
29
|
+
console.log(chalk.gray(`Run 'snapshot setup' to install ${fixable.length} missing prerequisite(s).`));
|
|
30
|
+
}
|
|
31
|
+
const manual = missing.filter(r => !r.installCommand);
|
|
32
|
+
if (manual.length > 0) {
|
|
33
|
+
console.log(chalk.gray(`${manual.length} require manual action (see above).`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEpF,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,kBAAkB,CAAC,qBAAqB,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAElD,gBAAgB;IAChB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACxE,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;YAC3D,CAAC;YACD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;IAC7D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,kBAAkB,CAAC,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mCAAmC,OAAO,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC;QACxG,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QACtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,qCAAqC,CAAC,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAiBjE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAkeD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,KAAK,GAAE,MAAU,EACjB,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAM,EACtD,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;CAAE,CAAC,CA8VjF;AA4FD,MAAM,WAAW,qBAAqB;IACpC,sFAAsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAoLD,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA6arG"}
|
package/dist/commands/install.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { execSync
|
|
3
|
-
import { writeFileSync, existsSync, readFileSync, unlinkSync
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { writeFileSync, existsSync, readFileSync, unlinkSync } from 'fs';
|
|
4
4
|
import { homedir, platform, arch } from 'os';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { fetchSnapshotFromRepo, snapshotNameToRepoName } from '../lib/gitlab.js';
|
|
@@ -14,12 +14,6 @@ import { authorizeSession, packagesToSpecs } from '../lib/session.js';
|
|
|
14
14
|
import { parallelInstall, recordToEntries } from '@microwiseai/snapshot-parallel';
|
|
15
15
|
import { dedupWithReport, dedupInstallBlock, recordInstalledSpecs, recordReferences, computeReplacePlan, applyReplaceHistory, } from '@microwiseai/snapshot-dedup';
|
|
16
16
|
import { createInterface } from 'readline';
|
|
17
|
-
import { createInstallTimeline } from '../lib/install-timeline.js';
|
|
18
|
-
// Install-timeline instrumentation (investigation only, env opt-in). A disabled
|
|
19
|
-
// no-op is the default so module-level callers (runCommand, installSkitPackages)
|
|
20
|
-
// always have a valid handle; installCommand() reassigns a live instance when
|
|
21
|
-
// SNAPSHOT_INSTALL_TIMELINE is set. Adds zero behavior — only measurement.
|
|
22
|
-
let activeTimeline = createInstallTimeline({ enabled: false });
|
|
23
17
|
// CLI version — dynamically read from package.json (SSOT)
|
|
24
18
|
import { createRequire } from 'module';
|
|
25
19
|
const _require = createRequire(import.meta.url);
|
|
@@ -200,74 +194,27 @@ function commandExists(cmd) {
|
|
|
200
194
|
// Captures the failure reason of the most recent runCommand() call so callers
|
|
201
195
|
// can surface it (debugging visibility). Undefined when the last call succeeded.
|
|
202
196
|
let lastCommandError;
|
|
203
|
-
// Bound the captured-stderr buffer so a chatty command can't grow it without
|
|
204
|
-
// limit; on overflow we keep the tail (where the actual failure usually is).
|
|
205
|
-
const MAX_STDERR_CAPTURE = 64 * 1024;
|
|
206
|
-
// Runs a shell command, streaming its output live while still capturing stderr
|
|
207
|
-
// for failure diagnostics.
|
|
208
|
-
//
|
|
209
|
-
// Why spawn (async) instead of execSync: long installs (e.g. `npm install -g`
|
|
210
|
-
// of the whole pre-install batch) push their live progress to STDERR. execSync
|
|
211
|
-
// with a piped stderr hid that progress entirely — the terminal sat silent for
|
|
212
|
-
// minutes, then dumped a summary. Here stderr is piped so we can BOTH tee it to
|
|
213
|
-
// our own stderr in real time (visible progress) AND accumulate it, so the raw
|
|
214
|
-
// reason is still available via lastCommandError when the command fails.
|
|
215
|
-
//
|
|
216
|
-
// stdout stays inherited (live, unbuffered). shell:true preserves the previous
|
|
217
|
-
// execSync parsing of the full command string.
|
|
218
197
|
function runCommand(command) {
|
|
219
198
|
lastCommandError = undefined;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// in practice, but guard against a double-resolve just in case.
|
|
240
|
-
let settled = false;
|
|
241
|
-
const finish = (ok) => {
|
|
242
|
-
if (settled)
|
|
243
|
-
return;
|
|
244
|
-
settled = true;
|
|
245
|
-
resolve(ok);
|
|
246
|
-
};
|
|
247
|
-
child.on('error', (err) => {
|
|
248
|
-
// Spawn-level failure (e.g. shell missing) — no exit code available.
|
|
249
|
-
lastCommandError = `spawn error — ${err.message}`;
|
|
250
|
-
span.end({ success: false, exitReason: 'spawn error' });
|
|
251
|
-
console.log(chalk.red(` Command failed to start: ${command}`));
|
|
252
|
-
console.log(chalk.red(err.message));
|
|
253
|
-
finish(false);
|
|
254
|
-
});
|
|
255
|
-
child.on('close', (code) => {
|
|
256
|
-
if (code === 0) {
|
|
257
|
-
span.end({ success: true });
|
|
258
|
-
finish(true);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
const detail = stderrBuf.trim() || 'unknown error';
|
|
262
|
-
lastCommandError = `exit code ${code ?? 'unknown'} — ${detail}`;
|
|
263
|
-
span.end({ success: false, exitReason: `exit code ${code ?? 'unknown'}` });
|
|
264
|
-
// stderr was already streamed live above, so we don't re-print the full
|
|
265
|
-
// detail here (that would duplicate it). Callers surface lastCommandError
|
|
266
|
-
// where they want a compact "Reason:" line.
|
|
267
|
-
console.log(chalk.red(` Command failed (exit code ${code ?? 'unknown'}): ${command}`));
|
|
268
|
-
finish(false);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
199
|
+
try {
|
|
200
|
+
// Keep stdout visible live, but capture stderr so we can surface the raw
|
|
201
|
+
// error reason instead of swallowing it on failure.
|
|
202
|
+
execSync(command, { stdio: ['inherit', 'inherit', 'pipe'] });
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
const exitCode = err?.status;
|
|
207
|
+
const stderr = err?.stderr?.toString().trim();
|
|
208
|
+
const message = err?.message ? String(err.message).trim() : undefined;
|
|
209
|
+
const detail = stderr || message || 'unknown error';
|
|
210
|
+
lastCommandError = `exit code ${exitCode ?? 'unknown'} — ${detail}`;
|
|
211
|
+
// Surface the raw failure (stderr is no longer shown live since it is piped).
|
|
212
|
+
console.log(chalk.red(` Command failed (exit code ${exitCode ?? 'unknown'}): ${command}`));
|
|
213
|
+
if (detail) {
|
|
214
|
+
console.log(chalk.red(detail));
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
271
218
|
}
|
|
272
219
|
/**
|
|
273
220
|
* Check if a skit package is already installed with the specified version
|
|
@@ -300,71 +247,6 @@ function isNpmGlobalInstalled(name, version) {
|
|
|
300
247
|
return false;
|
|
301
248
|
}
|
|
302
249
|
}
|
|
303
|
-
// Preflight is a diagnostic: it runs once per process and warns loudly, but
|
|
304
|
-
// does NOT abort — skit still tries, so any real error path stays intact.
|
|
305
|
-
let istWritabilityChecked = false;
|
|
306
|
-
/**
|
|
307
|
-
* Verify the current user can actually write into ~/.ist before skit installs.
|
|
308
|
-
*
|
|
309
|
-
* Motivation: when ~/.ist is mounted as a root:root Docker named volume and the
|
|
310
|
-
* process runs as a non-root user, skit's writes fail with EACCES. skit swallows
|
|
311
|
-
* that into an opaque "Error: undefined" and the install silently under-counts
|
|
312
|
-
* (e.g. 7/12). This surfaces the real cause up front with a concrete fix.
|
|
313
|
-
*
|
|
314
|
-
* Detection is a real write probe (create+delete a temp file), which is more
|
|
315
|
-
* reliable than fs.accessSync(W_OK) — the latter can misreport under root/ACLs.
|
|
316
|
-
* Missing directories are created first (that itself exercises writability of
|
|
317
|
-
* the parent). Never throws; a probe failure just flags the directory.
|
|
318
|
-
*/
|
|
319
|
-
function checkIstWritable() {
|
|
320
|
-
if (istWritabilityChecked)
|
|
321
|
-
return;
|
|
322
|
-
istWritabilityChecked = true;
|
|
323
|
-
const istRoot = join(homedir(), '.ist');
|
|
324
|
-
// Directories skit writes into: the root itself (installed-kits.json lives
|
|
325
|
-
// here), global prompts, and profiles.
|
|
326
|
-
const targets = [
|
|
327
|
-
istRoot,
|
|
328
|
-
join(istRoot, 'prompts', 'global'),
|
|
329
|
-
join(istRoot, 'profiles'),
|
|
330
|
-
];
|
|
331
|
-
const notWritable = [];
|
|
332
|
-
for (const dir of targets) {
|
|
333
|
-
try {
|
|
334
|
-
if (!existsSync(dir)) {
|
|
335
|
-
// Creating the dir also proves the parent is writable. If this throws
|
|
336
|
-
// (EACCES on a root-owned volume), the dir is not usable → flag it.
|
|
337
|
-
mkdirSync(dir, { recursive: true });
|
|
338
|
-
}
|
|
339
|
-
const probe = join(dir, `.snapshot-write-probe-${process.pid}`);
|
|
340
|
-
writeFileSync(probe, '');
|
|
341
|
-
unlinkSync(probe);
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
notWritable.push(dir);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
// installed-kits.json may already exist with restrictive ownership even when
|
|
348
|
-
// its directory is writable — probe the file directly if present.
|
|
349
|
-
const kitsFile = join(istRoot, 'installed-kits.json');
|
|
350
|
-
if (existsSync(kitsFile)) {
|
|
351
|
-
try {
|
|
352
|
-
writeFileSync(kitsFile, readFileSync(kitsFile));
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
notWritable.push(kitsFile);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (notWritable.length > 0) {
|
|
359
|
-
console.log('');
|
|
360
|
-
console.log(chalk.yellow('⚠ ~/.ist is not writable by current user (owned by root?) — skit installs will fail.'));
|
|
361
|
-
for (const p of notWritable) {
|
|
362
|
-
console.log(chalk.gray(` not writable: ${p}`));
|
|
363
|
-
}
|
|
364
|
-
console.log(chalk.gray(' Fix directory ownership: chown -R $(whoami) ~/.ist'));
|
|
365
|
-
console.log('');
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
250
|
/**
|
|
369
251
|
* Skit 패키지 설치 (lib 우선, CLI fallback)
|
|
370
252
|
* @ist/skit lib가 있으면 직접 호출, 없으면 CLI 사용
|
|
@@ -375,10 +257,6 @@ async function installSkitPackages(packages, snapshotName, indent, sessionId) {
|
|
|
375
257
|
if (packageEntries.length === 0) {
|
|
376
258
|
return result;
|
|
377
259
|
}
|
|
378
|
-
// Diagnostic preflight (once/process): if ~/.ist isn't writable by the current
|
|
379
|
-
// user, skit installs fail opaquely ("Error: undefined", under-counted total).
|
|
380
|
-
// Warn with the concrete fix before we start the skit loop.
|
|
381
|
-
checkIstWritable();
|
|
382
260
|
const total = packageEntries.length;
|
|
383
261
|
console.log('');
|
|
384
262
|
console.log(chalk.yellow(`${indent}=== ${snapshotName}: ${total} skits ===`));
|
|
@@ -391,14 +269,7 @@ async function installSkitPackages(packages, snapshotName, indent, sessionId) {
|
|
|
391
269
|
if (isSkitInstalled(entry.name, entry.version)) {
|
|
392
270
|
return { success: true, skipped: true };
|
|
393
271
|
}
|
|
394
|
-
// Per-package timing (investigation only) — surfaces individual skit
|
|
395
|
-
// hotspots (e.g. infra-kit/mwd-kit) under the install-snapshot phase.
|
|
396
|
-
const pkgSpan = activeTimeline.start(`${entry.name}@${entry.version}`, {
|
|
397
|
-
category: 'package',
|
|
398
|
-
meta: { installer: 'skit' },
|
|
399
|
-
});
|
|
400
272
|
const installResult = await installSkitPackage(entry.name, entry.version, skitOptions);
|
|
401
|
-
pkgSpan.end({ success: installResult.success });
|
|
402
273
|
return {
|
|
403
274
|
success: installResult.success,
|
|
404
275
|
error: installResult.error,
|
|
@@ -450,7 +321,7 @@ async function installPackagesByType(packages, installerName, installers, snapsh
|
|
|
450
321
|
if (!commandExists(cmdName)) {
|
|
451
322
|
console.log(chalk.yellow(`${indent}${installerName} not found. Attempting auto-install...`));
|
|
452
323
|
console.log(chalk.gray(`${indent} Running: ${installerConfig.hint}`));
|
|
453
|
-
if (
|
|
324
|
+
if (runCommand(installerConfig.hint)) {
|
|
454
325
|
console.log(chalk.green(`${indent} ✓ ${installerName} installed successfully`));
|
|
455
326
|
}
|
|
456
327
|
else {
|
|
@@ -483,7 +354,7 @@ async function installPackagesByType(packages, installerName, installers, snapsh
|
|
|
483
354
|
const specs = toInstall.map(e => `${e.name}@${e.version}`);
|
|
484
355
|
const batchCommand = installerConfig.command.replace('{pkg}', specs.join(' '));
|
|
485
356
|
console.log(chalk.gray(`${indent} Running batch: npm install -g (${specs.length} packages)`));
|
|
486
|
-
const success =
|
|
357
|
+
const success = runCommand(batchCommand);
|
|
487
358
|
if (success) {
|
|
488
359
|
for (const spec of specs) {
|
|
489
360
|
console.log(`${indent}${chalk.blue('batch')} ${spec}...${chalk.green(' ✓')}`);
|
|
@@ -498,7 +369,7 @@ async function installPackagesByType(packages, installerName, installers, snapsh
|
|
|
498
369
|
for (const entry of toInstall) {
|
|
499
370
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
500
371
|
const command = installerConfig.command.replace('{pkg}', pkgSpec);
|
|
501
|
-
const ok =
|
|
372
|
+
const ok = runCommand(command);
|
|
502
373
|
if (ok) {
|
|
503
374
|
console.log(`${indent}${chalk.blue('fallback')} ${pkgSpec}...${chalk.green(' ✓')}`);
|
|
504
375
|
result.installed.push(pkgSpec);
|
|
@@ -516,7 +387,7 @@ async function installPackagesByType(packages, installerName, installers, snapsh
|
|
|
516
387
|
const parallelResult = await parallelInstall(entries, async (entry) => {
|
|
517
388
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
518
389
|
const command = installerConfig.command.replace('{pkg}', pkgSpec);
|
|
519
|
-
const success =
|
|
390
|
+
const success = runCommand(command);
|
|
520
391
|
return { success, error: success ? undefined : `Command failed: ${command}` };
|
|
521
392
|
}, {
|
|
522
393
|
concurrency: 4,
|
|
@@ -691,7 +562,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
691
562
|
if (!commandExists(cmdName)) {
|
|
692
563
|
console.log(chalk.yellow(`${indent}${installerName} not found. Attempting auto-install...`));
|
|
693
564
|
console.log(chalk.gray(`${indent} Running: ${installerConfig.hint}`));
|
|
694
|
-
if (
|
|
565
|
+
if (runCommand(installerConfig.hint)) {
|
|
695
566
|
console.log(chalk.green(`${indent} ✓ ${installerName} installed successfully`));
|
|
696
567
|
}
|
|
697
568
|
else {
|
|
@@ -725,7 +596,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
725
596
|
const specs = toInstall.map(e => `${e.name}@${e.version}`);
|
|
726
597
|
const batchCommand = installCfg.command.replace('{pkg}', specs.join(' '));
|
|
727
598
|
console.log(chalk.gray(`${indent} Running batch: npm install -g (${specs.length} packages)`));
|
|
728
|
-
const success =
|
|
599
|
+
const success = runCommand(batchCommand);
|
|
729
600
|
if (success) {
|
|
730
601
|
for (const spec of specs) {
|
|
731
602
|
console.log(`${indent}${chalk.blue('batch')} ${spec}...${chalk.green(' ✓')}`);
|
|
@@ -740,7 +611,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
740
611
|
for (const entry of toInstall) {
|
|
741
612
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
742
613
|
const command = installCfg.command.replace('{pkg}', pkgSpec);
|
|
743
|
-
const ok =
|
|
614
|
+
const ok = runCommand(command);
|
|
744
615
|
if (ok) {
|
|
745
616
|
console.log(`${indent}${chalk.blue('fallback')} ${pkgSpec}...${chalk.green(' ✓')}`);
|
|
746
617
|
result.installed.push(pkgSpec);
|
|
@@ -758,7 +629,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
758
629
|
const parallelResult = await parallelInstall(installEntries, async (entry) => {
|
|
759
630
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
760
631
|
const command = installCfg.command.replace('{pkg}', pkgSpec);
|
|
761
|
-
const success =
|
|
632
|
+
const success = runCommand(command);
|
|
762
633
|
return { success, error: success ? undefined : `Command failed: ${command}` };
|
|
763
634
|
}, {
|
|
764
635
|
concurrency: 4,
|
|
@@ -788,7 +659,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
788
659
|
if (!commandExists(cmdName)) {
|
|
789
660
|
console.log(chalk.yellow(`${indent}glpkg not found. Attempting auto-install...`));
|
|
790
661
|
console.log(chalk.gray(`${indent} Running: ${installerConfig.hint}`));
|
|
791
|
-
if (
|
|
662
|
+
if (runCommand(installerConfig.hint)) {
|
|
792
663
|
console.log(chalk.green(`${indent} ✓ glpkg installed successfully`));
|
|
793
664
|
}
|
|
794
665
|
else {
|
|
@@ -806,7 +677,7 @@ export async function installSnapshot(snapshot, depth = 0, parentInstallers = {}
|
|
|
806
677
|
const parallelResult = await parallelInstall(legacyEntries, async (entry) => {
|
|
807
678
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
808
679
|
const command = legacyCfg.command.replace('{pkg}', pkgSpec);
|
|
809
|
-
const success =
|
|
680
|
+
const success = runCommand(command);
|
|
810
681
|
return { success, error: success ? undefined : `Command failed: ${command}` };
|
|
811
682
|
}, {
|
|
812
683
|
concurrency: 4,
|
|
@@ -1017,7 +888,7 @@ async function runReplaceMode(snapshotName, newSet, options, installers) {
|
|
|
1017
888
|
continue;
|
|
1018
889
|
}
|
|
1019
890
|
console.log(chalk.gray(` Removing ${item.name}...`));
|
|
1020
|
-
if (
|
|
891
|
+
if (runCommand(cmd)) {
|
|
1021
892
|
console.log(` ${item.name}${chalk.green(' ✓ removed')}`);
|
|
1022
893
|
removed.push(item);
|
|
1023
894
|
}
|
|
@@ -1078,10 +949,6 @@ async function previewInstall(snapshot, options) {
|
|
|
1078
949
|
console.log(chalk.gray('Dry-run complete: no installs, no uninstalls, no .npmrc or history changes.'));
|
|
1079
950
|
}
|
|
1080
951
|
export async function installCommand(name, options = {}) {
|
|
1081
|
-
// Install-timeline instrumentation: live instance when SNAPSHOT_INSTALL_TIMELINE
|
|
1082
|
-
// is set, otherwise a zero-overhead no-op. Purely additive — never alters the
|
|
1083
|
-
// install path or its output when disabled.
|
|
1084
|
-
activeTimeline = createInstallTimeline();
|
|
1085
952
|
console.log(chalk.blue.bold(`
|
|
1086
953
|
╔══════════════════════════════════════════════════════════════╗
|
|
1087
954
|
║ Snapshot Installer ║
|
|
@@ -1115,7 +982,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1115
982
|
//
|
|
1116
983
|
// License key CANNOT be used as Bearer token for registry-proxy — they are
|
|
1117
984
|
// distinct: AuthN (uid via IST JWT) vs AuthZ (entitlement via license key).
|
|
1118
|
-
const authSpan = activeTimeline.start('auth-token', { category: 'phase' });
|
|
1119
985
|
const licenseKey = getLicenseKey();
|
|
1120
986
|
let authToken = null;
|
|
1121
987
|
let authType = 'anonymous';
|
|
@@ -1132,16 +998,13 @@ export async function installCommand(name, options = {}) {
|
|
|
1132
998
|
console.log(chalk.yellow('⚠') + ' No auth token — anonymous access');
|
|
1133
999
|
}
|
|
1134
1000
|
const useProxy = true;
|
|
1135
|
-
authSpan.end({ authType });
|
|
1136
1001
|
// Fetch snapshot first — access check depends on snapshot.json content
|
|
1137
1002
|
console.log(chalk.gray('Fetching snapshot...'));
|
|
1138
|
-
const fetchSpan = activeTimeline.start('fetch-snapshot', { category: 'phase' });
|
|
1139
1003
|
let snapshot;
|
|
1140
1004
|
try {
|
|
1141
1005
|
snapshot = await fetchSnapshotFromRepo(name, authToken);
|
|
1142
1006
|
}
|
|
1143
1007
|
catch (error) {
|
|
1144
|
-
fetchSpan.end({ success: false });
|
|
1145
1008
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1146
1009
|
if (options.dryRun) {
|
|
1147
1010
|
// Pure preview: a fetch failure (offline / no auth) is not fatal — we just
|
|
@@ -1156,7 +1019,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1156
1019
|
console.log(chalk.yellow('Check the snapshot name and version, e.g. snapshot install @scope/name@version'));
|
|
1157
1020
|
process.exit(1);
|
|
1158
1021
|
}
|
|
1159
|
-
fetchSpan.end({ success: true });
|
|
1160
1022
|
console.log(chalk.green('✓') + ` Snapshot: ${snapshot.name} v${snapshot.version}`);
|
|
1161
1023
|
if (snapshot.description) {
|
|
1162
1024
|
console.log(chalk.gray(` ${snapshot.description}`));
|
|
@@ -1175,29 +1037,36 @@ export async function installCommand(name, options = {}) {
|
|
|
1175
1037
|
return;
|
|
1176
1038
|
}
|
|
1177
1039
|
// ========== Access Check (based on snapshot.json access field) ==========
|
|
1178
|
-
//
|
|
1179
|
-
// - access: "public"
|
|
1040
|
+
// Determine access type from snapshot.json:
|
|
1041
|
+
// - access: "public" → no login/license required
|
|
1042
|
+
// - access: "authenticated" → IST login required, no license needed
|
|
1180
1043
|
// - access: { type: "licensed", productId } → license required
|
|
1181
|
-
// -
|
|
1182
|
-
// (registry-proxy / license API are authoritative; a 401/403 fails loud).
|
|
1183
|
-
// No client-side login gate: access control is backend-authoritative.
|
|
1044
|
+
// - access field absent → treated as licensed (requires license)
|
|
1184
1045
|
const accessField = snapshot.access;
|
|
1185
1046
|
const isPublicAccess = accessField === 'public';
|
|
1047
|
+
const isAuthenticatedAccess = accessField === 'authenticated';
|
|
1186
1048
|
const isLicensedAccess = !!accessField && typeof accessField === 'object' && accessField.type === 'licensed';
|
|
1187
|
-
//
|
|
1188
|
-
// A wrapper (e.g. ist-setup) may layer a login system on top of snapshot via
|
|
1189
|
-
// the authToken injection point (IST_SESSION_TOKEN env / getProxyAuthToken).
|
|
1049
|
+
// --- IST Login Check ---
|
|
1190
1050
|
const istCredentials = getIstCredentials();
|
|
1051
|
+
if (istCredentials?.userId) {
|
|
1052
|
+
console.log(chalk.green('✓') + ' IST login verified');
|
|
1053
|
+
}
|
|
1054
|
+
else if (isPublicAccess) {
|
|
1055
|
+
console.log(chalk.yellow('⚠') + ' No IST login - installing public package');
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
console.log(chalk.red('⛔ IST login required for non-public packages'));
|
|
1059
|
+
console.log(chalk.gray(' Run: ist auth login'));
|
|
1060
|
+
console.log(chalk.gray(' Or install a public snapshot first (no login required)'));
|
|
1061
|
+
process.exit(1);
|
|
1062
|
+
}
|
|
1191
1063
|
// --- License Check ---
|
|
1192
|
-
const accessSpan = activeTimeline.start('access-license', {
|
|
1193
|
-
category: 'phase',
|
|
1194
|
-
meta: { access: isPublicAccess ? 'public' : isLicensedAccess ? 'licensed' : 'server-decided' },
|
|
1195
|
-
});
|
|
1196
1064
|
const storedLicenseKey = getLicenseKey();
|
|
1197
1065
|
let licenseValid = false;
|
|
1198
|
-
if (isPublicAccess) {
|
|
1199
|
-
// Public snapshots — skip license check entirely
|
|
1200
|
-
|
|
1066
|
+
if (isPublicAccess || isAuthenticatedAccess) {
|
|
1067
|
+
// Public and authenticated snapshots — skip license check entirely
|
|
1068
|
+
const label = isAuthenticatedAccess ? 'Authenticated' : 'Public';
|
|
1069
|
+
console.log(chalk.green('✓') + ` ${label} snapshot - no license required`);
|
|
1201
1070
|
}
|
|
1202
1071
|
else if (storedLicenseKey) {
|
|
1203
1072
|
try {
|
|
@@ -1239,10 +1108,8 @@ export async function installCommand(name, options = {}) {
|
|
|
1239
1108
|
}
|
|
1240
1109
|
console.log(chalk.green('✓') + ` License verified for ${productId}`);
|
|
1241
1110
|
}
|
|
1242
|
-
accessSpan.end({ licenseValid });
|
|
1243
1111
|
// =======================================================================
|
|
1244
1112
|
// ========== Authorize Session with Registry Proxy ==========
|
|
1245
|
-
const authorizeSpan = activeTimeline.start('authorize-session', { category: 'phase' });
|
|
1246
1113
|
let sessionId;
|
|
1247
1114
|
if (authToken) {
|
|
1248
1115
|
// Collect all packages for session authorization
|
|
@@ -1278,10 +1145,8 @@ export async function installCommand(name, options = {}) {
|
|
|
1278
1145
|
}
|
|
1279
1146
|
}
|
|
1280
1147
|
}
|
|
1281
|
-
authorizeSpan.end({ sessionAuthorized: sessionId !== undefined });
|
|
1282
1148
|
// =============================================================
|
|
1283
1149
|
// Collect all packages from entire extends chain to determine scopes
|
|
1284
|
-
const configureSpan = activeTimeline.start('configure-npmrc', { category: 'phase' });
|
|
1285
1150
|
const allPackages = await collectAllPackages(snapshot);
|
|
1286
1151
|
// Configure .npmrc for proxy (with or without auth token)
|
|
1287
1152
|
let npmrcBackup = null;
|
|
@@ -1301,7 +1166,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1301
1166
|
}
|
|
1302
1167
|
}
|
|
1303
1168
|
}
|
|
1304
|
-
configureSpan.end({ packageCount: Object.keys(allPackages).length });
|
|
1305
1169
|
// Guarantee .npmrc restoration even on ^C. A try/finally only covers thrown
|
|
1306
1170
|
// errors and normal returns — it does NOT run on SIGINT/SIGTERM, so an
|
|
1307
1171
|
// interrupt during the pre-install batch or the main install would leave a
|
|
@@ -1339,8 +1203,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1339
1203
|
// ========== Pre-install: batch all npm packages from extends chain ==========
|
|
1340
1204
|
// Collect all npm-type packages across the entire extends chain and install
|
|
1341
1205
|
// them in a single `npm install -g` call, instead of one per snapshot layer.
|
|
1342
|
-
const batchSpan = activeTimeline.start('npm-batch-preinstall', { category: 'phase' });
|
|
1343
|
-
let batchSpecs = [];
|
|
1344
1206
|
const allNpmPackages = await collectAllNpmPackages(snapshot, useProxy);
|
|
1345
1207
|
const npmEntries = Object.entries(allNpmPackages);
|
|
1346
1208
|
if (npmEntries.length > 0) {
|
|
@@ -1358,10 +1220,9 @@ export async function installCommand(name, options = {}) {
|
|
|
1358
1220
|
}
|
|
1359
1221
|
if (toInstall.length > 0) {
|
|
1360
1222
|
const specs = toInstall.map(e => `${e.name}@${e.version}`);
|
|
1361
|
-
batchSpecs = specs;
|
|
1362
1223
|
const npmCommand = DEFAULT_INSTALLERS['npm-proxy'].command.replace('{pkg}', specs.join(' '));
|
|
1363
1224
|
console.log(chalk.gray(` Running single batch: npm install -g (${specs.length} packages)`));
|
|
1364
|
-
const batchSuccess =
|
|
1225
|
+
const batchSuccess = runCommand(npmCommand);
|
|
1365
1226
|
if (batchSuccess) {
|
|
1366
1227
|
for (const spec of specs) {
|
|
1367
1228
|
console.log(` ${chalk.blue('batch')} ${spec}...${chalk.green(' ✓')}`);
|
|
@@ -1377,7 +1238,7 @@ export async function installCommand(name, options = {}) {
|
|
|
1377
1238
|
for (const entry of toInstall) {
|
|
1378
1239
|
const pkgSpec = `${entry.name}@${entry.version}`;
|
|
1379
1240
|
const cmd = DEFAULT_INSTALLERS['npm-proxy'].command.replace('{pkg}', pkgSpec);
|
|
1380
|
-
const ok =
|
|
1241
|
+
const ok = runCommand(cmd);
|
|
1381
1242
|
if (ok) {
|
|
1382
1243
|
console.log(` ${chalk.blue('fallback')} ${pkgSpec}...${chalk.green(' ✓')}`);
|
|
1383
1244
|
}
|
|
@@ -1391,13 +1252,11 @@ export async function installCommand(name, options = {}) {
|
|
|
1391
1252
|
console.log(chalk.green('✓ All npm packages already installed'));
|
|
1392
1253
|
}
|
|
1393
1254
|
}
|
|
1394
|
-
batchSpan.end({ specs: batchSpecs, npmDeclared: npmEntries.length, npmToInstall: batchSpecs.length });
|
|
1395
1255
|
// =============================================================================
|
|
1396
1256
|
// Install (handles extends recursively)
|
|
1397
1257
|
// installSnapshot's internal isNpmGlobalInstalled checks will skip already-installed packages
|
|
1398
1258
|
let installError = null;
|
|
1399
1259
|
let result = { installed: [], failed: [] };
|
|
1400
|
-
const installSpan = activeTimeline.start('install-snapshot', { category: 'phase' });
|
|
1401
1260
|
try {
|
|
1402
1261
|
// Auto-detect 모드: snapshot.install이 없으면 자동 판별
|
|
1403
1262
|
const autoDetect = !snapshot.install;
|
|
@@ -1412,11 +1271,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1412
1271
|
installError = err instanceof Error ? err : new Error(String(err));
|
|
1413
1272
|
}
|
|
1414
1273
|
finally {
|
|
1415
|
-
installSpan.end({
|
|
1416
|
-
installed: result.installed.length,
|
|
1417
|
-
failed: result.failed.length,
|
|
1418
|
-
success: installError === null,
|
|
1419
|
-
});
|
|
1420
1274
|
// Restore .npmrc + clear the session-token env (idempotent: a SIGINT/SIGTERM
|
|
1421
1275
|
// handler may have already run this). Then drop the signal handlers we
|
|
1422
1276
|
// registered, so they don't leak across multiple installs in one process.
|
|
@@ -1425,7 +1279,6 @@ export async function installCommand(name, options = {}) {
|
|
|
1425
1279
|
process.removeListener('SIGTERM', onSignal);
|
|
1426
1280
|
}
|
|
1427
1281
|
if (installError) {
|
|
1428
|
-
activeTimeline.finalize();
|
|
1429
1282
|
console.log(chalk.red(`Error during installation: ${installError.message}`));
|
|
1430
1283
|
process.exit(1);
|
|
1431
1284
|
}
|
|
@@ -1472,9 +1325,18 @@ export async function installCommand(name, options = {}) {
|
|
|
1472
1325
|
await runReplaceMode(snapshot.name, allPackages, options, effectiveInstallers);
|
|
1473
1326
|
}
|
|
1474
1327
|
}
|
|
1475
|
-
//
|
|
1476
|
-
|
|
1477
|
-
|
|
1328
|
+
// Post-install: run doctor to check prerequisites
|
|
1329
|
+
console.log('');
|
|
1330
|
+
const { checkPrerequisites, MINIMAL_PREREQUISITES } = await import('../lib/prerequisites.js');
|
|
1331
|
+
const prereqResults = checkPrerequisites(MINIMAL_PREREQUISITES);
|
|
1332
|
+
const missingPrereqs = prereqResults.filter(r => !r.installed);
|
|
1333
|
+
if (missingPrereqs.length > 0) {
|
|
1334
|
+
console.log(chalk.yellow(`\n⚠ ${missingPrereqs.length} prerequisite(s) not satisfied:`));
|
|
1335
|
+
for (const r of missingPrereqs) {
|
|
1336
|
+
console.log(` ${chalk.red('❌')} ${r.name} — required by ${r.requiredBy.join(', ')}`);
|
|
1337
|
+
}
|
|
1338
|
+
console.log(chalk.gray(`\nRun 'snapshot setup' to install, or 'snapshot doctor' for details.`));
|
|
1339
|
+
}
|
|
1478
1340
|
// Send telemetry (fire and forget)
|
|
1479
1341
|
sendInstallTelemetry({
|
|
1480
1342
|
package: snapshot.name,
|