@skillsmith/core 0.12.3 → 0.12.4
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/CHANGELOG.md +164 -0
- package/README.md +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/config/owned-lock.acquire.d.ts +8 -0
- package/dist/src/config/owned-lock.acquire.d.ts.map +1 -1
- package/dist/src/config/owned-lock.acquire.js +19 -4
- package/dist/src/config/owned-lock.acquire.js.map +1 -1
- package/dist/src/config/owned-lock.claim.d.ts +4 -2
- package/dist/src/config/owned-lock.claim.d.ts.map +1 -1
- package/dist/src/config/owned-lock.claim.js +42 -8
- package/dist/src/config/owned-lock.claim.js.map +1 -1
- package/dist/src/config/owned-lock.d.ts.map +1 -1
- package/dist/src/config/owned-lock.js +1 -0
- package/dist/src/config/owned-lock.js.map +1 -1
- package/dist/src/config/owned-lock.test.js +126 -2
- package/dist/src/config/owned-lock.test.js.map +1 -1
- package/dist/src/config/owned-lock.types.d.ts +9 -0
- package/dist/src/config/owned-lock.types.d.ts.map +1 -1
- package/dist/src/db/createDatabase.d.ts.map +1 -1
- package/dist/src/db/createDatabase.js +0 -2
- package/dist/src/db/createDatabase.js.map +1 -1
- package/dist/src/exports/services.install.d.ts +1 -0
- package/dist/src/exports/services.install.d.ts.map +1 -1
- package/dist/src/exports/services.install.js +5 -0
- package/dist/src/exports/services.install.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/install/fan-out.cleanup.d.ts +37 -0
- package/dist/src/install/fan-out.cleanup.d.ts.map +1 -0
- package/dist/src/install/fan-out.cleanup.js +75 -0
- package/dist/src/install/fan-out.cleanup.js.map +1 -0
- package/dist/src/install/fan-out.d.ts +41 -31
- package/dist/src/install/fan-out.d.ts.map +1 -1
- package/dist/src/install/fan-out.js +213 -112
- package/dist/src/install/fan-out.js.map +1 -1
- package/dist/src/install/fan-out.leftovers.d.ts +60 -0
- package/dist/src/install/fan-out.leftovers.d.ts.map +1 -0
- package/dist/src/install/fan-out.leftovers.js +251 -0
- package/dist/src/install/fan-out.leftovers.js.map +1 -0
- package/dist/src/install/fan-out.locks.d.ts +12 -0
- package/dist/src/install/fan-out.locks.d.ts.map +1 -0
- package/dist/src/install/fan-out.locks.js +114 -0
- package/dist/src/install/fan-out.locks.js.map +1 -0
- package/dist/src/install/fan-out.manifest.d.ts +64 -0
- package/dist/src/install/fan-out.manifest.d.ts.map +1 -0
- package/dist/src/install/fan-out.manifest.js +130 -0
- package/dist/src/install/fan-out.manifest.js.map +1 -0
- package/dist/src/install/fan-out.overwrite.d.ts +51 -0
- package/dist/src/install/fan-out.overwrite.d.ts.map +1 -0
- package/dist/src/install/fan-out.overwrite.js +343 -0
- package/dist/src/install/fan-out.overwrite.js.map +1 -0
- package/dist/src/install/remove-if-same.d.ts +58 -0
- package/dist/src/install/remove-if-same.d.ts.map +1 -0
- package/dist/src/install/remove-if-same.js +222 -0
- package/dist/src/install/remove-if-same.js.map +1 -0
- package/dist/src/provenance/backfill.d.ts.map +1 -1
- package/dist/src/provenance/backfill.js +15 -2
- package/dist/src/provenance/backfill.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js +8 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts +22 -2
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.value-gate.js +143 -6
- package/dist/src/security/scanner/SecurityScanner.value-gate.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts +2 -2
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js +185 -190
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +47 -2
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +48 -3
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts +31 -3
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.sensitive-path.js +59 -4
- package/dist/src/security/scanner/patterns.sensitive-path.js.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.js +3 -2
- package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
- package/dist/src/services/skill-installation.content.d.ts +7 -2
- package/dist/src/services/skill-installation.content.d.ts.map +1 -1
- package/dist/src/services/skill-installation.content.js +75 -28
- package/dist/src/services/skill-installation.content.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +8 -0
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts +1 -29
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.dirs.d.ts +15 -0
- package/dist/src/services/skill-installation.io.dirs.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.dirs.js +84 -0
- package/dist/src/services/skill-installation.io.dirs.js.map +1 -0
- package/dist/src/services/skill-installation.io.js +6 -176
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/services/skill-installation.io.rollback.d.ts +138 -0
- package/dist/src/services/skill-installation.io.rollback.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.rollback.js +218 -0
- package/dist/src/services/skill-installation.io.rollback.js.map +1 -0
- package/dist/src/services/skill-installation.io.write.d.ts +69 -0
- package/dist/src/services/skill-installation.io.write.d.ts.map +1 -0
- package/dist/src/services/skill-installation.io.write.js +417 -0
- package/dist/src/services/skill-installation.io.write.js.map +1 -0
- package/dist/src/services/skill-installation.service.d.ts.map +1 -1
- package/dist/src/services/skill-installation.service.finalize.d.ts +62 -0
- package/dist/src/services/skill-installation.service.finalize.d.ts.map +1 -0
- package/dist/src/services/skill-installation.service.finalize.js +136 -0
- package/dist/src/services/skill-installation.service.finalize.js.map +1 -0
- package/dist/src/services/skill-installation.service.js +66 -80
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/services/skill-installation.target-guard.d.ts +82 -0
- package/dist/src/services/skill-installation.target-guard.d.ts.map +1 -0
- package/dist/src/services/skill-installation.target-guard.js +410 -0
- package/dist/src/services/skill-installation.target-guard.js.map +1 -0
- package/dist/src/services/skill-installation.types.d.ts +13 -1
- package/dist/src/services/skill-installation.types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.types.js.map +1 -1
- package/dist/src/services/skill-installation.uninstall.d.ts +3 -1
- package/dist/src/services/skill-installation.uninstall.d.ts.map +1 -1
- package/dist/src/services/skill-installation.uninstall.helpers.d.ts +59 -0
- package/dist/src/services/skill-installation.uninstall.helpers.d.ts.map +1 -0
- package/dist/src/services/skill-installation.uninstall.helpers.js +124 -0
- package/dist/src/services/skill-installation.uninstall.helpers.js.map +1 -0
- package/dist/src/services/skill-installation.uninstall.js +148 -25
- package/dist/src/services/skill-installation.uninstall.js.map +1 -1
- package/dist/src/utils/safe-fs.d.ts +69 -0
- package/dist/src/utils/safe-fs.d.ts.map +1 -1
- package/dist/src/utils/safe-fs.js +113 -2
- package/dist/src/utils/safe-fs.js.map +1 -1
- package/dist/tests/install/fan-out.test.js +1951 -13
- package/dist/tests/install/fan-out.test.js.map +1 -1
- package/dist/tests/install/remove-if-same.test.d.ts +2 -0
- package/dist/tests/install/remove-if-same.test.d.ts.map +1 -0
- package/dist/tests/install/remove-if-same.test.js +298 -0
- package/dist/tests/install/remove-if-same.test.js.map +1 -0
- package/dist/tests/integration/fan-out-lock-contention.test.d.ts +11 -0
- package/dist/tests/integration/fan-out-lock-contention.test.d.ts.map +1 -0
- package/dist/tests/integration/fan-out-lock-contention.test.js +53 -0
- package/dist/tests/integration/fan-out-lock-contention.test.js.map +1 -0
- package/dist/tests/provenance/backfill.test.js +43 -0
- package/dist/tests/provenance/backfill.test.js.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +33 -5
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/sensitive-path-adversarial-review.test.js +120 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.js.map +1 -1
- package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.js +151 -0
- package/dist/tests/security/sensitive-path-boolean-flag.test.js.map +1 -0
- package/dist/tests/security/sensitive-path-fp.test.js +71 -5
- package/dist/tests/security/sensitive-path-fp.test.js.map +1 -1
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.js +176 -0
- package/dist/tests/security/sensitive-path-prefixed-secrets.test.js.map +1 -0
- package/dist/tests/security/weak-password-lexicon.test.js +14 -4
- package/dist/tests/security/weak-password-lexicon.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.content.test.js +23 -0
- package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js +35 -14
- package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.js +984 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js +148 -0
- package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js +19 -0
- package/dist/tests/unit/services/skill-installation.service.error-codes.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.js +147 -0
- package/dist/tests/unit/services/skill-installation.service.finalize.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.test.js +43 -0
- package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.js +590 -0
- package/dist/tests/unit/services/skill-installation.target-guard.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts +2 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js +473 -0
- package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js.map +1 -0
- package/dist/tests/utils/safe-fs.test.js +129 -2
- package/dist/tests/utils/safe-fs.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pre-write install-target safety guard.
|
|
3
|
+
* @module @skillsmith/core/services/skill-installation.target-guard
|
|
4
|
+
* @see SMI-6529 Wave A0
|
|
5
|
+
*
|
|
6
|
+
* On a real user machine, `skillsmith update --all` overwrote uncommitted
|
|
7
|
+
* local work in git-cloned skill directories and wrote into the wrong
|
|
8
|
+
* directory (a target named after the upstream repo, not the directory
|
|
9
|
+
* being updated). `checkInstallTarget()` is the single pre-write gate every
|
|
10
|
+
* `install()`/`installFromContent()` call runs BEFORE any content fetch or
|
|
11
|
+
* disk write — it never mutates anything itself, only inspects the
|
|
12
|
+
* filesystem and the manifest and reports whether the write may proceed.
|
|
13
|
+
*
|
|
14
|
+
* Rules are evaluated in order; the first that fails wins:
|
|
15
|
+
* (pre) A matching manifest entry's `installPath` must be a usable, real
|
|
16
|
+
* absolute path — a missing or non-absolute value refuses rather than
|
|
17
|
+
* reaching `path.resolve()`/`fs.realpath()` (SMI-6529 L17).
|
|
18
|
+
* (a) `expectedInstallPath` (set by `update`) must match `installPath`
|
|
19
|
+
* exactly, AND must exist on disk — `force` does NOT override this.
|
|
20
|
+
* (b) `installPath` missing (ENOENT) -> fresh install, nothing else to
|
|
21
|
+
* check, UNLESS the manifest key it will occupy already has an entry
|
|
22
|
+
* whose recorded path is a live conflict (SMI-6529 M8) — see below.
|
|
23
|
+
* Any other `lstat` error is rethrown (fail closed).
|
|
24
|
+
* (c) `installPath` must be a directory, or a symlink that resolves to a
|
|
25
|
+
* directory inside `skillsDir`.
|
|
26
|
+
* (d) Filesystem-only git-working-tree check (never spawns git): a `.git`
|
|
27
|
+
* entry (file or directory) at `installPath` or any ancestor up to and
|
|
28
|
+
* including `skillsDir` refuses the write — `force` does NOT override
|
|
29
|
+
* this. Walks BOTH the lexical ancestor chain and (SMI-6529 H1) the
|
|
30
|
+
* REALPATH ancestor chain, since a symlinked `installPath` can resolve
|
|
31
|
+
* into a git clone whose ancestry `path.dirname()` on the symlink's own
|
|
32
|
+
* lexical string never crosses into.
|
|
33
|
+
* (e) The manifest must already track this exact path (by realpath) —
|
|
34
|
+
* an untracked pre-existing directory is refused — `force` does NOT
|
|
35
|
+
* override this either. Even a path-matching entry refuses if it is
|
|
36
|
+
* itself untracked by Skillsmith's trust model (`provenance:'local'`
|
|
37
|
+
* or `source:'unknown'` — ADR-139 adoption or a user's local
|
|
38
|
+
* assertion) — `force` does NOT override this either.
|
|
39
|
+
* (f) Tracked + matching path: existing `ALREADY_INSTALLED` behavior
|
|
40
|
+
* (refuse without `force`, proceed with it).
|
|
41
|
+
*/
|
|
42
|
+
import * as fs from 'fs/promises';
|
|
43
|
+
import * as path from 'path';
|
|
44
|
+
/** `fs.realpath`, falling back to a lexical `path.resolve` when the path can't be resolved
|
|
45
|
+
* (e.g. it doesn't exist, or a component was removed mid-check) — never throws. */
|
|
46
|
+
async function resolveRealOrFallback(target) {
|
|
47
|
+
try {
|
|
48
|
+
return await fs.realpath(target);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return path.resolve(target);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function pathHasGitEntry(dir) {
|
|
55
|
+
try {
|
|
56
|
+
// lstat (not stat): a `.git` FILE (worktree pointer) or directory both count,
|
|
57
|
+
// and we don't want a `.git` symlink silently resolved away.
|
|
58
|
+
await fs.lstat(path.join(dir, '.git'));
|
|
59
|
+
return 'found';
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const code = err.code;
|
|
63
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
64
|
+
return 'absent';
|
|
65
|
+
// SMI-6529 L16: fail CLOSED — an lstat error we can't interpret as
|
|
66
|
+
// "definitely absent" (EACCES, EIO, an unreadable ancestor, etc.) must
|
|
67
|
+
// never silently read as "no .git here." The CALLER still refuses the
|
|
68
|
+
// write (same as a real hit) so it never guesses wrong in the data-loss
|
|
69
|
+
// direction — but the message says which is which (#16).
|
|
70
|
+
return { errorCode: code ?? 'UNKNOWN' };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Walk from `startAbs` up through `stopAtAbs` (inclusive), returning the first
|
|
74
|
+
* directory with a `.git` entry (or the first unverifiable one), or `null` if
|
|
75
|
+
* none is found before `stopAtAbs`. */
|
|
76
|
+
async function walkForGitEntry(startAbs, stopAtAbs) {
|
|
77
|
+
let current = startAbs;
|
|
78
|
+
// Bound the walk: every real caller builds installPath as a descendant of
|
|
79
|
+
// skillsDir (so this always terminates via the `current === stopAtAbs`
|
|
80
|
+
// check below), but a future caller that doesn't must never loop forever.
|
|
81
|
+
for (let i = 0; i < 64; i++) {
|
|
82
|
+
const check = await pathHasGitEntry(current);
|
|
83
|
+
if (check === 'found')
|
|
84
|
+
return { kind: 'found', path: current };
|
|
85
|
+
if (typeof check === 'object')
|
|
86
|
+
return { kind: 'error', path: current, errorCode: check.errorCode };
|
|
87
|
+
if (current === stopAtAbs)
|
|
88
|
+
return null;
|
|
89
|
+
const parent = path.dirname(current);
|
|
90
|
+
if (parent === current)
|
|
91
|
+
return null; // filesystem root — stopAtAbs was never an ancestor
|
|
92
|
+
current = parent;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Walk from `installPath` up through its ancestors, inclusive of both
|
|
98
|
+
* `installPath` and `skillsDir` (never above `skillsDir`), checking each
|
|
99
|
+
* level for a `.git` entry. Filesystem-only — never spawns `git`. Returns the
|
|
100
|
+
* directory the `.git` entry was found in (for SMI-6529 L13's tailored tip),
|
|
101
|
+
* or `null` if none was found.
|
|
102
|
+
*
|
|
103
|
+
* SMI-6529 H1: a symlinked `installPath` (e.g. a fan-out symlink, or one
|
|
104
|
+
* planted by an attacker) can resolve into a git clone living elsewhere
|
|
105
|
+
* inside `skillsDir` — walking the LEXICAL chain alone misses this, because
|
|
106
|
+
* `path.dirname()` on the symlink's own path string jumps straight to the
|
|
107
|
+
* symlink's own lexical parent, never through the target it points at (only
|
|
108
|
+
* the FINAL path segment of any one `fs` call is exempt from symlink
|
|
109
|
+
* resolution; the "climb to parent" step here works on the string alone).
|
|
110
|
+
* Concretely: `skillsDir/pdf` -> `skillsDir/clone/docs/pdf` where `clone` (not
|
|
111
|
+
* `skillsDir/pdf`'s own lexical parent) has `.git` — only a walk starting from
|
|
112
|
+
* `installPath`'s REALPATH ever visits `clone`. Bounded by `skillsDir`'s own
|
|
113
|
+
* realpath (never climbs above it) — safe because rule (c) has already
|
|
114
|
+
* proven a symlinked `installPath`'s realpath resolves inside (real)
|
|
115
|
+
* `skillsDir` before this ever runs.
|
|
116
|
+
*/
|
|
117
|
+
async function hasGitAncestorBetween(installPath, skillsDir) {
|
|
118
|
+
const resolvedSkillsDir = path.resolve(skillsDir);
|
|
119
|
+
const resolvedInstall = path.resolve(installPath);
|
|
120
|
+
const lexicalHit = await walkForGitEntry(resolvedInstall, resolvedSkillsDir);
|
|
121
|
+
if (lexicalHit)
|
|
122
|
+
return lexicalHit;
|
|
123
|
+
const realInstall = await resolveRealOrFallback(installPath);
|
|
124
|
+
if (realInstall === resolvedInstall)
|
|
125
|
+
return null; // no symlink involved — already covered above
|
|
126
|
+
const realSkillsDir = await resolveRealOrFallback(skillsDir);
|
|
127
|
+
return walkForGitEntry(realInstall, realSkillsDir);
|
|
128
|
+
}
|
|
129
|
+
/** Rule (c): `installPath` is a usable directory, either directly or via a symlink
|
|
130
|
+
* that resolves to a directory inside `skillsDir`. */
|
|
131
|
+
async function isUsableDirectory(installPath, skillsDir, stats) {
|
|
132
|
+
if (stats.isDirectory())
|
|
133
|
+
return true;
|
|
134
|
+
if (!stats.isSymbolicLink())
|
|
135
|
+
return false;
|
|
136
|
+
try {
|
|
137
|
+
const followed = await fs.stat(installPath);
|
|
138
|
+
if (!followed.isDirectory())
|
|
139
|
+
return false;
|
|
140
|
+
// SMI-4758: resolve BOTH sides through the identical helper (never a raw
|
|
141
|
+
// fs.realpath on one side and a fallback-guarded resolve on the other) —
|
|
142
|
+
// asymmetric methodology is exactly the bug class this comparison must
|
|
143
|
+
// avoid, not merely a lint-satisfying detail.
|
|
144
|
+
const real = await resolveRealOrFallback(installPath);
|
|
145
|
+
const resolvedSkillsDir = await resolveRealOrFallback(skillsDir);
|
|
146
|
+
return real === resolvedSkillsDir || real.startsWith(resolvedSkillsDir + path.sep);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false; // broken symlink or unreadable target
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* SMI-6529 L17: a manifest entry's `installPath` is typed as required but
|
|
154
|
+
* "runtime JSON may omit it" (see `SkillManifestEntry.installPath`'s own doc
|
|
155
|
+
* comment). Every rule below that reads it (rule (e)'s tracked-path
|
|
156
|
+
* comparison, and (b)'s M8 stale-vs-live check) needs it to be a real
|
|
157
|
+
* absolute path — otherwise it would reach `path.resolve()`/`fs.realpath()`
|
|
158
|
+
* and either throw a raw TypeError (on `undefined`) or silently resolve
|
|
159
|
+
* against `process.cwd()` (on a relative string) instead of surfacing a
|
|
160
|
+
* clear, structured refusal.
|
|
161
|
+
*/
|
|
162
|
+
function hasUsableInstallPath(entry) {
|
|
163
|
+
return typeof entry.installPath === 'string' && path.isAbsolute(entry.installPath);
|
|
164
|
+
}
|
|
165
|
+
/** SMI-6529 M8: does `p` still exist on disk? Fails CLOSED on an unexpected
|
|
166
|
+
* lstat error (EACCES, etc.) — treated as "still there" so the caller refuses
|
|
167
|
+
* rather than guessing a manifest conflict away. */
|
|
168
|
+
async function pathStillExists(p) {
|
|
169
|
+
try {
|
|
170
|
+
await fs.lstat(p);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
if (err.code === 'ENOENT')
|
|
175
|
+
return false;
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The pre-write gate: decide, without changing anything, whether an install
|
|
181
|
+
* or update may write into `installPath`. The rules (pre) and (a)–(f) in the
|
|
182
|
+
* module comment are checked in order, and the first to fail wins. Callers
|
|
183
|
+
* run it before any content fetch or disk write.
|
|
184
|
+
*/
|
|
185
|
+
export async function checkInstallTarget(params) {
|
|
186
|
+
const { installPath, skillsDir, manifestEntry, force, expectedInstallPath } = params;
|
|
187
|
+
// (pre) SMI-6529 L17 — see hasUsableInstallPath's doc comment.
|
|
188
|
+
if (manifestEntry !== undefined && !hasUsableInstallPath(manifestEntry)) {
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
code: 'INSTALL_TARGET_UNTRACKED',
|
|
192
|
+
error: 'Manifest entry for "' +
|
|
193
|
+
path.basename(installPath) +
|
|
194
|
+
'" has a missing or invalid installPath; refusing to trust it.',
|
|
195
|
+
tips: ['Repair the manifest entry: apply_manifest_reconcile'],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// (a) `update` records the exact directory it diffed against — refuse to
|
|
199
|
+
// write anywhere else, even under force. SMI-4758/F6: resolve BOTH sides
|
|
200
|
+
// through the identical helper (never a raw path.resolve on one side and a
|
|
201
|
+
// realpath'd value on the other) — a symlinked path spelling (macOS
|
|
202
|
+
// /var -> /private/var) must never produce a false mismatch.
|
|
203
|
+
if (expectedInstallPath !== undefined) {
|
|
204
|
+
const resolvedExpected = await resolveRealOrFallback(expectedInstallPath);
|
|
205
|
+
const resolvedInstall = await resolveRealOrFallback(installPath);
|
|
206
|
+
if (resolvedExpected !== resolvedInstall) {
|
|
207
|
+
return {
|
|
208
|
+
ok: false,
|
|
209
|
+
code: 'INSTALL_TARGET_MISMATCH',
|
|
210
|
+
error: 'Update would write to "' +
|
|
211
|
+
installPath +
|
|
212
|
+
'", but the skill being updated lives at "' +
|
|
213
|
+
expectedInstallPath +
|
|
214
|
+
'"; refusing.',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// SMI-6529 M5: the path MATCHES, but if it doesn't exist on disk at all,
|
|
218
|
+
// `update` is not "reinstalling the directory it compared against" — it
|
|
219
|
+
// would silently CREATE a brand-new directory at a path whose skill was
|
|
220
|
+
// deleted or moved after the diff ran. Refuse explicitly instead of
|
|
221
|
+
// falling through to rule (b)'s fresh-ENOENT "ok" path, which exists for
|
|
222
|
+
// a genuinely NEW install, not an update.
|
|
223
|
+
if (!(await pathStillExists(expectedInstallPath))) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
code: 'INSTALL_TARGET_MISMATCH',
|
|
227
|
+
error: 'Update target "' +
|
|
228
|
+
expectedInstallPath +
|
|
229
|
+
'" no longer exists on disk; refusing to create a new directory there.',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// (b)
|
|
234
|
+
let stats;
|
|
235
|
+
try {
|
|
236
|
+
stats = await fs.lstat(installPath);
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
if (err.code === 'ENOENT') {
|
|
240
|
+
// SMI-6529 M8: a fresh (recomputed) install path is only safe to treat
|
|
241
|
+
// as a brand-new install when the manifest key it will occupy is
|
|
242
|
+
// either unclaimed or points at a directory that's ALSO gone (the
|
|
243
|
+
// round-1 F8-documented stale-entry case). If the SAME manifest key's
|
|
244
|
+
// entry still points at a DIFFERENT directory that (a) still exists on
|
|
245
|
+
// disk, or (b) is marked provenance:'local' (a user assertion that
|
|
246
|
+
// must never be silently superseded, regardless of whether its
|
|
247
|
+
// recorded directory still exists), this is a real conflict — refuse
|
|
248
|
+
// rather than silently adopting the key for an unrelated new install.
|
|
249
|
+
if (manifestEntry) {
|
|
250
|
+
if (manifestEntry.provenance === 'local') {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
code: 'INSTALL_TARGET_UNTRACKED',
|
|
254
|
+
error: 'Manifest entry for "' +
|
|
255
|
+
path.basename(installPath) +
|
|
256
|
+
'" is marked local / not installed by Skillsmith; refusing to reuse it for a new install.',
|
|
257
|
+
tips: [
|
|
258
|
+
'If this is actually a registry skill, relink it first: apply_manifest_reconcile (action: relink)',
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (await pathStillExists(manifestEntry.installPath)) {
|
|
263
|
+
return {
|
|
264
|
+
ok: false,
|
|
265
|
+
code: 'INSTALL_TARGET_MISMATCH',
|
|
266
|
+
error: 'Manifest entry for "' +
|
|
267
|
+
path.basename(installPath) +
|
|
268
|
+
'" already points at "' +
|
|
269
|
+
manifestEntry.installPath +
|
|
270
|
+
'", which still exists; refusing to install at a different path under the same tracked name.',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// Stale entry: recorded path is gone and not marked local — the
|
|
274
|
+
// documented round-1 (F8) behavior — proceed as a fresh install,
|
|
275
|
+
// reusing this manifest key.
|
|
276
|
+
}
|
|
277
|
+
return { ok: true, preExisted: false };
|
|
278
|
+
}
|
|
279
|
+
throw err; // fail closed — do not guess at an unexpected lstat error
|
|
280
|
+
}
|
|
281
|
+
// (c)
|
|
282
|
+
if (!(await isUsableDirectory(installPath, skillsDir, stats))) {
|
|
283
|
+
return {
|
|
284
|
+
ok: false,
|
|
285
|
+
code: 'INSTALL_TARGET_NOT_DIRECTORY',
|
|
286
|
+
error: 'Install target "' + installPath + '" exists but is not a directory.',
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
// SMI-6529 N5 (round 4): containment — a CALLER (e.g. a pre-flight that
|
|
290
|
+
// computed `installPath` from a different client's manifest entry than the
|
|
291
|
+
// `skillsDir` it passed) could hand this function an `installPath` that
|
|
292
|
+
// isn't actually inside `skillsDir` at all. Without this check, rule (d)'s
|
|
293
|
+
// ancestor walk below never meets `skillsDir` and silently climbs all the
|
|
294
|
+
// way to the filesystem root. Valid if EITHER the lexical OR the realpath
|
|
295
|
+
// comparison holds — matching rule (c)'s own permissiveness for a
|
|
296
|
+
// legitimate symlink whose target resolves inside `skillsDir` even though
|
|
297
|
+
// its own lexical spelling doesn't; refuse only when BOTH fail.
|
|
298
|
+
const resolvedInstallForContainment = path.resolve(installPath);
|
|
299
|
+
const resolvedSkillsDirForContainment = path.resolve(skillsDir);
|
|
300
|
+
const lexicallyContained = resolvedInstallForContainment === resolvedSkillsDirForContainment ||
|
|
301
|
+
resolvedInstallForContainment.startsWith(resolvedSkillsDirForContainment + path.sep);
|
|
302
|
+
if (!lexicallyContained) {
|
|
303
|
+
const realInstallForContainment = await resolveRealOrFallback(installPath);
|
|
304
|
+
const realSkillsDirForContainment = await resolveRealOrFallback(skillsDir);
|
|
305
|
+
const reallyContained = realInstallForContainment === realSkillsDirForContainment ||
|
|
306
|
+
realInstallForContainment.startsWith(realSkillsDirForContainment + path.sep);
|
|
307
|
+
if (!reallyContained) {
|
|
308
|
+
// Round 5: MISMATCH, not NOT_DIRECTORY — the target may well be a
|
|
309
|
+
// directory; what's wrong is that it isn't where installs belong.
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
code: 'INSTALL_TARGET_MISMATCH',
|
|
313
|
+
error: 'Install target "' +
|
|
314
|
+
installPath +
|
|
315
|
+
'" is not inside skills directory "' +
|
|
316
|
+
skillsDir +
|
|
317
|
+
'"; refusing.',
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// (d) Filesystem-only — never spawns git. `force` does NOT override this.
|
|
322
|
+
const gitHit = await hasGitAncestorBetween(installPath, skillsDir);
|
|
323
|
+
if (gitHit) {
|
|
324
|
+
// SMI-6529 #16 (round 4): a fail-closed "couldn't verify" (EACCES, etc.)
|
|
325
|
+
// gets an ACCURATE message naming the real reason, never the false claim
|
|
326
|
+
// that a git working tree was actually found — L16's refusal itself is
|
|
327
|
+
// unchanged (same code, still refuses).
|
|
328
|
+
if (gitHit.kind === 'error') {
|
|
329
|
+
return {
|
|
330
|
+
ok: false,
|
|
331
|
+
code: 'INSTALL_TARGET_GIT_WORKTREE',
|
|
332
|
+
error: 'Could not check "' +
|
|
333
|
+
gitHit.path +
|
|
334
|
+
'" for a git repository: ' +
|
|
335
|
+
gitHit.errorCode +
|
|
336
|
+
'. Refusing to proceed.',
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
// SMI-6529 L13: tailor the message/tip depending on whether `.git` was
|
|
340
|
+
// found AT installPath itself vs. an ancestor (or skillsDir) — naming
|
|
341
|
+
// the actual git root makes the remediation command runnable as-is
|
|
342
|
+
// instead of pointed at a directory that isn't really the clone root.
|
|
343
|
+
const resolvedInstall = path.resolve(installPath);
|
|
344
|
+
const realInstall = await resolveRealOrFallback(installPath);
|
|
345
|
+
const atInstallItself = gitHit.path === resolvedInstall || gitHit.path === realInstall;
|
|
346
|
+
return {
|
|
347
|
+
ok: false,
|
|
348
|
+
code: 'INSTALL_TARGET_GIT_WORKTREE',
|
|
349
|
+
error: atInstallItself
|
|
350
|
+
? 'Install target "' + installPath + '" is a git working tree — refusing to overwrite it.'
|
|
351
|
+
: 'Install target "' +
|
|
352
|
+
installPath +
|
|
353
|
+
'" lives inside a git working tree rooted at "' +
|
|
354
|
+
gitHit.path +
|
|
355
|
+
'" — refusing to overwrite it.',
|
|
356
|
+
tips: [
|
|
357
|
+
atInstallItself
|
|
358
|
+
? 'This skill is a git clone. Update it with: git -C "' + installPath + '" pull --ff-only'
|
|
359
|
+
: 'This skill lives inside a git clone rooted at "' +
|
|
360
|
+
gitHit.path +
|
|
361
|
+
'". Update it with: git -C "' +
|
|
362
|
+
gitHit.path +
|
|
363
|
+
'" pull --ff-only',
|
|
364
|
+
],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
// (e) `force` does NOT override this either.
|
|
368
|
+
const realInstall = await resolveRealOrFallback(installPath);
|
|
369
|
+
const realEntry = manifestEntry ? await resolveRealOrFallback(manifestEntry.installPath) : null;
|
|
370
|
+
if (!manifestEntry || realEntry !== realInstall) {
|
|
371
|
+
return {
|
|
372
|
+
ok: false,
|
|
373
|
+
code: 'INSTALL_TARGET_UNTRACKED',
|
|
374
|
+
error: 'A directory already exists at "' +
|
|
375
|
+
installPath +
|
|
376
|
+
'" that Skillsmith didn\'t install (or the manifest entry points elsewhere); refusing to overwrite it.',
|
|
377
|
+
tips: ['Move the existing directory aside, then retry', 'Or remove it, then retry'],
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
// SMI-6529 F5 / ADR-155 §3+§5: a path-matching entry that is ITSELF
|
|
381
|
+
// untracked by Skillsmith's own trust model — an ADR-139 adoption
|
|
382
|
+
// (source:'unknown') or a user's explicit provenance:'local' assertion —
|
|
383
|
+
// must still refuse, even though the path matches. `force` means "reinstall
|
|
384
|
+
// what Skillsmith installed," not "overwrite an adopted or local skill";
|
|
385
|
+
// `force` does NOT override this.
|
|
386
|
+
if (manifestEntry.provenance === 'local' || manifestEntry.source === 'unknown') {
|
|
387
|
+
return {
|
|
388
|
+
ok: false,
|
|
389
|
+
code: 'INSTALL_TARGET_UNTRACKED',
|
|
390
|
+
error: 'Skill at "' +
|
|
391
|
+
installPath +
|
|
392
|
+
'" is marked local / not installed by Skillsmith; refusing to overwrite it.',
|
|
393
|
+
tips: [
|
|
394
|
+
'If this is actually a registry skill, relink it first: apply_manifest_reconcile (action: relink)',
|
|
395
|
+
],
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
// (f)
|
|
399
|
+
if (!force) {
|
|
400
|
+
return {
|
|
401
|
+
ok: false,
|
|
402
|
+
code: 'ALREADY_INSTALLED',
|
|
403
|
+
error: 'Skill "' +
|
|
404
|
+
path.basename(installPath) +
|
|
405
|
+
'" is already installed. Use force=true to reinstall.',
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
return { ok: true, preExisted: true };
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=skill-installation.target-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.target-guard.js","sourceRoot":"","sources":["../../../src/services/skill-installation.target-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AAEjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AA6B5B;mFACmF;AACnF,KAAK,UAAU,qBAAqB,CAAC,MAAc;IACjD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAYD,KAAK,UAAU,eAAe,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,8EAA8E;QAC9E,6DAA6D;QAC7D,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;QACtC,OAAO,OAAO,CAAA;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAA;QAChD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAA;QAC5D,mEAAmE;QACnE,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,yDAAyD;QACzD,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,CAAA;IACzC,CAAC;AACH,CAAC;AAQD;;uCAEuC;AACvC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,SAAiB;IAChE,IAAI,OAAO,GAAG,QAAQ,CAAA;IACtB,0EAA0E;IAC1E,uEAAuE;IACvE,0EAA0E;IAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAA;QAC5C,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;QAC9D,IAAI,OAAO,KAAK,KAAK,QAAQ;YAC3B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAA;QACrE,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAA;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACpC,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,IAAI,CAAA,CAAC,oDAAoD;QACxF,OAAO,GAAG,MAAM,CAAA;IAClB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,KAAK,UAAU,qBAAqB,CAClC,WAAmB,EACnB,SAAiB;IAEjB,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACjD,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAA;IAC5E,IAAI,UAAU;QAAE,OAAO,UAAU,CAAA;IAEjC,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;IAC5D,IAAI,WAAW,KAAK,eAAe;QAAE,OAAO,IAAI,CAAA,CAAC,8CAA8C;IAC/F,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,SAAS,CAAC,CAAA;IAC5D,OAAO,eAAe,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA;AACpD,CAAC;AAED;sDACsD;AACtD,KAAK,UAAU,iBAAiB,CAC9B,WAAmB,EACnB,SAAiB,EACjB,KAAY;IAEZ,IAAI,KAAK,CAAC,WAAW,EAAE;QAAE,OAAO,IAAI,CAAA;IACpC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAAE,OAAO,KAAK,CAAA;IACzC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC3C,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE;YAAE,OAAO,KAAK,CAAA;QACzC,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,8CAA8C;QAC9C,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;QACrD,MAAM,iBAAiB,GAAG,MAAM,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAChE,OAAO,IAAI,KAAK,iBAAiB,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA,CAAC,sCAAsC;IACrD,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,oBAAoB,CAC3B,KAAyB;IAEzB,OAAO,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;AACpF,CAAC;AAED;;oDAEoD;AACpD,KAAK,UAAU,eAAe,CAAC,CAAS;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACjB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAA;QAClE,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAgC;IAEhC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAAA;IAEpF,+DAA+D;IAC/D,IAAI,aAAa,KAAK,SAAS,IAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,EAAE,CAAC;QACxE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,0BAA0B;YAChC,KAAK,EACH,sBAAsB;gBACtB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAC1B,+DAA+D;YACjE,IAAI,EAAE,CAAC,qDAAqD,CAAC;SAC9D,CAAA;IACH,CAAC;IAED,yEAAyE;IACzE,yEAAyE;IACzE,2EAA2E;IAC3E,oEAAoE;IACpE,6DAA6D;IAC7D,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,gBAAgB,GAAG,MAAM,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;QACzE,MAAM,eAAe,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;QAChE,IAAI,gBAAgB,KAAK,eAAe,EAAE,CAAC;YACzC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EACH,yBAAyB;oBACzB,WAAW;oBACX,2CAA2C;oBAC3C,mBAAmB;oBACnB,cAAc;aACjB,CAAA;QACH,CAAC;QACD,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,oEAAoE;QACpE,yEAAyE;QACzE,0CAA0C;QAC1C,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC;YAClD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EACH,iBAAiB;oBACjB,mBAAmB;oBACnB,uEAAuE;aAC1E,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM;IACN,IAAI,KAAY,CAAA;IAChB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,uEAAuE;YACvE,iEAAiE;YACjE,kEAAkE;YAClE,sEAAsE;YACtE,uEAAuE;YACvE,mEAAmE;YACnE,+DAA+D;YAC/D,qEAAqE;YACrE,sEAAsE;YACtE,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,aAAa,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;oBACzC,OAAO;wBACL,EAAE,EAAE,KAAK;wBACT,IAAI,EAAE,0BAA0B;wBAChC,KAAK,EACH,sBAAsB;4BACtB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;4BAC1B,0FAA0F;wBAC5F,IAAI,EAAE;4BACJ,kGAAkG;yBACnG;qBACF,CAAA;gBACH,CAAC;gBACD,IAAI,MAAM,eAAe,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;oBACrD,OAAO;wBACL,EAAE,EAAE,KAAK;wBACT,IAAI,EAAE,yBAAyB;wBAC/B,KAAK,EACH,sBAAsB;4BACtB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;4BAC1B,uBAAuB;4BACvB,aAAa,CAAC,WAAW;4BACzB,6FAA6F;qBAChG,CAAA;gBACH,CAAC;gBACD,gEAAgE;gBAChE,iEAAiE;gBACjE,6BAA6B;YAC/B,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAA;QACxC,CAAC;QACD,MAAM,GAAG,CAAA,CAAC,0DAA0D;IACtE,CAAC;IAED,MAAM;IACN,IAAI,CAAC,CAAC,MAAM,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,8BAA8B;YACpC,KAAK,EAAE,kBAAkB,GAAG,WAAW,GAAG,kCAAkC;SAC7E,CAAA;IACH,CAAC;IAED,wEAAwE;IACxE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,0EAA0E;IAC1E,0EAA0E;IAC1E,kEAAkE;IAClE,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,6BAA6B,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC/D,MAAM,+BAA+B,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/D,MAAM,kBAAkB,GACtB,6BAA6B,KAAK,+BAA+B;QACjE,6BAA6B,CAAC,UAAU,CAAC,+BAA+B,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IACtF,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,yBAAyB,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;QAC1E,MAAM,2BAA2B,GAAG,MAAM,qBAAqB,CAAC,SAAS,CAAC,CAAA;QAC1E,MAAM,eAAe,GACnB,yBAAyB,KAAK,2BAA2B;YACzD,yBAAyB,CAAC,UAAU,CAAC,2BAA2B,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QAC9E,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,kEAAkE;YAClE,kEAAkE;YAClE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EACH,kBAAkB;oBAClB,WAAW;oBACX,oCAAoC;oBACpC,SAAS;oBACT,cAAc;aACjB,CAAA;QACH,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;IAClE,IAAI,MAAM,EAAE,CAAC;QACX,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,wCAAwC;QACxC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,6BAA6B;gBACnC,KAAK,EACH,mBAAmB;oBACnB,MAAM,CAAC,IAAI;oBACX,0BAA0B;oBAC1B,MAAM,CAAC,SAAS;oBAChB,wBAAwB;aAC3B,CAAA;QACH,CAAC;QACD,uEAAuE;QACvE,sEAAsE;QACtE,mEAAmE;QACnE,sEAAsE;QACtE,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACjD,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;QAC5D,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,KAAK,eAAe,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,CAAA;QACtF,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,eAAe;gBACpB,CAAC,CAAC,kBAAkB,GAAG,WAAW,GAAG,qDAAqD;gBAC1F,CAAC,CAAC,kBAAkB;oBAClB,WAAW;oBACX,+CAA+C;oBAC/C,MAAM,CAAC,IAAI;oBACX,+BAA+B;YACnC,IAAI,EAAE;gBACJ,eAAe;oBACb,CAAC,CAAC,qDAAqD,GAAG,WAAW,GAAG,kBAAkB;oBAC1F,CAAC,CAAC,iDAAiD;wBACjD,MAAM,CAAC,IAAI;wBACX,6BAA6B;wBAC7B,MAAM,CAAC,IAAI;wBACX,kBAAkB;aACvB;SACF,CAAA;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,WAAW,CAAC,CAAA;IAC5D,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,MAAM,qBAAqB,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/F,IAAI,CAAC,aAAa,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAChD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,0BAA0B;YAChC,KAAK,EACH,iCAAiC;gBACjC,WAAW;gBACX,uGAAuG;YACzG,IAAI,EAAE,CAAC,+CAA+C,EAAE,0BAA0B,CAAC;SACpF,CAAA;IACH,CAAC;IAED,oEAAoE;IACpE,kEAAkE;IAClE,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,kCAAkC;IAClC,IAAI,aAAa,CAAC,UAAU,KAAK,OAAO,IAAI,aAAa,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/E,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,0BAA0B;YAChC,KAAK,EACH,YAAY;gBACZ,WAAW;gBACX,4EAA4E;YAC9E,IAAI,EAAE;gBACJ,kGAAkG;aACnG;SACF,CAAA;IACH,CAAC;IAED,MAAM;IACN,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,mBAAmB;YACzB,KAAK,EACH,SAAS;gBACT,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAC1B,sDAAsD;SACzD,CAAA;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;AACvC,CAAC"}
|
|
@@ -61,6 +61,18 @@ export interface InstallOptions {
|
|
|
61
61
|
conflictAction?: ConflictAction;
|
|
62
62
|
/** SMI-3863: User has confirmed they want to install despite security warnings */
|
|
63
63
|
confirmed?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* SMI-6529 Wave A0: set by `update` to the exact directory it diffed
|
|
66
|
+
* against (the manifest entry's `installPath`) — `install()` refuses to
|
|
67
|
+
* write anywhere else, even with `force: true`. Closes the "repo name !=
|
|
68
|
+
* install directory name" data-loss class (e.g. a registry lookup or a
|
|
69
|
+
* raw-URL `repo` resolving to a DIFFERENT on-disk name than the directory
|
|
70
|
+
* `update` actually compared), where a force-reinstall silently wrote a
|
|
71
|
+
* second, wrongly-named directory instead of overwriting the one the user
|
|
72
|
+
* meant to update. Omitted by a plain `install()` call, which has no
|
|
73
|
+
* "directory it compared against" to enforce.
|
|
74
|
+
*/
|
|
75
|
+
expectedInstallPath?: string;
|
|
64
76
|
}
|
|
65
77
|
/** Dependency intelligence result from an install */
|
|
66
78
|
export interface DepIntelResult {
|
|
@@ -97,7 +109,7 @@ export interface OptimizationInfo {
|
|
|
97
109
|
* stable across releases. Adding a new code requires (a) a real new failure
|
|
98
110
|
* return path and (b) updating downstream funnel queries.
|
|
99
111
|
*/
|
|
100
|
-
export type InstallErrorCode = 'REGISTRY_LOOKUP_UNAVAILABLE' | 'REGISTRY_SKILL_NOT_FOUND' | 'QUARANTINED' | 'ALREADY_INSTALLED' | 'FETCH_FAILED' | 'VALIDATION_FAILED' | 'SKIP_SCAN_FORBIDDEN' | 'SCAN_REJECTED' | 'CONFIRMATION_REQUIRED' | 'INVALID_CONTENT' | 'UNKNOWN';
|
|
112
|
+
export type InstallErrorCode = 'REGISTRY_LOOKUP_UNAVAILABLE' | 'REGISTRY_SKILL_NOT_FOUND' | 'QUARANTINED' | 'ALREADY_INSTALLED' | 'FETCH_FAILED' | 'VALIDATION_FAILED' | 'SKIP_SCAN_FORBIDDEN' | 'SCAN_REJECTED' | 'CONFIRMATION_REQUIRED' | 'INVALID_CONTENT' | 'INSTALL_TARGET_MISMATCH' | 'INSTALL_TARGET_NOT_DIRECTORY' | 'INSTALL_TARGET_GIT_WORKTREE' | 'INSTALL_TARGET_UNTRACKED' | 'UNKNOWN';
|
|
101
113
|
/** Result of an install operation */
|
|
102
114
|
export interface InstallResult {
|
|
103
115
|
success: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.types.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAMnD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;AAMtE,qEAAqE;AACrE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,OAAO,EAAE,YAAY,CAAA;IACrB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,kFAAkF;IAClF,SAAS,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.types.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAMnD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;AAMtE,qEAAqE;AACrE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,OAAO,EAAE,YAAY,CAAA;IACrB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,kFAAkF;IAClF,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B,8DAA8D;IAC9D,YAAY,EAAE,qBAAqB,GAAG,SAAS,CAAA;IAC/C,+DAA+D;IAC/D,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GACxB,6BAA6B,GAC7B,0BAA0B,GAC1B,aAAa,GACb,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,uBAAuB,GACvB,iBAAiB,GAEjB,yBAAyB,GACzB,8BAA8B,GAC9B,6BAA6B,GAC7B,0BAA0B,GAC1B,SAAS,CAAA;AAEb,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,UAAU,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,wDAAwD;IACxD,YAAY,CAAC,EAAE,gBAAgB,CAAA;IAC/B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,8DAA8D;AAC9D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,CAAA;AAErD,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,MAAM,EAAE;QACrB,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,OAAO,CAAA;QACpB,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;QACrC,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAMD,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAMD,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oBAAoB;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;IAC1C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CACpD;AAMD,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,SAAS,CAAA;IACpB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;CAC3D;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAClD;AAMD,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,SAAS,EAAE,cAAc,CAkCxE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.types.js","sourceRoot":"","sources":["../../../src/services/skill-installation.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"skill-installation.types.js","sourceRoot":"","sources":["../../../src/services/skill-installation.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2TH,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,iDAAiD;AACjD,MAAM,CAAC,MAAM,0BAA0B,GAAsC;IAC3E,QAAQ,EAAE;QACR,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,SAAS;KAC5B;IACD,OAAO,EAAE;QACP,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,SAAS;KAC5B;IACD,SAAS,EAAE;QACT,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,SAAS;KAC5B;IACD,KAAK,EAAE;QACL,aAAa,EAAE,GAAG;QAClB,gBAAgB,EAAE,UAAU;KAC7B;IACD,YAAY,EAAE;QACZ,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,OAAO;KAC1B;IACD,OAAO,EAAE;QACP,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,OAAO;KAC1B;IACD,6BAA6B;IAC7B,QAAQ,EAAE;QACR,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,SAAS;KAC5B;IACD,UAAU,EAAE;QACV,aAAa,EAAE,EAAE,EAAE,+DAA+D;QAClF,gBAAgB,EAAE,OAAO;KAC1B;CACF,CAAA"}
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Split out of `skill-installation.helpers.ts` to stay under the 500-line
|
|
7
7
|
* standard once ADR-139's adoption logic was added — mirrors the existing
|
|
8
|
-
* `skill-installation.io.ts` sibling-split convention.
|
|
8
|
+
* `skill-installation.io.ts` sibling-split convention. Round 25 split its own
|
|
9
|
+
* helpers out again, into `skill-installation.uninstall.helpers.ts`, for the
|
|
10
|
+
* same reason. `performUninstall`
|
|
9
11
|
* has exactly one internal consumer (`skill-installation.service.ts`) and
|
|
10
12
|
* is not part of `@skillsmith/core`'s public export surface.
|
|
11
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.uninstall.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.uninstall.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"skill-installation.uninstall.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.uninstall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAGtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAQvE;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAsC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAmCtF;AAED,2EAA2E;AAC3E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,eAAe,CAAA;IACzB,mBAAmB,EAAE,yBAAyB,CAAA;IAC9C,UAAU,EAAE,gBAAgB,CAAA;IAC5B;6EACyE;IACzE,MAAM,CAAC,EAAE,QAAQ,CAAA;CAClB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgQ3B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helpers for `performUninstall`: what it found before removing,
|
|
3
|
+
* progress reporting that cannot change the outcome, record comparison, and
|
|
4
|
+
* the parked-leftover warnings.
|
|
5
|
+
* @module @skillsmith/core/services/skill-installation.uninstall.helpers
|
|
6
|
+
*
|
|
7
|
+
* Split out of `skill-installation.uninstall.ts` in SMI-6529 round 25, when
|
|
8
|
+
* that file reached 504 lines against the 500-line standard — the same
|
|
9
|
+
* sibling-split convention as `skill-installation.io.ts`. These are the pieces
|
|
10
|
+
* `performUninstall` calls but does not itself define; nothing here is part of
|
|
11
|
+
* `@skillsmith/core`'s public export surface.
|
|
12
|
+
*/
|
|
13
|
+
import type { Stats } from 'fs';
|
|
14
|
+
import type { ProgressCallback, SkillManifestEntry } from './skill-installation.types.js';
|
|
15
|
+
/**
|
|
16
|
+
* SMI-6529 round 15 (cross-model review, Critical): what uninstall found at
|
|
17
|
+
* `installPath` before removing it. A folder with `.git` at its root is a git
|
|
18
|
+
* working tree, which git owns (ADR-155), so it is refused with or without
|
|
19
|
+
* `force`: deleting it would take unpushed commits and uncommitted edits with
|
|
20
|
+
* it. A symlink or a file needs no such check, since removing it leaves what
|
|
21
|
+
* it points at alone. Returns the entry's identity (null when nothing is
|
|
22
|
+
* there), so the delete can confirm it is still the same entry.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inspectForRemoval(installPath: string): Promise<{
|
|
25
|
+
stat: Stats | null;
|
|
26
|
+
} | {
|
|
27
|
+
refusal: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Report progress without letting a caller's listener change the outcome.
|
|
31
|
+
* Round 18 (cross-model review): a listener that threw after the folder was
|
|
32
|
+
* already removed reached the generic catch, which reported neither what had
|
|
33
|
+
* been removed nor what was left parked.
|
|
34
|
+
*
|
|
35
|
+
* Round 28 (pre-merge gate, PR-07): the failure is still swallowed as far as
|
|
36
|
+
* the uninstall's outcome goes — that part was deliberate and stays — but it
|
|
37
|
+
* is no longer swallowed as far as the USER goes. It is pushed onto
|
|
38
|
+
* `problems`, which the caller surfaces alongside its other warnings. An empty
|
|
39
|
+
* catch here was the same "could not tell" silence this wave removed
|
|
40
|
+
* everywhere else, in the one place that reports to the user for a living.
|
|
41
|
+
*/
|
|
42
|
+
export declare function notify(onProgress: ProgressCallback, problems: string[], ...args: Parameters<ProgressCallback>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Whether `entry` is still, field for field, the record this uninstall
|
|
45
|
+
* loaded. Round 18 (cross-model review): timestamps alone are not a unique
|
|
46
|
+
* generation token — two installs can share a millisecond, an older writer
|
|
47
|
+
* can omit them, and a copied record keeps them — so every field has to
|
|
48
|
+
* match. An install that claims the same name writes its own values, so any
|
|
49
|
+
* difference means a different generation. A durable generation id belongs
|
|
50
|
+
* with A1's manifest work (SMI-6531).
|
|
51
|
+
*/
|
|
52
|
+
export declare function sameRecord(entry: SkillManifestEntry, loaded: SkillManifestEntry): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Warnings naming anything an earlier removal left parked next to
|
|
55
|
+
* `installPath`. Round 17 (cross-model review): every exit after the removal
|
|
56
|
+
* reports these, not only the successful one.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parkedWarnings(installPath: string): Promise<string[]>;
|
|
59
|
+
//# sourceMappingURL=skill-installation.uninstall.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installation.uninstall.helpers.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.uninstall.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,CAAA;AAK/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAEzF;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,IAAI,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CACpB,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,GACpC,IAAI,CAWN;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAEzF;AAqBD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAK3E"}
|