@deftai/directive-core 0.56.0 → 0.56.2
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.
|
@@ -63,6 +63,10 @@ const SKIP_DIRS = new Set([
|
|
|
63
63
|
"dist",
|
|
64
64
|
"tests",
|
|
65
65
|
".deft-cache",
|
|
66
|
+
// node_modules holds third-party markdown (dependency READMEs) that is not
|
|
67
|
+
// framework content; scanning it produced volatile, version-pinned cases that
|
|
68
|
+
// broke CI when dependency versions drifted (#1993 follow-up).
|
|
69
|
+
"node_modules",
|
|
66
70
|
]);
|
|
67
71
|
const MIGRATION_ARTIFACT_TREES = ["vbrief/migration"];
|
|
68
72
|
export function isMigrationArtifact(rel) {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* at freeze time. Leave it `null` otherwise. This is the only edit required to
|
|
38
38
|
* pin the bridge version; the freeze + drift gates pick it up automatically.
|
|
39
39
|
*/
|
|
40
|
-
export const LAST_GO_INSTALLER =
|
|
40
|
+
export const LAST_GO_INSTALLER = "v0.56.0";
|
|
41
41
|
/** Returns the frozen final Go-installer tag, or `null` while unfrozen. */
|
|
42
42
|
export function lastGoInstaller() {
|
|
43
43
|
return LAST_GO_INSTALLER;
|
|
@@ -148,6 +148,32 @@ function metaFetchedAt(metaPath) {
|
|
|
148
148
|
return null;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* #1991: report whether a cache entry's upstream issue is terminal-closed.
|
|
153
|
+
*
|
|
154
|
+
* Reads the sibling raw.json (the cached issue payload) and returns true only
|
|
155
|
+
* when its `state` is positively "closed". Unknown / missing state returns
|
|
156
|
+
* false so we never exclude an entry we are not sure about. Closed issues are
|
|
157
|
+
* terminal: their `fetched_at` age is irrelevant to dispatch-queue freshness,
|
|
158
|
+
* and `cache:fetch-all --force` (open-only enumeration) can never refresh them,
|
|
159
|
+
* so the age gate must not wedge on them.
|
|
160
|
+
*/
|
|
161
|
+
function metaEntryIsTerminalClosed(metaPath) {
|
|
162
|
+
const rawPath = join(metaPath, "..", "raw.json");
|
|
163
|
+
if (!existsSync(rawPath))
|
|
164
|
+
return false;
|
|
165
|
+
try {
|
|
166
|
+
const raw = JSON.parse(readFileSync(rawPath, "utf8"));
|
|
167
|
+
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
168
|
+
const state = raw.state;
|
|
169
|
+
return typeof state === "string" && state.toLowerCase() === "closed";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
/* unknown state -> keep in scope */
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
151
177
|
function loadScopeRules(projectRoot) {
|
|
152
178
|
const defPath = join(projectRoot, "vbrief", "PROJECT-DEFINITION.vbrief.json");
|
|
153
179
|
if (!existsSync(defPath))
|
|
@@ -398,10 +424,16 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
398
424
|
allMetaPaths.length > 0 &&
|
|
399
425
|
scopedMetaPaths.length === 0 &&
|
|
400
426
|
candState === "populated";
|
|
401
|
-
// Step 5: Oldest in-scope entry age + drift probe (#1886)
|
|
427
|
+
// Step 5: Oldest in-scope entry age + drift probe (#1886).
|
|
428
|
+
// #1991: judge age over OPEN entries only. Terminal-closed entries are
|
|
429
|
+
// excluded because `cache:fetch-all --force` (open-only enumeration) can never
|
|
430
|
+
// refresh their `fetched_at`, so including them would let a stale closed entry
|
|
431
|
+
// wedge the gate with no working recovery command.
|
|
432
|
+
const rawCandidatePaths = scopedMetaPaths.length > 0 ? scopedMetaPaths : allMetaPaths;
|
|
433
|
+
const ageCandidatePaths = rawCandidatePaths.filter((p) => !metaEntryIsTerminalClosed(p));
|
|
402
434
|
let minFetchedAt = null;
|
|
403
435
|
let minMetaPath = null;
|
|
404
|
-
for (const p of
|
|
436
|
+
for (const p of ageCandidatePaths) {
|
|
405
437
|
const ts = metaFetchedAt(p);
|
|
406
438
|
if (ts !== null && (minFetchedAt === null || ts < minFetchedAt)) {
|
|
407
439
|
minFetchedAt = ts;
|
|
@@ -411,7 +443,11 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
411
443
|
const now = nowFn();
|
|
412
444
|
const ageMs = minFetchedAt !== null ? now.getTime() - minFetchedAt.getTime() : Infinity;
|
|
413
445
|
const ageH = ageMs / (1000 * 3600);
|
|
414
|
-
|
|
446
|
+
// When every candidate is terminal-closed (non-empty input, no open entry to
|
|
447
|
+
// judge) there is nothing open whose age can be stale -> fresh. The truly
|
|
448
|
+
// empty-cache case (no entries at all) preserves its prior staleness result.
|
|
449
|
+
const allCandidatesClosed = minFetchedAt === null && rawCandidatePaths.length > 0;
|
|
450
|
+
const stale = allCandidatesClosed ? false : ageH > maxAgeHours;
|
|
415
451
|
// Step 5b: --allow-stale bypass
|
|
416
452
|
if (stale && allowStale) {
|
|
417
453
|
const warning = [
|
|
@@ -83,17 +83,17 @@ export type BridgeAssetSelection = {
|
|
|
83
83
|
candidates: string[];
|
|
84
84
|
};
|
|
85
85
|
/**
|
|
86
|
-
* Pick the
|
|
86
|
+
* Pick the installer bridge binary matching the current host platform/arch.
|
|
87
87
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* -
|
|
95
|
-
* -
|
|
96
|
-
*
|
|
88
|
+
* The release publishes one asset per OS (`install-<os>-<arch>`, with macOS as a
|
|
89
|
+
* single `install-macos-universal` fat binary). Selecting `assets[0]`
|
|
90
|
+
* alphabetically silently picks the wrong binary on a multi-asset release,
|
|
91
|
+
* producing an exec-format failure on a mismatched runner. This resolves the
|
|
92
|
+
* host-matching asset instead and reports a precise outcome when none matches:
|
|
93
|
+
* - single installer asset -> use it (single-binary releases, back-compat);
|
|
94
|
+
* - prefer an asset whose name carries BOTH the OS and arch tokens;
|
|
95
|
+
* - else fall back to an OS-only match (e.g. the macOS universal binary, which
|
|
96
|
+
* carries no arch suffix, or releases that omit the arch suffix);
|
|
97
97
|
* - else `no-platform-match` so the caller fails loudly rather than guessing.
|
|
98
98
|
*/
|
|
99
99
|
export declare function selectBridgeAsset(fileNames: readonly string[], platform?: NodeJS.Platform, arch?: string): BridgeAssetSelection;
|
|
@@ -269,11 +269,21 @@ export function assertNpmHybridMigration(workRoot, agentsTemplatePath, seams = {
|
|
|
269
269
|
`directive migrate -> managed_by: '${NPM_MANAGED_SENTINEL_VALUE}', idempotent)`,
|
|
270
270
|
];
|
|
271
271
|
}
|
|
272
|
-
/**
|
|
272
|
+
/**
|
|
273
|
+
* Map a Node `process.platform` value to the published release OS asset token.
|
|
274
|
+
*
|
|
275
|
+
* The release workflow (.github/workflows/release.yml) uploads assets named
|
|
276
|
+
* `install-<os>-<arch>` where <os> is `windows` / `macos` / `linux` -- NOT the Go
|
|
277
|
+
* `GOOS` token. In particular macOS publishes a single `install-macos-universal`
|
|
278
|
+
* binary (the lipo'd fat binary), so a darwin host must look for `macos`, not
|
|
279
|
+
* `darwin`.
|
|
280
|
+
*/
|
|
273
281
|
function goOsToken(platform) {
|
|
274
282
|
if (platform === "win32")
|
|
275
283
|
return "windows";
|
|
276
|
-
|
|
284
|
+
if (platform === "darwin")
|
|
285
|
+
return "macos"; // assets are install-macos-universal
|
|
286
|
+
return platform; // "linux" lines up as-is
|
|
277
287
|
}
|
|
278
288
|
/** Map a Node `process.arch` value to the Go release `GOARCH` asset token. */
|
|
279
289
|
function goArchToken(arch) {
|
|
@@ -282,21 +292,29 @@ function goArchToken(arch) {
|
|
|
282
292
|
return arch; // "arm64" lines up with GOARCH as-is
|
|
283
293
|
}
|
|
284
294
|
/**
|
|
285
|
-
*
|
|
295
|
+
* Match an installer binary asset. The release workflow uploads
|
|
296
|
+
* `install-<os>-<arch>` (e.g. `install-linux-amd64`, `install-windows-amd64.exe`,
|
|
297
|
+
* `install-macos-universal`); the legacy single-binary / `deft-install-` naming is
|
|
298
|
+
* also accepted for back-compat. Anchored at the start of the basename so sibling
|
|
299
|
+
* assets like `checksums.txt` / `README.md` are excluded.
|
|
300
|
+
*/
|
|
301
|
+
const INSTALLER_ASSET_RE = /^(?:deft-)?install(?:-|\.exe$|$)/i;
|
|
302
|
+
/**
|
|
303
|
+
* Pick the installer bridge binary matching the current host platform/arch.
|
|
286
304
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* -
|
|
294
|
-
* -
|
|
295
|
-
*
|
|
305
|
+
* The release publishes one asset per OS (`install-<os>-<arch>`, with macOS as a
|
|
306
|
+
* single `install-macos-universal` fat binary). Selecting `assets[0]`
|
|
307
|
+
* alphabetically silently picks the wrong binary on a multi-asset release,
|
|
308
|
+
* producing an exec-format failure on a mismatched runner. This resolves the
|
|
309
|
+
* host-matching asset instead and reports a precise outcome when none matches:
|
|
310
|
+
* - single installer asset -> use it (single-binary releases, back-compat);
|
|
311
|
+
* - prefer an asset whose name carries BOTH the OS and arch tokens;
|
|
312
|
+
* - else fall back to an OS-only match (e.g. the macOS universal binary, which
|
|
313
|
+
* carries no arch suffix, or releases that omit the arch suffix);
|
|
296
314
|
* - else `no-platform-match` so the caller fails loudly rather than guessing.
|
|
297
315
|
*/
|
|
298
316
|
export function selectBridgeAsset(fileNames, platform = process.platform, arch = process.arch) {
|
|
299
|
-
const candidates = fileNames.filter((name) =>
|
|
317
|
+
const candidates = fileNames.filter((name) => INSTALLER_ASSET_RE.test(name));
|
|
300
318
|
if (candidates.length === 0) {
|
|
301
319
|
return { kind: "none" };
|
|
302
320
|
}
|
|
@@ -353,14 +371,14 @@ export function downloadAndRunFrozenBridge(pin, fixtureDir, seams = {}) {
|
|
|
353
371
|
if (selection.kind === "none") {
|
|
354
372
|
return [
|
|
355
373
|
false,
|
|
356
|
-
`frozen bridge ${pin} downloaded but no
|
|
374
|
+
`frozen bridge ${pin} downloaded but no installer asset (install-*) found in ${assetDir} ` +
|
|
357
375
|
`(see ${GO_BRIDGE_RELEASES_URL})`,
|
|
358
376
|
];
|
|
359
377
|
}
|
|
360
378
|
if (selection.kind === "no-platform-match") {
|
|
361
379
|
return [
|
|
362
380
|
false,
|
|
363
|
-
`frozen bridge ${pin} downloaded but no
|
|
381
|
+
`frozen bridge ${pin} downloaded but no installer asset matches ` +
|
|
364
382
|
`${goOsToken(process.platform)}/${goArchToken(process.arch)} ` +
|
|
365
383
|
`(candidates: ${selection.candidates.join(", ")}; see ${GO_BRIDGE_RELEASES_URL})`,
|
|
366
384
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.56.
|
|
3
|
+
"version": "0.56.2",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -189,6 +189,10 @@
|
|
|
189
189
|
"./init-deposit": {
|
|
190
190
|
"types": "./dist/init-deposit/index.d.ts",
|
|
191
191
|
"default": "./dist/init-deposit/index.js"
|
|
192
|
+
},
|
|
193
|
+
"./dist/*.js": {
|
|
194
|
+
"types": "./dist/*.d.ts",
|
|
195
|
+
"default": "./dist/*.js"
|
|
192
196
|
}
|
|
193
197
|
},
|
|
194
198
|
"files": [
|
|
@@ -205,8 +209,8 @@
|
|
|
205
209
|
"provenance": true
|
|
206
210
|
},
|
|
207
211
|
"dependencies": {
|
|
208
|
-
"@deftai/directive-content": "^0.56.
|
|
209
|
-
"@deftai/directive-types": "^0.56.
|
|
212
|
+
"@deftai/directive-content": "^0.56.2",
|
|
213
|
+
"@deftai/directive-types": "^0.56.2"
|
|
210
214
|
},
|
|
211
215
|
"scripts": {
|
|
212
216
|
"build": "tsc -b",
|