@avantf/dsh-plugin-base 0.5.1 → 0.5.3

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.
Files changed (104) hide show
  1. package/README.md +1 -7
  2. package/{dist → lib}/bootstrap.d.ts +1 -1
  3. package/{dist → lib}/bootstrap.d.ts.map +1 -1
  4. package/{dist → lib}/bootstrap.js +45 -8
  5. package/{dist → lib}/compat.d.ts +12 -1
  6. package/{dist → lib}/compat.d.ts.map +1 -1
  7. package/{dist → lib}/compat.js +42 -13
  8. package/{dist → lib}/lint.d.ts.map +1 -1
  9. package/{dist → lib}/lint.js +3 -1
  10. package/{dist → lib}/providers/model.d.ts.map +1 -1
  11. package/{dist → lib}/providers/model.js +100 -17
  12. package/{dist → lib}/providers/npm.d.ts.map +1 -1
  13. package/{dist → lib}/providers/npm.js +4 -0
  14. package/{dist → lib}/provisioner.d.ts.map +1 -1
  15. package/{dist → lib}/provisioner.js +51 -32
  16. package/package.json +24 -23
  17. package/dist/cli.d.ts +0 -4
  18. package/dist/cli.d.ts.map +0 -1
  19. package/dist/cli.js +0 -37
  20. /package/{dist → lib}/artifact.d.ts +0 -0
  21. /package/{dist → lib}/artifact.d.ts.map +0 -0
  22. /package/{dist → lib}/artifact.js +0 -0
  23. /package/{dist → lib}/conformance.d.ts +0 -0
  24. /package/{dist → lib}/conformance.d.ts.map +0 -0
  25. /package/{dist → lib}/conformance.js +0 -0
  26. /package/{dist → lib}/errors.d.ts +0 -0
  27. /package/{dist → lib}/errors.d.ts.map +0 -0
  28. /package/{dist → lib}/errors.js +0 -0
  29. /package/{dist → lib}/fs.d.ts +0 -0
  30. /package/{dist → lib}/fs.d.ts.map +0 -0
  31. /package/{dist → lib}/fs.js +0 -0
  32. /package/{dist → lib}/index.d.ts +0 -0
  33. /package/{dist → lib}/index.d.ts.map +0 -0
  34. /package/{dist → lib}/index.js +0 -0
  35. /package/{dist → lib}/integrity.d.ts +0 -0
  36. /package/{dist → lib}/integrity.d.ts.map +0 -0
  37. /package/{dist → lib}/integrity.js +0 -0
  38. /package/{dist → lib}/interface.d.ts +0 -0
  39. /package/{dist → lib}/interface.d.ts.map +0 -0
  40. /package/{dist → lib}/interface.js +0 -0
  41. /package/{dist → lib}/interface_gate.d.ts +0 -0
  42. /package/{dist → lib}/interface_gate.d.ts.map +0 -0
  43. /package/{dist → lib}/interface_gate.js +0 -0
  44. /package/{dist → lib}/internal.d.ts +0 -0
  45. /package/{dist → lib}/internal.d.ts.map +0 -0
  46. /package/{dist → lib}/internal.js +0 -0
  47. /package/{dist → lib}/kit/family.d.ts +0 -0
  48. /package/{dist → lib}/kit/family.d.ts.map +0 -0
  49. /package/{dist → lib}/kit/family.js +0 -0
  50. /package/{dist → lib}/kit/index.d.ts +0 -0
  51. /package/{dist → lib}/kit/index.d.ts.map +0 -0
  52. /package/{dist → lib}/kit/index.js +0 -0
  53. /package/{dist → lib}/kit/logger.d.ts +0 -0
  54. /package/{dist → lib}/kit/logger.d.ts.map +0 -0
  55. /package/{dist → lib}/kit/logger.js +0 -0
  56. /package/{dist → lib}/kit/prompt_files.d.ts +0 -0
  57. /package/{dist → lib}/kit/prompt_files.d.ts.map +0 -0
  58. /package/{dist → lib}/kit/prompt_files.js +0 -0
  59. /package/{dist → lib}/kit/typert.d.ts +0 -0
  60. /package/{dist → lib}/kit/typert.d.ts.map +0 -0
  61. /package/{dist → lib}/kit/typert.js +0 -0
  62. /package/{dist → lib}/kit/wellformed.d.ts +0 -0
  63. /package/{dist → lib}/kit/wellformed.d.ts.map +0 -0
  64. /package/{dist → lib}/kit/wellformed.js +0 -0
  65. /package/{dist → lib}/layout.d.ts +0 -0
  66. /package/{dist → lib}/layout.d.ts.map +0 -0
  67. /package/{dist → lib}/layout.js +0 -0
  68. /package/{dist → lib}/lint.d.ts +0 -0
  69. /package/{dist → lib}/lock.d.ts +0 -0
  70. /package/{dist → lib}/lock.d.ts.map +0 -0
  71. /package/{dist → lib}/lock.js +0 -0
  72. /package/{dist → lib}/manifest.d.ts +0 -0
  73. /package/{dist → lib}/manifest.d.ts.map +0 -0
  74. /package/{dist → lib}/manifest.js +0 -0
  75. /package/{dist → lib}/net.d.ts +0 -0
  76. /package/{dist → lib}/net.d.ts.map +0 -0
  77. /package/{dist → lib}/net.js +0 -0
  78. /package/{dist → lib}/package-name.d.ts +0 -0
  79. /package/{dist → lib}/package-name.d.ts.map +0 -0
  80. /package/{dist → lib}/package-name.js +0 -0
  81. /package/{dist → lib}/preset.d.ts +0 -0
  82. /package/{dist → lib}/preset.d.ts.map +0 -0
  83. /package/{dist → lib}/preset.js +0 -0
  84. /package/{dist → lib}/providers/archive.d.ts +0 -0
  85. /package/{dist → lib}/providers/archive.d.ts.map +0 -0
  86. /package/{dist → lib}/providers/archive.js +0 -0
  87. /package/{dist → lib}/providers/model.d.ts +0 -0
  88. /package/{dist → lib}/providers/npm.d.ts +0 -0
  89. /package/{dist → lib}/provisioner.d.ts +0 -0
  90. /package/{dist → lib}/semver.d.ts +0 -0
  91. /package/{dist → lib}/semver.d.ts.map +0 -0
  92. /package/{dist → lib}/semver.js +0 -0
  93. /package/{dist → lib}/state.d.ts +0 -0
  94. /package/{dist → lib}/state.d.ts.map +0 -0
  95. /package/{dist → lib}/state.js +0 -0
  96. /package/{dist → lib}/tar.d.ts +0 -0
  97. /package/{dist → lib}/tar.d.ts.map +0 -0
  98. /package/{dist → lib}/tar.js +0 -0
  99. /package/{dist → lib}/types.d.ts +0 -0
  100. /package/{dist → lib}/types.d.ts.map +0 -0
  101. /package/{dist → lib}/types.js +0 -0
  102. /package/{dist → lib}/zip.d.ts +0 -0
  103. /package/{dist → lib}/zip.d.ts.map +0 -0
  104. /package/{dist → lib}/zip.js +0 -0
package/README.md CHANGED
@@ -69,7 +69,7 @@ npm install @avantf/dsh-plugin-base
69
69
 
70
70
  ```ts
71
71
  // ── ① 构建期:把零依赖的 bootstrap 拷进自己的源码树,按相对路径 import ──────────────
72
- // cp node_modules/@avantf/dsh-plugin-base/dist/bootstrap.js src/envinit-bootstrap.js
72
+ // cp node_modules/@avantf/dsh-plugin-base/lib/bootstrap.js src/envinit-bootstrap.js
73
73
  // (有打包步骤时用 ./preset 自动内联,见「运行时装载」。)
74
74
  import { loadFramework } from './envinit-bootstrap.js'
75
75
 
@@ -229,12 +229,6 @@ framework.createProvisioner({
229
229
  })
230
230
  ```
231
231
 
232
- 清单可以不联网检查:
233
-
234
- ```bash
235
- provision lint manifest.json
236
- ```
237
-
238
232
  ### 取用就绪资源
239
233
 
240
234
  ```ts
@@ -3,7 +3,7 @@
3
3
  * @module bootstrap
4
4
  */
5
5
  /** The bootstrap version; the resolved framework must satisfy {@link supportedRange}. */
6
- export declare const VERSION = "0.5.1";
6
+ export declare const VERSION = "0.5.3";
7
7
  /**
8
8
  * The framework interval this bootstrap can launch.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,yFAAyF;AACzF,eAAO,MAAM,OAAO,UAAU,CAAA;AAC9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAI9C,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAA;IACjC,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAC5B;AA8LD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAoC7G;AAED,oFAAoF;AACpF,wBAAsB,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAaxG;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyCnG"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,yFAAyF;AACzF,eAAO,MAAM,OAAO,UAAU,CAAA;AAC9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAI9C,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAA;IACjC,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAC5B;AAqOD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAkC7G;AAED,oFAAoF;AACpF,wBAAsB,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAaxG;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyCnG"}
@@ -2,12 +2,11 @@
2
2
  * Resolve the framework copy the caller's tree already has.
3
3
  * @module bootstrap
4
4
  */
5
- import { promises as fsp } from 'node:fs';
6
- import { createRequire } from 'node:module';
5
+ import { existsSync, promises as fsp, readFileSync } from 'node:fs';
7
6
  import { dirname, join } from 'node:path';
8
7
  import { fileURLToPath, pathToFileURL } from 'node:url';
9
8
  /** The bootstrap version; the resolved framework must satisfy {@link supportedRange}. */
10
- export const VERSION = '0.5.1';
9
+ export const VERSION = '0.5.3';
11
10
  /**
12
11
  * The framework interval this bootstrap can launch.
13
12
  *
@@ -211,6 +210,47 @@ function entryOf(dir, manifest) {
211
210
  relative = manifest['main'];
212
211
  return join(dir, relative ?? 'index.js');
213
212
  }
213
+ /** The name a `package.json` declares, or `undefined`. */
214
+ function manifestName(manifestPath) {
215
+ try {
216
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
217
+ const name = parsed.name;
218
+ return typeof name === 'string' ? name : undefined;
219
+ }
220
+ catch {
221
+ return undefined;
222
+ }
223
+ }
224
+ /**
225
+ * The framework directory, resolved by walking THIS FILE's ancestor chain only.
226
+ *
227
+ * WHY THIS DOES NOT USE `createRequire(...).resolve()`. Node falls back to `Module.globalPaths`
228
+ * when a package is not found under any ancestor's `node_modules`, and `Module.globalPaths` is
229
+ * seeded from `NODE_PATH` AT PROCESS START (pnpm sets it for every `pnpm run` process, so it
230
+ * survives deleting the variable at runtime). With that fallback a caller whose own tree has NO
231
+ * base silently resolved whatever the process environment happened to provide — a copy outside the
232
+ * plugin's tree, exactly the false green this lookup exists to avoid (`compat.ts`'s
233
+ * `resolveRuntimeVersion` and `preset.ts`'s `resolveFrameworkDir` record the same trap and walk
234
+ * the ancestors for the same reason).
235
+ *
236
+ * Two answers are legitimate, in walk order: `<dir>/node_modules/<PACKAGE>` (the package manager's
237
+ * copy) and a `package.json` whose OWN name is the package (self-reference — running from inside
238
+ * the framework's own tree: a dev checkout, this repo's tests).
239
+ */
240
+ function resolveFrameworkDir() {
241
+ let current = dirname(fileURLToPath(import.meta.url));
242
+ for (;;) {
243
+ const installed = join(current, 'node_modules', ...PACKAGE.split('/'));
244
+ if (existsSync(join(installed, 'package.json')))
245
+ return installed;
246
+ if (manifestName(join(current, 'package.json')) === PACKAGE)
247
+ return current;
248
+ const parent = dirname(current);
249
+ if (parent === current)
250
+ return undefined;
251
+ current = parent;
252
+ }
253
+ }
214
254
  /**
215
255
  * Report the framework copy this caller's tree resolves to, or `undefined` with a warning.
216
256
  * Never throws and never installs anything.
@@ -233,11 +273,8 @@ export async function ensureFramework(options) {
233
273
  return undefined;
234
274
  }
235
275
  const supported = options.supportedRange ?? supportedRange;
236
- let dir;
237
- try {
238
- dir = dirname(createRequire(import.meta.url).resolve(`${PACKAGE}/package.json`));
239
- }
240
- catch {
276
+ const dir = resolveFrameworkDir();
277
+ if (dir === undefined) {
241
278
  warn(`${PACKAGE} is not installed for this plugin — install it with the package manager (pnpm install)`);
242
279
  return undefined;
243
280
  }
@@ -202,6 +202,16 @@ export declare function readBuildVersions(buildUrl: string | URL, packages: read
202
202
  * @param name - the peer package.
203
203
  * @param base - module URL to resolve from (defaults to this package).
204
204
  * @returns the version string, or `undefined`.
205
+ *
206
+ * WHY THIS DOES NOT USE `require.resolve`. Node falls back to `Module.globalPaths` when a package is
207
+ * not found under the base's own `node_modules`, and `Module.globalPaths` is seeded from `NODE_PATH`
208
+ * AT PROCESS START (pnpm sets it for every `pnpm run` process, so it survives deleting the variable at
209
+ * runtime). With that fallback, a base with no links of its own silently resolved whatever the
210
+ * WORKSPACE happened to install — but the one question this function answers is "what do THIS caller's
211
+ * links point at". Found by the suite: `compat_probes.spec.ts`'s "answers undefined for one without
212
+ * links" passed under bare `vitest` and failed under `pnpm test`, where the base resolved the
213
+ * workspace's local-build `dsh-tools@0.2.1-rc.2` (measured 2026-10-19). The walk below therefore
214
+ * visits ONLY the base directory's ancestors and never consults the process-wide path list.
205
215
  */
206
216
  export declare function resolveRuntimeVersion(name: string, base?: string | URL): string | undefined;
207
217
  /**
@@ -405,7 +415,8 @@ export declare function declaredSchemaKeys(packageId: string, names: readonly st
405
415
  * dependency and does not know what a scope IS: the value is passed through opaquely, exactly as the
406
416
  * caller's own registry expects it. Omitting it keeps the older reading — the second argument is then
407
417
  * `undefined`, which any registry that ignores it (every one that existed through v3) treats exactly as
408
- * it treated the single-argument call.
418
+ * it treated the single-argument call. The call is made through `Reflect.apply` with the service as
419
+ * `this`, because a live service's method must not be extracted (see the note at the call site).
409
420
  * @param input - the live context, the real tool names, the logger, and the caller's optional scope.
410
421
  * @returns the registrations that did not show up (empty on success).
411
422
  */
@@ -1 +1 @@
1
- {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../src/compat.ts"],"names":[],"mappings":"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"}
@@ -33,8 +33,8 @@
33
33
  *
34
34
  * @module @avantf/dsh-plugin-base
35
35
  */
36
- import { readFileSync } from 'node:fs';
37
- import { createRequire } from 'node:module';
36
+ import { readFileSync, statSync } from 'node:fs';
37
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
38
38
  import { fileURLToPath } from 'node:url';
39
39
  /**
40
40
  * Every line this module logs carries this token, so a support question can be answered
@@ -161,14 +161,37 @@ export function readBuildVersions(buildUrl, packages, fallback = {}) {
161
161
  * @param name - the peer package.
162
162
  * @param base - module URL to resolve from (defaults to this package).
163
163
  * @returns the version string, or `undefined`.
164
+ *
165
+ * WHY THIS DOES NOT USE `require.resolve`. Node falls back to `Module.globalPaths` when a package is
166
+ * not found under the base's own `node_modules`, and `Module.globalPaths` is seeded from `NODE_PATH`
167
+ * AT PROCESS START (pnpm sets it for every `pnpm run` process, so it survives deleting the variable at
168
+ * runtime). With that fallback, a base with no links of its own silently resolved whatever the
169
+ * WORKSPACE happened to install — but the one question this function answers is "what do THIS caller's
170
+ * links point at". Found by the suite: `compat_probes.spec.ts`'s "answers undefined for one without
171
+ * links" passed under bare `vitest` and failed under `pnpm test`, where the base resolved the
172
+ * workspace's local-build `dsh-tools@0.2.1-rc.2` (measured 2026-10-19). The walk below therefore
173
+ * visits ONLY the base directory's ancestors and never consults the process-wide path list.
164
174
  */
165
175
  export function resolveRuntimeVersion(name, base = import.meta.url) {
166
176
  try {
167
- const require = createRequire(base);
168
- // Built at runtime so a bundler cannot constant-fold it and resolve at build time.
169
- const manifest = require.resolve(`${name}/package.json`);
170
- const parsed = JSON.parse(readFileSync(manifest, 'utf8'));
171
- return typeof parsed.version === 'string' && parsed.version !== '' ? parsed.version : undefined;
177
+ const start = typeof base === 'string' && isAbsolute(base)
178
+ ? dirname(base)
179
+ : dirname(fileURLToPath(base));
180
+ for (let dir = resolve(start);; dir = dirname(dir)) {
181
+ const manifest = join(dir, 'node_modules', name, 'package.json');
182
+ try {
183
+ // A directory hit can still be a dangling link; the stat keeps "unknown" honest.
184
+ if (statSync(manifest).isFile()) {
185
+ const parsed = JSON.parse(readFileSync(manifest, 'utf8'));
186
+ return typeof parsed.version === 'string' && parsed.version !== '' ? parsed.version : undefined;
187
+ }
188
+ }
189
+ catch {
190
+ // Not here; keep walking.
191
+ }
192
+ if (dirname(dir) === dir)
193
+ return undefined;
194
+ }
172
195
  }
173
196
  catch {
174
197
  return undefined;
@@ -724,7 +747,8 @@ export function declaredSchemaKeys(packageId, names) {
724
747
  * dependency and does not know what a scope IS: the value is passed through opaquely, exactly as the
725
748
  * caller's own registry expects it. Omitting it keeps the older reading — the second argument is then
726
749
  * `undefined`, which any registry that ignores it (every one that existed through v3) treats exactly as
727
- * it treated the single-argument call.
750
+ * it treated the single-argument call. The call is made through `Reflect.apply` with the service as
751
+ * `this`, because a live service's method must not be extracted (see the note at the call site).
728
752
  * @param input - the live context, the real tool names, the logger, and the caller's optional scope.
729
753
  * @returns the registrations that did not show up (empty on success).
730
754
  */
@@ -750,11 +774,16 @@ export function verifyRegisteredFaces(input) {
750
774
  if (isRecord(tools) && typeof tools['get'] === 'function') {
751
775
  for (const name of input.toolNames) {
752
776
  try {
753
- // The property is read once and called with the caller's scope; a `get` that only accepts one
754
- // parameter (every registry v3 and earlier knew) simply ignores the second argument, so the
755
- // omitted-scope reading is unchanged.
756
- const get = tools['get'];
757
- if (get(name, input.scope) === undefined)
777
+ // The property is read once and APPLIED WITH THE SERVICE AS `this` — the receiver is not
778
+ // decoration. A live Cordis service hands out PROXIED methods, and the dsh tools registry
779
+ // implements this one as `this.view(scope).visible.get(name)`: extract the function and call
780
+ // it bare and `this` is `undefined`, so it throws `Cannot read properties of undefined
781
+ // (reading 'view')`, falls into the catch below, and EVERY name is reported missing even
782
+ // though the registry was answering fine. Measured on dsh 0.2.0-rc.2:
783
+ // `tools.get(n, scope)` resolves while `const g = tools.get; g(n, scope)` throws. A registry
784
+ // whose `get` ignores the second argument (every generation through v3) is unaffected: the
785
+ // omitted-scope reading stays the one-argument lookup it always was.
786
+ if (Reflect.apply(tools['get'], tools, [name, input.scope]) === undefined)
758
787
  missing.push(name);
759
788
  }
760
789
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AASA,mDAAmD;AACnD,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAsF,CAAA;AAEjI,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAA6B,CAAA;AAElF,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAAiC,CAAA;AAExF,+EAA+E;AAC/E,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAMhE,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAoC,CAAA;AAEjF,oCAAoC;AACpC,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAUxC,CAAA;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAGzF;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;CAC1C;AAUD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIpD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAqDD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,UAAU,CAgJ1D;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CASzD"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAWA,mDAAmD;AACnD,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAsF,CAAA;AAEjI,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAA6B,CAAA;AAElF,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAAiC,CAAA;AAExF,+EAA+E;AAC/E,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAMhE,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAoC,CAAA;AAEjF,oCAAoC;AACpC,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAUxC,CAAA;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAGzF;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;CAC1C;AAUD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIpD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAqDD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,UAAU,CAgJ1D;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CASzD"}
@@ -1,5 +1,7 @@
1
1
  /**
2
- * `provision lint` — the offline, purely static manifest check.
2
+ * The envinit manifest's static lint rules: the field tables and shape checks the provisioner reads
3
+ * at `declare()` time. Internal (`./internal`, no compatibility promise) — the base ships no CLI, so
4
+ * these are exercised by the framework and its tests, not by an executable entry point.
3
5
  * @module lint
4
6
  */
5
7
  import { ProvisionError } from './errors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/providers/model.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAGV,QAAQ,EAOT,MAAM,aAAa,CAAA;AAEpB,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAG7C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,QAAyB,CAAA;AAuBrD;cACc;AACd,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,CAAA;AAY7C,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;IAClC,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,wDAAwD;IACxD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC3C;AA4gBD;sEACsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,QAAQ,CA8GpF"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/providers/model.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAGV,QAAQ,EAOT,MAAM,aAAa,CAAA;AAEpB,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAG7C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,QAAyB,CAAA;AAuBrD;cACc;AACd,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,CAAA;AAY7C,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;IAClC,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,wDAAwD;IACxD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC3C;AAilBD;sEACsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,QAAQ,CAyHpF"}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { createHash } from 'node:crypto';
6
6
  import { once } from 'node:events';
7
- import { createWriteStream } from 'node:fs';
7
+ import { createReadStream, createWriteStream } from 'node:fs';
8
8
  import { basename, dirname, join, relative, resolve } from 'node:path';
9
9
  import { finished } from 'node:stream/promises';
10
10
  import { ProvisionError } from '../errors.js';
@@ -138,6 +138,19 @@ function flatSidecarDir(home, targetRoot, repo) {
138
138
  function sha256Hex(bytes) {
139
139
  return createHash('sha256').update(bytes).digest('hex');
140
140
  }
141
+ /**
142
+ * sha256 of a file on disk, STREAMED one chunk at a time: `fs.readFile` would hold a 2 GiB model
143
+ * file entirely in the heap just to hash it, so `verify` used to spike to the file's full size on
144
+ * every required file. The read goes through `node:fs` for the same reason {@link openBlobSpill}'s
145
+ * write does — {@link ProvisionFs} has no streaming API and this change must not move the interface
146
+ * generation; the path has already been vetted by the seam's `stat` before this runs.
147
+ */
148
+ async function sha256File(path) {
149
+ const hash = createHash('sha256');
150
+ for await (const chunk of createReadStream(path))
151
+ hash.update(chunk);
152
+ return hash.digest('hex');
153
+ }
141
154
  function text(value) {
142
155
  return new TextDecoder().decode(value);
143
156
  }
@@ -455,8 +468,26 @@ async function writeBlob(fs, blob, bytes) {
455
468
  return;
456
469
  await fs.atomicWrite(blob, bytes);
457
470
  }
458
- /** Throw a `verify/failed` for the first file that is not provably usable. */
471
+ /**
472
+ * A `verify/failed` the provider's own `probe` ALSO reports as a miss.
473
+ *
474
+ * The distinction decides whether a shared completion marker is withdrawn (see `verify` below): a
475
+ * missing, empty or directory required file is already a `found: false` for THIS item, so removing
476
+ * `refs/<revision>` / `record.json` would only force healthy SIBLING items of the same
477
+ * repo+revision to reinstall. A content mismatch (`verifyCorrupt`) is invisible to the probe's
478
+ * existence checks and must withdraw it.
479
+ */
480
+ class ProbeDetectedVerifyFailure extends ProvisionError {
481
+ constructor(message) {
482
+ super('verify/failed', message);
483
+ }
484
+ }
485
+ /** Throw a `verify/failed` for a problem `probe` already reports as a miss. */
459
486
  function verifyFailure(message) {
487
+ throw new ProbeDetectedVerifyFailure(message);
488
+ }
489
+ /** Throw a `verify/failed` only CONTENT inspection can see — the marker must be withdrawn. */
490
+ function verifyCorrupt(message) {
460
491
  throw new ProvisionError('verify/failed', message);
461
492
  }
462
493
  /** Probe the `flat` layout from the sidecar alone: no network, no mutation. */
@@ -505,27 +536,27 @@ async function verifyHub(ctx, item, spec, resolved) {
505
536
  verifyFailure(`模型文件是目录 ${file}:${path}`);
506
537
  if (info.size === 0)
507
538
  verifyFailure(`模型文件为空 ${file}:${path}`);
508
- const digest = sha256Hex(await ctx.fs.readFile(path));
539
+ const digest = await sha256File(path);
509
540
  const link = await ctx.fs.readlink(path);
510
541
  if (link !== undefined) {
511
542
  // The entry resolves into blobs, so the read above already proved the blob's bytes.
512
543
  const target = resolve(dirname(path), link);
513
544
  if (!isInside(blobs, target))
514
- verifyFailure(`模型文件的链接指向 blobs 之外:${path}`);
545
+ verifyCorrupt(`模型文件的链接指向 blobs 之外:${path}`);
515
546
  const named = basename(target);
516
547
  if (!BLOB_PATTERN.test(named))
517
- verifyFailure(`模型文件的链接目标名不是 sha256:${path}`);
548
+ verifyCorrupt(`模型文件的链接目标名不是 sha256:${path}`);
518
549
  if (digest !== named)
519
- verifyFailure(`模型文件内容与 blob 名不符 ${file}:${path}`);
550
+ verifyCorrupt(`模型文件内容与 blob 名不符 ${file}:${path}`);
520
551
  continue;
521
552
  }
522
553
  // A real-file fallback carries no expectation; the blob named by its digest must exist intact.
523
554
  const blobPath = join(blobs, digest);
524
555
  const blobInfo = await ctx.fs.stat(blobPath);
525
556
  if (blobInfo === undefined || blobInfo.isDirectory || blobInfo.size === 0)
526
- verifyFailure(`blob 缺失或为空:${blobPath}`);
527
- if (sha256Hex(await ctx.fs.readFile(blobPath)) !== digest)
528
- verifyFailure(`blob 内容与名字不符:${blobPath}`);
557
+ verifyCorrupt(`blob 缺失或为空:${blobPath}`);
558
+ if ((await sha256File(blobPath)) !== digest)
559
+ verifyCorrupt(`blob 内容与名字不符:${blobPath}`);
529
560
  }
530
561
  }
531
562
  /** Prove every required `flat` entry matches its recorded digest or the blob its link names. */
@@ -549,23 +580,62 @@ async function verifyFlat(ctx, item, spec) {
549
580
  verifyFailure(`模型文件是目录 ${file}:${path}`);
550
581
  if (info.size === 0)
551
582
  verifyFailure(`模型文件为空 ${file}:${path}`);
552
- const digest = sha256Hex(await ctx.fs.readFile(path));
583
+ const digest = await sha256File(path);
553
584
  const link = await ctx.fs.readlink(path);
554
585
  if (link !== undefined) {
555
586
  // The entry resolves into the sidecar, so the read above already proved the blob's bytes.
556
587
  const target = resolve(dirname(path), link);
557
588
  if (!isInside(blobs, target))
558
- verifyFailure(`模型文件的链接指向 blobs 之外:${path}`);
589
+ verifyCorrupt(`模型文件的链接指向 blobs 之外:${path}`);
559
590
  const named = basename(target);
560
591
  if (!BLOB_PATTERN.test(named))
561
- verifyFailure(`模型文件的链接目标名不是 sha256:${path}`);
592
+ verifyCorrupt(`模型文件的链接目标名不是 sha256:${path}`);
562
593
  if (digest !== named)
563
- verifyFailure(`模型文件内容与 blob 名不符 ${file}:${path}`);
594
+ verifyCorrupt(`模型文件内容与 blob 名不符 ${file}:${path}`);
564
595
  }
565
596
  // A names-only record cannot prove content; a recorded digest always can.
566
597
  const expected = digests[file];
567
598
  if (expected !== undefined && digest !== expected)
568
- verifyFailure(`模型文件内容与 record.json 不符 ${file}:${path}`);
599
+ verifyCorrupt(`模型文件内容与 record.json 不符 ${file}:${path}`);
600
+ }
601
+ }
602
+ /**
603
+ * Withdraw the completion marker — `refs/<revision>` (hub) or `record.json` (flat) — after a
604
+ * CONTENT failure that `probe` cannot see. The core does not quarantine this kind's `dir`: it is
605
+ * the shared `snapshots/<sha>` / data directory that `refs/<revision>` and every sibling item of
606
+ * the same repo+sha point at, so renaming it would dangle the ref and fail healthy items with the
607
+ * broken one (see `provisioner.ts`'s `ensureOne`). The marker is the only state that says
608
+ * "installed", so dropping it makes the next `probe` a miss and the next `ensure` reinstall,
609
+ * while the snapshot/blobs stay for everyone else.
610
+ *
611
+ * The marker is SHARED by every item of the same repo+revision, which is why `verify` only calls
612
+ * this for corruption (`verifyCorrupt`) and not for a missing/empty file that this item's own probe
613
+ * already reports. The marker is re-read before removal and only dropped when it still names the
614
+ * sha THIS install wrote: a concurrent installer of another revision may already have replaced it,
615
+ * and its marker is not ours to withdraw. Best-effort throughout — the verify failure is the report.
616
+ */
617
+ async function withdrawMarker(ctx, item, spec, resolved) {
618
+ try {
619
+ if (layoutOf(spec) === FLAT_LAYOUT) {
620
+ const recordPath = join(flatSidecarDir(ctx.home, item.target.root, spec.repo), RECORD_FILE);
621
+ let record;
622
+ try {
623
+ record = decodeRecord(await ctx.fs.readFile(recordPath));
624
+ }
625
+ catch {
626
+ record = undefined;
627
+ }
628
+ if (record !== undefined && record.sha === resolved.version)
629
+ await ctx.fs.rm(recordPath);
630
+ return;
631
+ }
632
+ const refPath = join(hubRoot(ctx.home, item.target.root, spec.repo), 'refs', spec.revision ?? DEFAULT_REVISION);
633
+ const recorded = text(await ctx.fs.readFile(refPath)).trim();
634
+ if (recorded === resolved.version)
635
+ await ctx.fs.rm(refPath);
636
+ }
637
+ catch {
638
+ // A marker that cannot be read or removed changes nothing about the failure being reported.
569
639
  }
570
640
  }
571
641
  /** Build the built-in model-cache provider. */
@@ -668,9 +738,22 @@ export function modelCacheProvider(options = {}) {
668
738
  },
669
739
  async verify(item, resolved, ctx) {
670
740
  const spec = parseSpec(item);
671
- if (layoutOf(spec) === FLAT_LAYOUT)
672
- return verifyFlat(ctx, item, spec);
673
- return verifyHub(ctx, item, spec, resolved);
741
+ try {
742
+ if (layoutOf(spec) === FLAT_LAYOUT)
743
+ return await verifyFlat(ctx, item, spec);
744
+ return await verifyHub(ctx, item, spec, resolved);
745
+ }
746
+ catch (error) {
747
+ // This kind bypasses `publish()`, so the core will not quarantine the failed directory.
748
+ // A CONTENT failure is invisible to `probe`'s existence checks, so the shared marker must
749
+ // go or the broken copy keeps answering present. A failure the probe already detects
750
+ // (`ProbeDetectedVerifyFailure`: missing/empty/directory) must NOT withdraw it:
751
+ // that marker is shared by every item of the same repo+revision, and the failing item is a
752
+ // miss on its own next probe — removing it would only make healthy siblings reinstall.
753
+ if (!(error instanceof ProbeDetectedVerifyFailure))
754
+ await withdrawMarker(ctx, item, spec, resolved);
755
+ throw error;
756
+ }
674
757
  },
675
758
  };
676
759
  }
@@ -1 +1 @@
1
- {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/providers/npm.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAA+B,QAAQ,EAAuE,MAAM,aAAa,CAAA;AAE7I,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAM7C,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC1G;AA4KD;;;iGAGiG;AACjG,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAgB,GAAG,MAAM,GAAG,SAAS,CAS7H;AAgID,uCAAuC;AACvC,wBAAgB,kBAAkB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,QAAQ,CAqKnF"}
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/providers/npm.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAA+B,QAAQ,EAAuE,MAAM,aAAa,CAAA;AAE7I,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAM7C,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC1G;AA+KD;;;iGAGiG;AACjG,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAgB,GAAG,MAAM,GAAG,SAAS,CAS7H;AAgID,uCAAuC;AACvC,wBAAgB,kBAAkB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,QAAQ,CAqKnF"}
@@ -68,6 +68,10 @@ async function download(ctx, urls, integrity) {
68
68
  bytes = await readCapped(response, DEFAULT_MAX_BYTES);
69
69
  }
70
70
  catch (error) {
71
+ // Crossing the byte cap is terminal: every mirror serves the same oversized object, so the
72
+ // remaining candidates are not tried (same rule as `providers/model.ts` and `net.ts`).
73
+ if (error instanceof ProvisionError && error.code === 'fetch/too-large')
74
+ throw error;
71
75
  problems.push(`${url} → ${error instanceof Error ? error.message : String(error)}`);
72
76
  continue;
73
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../src/provisioner.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EAeL,aAAa,EAMb,WAAW,EAEX,kBAAkB,EAMnB,MAAM,YAAY,CAAA;AAEnB,yDAAyD;AACzD,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA6C,CAAA;AAEvF,kDAAkD;AAClD,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAepC,UAAU,iBAAiB;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB;AAED;;;iFAGiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,iBAAiB,CAKvF;AA0DD;cACc;AACd,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAk+B1E"}
1
+ {"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../src/provisioner.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EAeL,aAAa,EAMb,WAAW,EAEX,kBAAkB,EAMnB,MAAM,YAAY,CAAA;AAEnB,yDAAyD;AACzD,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA6C,CAAA;AAEvF,kDAAkD;AAClD,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAepC,UAAU,iBAAiB;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB;AAED;;;iFAGiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,iBAAiB,CAKvF;AA0DD;cACc;AACd,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAs/B1E"}
@@ -200,38 +200,48 @@ export function createProvisioner(options) {
200
200
  const listeners = new Set();
201
201
  /** Layout read-only state, decided once per process. */
202
202
  let readOnly;
203
- let homeChecked = false;
203
+ /**
204
+ * The in-flight (then settled) home verdict. Every caller awaits the SAME promise, so a
205
+ * concurrent `declare`/`ensure` cannot slip past the gate while `readLayout` is still deciding:
206
+ * the old boolean was set BEFORE the await, so a second caller returned early with `readOnly`
207
+ * still unset and `ensureOne` then wrote into a too-new/mismatched home. The promise is never
208
+ * re-created — the verdict is once-per-process, and a rejection is part of the verdict: the body
209
+ * already folds ordinary IO failures into a `layout/unreadable` read-only code, so a promise
210
+ * that still rejects is not something a silent retry is expected to fix, and later callers
211
+ * observe the same failure rather than racing the home again.
212
+ */
213
+ let homePromise;
204
214
  const pendingDeclared = [];
205
215
  /** Decide the home's layout once. */
206
- const checkHome = async () => {
207
- if (homeChecked)
208
- return;
209
- homeChecked = true;
210
- if (unsupported !== undefined && !unsupportedWarned) {
211
- unsupportedWarned = true;
212
- logger.warn(`unsupported-envinit: 本副本 ${FRAMEWORK_VERSION} 不满足插件声明的区间 "${String(options.envinitRange)}";跳过该清单、降级挂载`);
213
- }
214
- try {
215
- const result = await readLayout(fs, home, layout);
216
- if (result.kind === 'none') {
217
- await writeLayout(fs, home, layout, 'dsh-plugin-base');
216
+ const checkHome = () => {
217
+ homePromise ??= (async () => {
218
+ if (unsupported !== undefined && !unsupportedWarned) {
219
+ unsupportedWarned = true;
220
+ logger.warn(`unsupported-envinit: 本副本 ${FRAMEWORK_VERSION} 不满足插件声明的区间 "${String(options.envinitRange)}";跳过该清单、降级挂载`);
218
221
  }
219
- else if (result.kind === 'too-new') {
220
- readOnly = 'layout/too-new';
221
- logger.warn(`layout/too-new: ${home} 由更新的布局写入;本次只读不写`);
222
+ try {
223
+ const result = await readLayout(fs, home, layout);
224
+ if (result.kind === 'none') {
225
+ await writeLayout(fs, home, layout, 'dsh-plugin-base');
226
+ }
227
+ else if (result.kind === 'too-new') {
228
+ readOnly = 'layout/too-new';
229
+ logger.warn(`layout/too-new: ${home} 由更新的布局写入;本次只读不写`);
230
+ }
231
+ else if (result.kind === 'mismatch') {
232
+ readOnly = 'layout/mismatch';
233
+ logger.warn(`layout/mismatch: ${home} 的布局是 "${result.file.layout}",本次是 "${layout}";本次只读不写`);
234
+ }
222
235
  }
223
- else if (result.kind === 'mismatch') {
224
- readOnly = 'layout/mismatch';
225
- logger.warn(`layout/mismatch: ${home} 的布局是 "${result.file.layout}",本次是 "${layout}";本次只读不写`);
236
+ catch (error) {
237
+ // A read failure keeps the read-only CONSEQUENCE but gets its own code: every item's report
238
+ // would otherwise say `layout/too-new` — "a NEWER layout wrote this, leave it alone" — about a
239
+ // home whose layout nobody managed to read at all.
240
+ logger.warn(`layout: 无法读写 .layout.json(${reasonOf(error).message});按只读继续`);
241
+ readOnly = 'layout/unreadable';
226
242
  }
227
- }
228
- catch (error) {
229
- // A read failure keeps the read-only CONSEQUENCE but gets its own code: every item's report
230
- // would otherwise say `layout/too-new` — "a NEWER layout wrote this, leave it alone" — about a
231
- // home whose layout nobody managed to read at all.
232
- logger.warn(`layout: 无法读写 .layout.json(${reasonOf(error).message});按只读继续`);
233
- readOnly = 'layout/unreadable';
234
- }
243
+ })();
244
+ return homePromise;
235
245
  };
236
246
  /**
237
247
  * The failure a persisted row should carry: an explicit `lastError` if something ever sets one,
@@ -687,6 +697,8 @@ export function createProvisioner(options) {
687
697
  return settle(declared, started, 'skipped', 'policy/offline');
688
698
  const staging = stagingDir(home, item.id, (stagingSequence += 1));
689
699
  let published;
700
+ /** Set only when the core's own `publish()` placed the directory — see the catch below. */
701
+ let corePublished;
690
702
  const installCtx = {
691
703
  ...ctx,
692
704
  stage: async () => {
@@ -696,6 +708,7 @@ export function createProvisioner(options) {
696
708
  publish: async (dir, meta) => {
697
709
  const resolved = await publish(declared, dir, meta);
698
710
  published = resolved;
711
+ corePublished = resolved;
699
712
  return resolved;
700
713
  },
701
714
  ...(onProgress === undefined
@@ -735,14 +748,20 @@ export function createProvisioner(options) {
735
748
  }
736
749
  catch (error) {
737
750
  const { code, message } = reasonOf(error);
738
- // A published-but-unusable directory is quarantined, never deleted.
739
- if (published !== undefined) {
751
+ // A published-but-unusable directory is quarantined, never deleted — but ONLY a directory the
752
+ // core itself published through `ctx.publish()`. A provider that bypasses `publish()`
753
+ // (`model-cache`, whose `dir` is the shared `snapshots/<sha>` that `refs/<revision>` and every
754
+ // sibling item of the same repo+sha point at) manages its own storage: renaming that directory
755
+ // here would dangle the ref and take healthy sibling items down with the failed one. Such a
756
+ // provider withdraws its own completion marker on verify failure instead (see
757
+ // `providers/model.ts`), which keeps the next `probe` honest without touching shared state.
758
+ if (corePublished !== undefined) {
740
759
  try {
741
- await quarantineLocked(published.dir);
742
- logger.warn(`quarantined ${published.dir}: ${message}`);
760
+ await quarantineLocked(corePublished.dir);
761
+ logger.warn(`quarantined ${corePublished.dir}: ${message}`);
743
762
  }
744
763
  catch (renameError) {
745
- logger.warn(`could not quarantine ${published.dir}: ${reasonOf(renameError).message}`);
764
+ logger.warn(`could not quarantine ${corePublished.dir}: ${reasonOf(renameError).message}`);
746
765
  }
747
766
  }
748
767
  const state = { state: 'failed', code, detail: message };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avantf/dsh-plugin-base",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "The @avantf family base for DSH plugins: startup environment initialisation (declare → probe → fetch → verify → report) plus the host compatibility gate (provision / verdictOf / probes / report / post-registration verification). One package, loaded by the plugins through the inlined bootstrap.",
5
5
  "keywords": [
6
6
  "dsh",
@@ -19,48 +19,45 @@
19
19
  },
20
20
  "exports": {
21
21
  ".": {
22
- "types": "./dist/index.d.ts",
23
- "default": "./dist/index.js"
22
+ "types": "./lib/index.d.ts",
23
+ "default": "./lib/index.js"
24
24
  },
25
25
  "./bootstrap": {
26
- "types": "./dist/bootstrap.d.ts",
27
- "default": "./dist/bootstrap.js"
26
+ "types": "./lib/bootstrap.d.ts",
27
+ "default": "./lib/bootstrap.js"
28
28
  },
29
29
  "./preset": {
30
- "types": "./dist/preset.d.ts",
31
- "default": "./dist/preset.js"
30
+ "types": "./lib/preset.d.ts",
31
+ "default": "./lib/preset.js"
32
32
  },
33
33
  "./conformance": {
34
- "types": "./dist/conformance.d.ts",
35
- "default": "./dist/conformance.js"
34
+ "types": "./lib/conformance.d.ts",
35
+ "default": "./lib/conformance.js"
36
36
  },
37
37
  "./internal": {
38
- "types": "./dist/internal.d.ts",
39
- "default": "./dist/internal.js"
38
+ "types": "./lib/internal.d.ts",
39
+ "default": "./lib/internal.js"
40
40
  },
41
41
  "./compat": {
42
- "types": "./dist/compat.d.ts",
43
- "default": "./dist/compat.js"
42
+ "types": "./lib/compat.d.ts",
43
+ "default": "./lib/compat.js"
44
44
  },
45
45
  "./package.json": "./package.json"
46
46
  },
47
47
  "files": [
48
- "dist",
49
- "!dist/*.js.map",
50
- "!dist/**/*.js.map"
48
+ "lib",
49
+ "!lib/*.js.map",
50
+ "!lib/**/*.js.map"
51
51
  ],
52
52
  "sideEffects": false,
53
53
  "peerDependencies": {
54
54
  "zod": ">=4.4.3 <5"
55
55
  },
56
- "bin": {
57
- "provision": "./dist/cli.js"
58
- },
59
56
  "devDependencies": {
60
57
  "@types/node": "^22.0.0",
61
58
  "typescript": "^5.5.0",
62
59
  "vitest": "^2.1.0",
63
- "zod": "4.6.5"
60
+ "zod": "4.4.3"
64
61
  },
65
62
  "license": "MIT",
66
63
  "publishConfig": {
@@ -69,16 +66,20 @@
69
66
  },
70
67
  "repository": {
71
68
  "type": "git",
72
- "url": "git@gitee.com:ffeng86/dsh-plugins.git",
73
- "directory": "base/plugin-base"
69
+ "url": "git@gitee.com:ffeng86/dsh-plugin-base.git"
74
70
  },
75
71
  "author": "qunqi.ff",
76
72
  "scripts": {
73
+ "setup": "pnpm install --frozen-lockfile --ignore-scripts --config.strictDepBuilds=false",
74
+ "compile": "pnpm build",
77
75
  "build": "tsc -p tsconfig.json",
78
76
  "typecheck": "node scripts/link-dsh.mjs && tsc -p tsconfig.test.json",
79
77
  "test": "node scripts/link-dsh.mjs && vitest run",
80
78
  "check": "pnpm typecheck && pnpm test",
81
79
  "pack": "node scripts/pack.mjs",
82
- "release:check": "node scripts/release-check.mjs"
80
+ "release:check": "node scripts/release-check.mjs",
81
+ "version:check": "node scripts/version.mjs check",
82
+ "version:set": "node scripts/version.mjs set",
83
+ "version:prune": "node scripts/version.mjs prune"
83
84
  }
84
85
  }
package/dist/cli.d.ts DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- /** Run the CLI and return the process exit code: 0 ok, 1 lint findings, 2 usage/IO error. */
3
- export declare function main(argv: readonly string[]): Promise<number>;
4
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAUA,6FAA6F;AAC7F,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBnE"}
package/dist/cli.js DELETED
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * `provision` CLI — the offline `provision lint` command.
4
- * @module cli
5
- */
6
- import { readFile } from 'node:fs/promises';
7
- import { lintManifestText } from './lint.js';
8
- const USAGE = '用法:provision lint <manifest.json>';
9
- /** Run the CLI and return the process exit code: 0 ok, 1 lint findings, 2 usage/IO error. */
10
- export async function main(argv) {
11
- const [command, file] = argv;
12
- if (command !== 'lint' || file === undefined) {
13
- process.stderr.write(`${USAGE}\n`);
14
- return 2;
15
- }
16
- let text;
17
- try {
18
- text = await readFile(file, 'utf8');
19
- }
20
- catch (error) {
21
- process.stderr.write(`无法读取 ${file}:${error instanceof Error ? error.message : String(error)}\n`);
22
- return 2;
23
- }
24
- const result = lintManifestText(text);
25
- for (const finding of result.findings) {
26
- const where = finding.itemId === undefined ? '' : `${finding.itemId}: `;
27
- process.stdout.write(`${where}[${finding.rule}] ${finding.message}\n`);
28
- }
29
- if (result.ok) {
30
- process.stdout.write('provision lint: ok\n');
31
- return 0;
32
- }
33
- process.stderr.write(`provision lint: ${String(result.findings.length)} 个问题\n`);
34
- return 1;
35
- }
36
- process.exitCode = await main(process.argv.slice(2));
37
- //# sourceMappingURL=cli.js.map
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes