@tekmidian/pai 1.0.0 → 1.0.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +67 -5
- package/dist/aibroker-client-BxzeEHPF.mjs +3 -0
- package/dist/{aibroker-client-CXEYBzbf.mjs → aibroker-client-CQPGqLxL.mjs} +1 -1
- package/dist/atomic-file-BorMa4T3.mjs +21 -0
- package/dist/{auto-route-D5SXrMpB.mjs → auto-route-B7Yu6Uam.mjs} +3 -3
- package/dist/auto-route-CjjQqU1o.mjs +11 -0
- package/dist/budget-Dod6rnA9.mjs +15 -0
- package/dist/{caller-identity-CVTuiLSs.mjs → caller-identity-yLFb7CKq.mjs} +6 -5
- package/dist/{capacity-DaXrVj3J.mjs → capacity-DEveQdFV.mjs} +3 -3
- package/dist/{chain-BcQlIlEd.mjs → chain-7n9Gx56r.mjs} +5 -5
- package/dist/cli/index.mjs +1628 -370
- package/dist/{cockpit-actions-ODmt8yF9.mjs → cockpit-actions-BBHeyEZI.mjs} +84 -42
- package/dist/cockpit-actions-DR9X514g.mjs +20 -0
- package/dist/cockpit-memory--dnShXBA.mjs +20 -0
- package/dist/{config-BSXKyeJ1.mjs → config-Bu1_vbY8.mjs} +42 -3
- package/dist/{config-B955yYmo.mjs → config-Cxi-aBf4.mjs} +2 -2
- package/dist/{config-TaWmdTLx.mjs → config-DtENJ6cL.mjs} +1 -1
- package/dist/{config-D5chi2LO.mjs → config-XpZWOYOG.mjs} +2 -2
- package/dist/{controls-BGkK_Bdi.mjs → controls-CPoeudou.mjs} +37 -22
- package/dist/daemon/index.mjs +43 -42
- package/dist/daemon-DKdBNKfa.mjs +45 -0
- package/dist/{daemon-CorGh6Ub.mjs → daemon-DPAo6WTa.mjs} +103 -55
- package/dist/daemon-mcp/index.mjs +40 -39
- package/dist/{define-DGFu-sCf.mjs → define-CjJsmq-6.mjs} +1 -1
- package/dist/{detect-C1d5cUkT.mjs → detect-9CZNO0yf.mjs} +2 -2
- package/dist/detect-D1ziFFfA.mjs +10 -0
- package/dist/{embed-pass-CeQD8kRE.mjs → embed-pass-DIWjA68P.mjs} +5 -5
- package/dist/{embedding-gate-BHuBsgHD.mjs → embedding-gate-6rZrWRXa.mjs} +5 -5
- package/dist/{embedding-gate-Z7CJSOJk.mjs → embedding-gate-Cnk4YCyX.mjs} +3 -3
- package/dist/{embeddings-D4t6L-EH.mjs → embeddings-BVq3esBF.mjs} +1 -1
- package/dist/{embeddings-OF9Pt4by.mjs → embeddings-CIkJBKd-.mjs} +1 -1
- package/dist/{endreason-x1KOSrcO.mjs → endreason-C577xIQz.mjs} +7 -6
- package/dist/{env-DArqGq-Z.mjs → env-BE8mrsxr.mjs} +1 -1
- package/dist/factory-Bsib-qDG.mjs +9 -0
- package/dist/{factory-BtjbQ0np.mjs → factory-DjAAsqr_.mjs} +15 -13
- package/dist/{fallback-BIyEpPsQ.mjs → fallback-DFlItw1Z.mjs} +5 -5
- package/dist/{fmt-age-BiSHrTKN.mjs → fmt-age-ByDkvkoU.mjs} +1 -1
- package/dist/{health-C6jZWvRG.mjs → health-Dnym-qcT.mjs} +4 -4
- package/dist/{helpers-DFmYRCOa.mjs → helpers-BD6V-p9w.mjs} +2 -2
- package/dist/helpers-CMGjy_0N.mjs +10 -0
- package/dist/{helpers-CJpb_MNB.mjs → helpers-CRuBUas3.mjs} +87 -38
- package/dist/hooks/capture-all-events.mjs +52 -11
- package/dist/hooks/capture-session-summary.mjs +64 -47
- package/dist/hooks/capture-tool-output.mjs +49 -8
- package/dist/hooks/context-compression-hook.mjs +30 -10
- package/dist/hooks/initialize-session.mjs +1 -0
- package/dist/hooks/load-core-context.mjs +1 -1
- package/dist/hooks/load-project-context.mjs +32 -29
- package/dist/hooks/security-validator.mjs +45 -13
- package/dist/hooks/stop-hook.mjs +54 -16
- package/dist/hooks/supervision-banner.mjs +9 -0
- package/dist/hooks/sync-todo-to-md.mjs +147 -113
- package/dist/hooks/worker-edit-repair.mjs +2 -1
- package/dist/hooks/worker-guard.mjs +40 -16
- package/dist/hooks/worker-note.mjs +12 -5
- package/dist/hooks/worker-path-repair.mjs +1 -1
- package/dist/hooks/worker-pool-host.mjs +329 -231
- package/dist/hooks/worker-status-line.mjs +195 -114
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +12 -12
- package/dist/{indexer-backend-BM2CUSu8.mjs → indexer-backend-Cll7le4P.mjs} +149 -96
- package/dist/{ipc-client-BMqWw_gv.mjs → ipc-client-CaiGJnSM.mjs} +1 -1
- package/dist/{ipc-client-YYDD6zzU.mjs → ipc-client-ZRmDKd5Z.mjs} +1 -1
- package/dist/kill-tree-BG7dRVtp.mjs +10 -0
- package/dist/{kill-tree-BjYz5BxK.mjs → kill-tree-EWYaEk5v.mjs} +8 -8
- package/dist/{launch-CLp0OJxV.mjs → launch-CXVlZjHt.mjs} +8 -8
- package/dist/{launch-DgqQdAjI.mjs → launch-Jh5NVeNe.mjs} +3 -3
- package/dist/local-candidate-DLdwQt0s.mjs +112 -0
- package/dist/local-instances-DOxSgT_L.mjs +210 -0
- package/dist/local-instances-ZeRkkuS5.mjs +6 -0
- package/dist/{local-tiers-DPZYqv9m.mjs → local-tiers-BE4jGaMa.mjs} +2 -2
- package/dist/{local-tiers-DnBIAkbK.mjs → local-tiers-DNMI5W86.mjs} +3 -3
- package/dist/{main-config-ops-C0SflOWk.mjs → main-config-ops-BEMpuhDz.mjs} +3 -3
- package/dist/{main-resolver-DVPpSWVy.mjs → main-resolver-3Ns1HPmS.mjs} +18 -17
- package/dist/{migrate-BUJHadA8.mjs → migrate-DrA_OwlZ.mjs} +45 -14
- package/dist/min-max-DrSRUaWQ.mjs +16 -0
- package/dist/{model-inventory-Dasber3S.mjs → model-inventory-BFVSNFT3.mjs} +13 -12
- package/dist/{models-add-Bt7aUHDs.mjs → models-add-C1QIc1DA.mjs} +39 -38
- package/dist/operator-BTqvTIc8.mjs +7 -0
- package/dist/{operator-DZxcP-g8.mjs → operator-CyuB3aVw.mjs} +3 -3
- package/dist/{pai-marker-kIYuNt3c.mjs → pai-marker-BcZyJguM.mjs} +7 -6
- package/dist/{pane-audit-OGYrOAik.mjs → pane-audit-DKQt71s4.mjs} +7 -7
- package/dist/{paths-DoRuFc60.mjs → paths-CFHTloSe.mjs} +3 -3
- package/dist/{paths-DoIyRnhx.mjs → paths-CHifubDh.mjs} +3 -3
- package/dist/{planner-DLGOYlVb.mjs → planner-BmFACdLa.mjs} +31 -30
- package/dist/{pool-CZ5MZaXg.mjs → pool-Dh-q3iJi.mjs} +45 -6
- package/dist/{pool-cli-BxNSPzzY.mjs → pool-cli-BggU8GRp.mjs} +5 -5
- package/dist/{pool-cli-CbJfl37y.mjs → pool-cli-DQ6OzRy2.mjs} +6 -6
- package/dist/postgres-BdBNkL_l.mjs +7 -0
- package/dist/{postgres-CDnd_k5f.mjs → postgres-C6ti4haA.mjs} +55 -34
- package/dist/{project-setup-DM4npnL3.mjs → project-setup-jvfONJTQ.mjs} +131 -74
- package/dist/providers-BeDiVEb-.mjs +16 -0
- package/dist/{providers-UsKtLcUR.mjs → providers-CblTFrhz.mjs} +6 -6
- package/dist/{query-feedback-I-txSnld.mjs → query-feedback-BRfPtqki.mjs} +2 -2
- package/dist/registrable-BzH4TObg.mjs +9 -0
- package/dist/{registrable-LrRIVYQc.mjs → registrable-DpyTtajg.mjs} +4 -4
- package/dist/{render-BDbb4CF-.mjs → render-BYmQkuLd.mjs} +8 -8
- package/dist/render-CoMvwMPs.mjs +13 -0
- package/dist/{reranker-B4r4vykM.mjs → reranker-8g6xMwP7.mjs} +1 -1
- package/dist/{reranker-DxxMCtA0.mjs → reranker-CqCbpGnX.mjs} +1 -1
- package/dist/root-FtJsxYnm.mjs +36 -0
- package/dist/{root-BMeJuHOb.mjs → root-Xglu0OPD.mjs} +11 -11
- package/dist/route-table-B7zpoypi.mjs +15 -0
- package/dist/{router-L8Nu-OHF.mjs → router-CQSc3lk3.mjs} +17 -7
- package/dist/{router-D6Ec6Fid.mjs → router-DwHJRgG9.mjs} +1 -1
- package/dist/{routing-CUyiglfu.mjs → routing-BAAST9vT.mjs} +27 -308
- package/dist/{routing-ZuGlc0ge.mjs → routing-CENt4HH1.mjs} +13 -12
- package/dist/{run-ulAi5dFD.mjs → run-CSC7mgPf.mjs} +35 -33
- package/dist/{scan-B7I0gPe-.mjs → scan-C_Y5W9py.mjs} +6 -6
- package/dist/{local-candidate-jOjTNlOL.mjs → scheduler-CtUyWJ3O.mjs} +7 -109
- package/dist/scheduler-dmHXoRvm.mjs +3 -0
- package/dist/{scope-D5VSQxe4.mjs → scope-D-h6F6DN.mjs} +4 -4
- package/dist/{search-CPoE0830.mjs → search-RSyeQqj1.mjs} +18 -18
- package/dist/{session-CNXF6DLP.mjs → session-8iqQDkqb.mjs} +55 -31
- package/dist/session-BeqAbNPd.mjs +35 -0
- package/dist/session-scan-BJI_ZO8M.mjs +9 -0
- package/dist/{session-scan-BDLEc4w1.mjs → session-scan-W-gbwJHM.mjs} +6 -6
- package/dist/{sources-CjhUKKtO.mjs → sources-ESUkZaWv.mjs} +1 -1
- package/dist/{status-DYydKQUI.mjs → status-Dm4dYciN.mjs} +4 -4
- package/dist/{status-CvVi9cDf.mjs → status-ggJ7lgVg.mjs} +17 -9
- package/dist/{stop-words-SdSL1WKH.mjs → stop-words-DSEYQdNe.mjs} +1 -1
- package/dist/{test-isolation-CoDeOVXb.mjs → test-isolation-lqSmYxr0.mjs} +1 -1
- package/dist/{timeouts-OwEONmc8.mjs → timeouts-1MLooEBB.mjs} +1 -1
- package/dist/{tool-errors-AeRQuG3Y.mjs → tool-errors-7E13StWd.mjs} +4 -3
- package/dist/{tool-errors-R5E0fCgf.mjs → tool-errors-CJWODvlo.mjs} +6 -6
- package/dist/viewer-CqozWW39.mjs +22 -0
- package/dist/{viewer-DtWj5Vti.mjs → viewer-mDXUNHNy.mjs} +15 -15
- package/dist/{wait-BMUR-U-U.mjs → wait-ByV0OyMZ.mjs} +3 -3
- package/dist/{worker-config-BLqa1yiD.mjs → worker-config-CJhEMk19.mjs} +294 -92
- package/dist/worker-config-DESkIjMF.mjs +41 -0
- package/dist/{workers-config-DEI9lrV4.mjs → workers-config-f8KrkY4L.mjs} +2 -2
- package/dist/{worktree-zmRf556h.mjs → worktree-CRs2gyg0.mjs} +37 -7
- package/dist/{zettelkasten-nigq7fqs.mjs → zettelkasten-B_rRRRvD.mjs} +5 -5
- package/dist/{zettelkasten-Cr_Xj0mG.mjs → zettelkasten-BzMm8REH.mjs} +29 -19
- package/docs/README.md +2 -0
- package/docs/cockpit.md +10 -4
- package/docs/commands/README.md +4 -4
- package/docs/commands/backup.md +2 -0
- package/docs/commands/install.md +29 -4
- package/docs/commands/providers.md +1 -1
- package/docs/commands/restore.md +4 -0
- package/docs/commands/setup.md +29 -4
- package/docs/commands/worker.md +1 -1
- package/docs/config.md +2 -0
- package/docs/features.md +299 -0
- package/docs/images/architecture.drawio +51 -0
- package/docs/images/architecture.png +0 -0
- package/docs/images/architecture.svg +3 -0
- package/docs/images/cockpit-01-system-tokens.jpg +0 -0
- package/docs/images/cockpit-02-totals-cache.jpg +0 -0
- package/docs/images/cockpit-03-local-servers-models-pool.jpg +0 -0
- package/docs/images/cockpit-04-workers-memory.jpg +0 -0
- package/docs/images/cockpit-05-performance.jpg +0 -0
- package/docs/images/cockpit-06-runs-speed-routing.jpg +0 -0
- package/docs/images/cockpit-07-settings-registry.jpg +0 -0
- package/docs/install/index.md +29 -0
- package/docs/three-tier-coding.md +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -5
- package/pai-plugin.json +1 -1
- package/plugins/context-preservation/plugin.json +1 -1
- package/plugins/core/plugin.json +1 -1
- package/plugins/core/skills/setup/SKILL.md +41 -9
- package/plugins/creative/plugin.json +1 -1
- package/plugins/observability/plugin.json +1 -1
- package/plugins/productivity/plugin.json +1 -1
- package/plugins/semantic-search/plugin.json +1 -1
- package/plugins/ui/plugin.json +1 -1
- package/plugins/zettelkasten/plugin.json +1 -1
- package/src/hooks/session-autosave.sh +8 -1
- package/src/hooks/session-stop.sh +8 -1
- package/src/hooks/ts/capture-all-events.ts +7 -12
- package/src/hooks/ts/lib/context-fill.ts +2 -1
- package/src/hooks/ts/lib/path-repair.ts +1 -1
- package/src/hooks/ts/lib/project-utils/notify.ts +1 -0
- package/src/hooks/ts/lib/project-utils/session-notes.ts +23 -6
- package/src/hooks/ts/lib/raw-capture.ts +36 -0
- package/src/hooks/ts/lib/session-summary.ts +129 -0
- package/src/hooks/ts/lib/todo-sync.ts +120 -0
- package/src/hooks/ts/lib/worker-git-guard.ts +1 -1
- package/src/hooks/ts/post-tool-use/capture-tool-output.ts +6 -10
- package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +5 -105
- package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
- package/src/hooks/ts/pre-tool-use/security-validator.ts +35 -14
- package/src/hooks/ts/session-end/capture-session-summary.ts +20 -128
- package/src/hooks/ts/session-start/load-core-context.ts +3 -3
- package/src/hooks/ts/session-start/load-project-context.ts +18 -22
- package/src/hooks/ts/stop/stop-hook.ts +13 -5
- package/src/hooks/ztk-auto.sh +7 -2
- package/dist/aibroker-client-JjE74jo0.mjs +0 -3
- package/dist/auto-route-BRk8NEFv.mjs +0 -11
- package/dist/budget-DuhQ2zzG.mjs +0 -14
- package/dist/cockpit-actions-DcQ-ucU8.mjs +0 -19
- package/dist/cockpit-memory-KV45PPO2.mjs +0 -19
- package/dist/daemon-C7yt_ptC.mjs +0 -44
- package/dist/detect-DRUkmHnb.mjs +0 -10
- package/dist/factory-DAGG9_n8.mjs +0 -9
- package/dist/helpers-DPJEHPq6.mjs +0 -10
- package/dist/kill-tree-LV0Q7Mte.mjs +0 -10
- package/dist/operator-CfL0HiBK.mjs +0 -7
- package/dist/postgres-DgqWyqhN.mjs +0 -7
- package/dist/providers-CTu2s1z2.mjs +0 -15
- package/dist/registrable-D9aNZOTh.mjs +0 -9
- package/dist/render-BSfVlYHj.mjs +0 -13
- package/dist/root-xI9DqVIe.mjs +0 -35
- package/dist/route-table-HjvnLVEU.mjs +0 -14
- package/dist/session-BJHzmUg2.mjs +0 -34
- package/dist/session-scan-YORKE1NO.mjs +0 -9
- package/dist/viewer-B8709jDN.mjs +0 -22
- package/dist/worker-config-D8tRuvo7.mjs +0 -40
- package/docs/images/cockpit-limits-routing.jpg +0 -0
- package/docs/images/cockpit-models.jpg +0 -0
- package/docs/images/cockpit-usage.jpg +0 -0
- package/docs/images/cockpit-workers-system.jpg +0 -0
- package/scripts/build-docs.mjs +0 -41
- package/scripts/build-hooks.mjs +0 -311
- package/scripts/build-skill-stubs.mjs +0 -216
- package/scripts/copy-defaults.mjs +0 -46
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { L as isLoopbackHostname, tt as workersYamlPath } from "./config-XpZWOYOG.mjs";
|
|
2
|
+
import { _ as readLocalConfig } from "./local-tiers-BE4jGaMa.mjs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/workers/local-status.ts
|
|
7
|
+
/**
|
|
8
|
+
* local-status.ts — the one authoritative answer to "which local model is
|
|
9
|
+
* running?" (incident 2026-10-19: a session curled ports 8010/8011/8020, found
|
|
10
|
+
* nothing, and went looking for how to start a model server while the running
|
|
11
|
+
* one was never considered). The PAI daemon computes the answer every 30 s and
|
|
12
|
+
* caches it in <logDir>/local-status.json; everything else only reads it:
|
|
13
|
+
* `pai worker local`, the `local_status` MCP tool, resolveLocalProvider, the
|
|
14
|
+
* prompt hint and the Bash guard. Readers fall back to a direct probe only
|
|
15
|
+
* when the cache is missing or stale (daemon unreachable).
|
|
16
|
+
*/
|
|
17
|
+
const LOCAL_STATUS_REFRESH_MS = 3e4;
|
|
18
|
+
/** A cache older than this means the daemon is not refreshing it. */
|
|
19
|
+
const LOCAL_STATUS_MAX_AGE_MS = 18e4;
|
|
20
|
+
/** The model callers that need exactly one use: the first of `running` (the one `--local` favours). */
|
|
21
|
+
function primaryLocal(st) {
|
|
22
|
+
return st?.running?.[0] ?? null;
|
|
23
|
+
}
|
|
24
|
+
const localStatusPath = (logDir) => join(logDir, "local-status.json");
|
|
25
|
+
const NO_SERVER_HINT = "do not start model servers";
|
|
26
|
+
function localStatusLine(st) {
|
|
27
|
+
if (st.running.length) return `local: ${st.running.map((r) => `${r.provider} (${r.model})`).join(", ")} running — use \`pai worker run --local\``;
|
|
28
|
+
return `local: none running — local work is not available; ${NO_SERVER_HINT}`;
|
|
29
|
+
}
|
|
30
|
+
function writeLocalStatus(logDir, st) {
|
|
31
|
+
const path = localStatusPath(logDir);
|
|
32
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
33
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
34
|
+
writeFileSync(tmp, JSON.stringify(st) + "\n", "utf8");
|
|
35
|
+
renameSync(tmp, path);
|
|
36
|
+
}
|
|
37
|
+
/** Like readLocalStatus, but says why there is no answer: missing, unparsable, stale (age) or bad shape. */
|
|
38
|
+
function readLocalStatusWhy(logDir, now = Date.now(), maxAgeMs = LOCAL_STATUS_MAX_AGE_MS) {
|
|
39
|
+
const path = localStatusPath(logDir);
|
|
40
|
+
if (!existsSync(path)) return {
|
|
41
|
+
status: null,
|
|
42
|
+
reason: "status file missing"
|
|
43
|
+
};
|
|
44
|
+
let st;
|
|
45
|
+
try {
|
|
46
|
+
st = JSON.parse(readFileSync(path, "utf8"));
|
|
47
|
+
} catch {
|
|
48
|
+
return {
|
|
49
|
+
status: null,
|
|
50
|
+
reason: "status file unparsable"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!st || typeof st !== "object") return {
|
|
54
|
+
status: null,
|
|
55
|
+
reason: "status file has a bad shape"
|
|
56
|
+
};
|
|
57
|
+
const raw = st.running;
|
|
58
|
+
st.running = Array.isArray(raw) ? raw : raw && typeof raw === "object" ? [raw] : [];
|
|
59
|
+
const at = Date.parse(st.at);
|
|
60
|
+
if (Number.isNaN(at) || !Array.isArray(st.down) || !Array.isArray(st.disabled)) return {
|
|
61
|
+
status: null,
|
|
62
|
+
reason: "status file has a bad shape"
|
|
63
|
+
};
|
|
64
|
+
const age = now - at;
|
|
65
|
+
if (age > maxAgeMs) return {
|
|
66
|
+
status: null,
|
|
67
|
+
reason: `status ${Math.round(age / 1e3)} s old (max ${Math.round(maxAgeMs / 1e3)} s)`
|
|
68
|
+
};
|
|
69
|
+
return { status: st };
|
|
70
|
+
}
|
|
71
|
+
/** The daemon's cached answer; null when absent, damaged or older than maxAgeMs. */
|
|
72
|
+
function readLocalStatus(logDir, now = Date.now(), maxAgeMs = LOCAL_STATUS_MAX_AGE_MS) {
|
|
73
|
+
return readLocalStatusWhy(logDir, now, maxAgeMs).status;
|
|
74
|
+
}
|
|
75
|
+
/** Text + JSON of the answer for the CLI and the MCP tool. */
|
|
76
|
+
function localStatusReport(st) {
|
|
77
|
+
const extra = (st.down.length ? ` down: ${st.down.join(", ")}.` : "") + (st.disabled.length ? ` disabled: ${st.disabled.join(", ")}.` : "");
|
|
78
|
+
return {
|
|
79
|
+
text: localStatusLine(st) + (extra ? `\n${extra.trim()}` : ""),
|
|
80
|
+
json: st
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/workers/local-candidate.ts
|
|
86
|
+
/**
|
|
87
|
+
* Providers a `local:` tier launches. `pai worker local <tier>` and the cockpit
|
|
88
|
+
* start those without touching `enabled:`, so a running tier server is a
|
|
89
|
+
* candidate for --local even when its provider entry is switched off.
|
|
90
|
+
*/
|
|
91
|
+
function tierProviderNames(yamlPath = workersYamlPath()) {
|
|
92
|
+
try {
|
|
93
|
+
return Object.values(readLocalConfig(yamlPath).tiers).map((t) => t.provider);
|
|
94
|
+
} catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function isLoopbackBaseUrl(url) {
|
|
99
|
+
try {
|
|
100
|
+
return isLoopbackHostname(new URL(url).hostname);
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** A loopback provider --local may run on: enabled, or launched by a tier. */
|
|
106
|
+
function isLocalCandidate(p, name, tierProviders) {
|
|
107
|
+
return (p.enabled || tierProviders.includes(name)) && isLoopbackBaseUrl(p.baseUrl);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
export { localStatusReport as a, writeLocalStatus as c, localStatusLine as i, tierProviderNames as n, primaryLocal as o, LOCAL_STATUS_REFRESH_MS as r, readLocalStatus as s, isLocalCandidate as t };
|
|
112
|
+
//# sourceMappingURL=local-candidate-DLdwQt0s.mjs.map
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { n as minOf } from "./min-max-DrSRUaWQ.mjs";
|
|
2
|
+
import { h as parseServers, v as resolveModelPath } from "./local-tiers-BE4jGaMa.mjs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/workers/local-instances.ts
|
|
7
|
+
/**
|
|
8
|
+
* local-instances.ts — the running instances of local models.
|
|
9
|
+
*
|
|
10
|
+
* An instance is one server process of a catalogue model (a `local_tiers:`
|
|
11
|
+
* entry) on its own port. The source of truth is the running processes
|
|
12
|
+
* (`parseServers`); `<logDir>/local-instances.json` adds what ps cannot know
|
|
13
|
+
* (start time, log, failure). Processes win: `reconcileInstances` drops a row
|
|
14
|
+
* whose pid is gone and adopts a catalogue server that has no row.
|
|
15
|
+
*/
|
|
16
|
+
const localInstancesPath = (logDir) => join(logDir, "local-instances.json");
|
|
17
|
+
/** Is the process alive (signal 0; EPERM means it exists under another user). */
|
|
18
|
+
function pidAlive(pid) {
|
|
19
|
+
try {
|
|
20
|
+
process.kill(pid, 0);
|
|
21
|
+
return true;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return e.code === "EPERM";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function readInstances(logDir) {
|
|
27
|
+
const path = localInstancesPath(logDir);
|
|
28
|
+
if (!existsSync(path)) return [];
|
|
29
|
+
try {
|
|
30
|
+
const j = JSON.parse(readFileSync(path, "utf8"));
|
|
31
|
+
return Array.isArray(j) ? j.filter((r) => r && typeof r.id === "string" && typeof r.model === "string") : [];
|
|
32
|
+
} catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** The recorded rows minus managed ones whose process is gone (killed outside PAI); failed rows stay until dismissed. Readers that report or route use this; writers read the raw file. */
|
|
37
|
+
function readLiveInstances(logDir) {
|
|
38
|
+
return readInstances(logDir).filter((r) => !(r.origin === "managed" && r.state !== "failed" && r.pid && !pidAlive(r.pid)));
|
|
39
|
+
}
|
|
40
|
+
/** Atomic write (tmp + rename). */
|
|
41
|
+
function writeInstances(logDir, rows) {
|
|
42
|
+
const path = localInstancesPath(logDir);
|
|
43
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
44
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
45
|
+
writeFileSync(tmp, JSON.stringify(rows) + "\n", "utf8");
|
|
46
|
+
renameSync(tmp, path);
|
|
47
|
+
}
|
|
48
|
+
/** Router / scheduler engine key: n=1 keeps the bare provider name, later copies are `<provider>#<n>`. */
|
|
49
|
+
function engineKey(provider, n) {
|
|
50
|
+
return n <= 1 ? provider : `${provider}#${n}`;
|
|
51
|
+
}
|
|
52
|
+
/** The n of an instance id `<model>#<n>` (1 when it carries none). */
|
|
53
|
+
function instanceNumber(id) {
|
|
54
|
+
const m = /#(\d+)$/.exec(id);
|
|
55
|
+
return m ? Number(m[1]) : 1;
|
|
56
|
+
}
|
|
57
|
+
function instanceEngineKey(i) {
|
|
58
|
+
return engineKey(i.provider, instanceNumber(i.id));
|
|
59
|
+
}
|
|
60
|
+
/** vllm-mlx servers on a port no tier names (a second copy): the tier is the one whose model path the command serves. */
|
|
61
|
+
function vllmByModelPath(psOut, cfg, selfPid, byPath) {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const line of psOut.split("\n")) {
|
|
64
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
65
|
+
if (!m || Number(m[1]) === selfPid) continue;
|
|
66
|
+
const cmd = m[3];
|
|
67
|
+
const sv = /\bserve\s+(\S+)/.exec(cmd);
|
|
68
|
+
const pm = /--port\s+(\d+)/.exec(cmd);
|
|
69
|
+
if (!sv || !pm || !cmd.includes(cfg.launcher.processMatch)) continue;
|
|
70
|
+
const tier = byPath.get(sv[1]);
|
|
71
|
+
if (!tier) continue;
|
|
72
|
+
out.push({
|
|
73
|
+
pid: Number(m[1]),
|
|
74
|
+
rssKb: Number(m[2]),
|
|
75
|
+
port: Number(pm[1]),
|
|
76
|
+
tier,
|
|
77
|
+
provider: cfg.tiers[tier].provider
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** Servers of catalogue models, one per (model, port): the engine child of a Strata server is folded into its server. */
|
|
83
|
+
function instancesFromProcesses(psOut, cfg, listeners = {}, selfPid = process.pid) {
|
|
84
|
+
const seen = /* @__PURE__ */ new Set();
|
|
85
|
+
const out = [];
|
|
86
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const t of Object.values(cfg.tiers)) if (t.engine === "vllm-mlx") byPath.set(resolveModelPath(t.model), t.tier);
|
|
88
|
+
for (const s of [...parseServers(psOut, cfg, selfPid, listeners), ...vllmByModelPath(psOut, cfg, selfPid, byPath)]) {
|
|
89
|
+
if (!s.tier || s.port === null) continue;
|
|
90
|
+
const key = `${s.tier}:${s.port}`;
|
|
91
|
+
if (seen.has(key)) continue;
|
|
92
|
+
seen.add(key);
|
|
93
|
+
out.push(s);
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Join state rows with the live servers. A row follows its pid, else its
|
|
99
|
+
* port; a live server without a row is adopted as `ready` under the smallest
|
|
100
|
+
* free `<model>#<n>`. Rows with no live server are dropped, except `failed`
|
|
101
|
+
* ones (kept until the next start dismisses them).
|
|
102
|
+
*/
|
|
103
|
+
function reconcileInstances(rows, servers, cfg, now = Date.now()) {
|
|
104
|
+
const out = [];
|
|
105
|
+
const used = /* @__PURE__ */ new Set();
|
|
106
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
107
|
+
for (const r of rows) {
|
|
108
|
+
if (r.origin === "registered") continue;
|
|
109
|
+
if (r.state === "failed") {
|
|
110
|
+
out.push(r);
|
|
111
|
+
used.add(r.id);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const s = servers.find((x) => !claimed.has(x) && x.tier === r.model && (x.pid === r.pid || x.port === r.port));
|
|
115
|
+
if (!s) continue;
|
|
116
|
+
claimed.add(s);
|
|
117
|
+
out.push({
|
|
118
|
+
...r,
|
|
119
|
+
pid: s.pid,
|
|
120
|
+
port: s.port ?? r.port
|
|
121
|
+
});
|
|
122
|
+
used.add(r.id);
|
|
123
|
+
}
|
|
124
|
+
for (const s of servers) {
|
|
125
|
+
if (claimed.has(s) || !s.tier || s.port === null) continue;
|
|
126
|
+
const tier = cfg.tiers[s.tier];
|
|
127
|
+
if (!tier) continue;
|
|
128
|
+
let n = 1;
|
|
129
|
+
while (used.has(`${s.tier}#${n}`)) n++;
|
|
130
|
+
const id = `${s.tier}#${n}`;
|
|
131
|
+
used.add(id);
|
|
132
|
+
out.push({
|
|
133
|
+
id,
|
|
134
|
+
model: s.tier,
|
|
135
|
+
provider: tier.provider,
|
|
136
|
+
url: `http://127.0.0.1:${s.port}`,
|
|
137
|
+
origin: "managed",
|
|
138
|
+
port: s.port,
|
|
139
|
+
pid: s.pid,
|
|
140
|
+
state: "ready",
|
|
141
|
+
startedAt: now,
|
|
142
|
+
log: ""
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return out.sort((a, b) => a.model.localeCompare(b.model) || instanceNumber(a.id) - instanceNumber(b.id));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Endpoints declared in workers.yaml `providers:` with a url that no catalogue
|
|
149
|
+
* model launches: no pid, health is a probe (state `ready` until one says
|
|
150
|
+
* otherwise). Loopback or another machine alike.
|
|
151
|
+
*/
|
|
152
|
+
function registeredInstances(providers, cfg) {
|
|
153
|
+
const launched = new Set(Object.values(cfg.tiers).map((t) => t.provider));
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const [name, p] of Object.entries(providers)) {
|
|
156
|
+
if (!p.url || launched.has(name)) continue;
|
|
157
|
+
const port = /:(\d+)(?:\/|$)/.exec(p.url)?.[1];
|
|
158
|
+
out.push({
|
|
159
|
+
id: name,
|
|
160
|
+
model: name,
|
|
161
|
+
provider: name,
|
|
162
|
+
url: p.url.replace(/\/+$/, ""),
|
|
163
|
+
origin: "registered",
|
|
164
|
+
port: port ? Number(port) : 0,
|
|
165
|
+
state: "ready",
|
|
166
|
+
startedAt: 0,
|
|
167
|
+
log: "",
|
|
168
|
+
...p.characteristics ? { characteristics: p.characteristics } : {}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
const rrCounters = /* @__PURE__ */ new Map();
|
|
174
|
+
/**
|
|
175
|
+
* The instance (of `running`, already one provider's; `model` keys the round-robin) to send the next request to: the ready one with the
|
|
176
|
+
* fewest requests in flight (`busy` by engine key); ties go round-robin per
|
|
177
|
+
* model. Null when none is ready.
|
|
178
|
+
*/
|
|
179
|
+
function pickInstance(model, running, busy) {
|
|
180
|
+
const ready = running.filter((i) => i.state === "ready");
|
|
181
|
+
if (!ready.length) return null;
|
|
182
|
+
const load = ready.map((i) => busy(instanceEngineKey(i)));
|
|
183
|
+
const min = minOf(load);
|
|
184
|
+
const tied = ready.filter((_, k) => load[k] === min);
|
|
185
|
+
const n = rrCounters.get(model) ?? 0;
|
|
186
|
+
rrCounters.set(model, n + 1);
|
|
187
|
+
return tied[n % tied.length];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The endpoint registry: managed instances plus the enabled providers that
|
|
191
|
+
* declare `characteristics` (a url alone is a cloud or local provider, not a
|
|
192
|
+
* registered endpoint). A managed instance wins over a registered one of the
|
|
193
|
+
* same id.
|
|
194
|
+
*/
|
|
195
|
+
function endpointRegistry(managed, providers, cfg) {
|
|
196
|
+
const declared = {};
|
|
197
|
+
for (const [name, p] of Object.entries(providers)) {
|
|
198
|
+
if (p.enabled === false || !p.characteristics) continue;
|
|
199
|
+
declared[name] = {
|
|
200
|
+
url: p.baseUrl,
|
|
201
|
+
characteristics: p.characteristics
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const ids = new Set(managed.map((i) => i.id));
|
|
205
|
+
return [...managed, ...registeredInstances(declared, cfg).filter((i) => !ids.has(i.id))];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
export { instancesFromProcesses as a, pidAlive as c, reconcileInstances as d, registeredInstances as f, instanceNumber as i, readInstances as l, engineKey as n, localInstancesPath as o, writeInstances as p, instanceEngineKey as r, pickInstance as s, endpointRegistry as t, readLiveInstances as u };
|
|
210
|
+
//# sourceMappingURL=local-instances-DOxSgT_L.mjs.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "./config-XpZWOYOG.mjs";
|
|
2
|
+
import "./config-Bu1_vbY8.mjs";
|
|
3
|
+
import "./local-tiers-BE4jGaMa.mjs";
|
|
4
|
+
import { a as instancesFromProcesses, c as pidAlive, d as reconcileInstances, f as registeredInstances, i as instanceNumber, l as readInstances, n as engineKey, o as localInstancesPath, p as writeInstances, r as instanceEngineKey, s as pickInstance, t as endpointRegistry, u as readLiveInstances } from "./local-instances-DOxSgT_L.mjs";
|
|
5
|
+
|
|
6
|
+
export { endpointRegistry, engineKey, instanceEngineKey, instanceNumber, instancesFromProcesses, localInstancesPath, pickInstance, pidAlive, readInstances, readLiveInstances, reconcileInstances, registeredInstances, writeInstances };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
|
|
2
|
-
import { o as expandHome } from "./config-
|
|
2
|
+
import { o as expandHome } from "./config-Bu1_vbY8.mjs";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
@@ -467,4 +467,4 @@ const shellQuote = shq;
|
|
|
467
467
|
|
|
468
468
|
//#endregion
|
|
469
469
|
export { stopLines as C, stopLine as S, tierButtons as T, readLocalConfig as _, LocalTierError as a, splitCommand as b, fetchServedModels as c, installedModelDirs as d, listensOnPort as f, planStart as g, parseServers as h, DEFAULT_RESERVE_GB as i, fmtRss as l, parseLocalConfig as m, DEFAULT_HEALTH as n, defaultLauncher as o, logPathFor as p, DEFAULT_MAX_INSTANCES as r, defaultStrataPython as s, DEFAULT_COMMON_FLAGS as t, healthUrl as u, resolveModelPath as v, strataInfo as w, stopCommandFor as x, shellQuote as y };
|
|
470
|
-
//# sourceMappingURL=local-tiers-
|
|
470
|
+
//# sourceMappingURL=local-tiers-BE4jGaMa.mjs.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./config-
|
|
2
|
-
import "./config-
|
|
3
|
-
import { C as stopLines, S as stopLine, T as tierButtons, _ as readLocalConfig, a as LocalTierError, b as splitCommand, c as fetchServedModels, d as installedModelDirs, f as listensOnPort, g as planStart, h as parseServers, i as DEFAULT_RESERVE_GB, l as fmtRss, m as parseLocalConfig, n as DEFAULT_HEALTH, o as defaultLauncher, p as logPathFor, r as DEFAULT_MAX_INSTANCES, s as defaultStrataPython, t as DEFAULT_COMMON_FLAGS, u as healthUrl, v as resolveModelPath, w as strataInfo, x as stopCommandFor, y as shellQuote } from "./local-tiers-
|
|
1
|
+
import "./config-XpZWOYOG.mjs";
|
|
2
|
+
import "./config-Bu1_vbY8.mjs";
|
|
3
|
+
import { C as stopLines, S as stopLine, T as tierButtons, _ as readLocalConfig, a as LocalTierError, b as splitCommand, c as fetchServedModels, d as installedModelDirs, f as listensOnPort, g as planStart, h as parseServers, i as DEFAULT_RESERVE_GB, l as fmtRss, m as parseLocalConfig, n as DEFAULT_HEALTH, o as defaultLauncher, p as logPathFor, r as DEFAULT_MAX_INSTANCES, s as defaultStrataPython, t as DEFAULT_COMMON_FLAGS, u as healthUrl, v as resolveModelPath, w as strataInfo, x as stopCommandFor, y as shellQuote } from "./local-tiers-BE4jGaMa.mjs";
|
|
4
4
|
|
|
5
5
|
export { readLocalConfig };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as maskKey, j as readWorkersSection, tt as workersYamlPath } from "./config-
|
|
2
|
-
import { C as migrateMainConfigToYaml, E as writeYamlFileAtomic, c as loadConfig, d as paiConfigYamlFilePath, f as readMainConfigRaw, i as defaults, p as writeMainConfigRaw, u as paiConfigFilePath, x as userWorkersYamlRaw } from "./config-
|
|
1
|
+
import { _ as maskKey, j as readWorkersSection, tt as workersYamlPath } from "./config-XpZWOYOG.mjs";
|
|
2
|
+
import { C as migrateMainConfigToYaml, E as writeYamlFileAtomic, c as loadConfig, d as paiConfigYamlFilePath, f as readMainConfigRaw, i as defaults, p as writeMainConfigRaw, u as paiConfigFilePath, x as userWorkersYamlRaw } from "./config-Bu1_vbY8.mjs";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { Document } from "yaml";
|
|
5
5
|
|
|
@@ -239,4 +239,4 @@ function unsetConfigValueOp(dottedPath) {
|
|
|
239
239
|
|
|
240
240
|
//#endregion
|
|
241
241
|
export { listConfigOp as a, getConfigValueOp as i, formatConfigGetOutput as n, setConfigValueOp as o, formatConfigSource as r, unsetConfigValueOp as s, MainConfigOpsError as t };
|
|
242
|
-
//# sourceMappingURL=main-config-ops-
|
|
242
|
+
//# sourceMappingURL=main-config-ops-BEMpuhDz.mjs.map
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { _ as warn, c as ok, i as err, l as renderTable, n as dim, o as header } from "./utils-J2b9y8PC.mjs";
|
|
2
|
-
import "./config-
|
|
3
|
-
import "./config-
|
|
4
|
-
import "./paths-
|
|
5
|
-
import "./status-
|
|
6
|
-
import { d as terminalHasName } from "./scope-
|
|
7
|
-
import "./routing-
|
|
8
|
-
import "./tool-errors-
|
|
9
|
-
import "./wait-
|
|
10
|
-
import "./local-tiers-
|
|
11
|
-
import "./caller-identity-
|
|
12
|
-
import "./
|
|
13
|
-
import "./
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
2
|
+
import "./config-XpZWOYOG.mjs";
|
|
3
|
+
import "./config-Bu1_vbY8.mjs";
|
|
4
|
+
import "./paths-CHifubDh.mjs";
|
|
5
|
+
import "./status-ggJ7lgVg.mjs";
|
|
6
|
+
import { d as terminalHasName } from "./scope-D-h6F6DN.mjs";
|
|
7
|
+
import "./routing-BAAST9vT.mjs";
|
|
8
|
+
import "./tool-errors-7E13StWd.mjs";
|
|
9
|
+
import "./wait-ByV0OyMZ.mjs";
|
|
10
|
+
import "./local-tiers-BE4jGaMa.mjs";
|
|
11
|
+
import "./caller-identity-yLFb7CKq.mjs";
|
|
12
|
+
import "./capacity-DEveQdFV.mjs";
|
|
13
|
+
import "./local-instances-DOxSgT_L.mjs";
|
|
14
|
+
import "./local-candidate-DLdwQt0s.mjs";
|
|
15
|
+
import { c as resumeOfferBlock, l as resumeRouteFor, n as launchInDir } from "./launch-CXVlZjHt.mjs";
|
|
16
|
+
import { i as switchToSession, n as fetchLiveSessions } from "./aibroker-client-CQPGqLxL.mjs";
|
|
17
|
+
import { s as scanSessions, t as fmtAge } from "./session-scan-W-gbwJHM.mjs";
|
|
17
18
|
import { createReadStream, existsSync, realpathSync } from "node:fs";
|
|
18
19
|
import { basename, join } from "node:path";
|
|
19
20
|
import chalk from "chalk";
|
|
@@ -599,7 +600,7 @@ async function openMatch(entry, allSessions, dryRun, resume, auto) {
|
|
|
599
600
|
}
|
|
600
601
|
async function getRegisteredProjects(all = false) {
|
|
601
602
|
try {
|
|
602
|
-
const { getRegistryBackend } = await import("./factory-
|
|
603
|
+
const { getRegistryBackend } = await import("./factory-Bsib-qDG.mjs");
|
|
603
604
|
return await (await getRegistryBackend()).listProjectsWithSessionStats(all ? {} : { status: "active" });
|
|
604
605
|
} catch {
|
|
605
606
|
return [];
|
|
@@ -783,4 +784,4 @@ async function cmdMain(query, pickN, opts) {
|
|
|
783
784
|
|
|
784
785
|
//#endregion
|
|
785
786
|
export { cmdMain };
|
|
786
|
-
//# sourceMappingURL=main-resolver-
|
|
787
|
+
//# sourceMappingURL=main-resolver-3Ns1HPmS.mjs.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h as smartDecodeDir } from "./utils-J2b9y8PC.mjs";
|
|
2
2
|
import { l as claudeHomeDir } from "./runtime-paths-WsO9sJFc.mjs";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
|
|
6
7
|
//#region src/registry/migrate.ts
|
|
@@ -21,6 +22,39 @@ import { join } from "node:path";
|
|
|
21
22
|
* Legacy: "NNNN_YYYY-MM-DD_description.md" (underscores)
|
|
22
23
|
*/
|
|
23
24
|
/**
|
|
25
|
+
* Extract project entries from a parsed session-registry.json. Supports both
|
|
26
|
+
* formats:
|
|
27
|
+
* list-based: { "projects": [ { "encoded_dir", "original_path" }, ... ] }
|
|
28
|
+
* object-keyed: { "<encoded_dir>": { ... } } (original Claude format)
|
|
29
|
+
* The `version` key and non-object values are not projects.
|
|
30
|
+
*/
|
|
31
|
+
function parseRegistryEntries(parsed) {
|
|
32
|
+
const out = [];
|
|
33
|
+
if (!parsed || typeof parsed !== "object") return out;
|
|
34
|
+
const obj = parsed;
|
|
35
|
+
if (Array.isArray(obj.projects)) {
|
|
36
|
+
for (const item of obj.projects) {
|
|
37
|
+
if (!item || typeof item !== "object") continue;
|
|
38
|
+
const entry = item;
|
|
39
|
+
const key = entry.encoded_dir;
|
|
40
|
+
if (typeof key === "string" && key) out.push({
|
|
41
|
+
encodedDir: key,
|
|
42
|
+
entry
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
48
|
+
if (key === "version") continue;
|
|
49
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
50
|
+
out.push({
|
|
51
|
+
encodedDir: key,
|
|
52
|
+
entry: value
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
24
58
|
* Build a lookup table from session-registry.json mapping encoded_dir →
|
|
25
59
|
* original_path. This is the authoritative source for decoding because the
|
|
26
60
|
* encoding is ambiguous: `/`, ` ` (space), `.` (dot), and `-` (literal
|
|
@@ -40,17 +74,10 @@ function buildEncodedDirMap(jsonPath = join(claudeHomeDir(), "session-registry.j
|
|
|
40
74
|
const map = /* @__PURE__ */ new Map();
|
|
41
75
|
if (!existsSync(jsonPath)) return map;
|
|
42
76
|
try {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
if (Array.isArray(parsed.projects)) for (const entry of parsed.projects) {
|
|
46
|
-
const key = entry.encoded_dir;
|
|
77
|
+
const parsed = JSON.parse(readFileSync(jsonPath, "utf8"));
|
|
78
|
+
for (const { encodedDir, entry } of parseRegistryEntries(parsed)) {
|
|
47
79
|
const val = entry.original_path;
|
|
48
|
-
if (
|
|
49
|
-
}
|
|
50
|
-
else for (const [key, value] of Object.entries(parsed)) {
|
|
51
|
-
if (key === "version") continue;
|
|
52
|
-
const val = value?.original_path;
|
|
53
|
-
if (val) map.set(key, val);
|
|
80
|
+
if (typeof val === "string" && val) map.set(encodedDir, val);
|
|
54
81
|
}
|
|
55
82
|
} catch {}
|
|
56
83
|
return map;
|
|
@@ -100,6 +127,10 @@ function decodeEncodedDir(encoded, lookupMap) {
|
|
|
100
127
|
function slugify(value) {
|
|
101
128
|
return (value.includes("/") ? value.replace(/\/$/, "").split("/").pop() ?? value : value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
102
129
|
}
|
|
130
|
+
/** Stable slug for names slugify() reduces to nothing (e.g. non-ASCII only). */
|
|
131
|
+
function fallbackSlug(value) {
|
|
132
|
+
return "project-" + createHash("sha1").update(value).digest("hex").slice(0, 8);
|
|
133
|
+
}
|
|
103
134
|
/** Match `0027 - 2026-01-04 - Some Description.md` */
|
|
104
135
|
const MODERN_RE = /^(\d{4})\s+-\s+(\d{4}-\d{2}-\d{2})\s+-\s+(.+)\.md$/i;
|
|
105
136
|
/** Match `0027_2026-01-04_some_description.md` */
|
|
@@ -159,16 +190,16 @@ async function migrateFromJson(backend, registryPath = join(claudeHomeDir(), "se
|
|
|
159
190
|
let registry;
|
|
160
191
|
try {
|
|
161
192
|
const raw = readFileSync(registryPath, "utf8");
|
|
162
|
-
registry = JSON.parse(raw);
|
|
193
|
+
registry = parseRegistryEntries(JSON.parse(raw));
|
|
163
194
|
} catch (err) {
|
|
164
195
|
result.errors.push(`Failed to parse registry JSON: ${String(err)}`);
|
|
165
196
|
return result;
|
|
166
197
|
}
|
|
167
198
|
const now = Date.now();
|
|
168
199
|
const lookupMap = buildEncodedDirMap(registryPath);
|
|
169
|
-
for (const
|
|
200
|
+
for (const { encodedDir, entry } of registry) {
|
|
170
201
|
const rootPath = decodeEncodedDir(encodedDir, lookupMap);
|
|
171
|
-
const baseSlug = slugify(rootPath);
|
|
202
|
+
const baseSlug = slugify(rootPath) || fallbackSlug(rootPath);
|
|
172
203
|
const { id: projectId, created } = await backend.createProjectWithSlugRetry({
|
|
173
204
|
baseSlug,
|
|
174
205
|
displayName: entry.displayName ?? rootPath.split("/").pop() ?? rootPath,
|
|
@@ -212,4 +243,4 @@ async function migrateFromJson(backend, registryPath = join(claudeHomeDir(), "se
|
|
|
212
243
|
|
|
213
244
|
//#endregion
|
|
214
245
|
export { slugify as a, parseSessionFilename as i, decodeEncodedDir as n, migrateFromJson as r, buildEncodedDirMap as t };
|
|
215
|
-
//# sourceMappingURL=migrate-
|
|
246
|
+
//# sourceMappingURL=migrate-DrA_OwlZ.mjs.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/utils/min-max.ts
|
|
2
|
+
/** Min/max without argument spread: Math.min(...arr) throws RangeError above ~120k elements. */
|
|
3
|
+
function minOf(values) {
|
|
4
|
+
let m = Infinity;
|
|
5
|
+
for (const v of values) if (v < m) m = v;
|
|
6
|
+
return m;
|
|
7
|
+
}
|
|
8
|
+
function maxOf(values) {
|
|
9
|
+
let m = -Infinity;
|
|
10
|
+
for (const v of values) if (v > m) m = v;
|
|
11
|
+
return m;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { minOf as n, maxOf as t };
|
|
16
|
+
//# sourceMappingURL=min-max-DrSRUaWQ.mjs.map
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import "./config-
|
|
2
|
-
import "./config-
|
|
3
|
-
import "./paths-
|
|
4
|
-
import "./status-
|
|
5
|
-
import "./scope-
|
|
6
|
-
import { $ as parseFootprint, B as MODEL_INVENTORY_MAX_AGE_MS, G as fmtMem, H as ProbeHold, J as inventoryRows, K as fmtMemMapped, Q as modelLabel, R as DOWN_AFTER_MISSES, U as computeInventory, V as MODEL_INVENTORY_REFRESH_MS, W as currentInventory, X as liveDeps, Y as inventoryText, Z as modelKind, at as readInventoryInput, ct as writeInventory, et as parseSizeMb, it as readInventory, nt as processTreeMemory, ot as readModelLabels, q as inventoryPath, rt as processTreeRssMb, st as startInventoryRefresh, tt as parseTable, z as INVENTORY_HEADERS } from "./routing-
|
|
7
|
-
import "./tool-errors-
|
|
8
|
-
import "./wait-
|
|
9
|
-
import "./local-tiers-
|
|
10
|
-
import "./caller-identity-
|
|
11
|
-
import "./
|
|
12
|
-
import "./
|
|
1
|
+
import "./config-XpZWOYOG.mjs";
|
|
2
|
+
import "./config-Bu1_vbY8.mjs";
|
|
3
|
+
import "./paths-CHifubDh.mjs";
|
|
4
|
+
import "./status-ggJ7lgVg.mjs";
|
|
5
|
+
import "./scope-D-h6F6DN.mjs";
|
|
6
|
+
import { $ as parseFootprint, B as MODEL_INVENTORY_MAX_AGE_MS, G as fmtMem, H as ProbeHold, J as inventoryRows, K as fmtMemMapped, Q as modelLabel, R as DOWN_AFTER_MISSES, U as computeInventory, V as MODEL_INVENTORY_REFRESH_MS, W as currentInventory, X as liveDeps, Y as inventoryText, Z as modelKind, at as readInventoryInput, ct as writeInventory, et as parseSizeMb, it as readInventory, nt as processTreeMemory, ot as readModelLabels, q as inventoryPath, rt as processTreeRssMb, st as startInventoryRefresh, tt as parseTable, z as INVENTORY_HEADERS } from "./routing-BAAST9vT.mjs";
|
|
7
|
+
import "./tool-errors-7E13StWd.mjs";
|
|
8
|
+
import "./wait-ByV0OyMZ.mjs";
|
|
9
|
+
import "./local-tiers-BE4jGaMa.mjs";
|
|
10
|
+
import "./caller-identity-yLFb7CKq.mjs";
|
|
11
|
+
import "./capacity-DEveQdFV.mjs";
|
|
12
|
+
import "./local-instances-DOxSgT_L.mjs";
|
|
13
|
+
import "./local-candidate-DLdwQt0s.mjs";
|
|
13
14
|
|
|
14
15
|
export { computeInventory, currentInventory, processTreeRssMb, readInventoryInput, writeInventory };
|
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
import "./utils-J2b9y8PC.mjs";
|
|
2
|
-
import { tt as workersYamlPath } from "./config-
|
|
3
|
-
import "./config-
|
|
4
|
-
import "./paths-
|
|
5
|
-
import "./worktree-
|
|
6
|
-
import "./status-
|
|
7
|
-
import "./pool-
|
|
8
|
-
import "./helpers-
|
|
9
|
-
import "./scope-
|
|
10
|
-
import "./routing-
|
|
11
|
-
import "./tool-errors-
|
|
12
|
-
import "./wait-
|
|
13
|
-
import { g as planStart, m as parseLocalConfig, o as defaultLauncher, y as shellQuote } from "./local-tiers-
|
|
14
|
-
import "./caller-identity-
|
|
15
|
-
import "./
|
|
16
|
-
import "./
|
|
17
|
-
import "./
|
|
18
|
-
import "./
|
|
19
|
-
import "./
|
|
20
|
-
import "./
|
|
21
|
-
import "./
|
|
22
|
-
import "./
|
|
23
|
-
import "./
|
|
24
|
-
import "./
|
|
25
|
-
import "./
|
|
26
|
-
import "./
|
|
27
|
-
import "./
|
|
28
|
-
import "./
|
|
29
|
-
import "./
|
|
30
|
-
import "./
|
|
31
|
-
import "./
|
|
32
|
-
import "./
|
|
33
|
-
import
|
|
34
|
-
import "./
|
|
35
|
-
import "./
|
|
36
|
-
import "./
|
|
37
|
-
import "./
|
|
38
|
-
import "./
|
|
2
|
+
import { tt as workersYamlPath } from "./config-XpZWOYOG.mjs";
|
|
3
|
+
import "./config-Bu1_vbY8.mjs";
|
|
4
|
+
import "./paths-CHifubDh.mjs";
|
|
5
|
+
import "./worktree-CRs2gyg0.mjs";
|
|
6
|
+
import "./status-ggJ7lgVg.mjs";
|
|
7
|
+
import "./pool-Dh-q3iJi.mjs";
|
|
8
|
+
import "./helpers-CRuBUas3.mjs";
|
|
9
|
+
import "./scope-D-h6F6DN.mjs";
|
|
10
|
+
import "./routing-BAAST9vT.mjs";
|
|
11
|
+
import "./tool-errors-7E13StWd.mjs";
|
|
12
|
+
import "./wait-ByV0OyMZ.mjs";
|
|
13
|
+
import { g as planStart, m as parseLocalConfig, o as defaultLauncher, y as shellQuote } from "./local-tiers-BE4jGaMa.mjs";
|
|
14
|
+
import "./caller-identity-yLFb7CKq.mjs";
|
|
15
|
+
import "./capacity-DEveQdFV.mjs";
|
|
16
|
+
import "./local-instances-DOxSgT_L.mjs";
|
|
17
|
+
import "./local-candidate-DLdwQt0s.mjs";
|
|
18
|
+
import "./factory-DjAAsqr_.mjs";
|
|
19
|
+
import "./postgres-C6ti4haA.mjs";
|
|
20
|
+
import "./embedding-gate-6rZrWRXa.mjs";
|
|
21
|
+
import "./embeddings-BVq3esBF.mjs";
|
|
22
|
+
import "./router-CQSc3lk3.mjs";
|
|
23
|
+
import "./launch-Jh5NVeNe.mjs";
|
|
24
|
+
import "./health-Dnym-qcT.mjs";
|
|
25
|
+
import "./run-CSC7mgPf.mjs";
|
|
26
|
+
import "./controls-CPoeudou.mjs";
|
|
27
|
+
import "./endreason-C577xIQz.mjs";
|
|
28
|
+
import "./render-BYmQkuLd.mjs";
|
|
29
|
+
import "./operator-CyuB3aVw.mjs";
|
|
30
|
+
import "./kill-tree-EWYaEk5v.mjs";
|
|
31
|
+
import "./pool-cli-BggU8GRp.mjs";
|
|
32
|
+
import "./pane-audit-DKQt71s4.mjs";
|
|
33
|
+
import "./providers-CblTFrhz.mjs";
|
|
34
|
+
import { H as canBind, U as sizeOnDiskGb, V as allocatePort, at as defaultRegistryCtx, f as killInstance, g as portListeners, ot as hashText, p as launchDetached, st as saveRegistry, u as healthOk } from "./worker-config-CJhEMk19.mjs";
|
|
35
|
+
import "./main-config-ops-BEMpuhDz.mjs";
|
|
36
|
+
import "./cockpit-actions-BBHeyEZI.mjs";
|
|
37
|
+
import "./ipc-client-CaiGJnSM.mjs";
|
|
38
|
+
import "./embed-pass-DIWjA68P.mjs";
|
|
39
|
+
import "./fallback-DFlItw1Z.mjs";
|
|
39
40
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
40
41
|
import { basename, join } from "node:path";
|
|
41
42
|
import { homedir } from "node:os";
|
|
@@ -331,4 +332,4 @@ async function smokeTest(cfg, name, d = {}) {
|
|
|
331
332
|
|
|
332
333
|
//#endregion
|
|
333
334
|
export { addModel };
|
|
334
|
-
//# sourceMappingURL=models-add-
|
|
335
|
+
//# sourceMappingURL=models-add-C1QIc1DA.mjs.map
|