@tech-leads-club/harness-toolkit 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -26
- package/bin/tlc-build.mjs +93 -0
- package/bin/tlc-cli.ts +110 -66
- package/bin/tlc-exec.mjs +16 -13
- package/dist/compact-before.mjs +66 -8
- package/dist/doctor.mjs +178 -41
- package/dist/help-topic.mjs +0 -0
- package/dist/init-project.mjs +2 -7
- package/dist/install-runtime.mjs +100 -17
- package/dist/lessons-cli.mjs +67 -1
- package/dist/obs-cli.mjs +64 -1
- package/dist/price-lookup.mjs +45 -23
- package/dist/prompt-submit.mjs +66 -8
- package/dist/refresh-model-prices.mjs +7190 -46
- package/dist/response-after.mjs +66 -8
- package/dist/run.mjs +66 -8
- package/dist/session-end.mjs +66 -8
- package/dist/session-start.mjs +66 -8
- package/dist/shim.mjs +66 -3
- package/dist/stop.mjs +66 -8
- package/dist/subagent-start.mjs +66 -8
- package/dist/subagent-stop.mjs +66 -8
- package/dist/support.mjs +64 -1
- package/dist/tlc-cli.mjs +193 -87
- package/dist/tool-after.mjs +111 -31
- package/dist/tool-before.mjs +66 -8
- package/dist/tool-failure.mjs +66 -8
- package/dist/uninstall-runtime.mjs +9 -10
- package/docs/log.md +2 -0
- package/docs/measure.md +35 -31
- package/package.json +4 -4
- package/src/core/core.facade.ts +7 -0
- package/src/core/index.ts +1 -0
- package/src/core/pricing/pricing.freshness.ts +118 -0
- package/src/core/skill/skill.link.ts +14 -3
- package/src/entrypoints/shim.ts +8 -2
- package/src/platform/links.ts +73 -0
- package/src/platform/pricing.ts +139 -31
- package/src/providers/cursor/cursor.wiring.ts +11 -8
- package/tools/doctor.ts +78 -8
- package/tools/init-project.ts +7 -7
- package/tools/install-runtime.ts +89 -6
- package/tools/refresh-model-prices.ts +242 -75
- package/tools/uninstall-runtime.ts +23 -19
- package/bin/tlc-build +0 -80
- package/bin/tlc-exec +0 -10
- package/bin/tlc-exec.cmd +0 -4
- package/model-aliases.json +0 -12
- package/model-prices.cursor.json +0 -410
- package/model-prices.json +0 -1
package/dist/response-after.mjs
CHANGED
|
@@ -5273,6 +5273,62 @@ function release(root, provider, session) {
|
|
|
5273
5273
|
deletePresenceRecord(root, provider, session);
|
|
5274
5274
|
}
|
|
5275
5275
|
|
|
5276
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5277
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5278
|
+
var MS_PER_DAY = 86400000;
|
|
5279
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5280
|
+
if (meta === null) {
|
|
5281
|
+
return { state: "absent" };
|
|
5282
|
+
}
|
|
5283
|
+
const stamp = meta.refreshedAt;
|
|
5284
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5285
|
+
return { state: "undated" };
|
|
5286
|
+
}
|
|
5287
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5288
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5289
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5290
|
+
}
|
|
5291
|
+
function shouldRefetch(state) {
|
|
5292
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5293
|
+
}
|
|
5294
|
+
function freshnessMessage(state, catalogue) {
|
|
5295
|
+
switch (state.state) {
|
|
5296
|
+
case "absent":
|
|
5297
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5298
|
+
case "undated":
|
|
5299
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5300
|
+
case "fresh":
|
|
5301
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5302
|
+
default:
|
|
5303
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5307
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5308
|
+
if (incomingCount === 0) {
|
|
5309
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5310
|
+
}
|
|
5311
|
+
if (existingCount === 0) {
|
|
5312
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5313
|
+
}
|
|
5314
|
+
if (incomingCount >= existingCount) {
|
|
5315
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5316
|
+
}
|
|
5317
|
+
const retained = incomingCount / existingCount;
|
|
5318
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5319
|
+
replace: false,
|
|
5320
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
function describeAge(ageDays) {
|
|
5324
|
+
if (ageDays < 1) {
|
|
5325
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5326
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5327
|
+
}
|
|
5328
|
+
const days = Math.round(ageDays);
|
|
5329
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5276
5332
|
// src/core/release/release.decisions.ts
|
|
5277
5333
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5278
5334
|
import { join as join18 } from "node:path";
|
|
@@ -5622,7 +5678,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5622
5678
|
if (!probe.exists(resolved)) {
|
|
5623
5679
|
return { state: "dangling", target, resolved };
|
|
5624
5680
|
}
|
|
5625
|
-
const
|
|
5681
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5682
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5626
5683
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5627
5684
|
}
|
|
5628
5685
|
function linkHealthMessage(health) {
|
|
@@ -6769,6 +6826,12 @@ var coreFacade = {
|
|
|
6769
6826
|
coversHandler,
|
|
6770
6827
|
decideShim
|
|
6771
6828
|
},
|
|
6829
|
+
pricing: {
|
|
6830
|
+
freshness,
|
|
6831
|
+
freshnessMessage,
|
|
6832
|
+
mayReplace,
|
|
6833
|
+
shouldRefetch
|
|
6834
|
+
},
|
|
6772
6835
|
skill: {
|
|
6773
6836
|
linkHealth,
|
|
6774
6837
|
linkHealthMessage,
|
|
@@ -7719,14 +7782,9 @@ var ENTRY_SPECS2 = [
|
|
|
7719
7782
|
{ hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
|
|
7720
7783
|
{ hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
|
|
7721
7784
|
];
|
|
7722
|
-
function commandFor(runtime) {
|
|
7723
|
-
if (process.platform === "win32") {
|
|
7724
|
-
return { command: "cmd", argsPrefix: ["/c", "node", runtime.launcherPath] };
|
|
7725
|
-
}
|
|
7726
|
-
return { command: "node", argsPrefix: [runtime.launcherPath] };
|
|
7727
|
-
}
|
|
7728
7785
|
function cursorWiring(runtime) {
|
|
7729
|
-
const
|
|
7786
|
+
const command = "node";
|
|
7787
|
+
const argsPrefix = [runtime.launcherPath];
|
|
7730
7788
|
const entries = ENTRY_SPECS2.map((spec) => ({
|
|
7731
7789
|
hookEvent: spec.hookEvent,
|
|
7732
7790
|
handler: spec.handler,
|
package/dist/run.mjs
CHANGED
|
@@ -5273,6 +5273,62 @@ function release(root, provider, session) {
|
|
|
5273
5273
|
deletePresenceRecord(root, provider, session);
|
|
5274
5274
|
}
|
|
5275
5275
|
|
|
5276
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5277
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5278
|
+
var MS_PER_DAY = 86400000;
|
|
5279
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5280
|
+
if (meta === null) {
|
|
5281
|
+
return { state: "absent" };
|
|
5282
|
+
}
|
|
5283
|
+
const stamp = meta.refreshedAt;
|
|
5284
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5285
|
+
return { state: "undated" };
|
|
5286
|
+
}
|
|
5287
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5288
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5289
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5290
|
+
}
|
|
5291
|
+
function shouldRefetch(state) {
|
|
5292
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5293
|
+
}
|
|
5294
|
+
function freshnessMessage(state, catalogue) {
|
|
5295
|
+
switch (state.state) {
|
|
5296
|
+
case "absent":
|
|
5297
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5298
|
+
case "undated":
|
|
5299
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5300
|
+
case "fresh":
|
|
5301
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5302
|
+
default:
|
|
5303
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5307
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5308
|
+
if (incomingCount === 0) {
|
|
5309
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5310
|
+
}
|
|
5311
|
+
if (existingCount === 0) {
|
|
5312
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5313
|
+
}
|
|
5314
|
+
if (incomingCount >= existingCount) {
|
|
5315
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5316
|
+
}
|
|
5317
|
+
const retained = incomingCount / existingCount;
|
|
5318
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5319
|
+
replace: false,
|
|
5320
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
function describeAge(ageDays) {
|
|
5324
|
+
if (ageDays < 1) {
|
|
5325
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5326
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5327
|
+
}
|
|
5328
|
+
const days = Math.round(ageDays);
|
|
5329
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5276
5332
|
// src/core/release/release.decisions.ts
|
|
5277
5333
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5278
5334
|
import { join as join18 } from "node:path";
|
|
@@ -5622,7 +5678,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5622
5678
|
if (!probe.exists(resolved)) {
|
|
5623
5679
|
return { state: "dangling", target, resolved };
|
|
5624
5680
|
}
|
|
5625
|
-
const
|
|
5681
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5682
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5626
5683
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5627
5684
|
}
|
|
5628
5685
|
function linkHealthMessage(health) {
|
|
@@ -6769,6 +6826,12 @@ var coreFacade = {
|
|
|
6769
6826
|
coversHandler,
|
|
6770
6827
|
decideShim
|
|
6771
6828
|
},
|
|
6829
|
+
pricing: {
|
|
6830
|
+
freshness,
|
|
6831
|
+
freshnessMessage,
|
|
6832
|
+
mayReplace,
|
|
6833
|
+
shouldRefetch
|
|
6834
|
+
},
|
|
6772
6835
|
skill: {
|
|
6773
6836
|
linkHealth,
|
|
6774
6837
|
linkHealthMessage,
|
|
@@ -7717,14 +7780,9 @@ var ENTRY_SPECS2 = [
|
|
|
7717
7780
|
{ hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
|
|
7718
7781
|
{ hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
|
|
7719
7782
|
];
|
|
7720
|
-
function commandFor(runtime) {
|
|
7721
|
-
if (process.platform === "win32") {
|
|
7722
|
-
return { command: "cmd", argsPrefix: ["/c", "node", runtime.launcherPath] };
|
|
7723
|
-
}
|
|
7724
|
-
return { command: "node", argsPrefix: [runtime.launcherPath] };
|
|
7725
|
-
}
|
|
7726
7783
|
function cursorWiring(runtime) {
|
|
7727
|
-
const
|
|
7784
|
+
const command = "node";
|
|
7785
|
+
const argsPrefix = [runtime.launcherPath];
|
|
7728
7786
|
const entries = ENTRY_SPECS2.map((spec) => ({
|
|
7729
7787
|
hookEvent: spec.hookEvent,
|
|
7730
7788
|
handler: spec.handler,
|
package/dist/session-end.mjs
CHANGED
|
@@ -5273,6 +5273,62 @@ function release(root, provider, session) {
|
|
|
5273
5273
|
deletePresenceRecord(root, provider, session);
|
|
5274
5274
|
}
|
|
5275
5275
|
|
|
5276
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5277
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5278
|
+
var MS_PER_DAY = 86400000;
|
|
5279
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5280
|
+
if (meta === null) {
|
|
5281
|
+
return { state: "absent" };
|
|
5282
|
+
}
|
|
5283
|
+
const stamp = meta.refreshedAt;
|
|
5284
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5285
|
+
return { state: "undated" };
|
|
5286
|
+
}
|
|
5287
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5288
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5289
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5290
|
+
}
|
|
5291
|
+
function shouldRefetch(state) {
|
|
5292
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5293
|
+
}
|
|
5294
|
+
function freshnessMessage(state, catalogue) {
|
|
5295
|
+
switch (state.state) {
|
|
5296
|
+
case "absent":
|
|
5297
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5298
|
+
case "undated":
|
|
5299
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5300
|
+
case "fresh":
|
|
5301
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5302
|
+
default:
|
|
5303
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5307
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5308
|
+
if (incomingCount === 0) {
|
|
5309
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5310
|
+
}
|
|
5311
|
+
if (existingCount === 0) {
|
|
5312
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5313
|
+
}
|
|
5314
|
+
if (incomingCount >= existingCount) {
|
|
5315
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5316
|
+
}
|
|
5317
|
+
const retained = incomingCount / existingCount;
|
|
5318
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5319
|
+
replace: false,
|
|
5320
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
function describeAge(ageDays) {
|
|
5324
|
+
if (ageDays < 1) {
|
|
5325
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5326
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5327
|
+
}
|
|
5328
|
+
const days = Math.round(ageDays);
|
|
5329
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5276
5332
|
// src/core/release/release.decisions.ts
|
|
5277
5333
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5278
5334
|
import { join as join18 } from "node:path";
|
|
@@ -5622,7 +5678,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5622
5678
|
if (!probe.exists(resolved)) {
|
|
5623
5679
|
return { state: "dangling", target, resolved };
|
|
5624
5680
|
}
|
|
5625
|
-
const
|
|
5681
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5682
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5626
5683
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5627
5684
|
}
|
|
5628
5685
|
function linkHealthMessage(health) {
|
|
@@ -6769,6 +6826,12 @@ var coreFacade = {
|
|
|
6769
6826
|
coversHandler,
|
|
6770
6827
|
decideShim
|
|
6771
6828
|
},
|
|
6829
|
+
pricing: {
|
|
6830
|
+
freshness,
|
|
6831
|
+
freshnessMessage,
|
|
6832
|
+
mayReplace,
|
|
6833
|
+
shouldRefetch
|
|
6834
|
+
},
|
|
6772
6835
|
skill: {
|
|
6773
6836
|
linkHealth,
|
|
6774
6837
|
linkHealthMessage,
|
|
@@ -7778,14 +7841,9 @@ var ENTRY_SPECS2 = [
|
|
|
7778
7841
|
{ hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
|
|
7779
7842
|
{ hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
|
|
7780
7843
|
];
|
|
7781
|
-
function commandFor(runtime) {
|
|
7782
|
-
if (process.platform === "win32") {
|
|
7783
|
-
return { command: "cmd", argsPrefix: ["/c", "node", runtime.launcherPath] };
|
|
7784
|
-
}
|
|
7785
|
-
return { command: "node", argsPrefix: [runtime.launcherPath] };
|
|
7786
|
-
}
|
|
7787
7844
|
function cursorWiring(runtime) {
|
|
7788
|
-
const
|
|
7845
|
+
const command = "node";
|
|
7846
|
+
const argsPrefix = [runtime.launcherPath];
|
|
7789
7847
|
const entries = ENTRY_SPECS2.map((spec) => ({
|
|
7790
7848
|
hookEvent: spec.hookEvent,
|
|
7791
7849
|
handler: spec.handler,
|
package/dist/session-start.mjs
CHANGED
|
@@ -5273,6 +5273,62 @@ function release(root, provider, session) {
|
|
|
5273
5273
|
deletePresenceRecord(root, provider, session);
|
|
5274
5274
|
}
|
|
5275
5275
|
|
|
5276
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5277
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5278
|
+
var MS_PER_DAY = 86400000;
|
|
5279
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5280
|
+
if (meta === null) {
|
|
5281
|
+
return { state: "absent" };
|
|
5282
|
+
}
|
|
5283
|
+
const stamp = meta.refreshedAt;
|
|
5284
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5285
|
+
return { state: "undated" };
|
|
5286
|
+
}
|
|
5287
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5288
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5289
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5290
|
+
}
|
|
5291
|
+
function shouldRefetch(state) {
|
|
5292
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5293
|
+
}
|
|
5294
|
+
function freshnessMessage(state, catalogue) {
|
|
5295
|
+
switch (state.state) {
|
|
5296
|
+
case "absent":
|
|
5297
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5298
|
+
case "undated":
|
|
5299
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5300
|
+
case "fresh":
|
|
5301
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5302
|
+
default:
|
|
5303
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5307
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5308
|
+
if (incomingCount === 0) {
|
|
5309
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5310
|
+
}
|
|
5311
|
+
if (existingCount === 0) {
|
|
5312
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5313
|
+
}
|
|
5314
|
+
if (incomingCount >= existingCount) {
|
|
5315
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5316
|
+
}
|
|
5317
|
+
const retained = incomingCount / existingCount;
|
|
5318
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5319
|
+
replace: false,
|
|
5320
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
function describeAge(ageDays) {
|
|
5324
|
+
if (ageDays < 1) {
|
|
5325
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5326
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5327
|
+
}
|
|
5328
|
+
const days = Math.round(ageDays);
|
|
5329
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5276
5332
|
// src/core/release/release.decisions.ts
|
|
5277
5333
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5278
5334
|
import { join as join18 } from "node:path";
|
|
@@ -5622,7 +5678,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5622
5678
|
if (!probe.exists(resolved)) {
|
|
5623
5679
|
return { state: "dangling", target, resolved };
|
|
5624
5680
|
}
|
|
5625
|
-
const
|
|
5681
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5682
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5626
5683
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5627
5684
|
}
|
|
5628
5685
|
function linkHealthMessage(health) {
|
|
@@ -6769,6 +6826,12 @@ var coreFacade = {
|
|
|
6769
6826
|
coversHandler,
|
|
6770
6827
|
decideShim
|
|
6771
6828
|
},
|
|
6829
|
+
pricing: {
|
|
6830
|
+
freshness,
|
|
6831
|
+
freshnessMessage,
|
|
6832
|
+
mayReplace,
|
|
6833
|
+
shouldRefetch
|
|
6834
|
+
},
|
|
6772
6835
|
skill: {
|
|
6773
6836
|
linkHealth,
|
|
6774
6837
|
linkHealthMessage,
|
|
@@ -7778,14 +7841,9 @@ var ENTRY_SPECS2 = [
|
|
|
7778
7841
|
{ hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
|
|
7779
7842
|
{ hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
|
|
7780
7843
|
];
|
|
7781
|
-
function commandFor(runtime) {
|
|
7782
|
-
if (process.platform === "win32") {
|
|
7783
|
-
return { command: "cmd", argsPrefix: ["/c", "node", runtime.launcherPath] };
|
|
7784
|
-
}
|
|
7785
|
-
return { command: "node", argsPrefix: [runtime.launcherPath] };
|
|
7786
|
-
}
|
|
7787
7844
|
function cursorWiring(runtime) {
|
|
7788
|
-
const
|
|
7845
|
+
const command = "node";
|
|
7846
|
+
const argsPrefix = [runtime.launcherPath];
|
|
7789
7847
|
const entries = ENTRY_SPECS2.map((spec) => ({
|
|
7790
7848
|
hookEvent: spec.hookEvent,
|
|
7791
7849
|
handler: spec.handler,
|
package/dist/shim.mjs
CHANGED
|
@@ -5271,6 +5271,62 @@ function release(root, provider, session) {
|
|
|
5271
5271
|
deletePresenceRecord(root, provider, session);
|
|
5272
5272
|
}
|
|
5273
5273
|
|
|
5274
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5275
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5276
|
+
var MS_PER_DAY = 86400000;
|
|
5277
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5278
|
+
if (meta === null) {
|
|
5279
|
+
return { state: "absent" };
|
|
5280
|
+
}
|
|
5281
|
+
const stamp = meta.refreshedAt;
|
|
5282
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5283
|
+
return { state: "undated" };
|
|
5284
|
+
}
|
|
5285
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5286
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5287
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5288
|
+
}
|
|
5289
|
+
function shouldRefetch(state) {
|
|
5290
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5291
|
+
}
|
|
5292
|
+
function freshnessMessage(state, catalogue) {
|
|
5293
|
+
switch (state.state) {
|
|
5294
|
+
case "absent":
|
|
5295
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5296
|
+
case "undated":
|
|
5297
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5298
|
+
case "fresh":
|
|
5299
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5300
|
+
default:
|
|
5301
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5305
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5306
|
+
if (incomingCount === 0) {
|
|
5307
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5308
|
+
}
|
|
5309
|
+
if (existingCount === 0) {
|
|
5310
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5311
|
+
}
|
|
5312
|
+
if (incomingCount >= existingCount) {
|
|
5313
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5314
|
+
}
|
|
5315
|
+
const retained = incomingCount / existingCount;
|
|
5316
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5317
|
+
replace: false,
|
|
5318
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5319
|
+
};
|
|
5320
|
+
}
|
|
5321
|
+
function describeAge(ageDays) {
|
|
5322
|
+
if (ageDays < 1) {
|
|
5323
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5324
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5325
|
+
}
|
|
5326
|
+
const days = Math.round(ageDays);
|
|
5327
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5328
|
+
}
|
|
5329
|
+
|
|
5274
5330
|
// src/core/release/release.decisions.ts
|
|
5275
5331
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5276
5332
|
import { join as join18 } from "node:path";
|
|
@@ -5620,7 +5676,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5620
5676
|
if (!probe.exists(resolved)) {
|
|
5621
5677
|
return { state: "dangling", target, resolved };
|
|
5622
5678
|
}
|
|
5623
|
-
const
|
|
5679
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5680
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5624
5681
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5625
5682
|
}
|
|
5626
5683
|
function linkHealthMessage(health) {
|
|
@@ -6767,6 +6824,12 @@ var coreFacade = {
|
|
|
6767
6824
|
coversHandler,
|
|
6768
6825
|
decideShim
|
|
6769
6826
|
},
|
|
6827
|
+
pricing: {
|
|
6828
|
+
freshness,
|
|
6829
|
+
freshnessMessage,
|
|
6830
|
+
mayReplace,
|
|
6831
|
+
shouldRefetch
|
|
6832
|
+
},
|
|
6770
6833
|
skill: {
|
|
6771
6834
|
linkHealth,
|
|
6772
6835
|
linkHealthMessage,
|
|
@@ -6986,7 +7049,7 @@ if (!decision.run) {
|
|
|
6986
7049
|
process.exit(0);
|
|
6987
7050
|
}
|
|
6988
7051
|
var home = runtimeHome();
|
|
6989
|
-
var execBin = join26(home, "bin", "tlc-exec");
|
|
7052
|
+
var execBin = join26(home, "bin", "tlc-exec.mjs");
|
|
6990
7053
|
var distHandler = join26(home, "dist", `${handler}.mjs`);
|
|
6991
7054
|
var srcHandler = join26(home, "src", "entrypoints", `${handler}.ts`);
|
|
6992
7055
|
function run(command, args) {
|
|
@@ -7002,7 +7065,7 @@ function run(command, args) {
|
|
|
7002
7065
|
});
|
|
7003
7066
|
}
|
|
7004
7067
|
if (existsSync25(execBin)) {
|
|
7005
|
-
run(execBin,
|
|
7068
|
+
run(process.execPath, [execBin, handler]);
|
|
7006
7069
|
} else if (existsSync25(distHandler)) {
|
|
7007
7070
|
run(process.execPath, [distHandler]);
|
|
7008
7071
|
} else if (existsSync25(srcHandler)) {
|
package/dist/stop.mjs
CHANGED
|
@@ -5335,6 +5335,62 @@ function release(root, provider, session) {
|
|
|
5335
5335
|
deletePresenceRecord(root, provider, session);
|
|
5336
5336
|
}
|
|
5337
5337
|
|
|
5338
|
+
// src/core/pricing/pricing.freshness.ts
|
|
5339
|
+
var DEFAULT_TTL_DAYS = 7;
|
|
5340
|
+
var MS_PER_DAY = 86400000;
|
|
5341
|
+
function freshness(meta, now, ttlDays = DEFAULT_TTL_DAYS) {
|
|
5342
|
+
if (meta === null) {
|
|
5343
|
+
return { state: "absent" };
|
|
5344
|
+
}
|
|
5345
|
+
const stamp = meta.refreshedAt;
|
|
5346
|
+
if (stamp === undefined || Number.isNaN(Date.parse(stamp))) {
|
|
5347
|
+
return { state: "undated" };
|
|
5348
|
+
}
|
|
5349
|
+
const ageMs = now.getTime() - Date.parse(stamp);
|
|
5350
|
+
const ageDays = Math.max(0, ageMs / MS_PER_DAY);
|
|
5351
|
+
return ageDays > ttlDays ? { state: "stale", ageDays, refreshedAt: stamp } : { state: "fresh", ageDays, refreshedAt: stamp };
|
|
5352
|
+
}
|
|
5353
|
+
function shouldRefetch(state) {
|
|
5354
|
+
return state.state === "absent" || state.state === "undated" || state.state === "stale";
|
|
5355
|
+
}
|
|
5356
|
+
function freshnessMessage(state, catalogue) {
|
|
5357
|
+
switch (state.state) {
|
|
5358
|
+
case "absent":
|
|
5359
|
+
return `${catalogue}: not on this machine — run \`tlc harness prices refresh\``;
|
|
5360
|
+
case "undated":
|
|
5361
|
+
return `${catalogue}: present but carries no date — it will be refetched`;
|
|
5362
|
+
case "fresh":
|
|
5363
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old`;
|
|
5364
|
+
default:
|
|
5365
|
+
return `${catalogue}: ${describeAge(state.ageDays)} old — run \`tlc harness prices refresh\``;
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
var MIN_RETAINED_RATIO = 0.5;
|
|
5369
|
+
function mayReplace(existingCount, incomingCount, minRatio = MIN_RETAINED_RATIO) {
|
|
5370
|
+
if (incomingCount === 0) {
|
|
5371
|
+
return { replace: false, reason: "parsed no entries at all — the upstream format has changed" };
|
|
5372
|
+
}
|
|
5373
|
+
if (existingCount === 0) {
|
|
5374
|
+
return { replace: true, reason: `first catalogue, ${incomingCount} entries` };
|
|
5375
|
+
}
|
|
5376
|
+
if (incomingCount >= existingCount) {
|
|
5377
|
+
return { replace: true, reason: `${existingCount} → ${incomingCount} entries` };
|
|
5378
|
+
}
|
|
5379
|
+
const retained = incomingCount / existingCount;
|
|
5380
|
+
return retained >= minRatio ? { replace: true, reason: `${existingCount} → ${incomingCount} entries` } : {
|
|
5381
|
+
replace: false,
|
|
5382
|
+
reason: `would drop from ${existingCount} to ${incomingCount} entries, keeping the existing catalogue — the upstream format has probably changed`
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5385
|
+
function describeAge(ageDays) {
|
|
5386
|
+
if (ageDays < 1) {
|
|
5387
|
+
const hours = Math.max(1, Math.round(ageDays * 24));
|
|
5388
|
+
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
5389
|
+
}
|
|
5390
|
+
const days = Math.round(ageDays);
|
|
5391
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5338
5394
|
// src/core/release/release.decisions.ts
|
|
5339
5395
|
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync19 } from "node:fs";
|
|
5340
5396
|
import { join as join18 } from "node:path";
|
|
@@ -5684,7 +5740,8 @@ function linkHealth(target, runtimeHome2, probe) {
|
|
|
5684
5740
|
if (!probe.exists(resolved)) {
|
|
5685
5741
|
return { state: "dangling", target, resolved };
|
|
5686
5742
|
}
|
|
5687
|
-
const
|
|
5743
|
+
const resolveHome = probe.realpath ?? ((path) => path);
|
|
5744
|
+
const home = resolveHome(runtimeHome2).replace(/\/+$/, "");
|
|
5688
5745
|
return resolved === home || resolved.startsWith(`${home}/`) ? { state: "ok", target, resolved } : { state: "outside-runtime", target, resolved };
|
|
5689
5746
|
}
|
|
5690
5747
|
function linkHealthMessage(health) {
|
|
@@ -6831,6 +6888,12 @@ var coreFacade = {
|
|
|
6831
6888
|
coversHandler,
|
|
6832
6889
|
decideShim
|
|
6833
6890
|
},
|
|
6891
|
+
pricing: {
|
|
6892
|
+
freshness,
|
|
6893
|
+
freshnessMessage,
|
|
6894
|
+
mayReplace,
|
|
6895
|
+
shouldRefetch
|
|
6896
|
+
},
|
|
6834
6897
|
skill: {
|
|
6835
6898
|
linkHealth,
|
|
6836
6899
|
linkHealthMessage,
|
|
@@ -7781,14 +7844,9 @@ var ENTRY_SPECS2 = [
|
|
|
7781
7844
|
{ hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
|
|
7782
7845
|
{ hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
|
|
7783
7846
|
];
|
|
7784
|
-
function commandFor(runtime) {
|
|
7785
|
-
if (process.platform === "win32") {
|
|
7786
|
-
return { command: "cmd", argsPrefix: ["/c", "node", runtime.launcherPath] };
|
|
7787
|
-
}
|
|
7788
|
-
return { command: "node", argsPrefix: [runtime.launcherPath] };
|
|
7789
|
-
}
|
|
7790
7847
|
function cursorWiring(runtime) {
|
|
7791
|
-
const
|
|
7848
|
+
const command = "node";
|
|
7849
|
+
const argsPrefix = [runtime.launcherPath];
|
|
7792
7850
|
const entries = ENTRY_SPECS2.map((spec) => ({
|
|
7793
7851
|
hookEvent: spec.hookEvent,
|
|
7794
7852
|
handler: spec.handler,
|