@skillsmith/core 0.12.2 → 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 +173 -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 +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +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/scripts/__tests__/scan-imported-skills.test.js +9 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/scripts/scan-imported-skills.d.ts +7 -1
- package/dist/src/scripts/scan-imported-skills.d.ts.map +1 -1
- package/dist/src/scripts/scan-imported-skills.js +15 -2
- package/dist/src/scripts/scan-imported-skills.js.map +1 -1
- package/dist/src/scripts/skill-scanner/allowlist.d.ts +10 -0
- package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/allowlist.js +10 -0
- package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -1
- package/dist/src/scripts/skill-scanner/index.d.ts +8 -0
- package/dist/src/scripts/skill-scanner/index.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/index.js +13 -5
- package/dist/src/scripts/skill-scanner/index.js.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.js +6 -0
- package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.action-context.d.ts +15 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.js +209 -0
- package/dist/src/security/scanner/SecurityScanner.action-context.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts +25 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +40 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js +88 -51
- package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +39 -6
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts +30 -0
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js +8 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts +60 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js +228 -0
- package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js +42 -5
- package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts +69 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.js +271 -0
- package/dist/src/security/scanner/SecurityScanner.value-gate.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts +55 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js +308 -0
- package/dist/src/security/scanner/SecurityScanner.weak-passwords.js.map +1 -0
- package/dist/src/security/scanner/patterns.d.ts +61 -6
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +70 -61
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts +73 -0
- package/dist/src/security/scanner/patterns.sensitive-path.d.ts.map +1 -0
- package/dist/src/security/scanner/patterns.sensitive-path.js +192 -0
- package/dist/src/security/scanner/patterns.sensitive-path.js.map +1 -0
- package/dist/src/security/scanner/regex-utils.d.ts +31 -17
- package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
- package/dist/src/security/scanner/regex-utils.js +31 -17
- package/dist/src/security/scanner/regex-utils.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +9 -0
- package/dist/src/security/scanner/types.d.ts.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/index.d.ts +1 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/index.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/src/utils/skill-name.d.ts +19 -0
- package/dist/src/utils/skill-name.d.ts.map +1 -0
- package/dist/src/utils/skill-name.js +26 -0
- package/dist/src/utils/skill-name.js.map +1 -0
- package/dist/src/utils/skill-name.test.d.ts +2 -0
- package/dist/src/utils/skill-name.test.d.ts.map +1 -0
- package/dist/src/utils/skill-name.test.js +34 -0
- package/dist/src/utils/skill-name.test.js.map +1 -0
- 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/ContinuousSecurity.performance.test.js +12 -7
- package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
- package/dist/tests/security/ContinuousSecurity.test.js +14 -2
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +10 -12
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -1
- package/dist/tests/security/ai-defence-multiline-promotion.test.js +26 -33
- package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -1
- package/dist/tests/security/nested-instruction-long-attribute.test.js +20 -10
- package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -1
- package/dist/tests/security/scanner-evidence-tiers.test.js +15 -0
- package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -1
- package/dist/tests/security/scanner-multiline-cap.test.d.ts +21 -0
- package/dist/tests/security/scanner-multiline-cap.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-multiline-cap.test.js +152 -0
- package/dist/tests/security/scanner-multiline-cap.test.js.map +1 -0
- package/dist/tests/security/scanner-regression-guard.test.js +66 -1
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/scanner-witness-sw1.test.d.ts +32 -0
- package/dist/tests/security/scanner-witness-sw1.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-witness-sw1.test.js +255 -0
- package/dist/tests/security/scanner-witness-sw1.test.js.map +1 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts +2 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts.map +1 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.js +313 -0
- package/dist/tests/security/sensitive-path-adversarial-review.test.js.map +1 -0
- 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 +314 -0
- 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/smi5879-corroboration.core.test.js +44 -0
- package/dist/tests/security/smi5879-corroboration.core.test.js.map +1 -1
- package/dist/tests/security/weak-password-lexicon.test.d.ts +18 -0
- package/dist/tests/security/weak-password-lexicon.test.d.ts.map +1 -0
- package/dist/tests/security/weak-password-lexicon.test.js +132 -0
- package/dist/tests/security/weak-password-lexicon.test.js.map +1 -0
- package/dist/tests/skill-scanner/allowlist.test.js +58 -4
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
- package/dist/tests/skill-scanner/categorizer.test.d.ts +2 -0
- package/dist/tests/skill-scanner/categorizer.test.d.ts.map +1 -0
- package/dist/tests/skill-scanner/categorizer.test.js +71 -0
- package/dist/tests/skill-scanner/categorizer.test.js.map +1 -0
- package/dist/tests/skill-scanner/cli-guard.test.d.ts +23 -0
- package/dist/tests/skill-scanner/cli-guard.test.d.ts.map +1 -0
- package/dist/tests/skill-scanner/cli-guard.test.js +71 -0
- package/dist/tests/skill-scanner/cli-guard.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.content.test.js +59 -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.sensitive-path-fp.test.d.ts +29 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts.map +1 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js +200 -0
- package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.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 +6 -1
|
@@ -20,9 +20,40 @@ import { open, lstat, constants } from 'fs/promises';
|
|
|
20
20
|
* O_NOFOLLOW: refuse to open symlinks
|
|
21
21
|
* Prefer runtime constant from fs.constants, fall back to platform-specific values
|
|
22
22
|
* if unavailable (e.g., Windows where O_NOFOLLOW is not defined).
|
|
23
|
+
*
|
|
24
|
+
* Exported (SMI-6529 F2) so other symlink-safe fd-based writers — e.g.
|
|
25
|
+
* writeInstallFiles' own rollback restore — share this exact platform
|
|
26
|
+
* -fallback resolution instead of a second, driftable copy.
|
|
23
27
|
*/
|
|
24
|
-
const O_NOFOLLOW = constants.O_NOFOLLOW ??
|
|
28
|
+
export const O_NOFOLLOW = constants.O_NOFOLLOW ??
|
|
25
29
|
(process.platform === 'darwin' ? 0x100 : 0x20000);
|
|
30
|
+
/**
|
|
31
|
+
* SMI-6529 N3 (round 4): `FileHandle.write()` is permitted by POSIX (and
|
|
32
|
+
* measured live — 2048/10000 bytes under `RLIMIT_FSIZE`) to write FEWER
|
|
33
|
+
* bytes than requested WITHOUT throwing. Every fd-based writer in this
|
|
34
|
+
* module family used a single unchecked `await fd.write(data)` call, so a
|
|
35
|
+
* short write silently truncated content with no error — for
|
|
36
|
+
* `restoreSnapshots()` specifically, that meant a "successful" restore that
|
|
37
|
+
* left the file smaller than the true original with no `InstallRestoreError`
|
|
38
|
+
* ever raised. Loops until every byte is accounted for, or throws if a call
|
|
39
|
+
* makes zero progress (a short write that returns 0 can otherwise spin
|
|
40
|
+
* forever).
|
|
41
|
+
*
|
|
42
|
+
* @param position - Explicit starting file offset, or `null` to write
|
|
43
|
+
* sequentially from the handle's own current position (which auto-advances
|
|
44
|
+
* after each underlying `write()` call, short or not).
|
|
45
|
+
*/
|
|
46
|
+
export async function writeFullBuffer(fd, data, position = null) {
|
|
47
|
+
let written = 0;
|
|
48
|
+
while (written < data.length) {
|
|
49
|
+
const pos = position === null ? null : position + written;
|
|
50
|
+
const { bytesWritten } = await fd.write(data, written, data.length - written, pos);
|
|
51
|
+
if (bytesWritten <= 0) {
|
|
52
|
+
throw new Error(`Short write: made no progress after ${written}/${data.length} bytes written`);
|
|
53
|
+
}
|
|
54
|
+
written += bytesWritten;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
26
57
|
/**
|
|
27
58
|
* Error thrown when a symlink is detected at a write destination.
|
|
28
59
|
*/
|
|
@@ -110,7 +141,87 @@ export async function safeWriteFile(filePath, content, options) {
|
|
|
110
141
|
: typeof content === 'string'
|
|
111
142
|
? Buffer.from(content, 'utf-8')
|
|
112
143
|
: content;
|
|
113
|
-
await fd
|
|
144
|
+
await writeFullBuffer(fd, data);
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
await fd?.close();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Error thrown when `safeCreateFile`'s exclusive create finds the destination
|
|
152
|
+
* already occupied — created by something else between the caller's own
|
|
153
|
+
* pre-write classification and this call (a race, not a symlink/hardlink).
|
|
154
|
+
* SMI-6529 L14(b).
|
|
155
|
+
*/
|
|
156
|
+
export class ExclusiveCreateRaceError extends Error {
|
|
157
|
+
filePath;
|
|
158
|
+
constructor(filePath) {
|
|
159
|
+
super(`Target appeared during install (race): ${filePath}`);
|
|
160
|
+
this.name = 'ExclusiveCreateRaceError';
|
|
161
|
+
this.filePath = filePath;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* SMI-6529 L14(b): create a NEW file exclusively — refuses (throws
|
|
166
|
+
* {@link ExclusiveCreateRaceError}) if anything already exists at `filePath`,
|
|
167
|
+
* symlink or otherwise, instead of silently truncating/overwriting it. Only
|
|
168
|
+
* appropriate for a write target the caller has just classified as `absent`
|
|
169
|
+
* (`classifyPreWrite` in skill-installation.io.rollback.ts) — the atomicity
|
|
170
|
+
* of `O_CREAT|O_EXCL` means "did this call create the file" is knowable
|
|
171
|
+
* without needing `safeWriteFile`'s "record BEFORE writing" hedge (F1, round
|
|
172
|
+
* 1), which exists specifically to survive `O_TRUNC`'s non-atomicity on an
|
|
173
|
+
* OVERWRITE — a case this function is never used for.
|
|
174
|
+
*
|
|
175
|
+
* @param filePath - Absolute path to create (must not already exist)
|
|
176
|
+
* @param content - File content (string or Buffer)
|
|
177
|
+
* @param options - Optional fs.writeFile options (encoding, mode) or encoding string
|
|
178
|
+
* @param onCreated - SMI-6529 N9 (round 4): fired the MOMENT the exclusive
|
|
179
|
+
* `open()` succeeds — i.e. the instant the file is KNOWN to exist on disk —
|
|
180
|
+
* BEFORE any content byte is written. Lets a caller (`writeInstallFiles`)
|
|
181
|
+
* record its rollback "created fresh, safe to unlink" candidate at the
|
|
182
|
+
* earliest correct moment: without this, a write that later fails (e.g.
|
|
183
|
+
* ENOSPC) after `open()` already created the file left a 0-byte/partial
|
|
184
|
+
* orphan that rollback never knew to remove, since the caller previously
|
|
185
|
+
* only recorded it AFTER this whole function resolved.
|
|
186
|
+
* @throws {ExclusiveCreateRaceError} if the destination already exists
|
|
187
|
+
* @throws {SymlinkError} technically unreachable (O_EXCL fails before O_NOFOLLOW
|
|
188
|
+
* would matter) but kept out of the public contract intentionally — this
|
|
189
|
+
* function never follows anything, by construction.
|
|
190
|
+
*/
|
|
191
|
+
export async function safeCreateFile(filePath, content, options, onCreated) {
|
|
192
|
+
const mode = typeof options === 'string' ? 0o644 : (options?.mode ?? 0o644);
|
|
193
|
+
const encoding = typeof options === 'string'
|
|
194
|
+
? options
|
|
195
|
+
: (options?.encoding ?? undefined);
|
|
196
|
+
let fd;
|
|
197
|
+
try {
|
|
198
|
+
fd = await open(filePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | O_NOFOLLOW, mode);
|
|
199
|
+
// SMI-6529 R5 (round 5): pass the new file's (dev, ino) so a rollback can
|
|
200
|
+
// confirm the path still names THIS file before unlinking it, and never
|
|
201
|
+
// removes a file another process renamed over ours in between. If the
|
|
202
|
+
// stat fails, the identity is omitted and the caller falls back to
|
|
203
|
+
// unlinking by path.
|
|
204
|
+
let identity;
|
|
205
|
+
try {
|
|
206
|
+
const created = await fd.stat();
|
|
207
|
+
identity = { dev: created.dev, ino: created.ino };
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
identity = undefined;
|
|
211
|
+
}
|
|
212
|
+
onCreated?.(identity);
|
|
213
|
+
const data = typeof content === 'string' && encoding
|
|
214
|
+
? Buffer.from(content, encoding)
|
|
215
|
+
: typeof content === 'string'
|
|
216
|
+
? Buffer.from(content, 'utf-8')
|
|
217
|
+
: content;
|
|
218
|
+
await writeFullBuffer(fd, data);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
if (error.code === 'EEXIST') {
|
|
222
|
+
throw new ExclusiveCreateRaceError(filePath);
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
114
225
|
}
|
|
115
226
|
finally {
|
|
116
227
|
await fd?.close();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-fs.js","sourceRoot":"","sources":["../../../src/utils/safe-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"safe-fs.js","sourceRoot":"","sources":["../../../src/utils/safe-fs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared skill name validation utilities.
|
|
3
|
+
*
|
|
4
|
+
* Used by both `skillsmith author init` and `skillsmith create` to enforce
|
|
5
|
+
* a consistent, registry-safe naming convention across all scaffolding paths.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Valid skill names: lowercase letters, digits, and hyphens only.
|
|
9
|
+
* Must start with a lowercase letter.
|
|
10
|
+
* Matches the Skillsmith registry slug format.
|
|
11
|
+
*/
|
|
12
|
+
export declare const VALID_SKILL_NAME_RE: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* Validate a skill name against the canonical Skillsmith naming convention.
|
|
15
|
+
*
|
|
16
|
+
* @returns `true` if valid, or a string error message if invalid.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateSkillName(name: string): true | string;
|
|
19
|
+
//# sourceMappingURL=skill-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-name.d.ts","sourceRoot":"","sources":["../../../src/utils/skill-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAsB,CAAA;AAEtD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAM7D"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared skill name validation utilities.
|
|
3
|
+
*
|
|
4
|
+
* Used by both `skillsmith author init` and `skillsmith create` to enforce
|
|
5
|
+
* a consistent, registry-safe naming convention across all scaffolding paths.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Valid skill names: lowercase letters, digits, and hyphens only.
|
|
9
|
+
* Must start with a lowercase letter.
|
|
10
|
+
* Matches the Skillsmith registry slug format.
|
|
11
|
+
*/
|
|
12
|
+
export const VALID_SKILL_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
13
|
+
/**
|
|
14
|
+
* Validate a skill name against the canonical Skillsmith naming convention.
|
|
15
|
+
*
|
|
16
|
+
* @returns `true` if valid, or a string error message if invalid.
|
|
17
|
+
*/
|
|
18
|
+
export function validateSkillName(name) {
|
|
19
|
+
if (!name.trim())
|
|
20
|
+
return 'Skill name is required';
|
|
21
|
+
if (!VALID_SKILL_NAME_RE.test(name)) {
|
|
22
|
+
return 'Skill name must start with a lowercase letter and contain only lowercase letters, digits, and hyphens (e.g. my-skill)';
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=skill-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-name.js","sourceRoot":"","sources":["../../../src/utils/skill-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAA;AAEtD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,wBAAwB,CAAA;IACjD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,uHAAuH,CAAA;IAChI,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-name.test.d.ts","sourceRoot":"","sources":["../../../src/utils/skill-name.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-6472: validateSkillName tests.
|
|
3
|
+
*
|
|
4
|
+
* Ported from packages/cli/tests/create.test.ts:38-71 — @skillsmith/core is
|
|
5
|
+
* now the canonical source (packages/cli/src/utils/skill-name.ts re-exports
|
|
6
|
+
* from here), so this is where the direct unit coverage lives.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { validateSkillName } from './skill-name.js';
|
|
10
|
+
describe('validateSkillName', () => {
|
|
11
|
+
it('accepts valid lowercase-hyphen names', () => {
|
|
12
|
+
expect(validateSkillName('my-skill')).toBe(true);
|
|
13
|
+
expect(validateSkillName('skill')).toBe(true);
|
|
14
|
+
expect(validateSkillName('a1b2-c3')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it('rejects names with uppercase letters', () => {
|
|
17
|
+
const result = validateSkillName('My-Skill');
|
|
18
|
+
expect(result).not.toBe(true);
|
|
19
|
+
expect(typeof result).toBe('string');
|
|
20
|
+
});
|
|
21
|
+
it('rejects names with spaces', () => {
|
|
22
|
+
expect(validateSkillName('my skill')).not.toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it('rejects names starting with a digit', () => {
|
|
25
|
+
expect(validateSkillName('1skill')).not.toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it('rejects empty string', () => {
|
|
28
|
+
expect(validateSkillName('')).not.toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('rejects names with underscores', () => {
|
|
31
|
+
expect(validateSkillName('my_skill')).not.toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=skill-name.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-name.test.js","sourceRoot":"","sources":["../../../src/utils/skill-name.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAEnD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChD,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7C,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7B,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|