syntaur 0.44.1 → 0.46.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/.claude-plugin/plugin.json +1 -1
- package/dashboard/dist/assets/{_basePickBy-n0wU3YiR.js → _basePickBy-RQBuJKcX.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-CZE21ua2.js → _baseUniq-_J7s4kD3.js} +1 -1
- package/dashboard/dist/assets/{arc-Dd_UCPQq.js → arc-_9SyUgKQ.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-mgIxQnzX.js → architectureDiagram-2XIMDMQ5-C8LeFMgr.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-DhlycMw2.js → blockDiagram-WCTKOSBZ-gMh0EPEh.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-CuWqqKYM.js → c4Diagram-IC4MRINW-cHwecwLI.js} +1 -1
- package/dashboard/dist/assets/channel-C36dnl_e.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-CTP72FfB.js → chunk-4BX2VUAB-Bb2anYuQ.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-CE7fO2_0.js → chunk-55IACEB6-DYIRGzA1.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-DBsJdRy7.js → chunk-FMBD7UC4-sgRWBbaF.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-1dES-fUJ.js → chunk-JSJVCQXG-DlYKMl_j.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-U98s2l2I.js → chunk-KX2RTZJC-D0YDLAOF.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-DAy7YeTS.js → chunk-NQ4KR5QH-D-Y-CUx6.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-BeQRHiY8.js → chunk-QZHKN3VN-D7FpSvb5.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-Bt94wrMs.js → chunk-WL4C6EOR-CtXgQLdS.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-BsoGa6_a.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BsoGa6_a.js +1 -0
- package/dashboard/dist/assets/clone-Bz6jW3OY.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-CdQh3kdW.js → cose-bilkent-S5V4N54A-YbTaohoJ.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-CKWb9fD7.js → dagre-KLK3FWXG-CMtwGAnP.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-Dlkqi8ga.js → diagram-E7M64L7V-D8wBMBAX.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-HhRKBN6J.js → diagram-IFDJBPK2-DfudLpiJ.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-B70t50AJ.js → diagram-P4PSJMXO-CyMy61wE.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-Cc6Lz8R-.js → erDiagram-INFDFZHY-BlB4ZQl9.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-UfEconjz.js → flowDiagram-PKNHOUZH-DbhDQJM3.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-B13deOQz.js → ganttDiagram-A5KZAMGK-DJFqteNi.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-CHRxKecy.js → gitGraphDiagram-K3NZZRJ6-D8etA_mm.js} +1 -1
- package/dashboard/dist/assets/{graph-BO4rYEQo.js → graph-Ce86jeZn.js} +1 -1
- package/dashboard/dist/assets/index-DRng26Jg.js +567 -0
- package/dashboard/dist/assets/index-DzHQIE2n.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DYhPRnW_.js → infoDiagram-LFFYTUFH-Cx35U-h8.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-uCkY17Z8.js → ishikawaDiagram-PHBUUO56-C04Y2nj8.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DT3-91Dx.js → journeyDiagram-4ABVD52K-D8-cxbxE.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-CbNSU1jT.js → kanban-definition-K7BYSVSG-DVKqMylP.js} +1 -1
- package/dashboard/dist/assets/{layout-wdZYENqD.js → layout-98xZDpgu.js} +1 -1
- package/dashboard/dist/assets/{linear-DP2LUzjc.js → linear-0jk_IwAc.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-glRfe02B.js → mermaid.core-C337VWfr.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-Bq2Qe7iv.js → mindmap-definition-YRQLILUH-8sNYGYEP.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-BnG8dbQb.js → pieDiagram-SKSYHLDU-afcmzHxf.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-CBvIkuCW.js → quadrantDiagram-337W2JSQ-B4RjcpOq.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CsHJZ9D_.js → requirementDiagram-Z7DCOOCP-CRavU6cI.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DioS3JH6.js → sankeyDiagram-WA2Y5GQK-DFomU3z-.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-_noTQrZH.js → sequenceDiagram-2WXFIKYE-CGKO7nmK.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-K3R1Eh9N.js → stateDiagram-RAJIS63D-BjFI1K8h.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BtxefYKD.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-Bb3u4Ahz.js → timeline-definition-YZTLITO2-BBo8XJFG.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-BLiQz7AY.js → treemap-KZPCXAKY-COd6i6TE.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-BLsoQIAw.js → vennDiagram-LZ73GAT5-CGQweQ36.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-Dm1juAkc.js → xychartDiagram-JWTSCODW-mfJ5So7N.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +1617 -300
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +2314 -1586
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +21 -1
- package/dist/launch/index.js +685 -111
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/SESSION-ID-RESOLUTION.md +41 -4
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/claude-code/hooks/session-cleanup.sh +25 -64
- package/platforms/claude-code/hooks/session-start.sh +35 -109
- package/platforms/claude-code/skills/track-session/SKILL.md +12 -60
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- package/platforms/codex/skills/track-session/SKILL.md +12 -60
- package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
- package/skills/track-session/SKILL.md +12 -60
- package/dashboard/dist/assets/channel-BLQNOcNZ.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-Mx6la8yG.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Mx6la8yG.js +0 -1
- package/dashboard/dist/assets/clone-58HOBY7h.js +0 -1
- package/dashboard/dist/assets/index-BKdHsXLj.js +0 -566
- package/dashboard/dist/assets/index-D1f1wB-7.css +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BJes_eiS.js +0 -1
package/dist/launch/index.js
CHANGED
|
@@ -1028,7 +1028,11 @@ var init_fields = __esm({
|
|
|
1028
1028
|
tags: { kind: "list" },
|
|
1029
1029
|
archived: { kind: "bool" },
|
|
1030
1030
|
title: { kind: "substring" },
|
|
1031
|
-
search
|
|
1031
|
+
// `search` reads a dedicated `searchText` haystack when the item provides one
|
|
1032
|
+
// (so the dashboard can match title + slug + project like its filter box),
|
|
1033
|
+
// falling back to `title` when absent. Backward-compatible: title-only when no
|
|
1034
|
+
// searchText. The `title` field stays title-only.
|
|
1035
|
+
search: { kind: "substring", get: (i) => i["searchText"] ?? i["title"] },
|
|
1032
1036
|
created: { kind: "timestamp" },
|
|
1033
1037
|
updated: { kind: "timestamp" },
|
|
1034
1038
|
completedat: { kind: "timestamp", get: (i) => i["completedAt"] },
|
|
@@ -1663,17 +1667,7 @@ var init_query = __esm({
|
|
|
1663
1667
|
}
|
|
1664
1668
|
});
|
|
1665
1669
|
|
|
1666
|
-
// src/
|
|
1667
|
-
var derive_exports = {};
|
|
1668
|
-
__export(derive_exports, {
|
|
1669
|
-
DERIVE_FIELDS: () => DERIVE_FIELDS,
|
|
1670
|
-
acceptFactDeclarations: () => acceptFactDeclarations,
|
|
1671
|
-
buildDeriveRegistry: () => buildDeriveRegistry,
|
|
1672
|
-
buildQueryRegistry: () => buildQueryRegistry,
|
|
1673
|
-
deriveDimensions: () => deriveDimensions,
|
|
1674
|
-
factFieldNames: () => factFieldNames,
|
|
1675
|
-
validateDeriveCondition: () => validateDeriveCondition
|
|
1676
|
-
});
|
|
1670
|
+
// src/utils/fact-registry.ts
|
|
1677
1671
|
function factFieldNames(decl) {
|
|
1678
1672
|
const name = decl.name;
|
|
1679
1673
|
const exportNames = {
|
|
@@ -1740,85 +1734,63 @@ function buildQueryRegistry(accepted) {
|
|
|
1740
1734
|
for (const decl of accepted) addFactFields(registry, decl);
|
|
1741
1735
|
return registry;
|
|
1742
1736
|
}
|
|
1743
|
-
function
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1737
|
+
function queryFieldNames(declarations) {
|
|
1738
|
+
const builtins = [
|
|
1739
|
+
"status",
|
|
1740
|
+
"priority",
|
|
1741
|
+
"type",
|
|
1742
|
+
"assignee",
|
|
1743
|
+
"project",
|
|
1744
|
+
"tag",
|
|
1745
|
+
"tags",
|
|
1746
|
+
"archived",
|
|
1747
|
+
"title",
|
|
1748
|
+
"search",
|
|
1749
|
+
"created",
|
|
1750
|
+
"updated",
|
|
1751
|
+
"completedAt",
|
|
1752
|
+
"statusAge",
|
|
1753
|
+
"phase",
|
|
1754
|
+
"disposition",
|
|
1755
|
+
"phaseAge",
|
|
1756
|
+
"hasRealObjective",
|
|
1757
|
+
"acRealTotal",
|
|
1758
|
+
"acRealChecked",
|
|
1759
|
+
"acAllChecked",
|
|
1760
|
+
"planExists",
|
|
1761
|
+
"planApproved",
|
|
1762
|
+
"workspaceSet",
|
|
1763
|
+
"implementationStarted",
|
|
1764
|
+
"depsSatisfied",
|
|
1765
|
+
"unresolvedQuestions",
|
|
1766
|
+
"progressStaleDays",
|
|
1767
|
+
"blocked",
|
|
1768
|
+
"parked",
|
|
1769
|
+
"reviewRequested",
|
|
1770
|
+
"pinned"
|
|
1771
|
+
];
|
|
1772
|
+
const custom = [];
|
|
1773
|
+
for (const decl of declarations) {
|
|
1774
|
+
const names = factFieldNames(decl);
|
|
1775
|
+
if (decl.type === "attestation") {
|
|
1776
|
+
custom.push(
|
|
1777
|
+
names.exports.fact,
|
|
1778
|
+
names.exports.approved,
|
|
1779
|
+
names.exports.changesRequested,
|
|
1780
|
+
names.exports.by,
|
|
1781
|
+
names.exports.approvedBy
|
|
1782
|
+
);
|
|
1765
1783
|
} else {
|
|
1766
|
-
|
|
1767
|
-
if (!parsed.ast) {
|
|
1768
|
-
throw new CompileError(parsed.errors);
|
|
1769
|
-
}
|
|
1770
|
-
pred = compileNode(parsed.ast, registry);
|
|
1784
|
+
custom.push(names.exports.fact);
|
|
1771
1785
|
}
|
|
1772
|
-
cache.set(when, pred);
|
|
1773
1786
|
}
|
|
1774
|
-
return
|
|
1787
|
+
return [...builtins, ...custom];
|
|
1775
1788
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
if (terminalStatuses.has(currentStatus)) return null;
|
|
1780
|
-
const ctx = { now: 0 };
|
|
1781
|
-
const item = facts;
|
|
1782
|
-
let phase = derive.phaseLadder[0]?.phase ?? currentStatus;
|
|
1783
|
-
let nextAction = derive.phaseLadder[0]?.next ?? null;
|
|
1784
|
-
for (let i = derive.phaseLadder.length - 1; i >= 0; i--) {
|
|
1785
|
-
const rung = derive.phaseLadder[i];
|
|
1786
|
-
if (compiledWhen(registry, rung.when)(item, ctx)) {
|
|
1787
|
-
phase = rung.phase;
|
|
1788
|
-
nextAction = rung.next ?? null;
|
|
1789
|
-
break;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
let disposition = "active";
|
|
1793
|
-
for (const rule of derive.disposition) {
|
|
1794
|
-
if (rule.when === null || compiledWhen(registry, rule.when)(item, ctx)) {
|
|
1795
|
-
disposition = rule.is;
|
|
1796
|
-
break;
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
let derivedStatus;
|
|
1800
|
-
switch (disposition) {
|
|
1801
|
-
case "parked":
|
|
1802
|
-
derivedStatus = knownStatusIds.has(derive.headline.parked) ? derive.headline.parked : phase;
|
|
1803
|
-
break;
|
|
1804
|
-
case "blocked":
|
|
1805
|
-
derivedStatus = knownStatusIds.has(derive.headline.blocked) ? derive.headline.blocked : phase;
|
|
1806
|
-
break;
|
|
1807
|
-
default:
|
|
1808
|
-
derivedStatus = phase;
|
|
1809
|
-
}
|
|
1810
|
-
let status = derivedStatus;
|
|
1811
|
-
if (override && override.status && !terminalStatuses.has(override.status) && knownStatusIds.has(override.status)) {
|
|
1812
|
-
status = override.status;
|
|
1813
|
-
}
|
|
1814
|
-
return { phase, disposition, derivedStatus, status, nextAction };
|
|
1815
|
-
}
|
|
1816
|
-
var DERIVE_FIELDS, conditionCache;
|
|
1817
|
-
var init_derive = __esm({
|
|
1818
|
-
"src/lifecycle/derive.ts"() {
|
|
1789
|
+
var DERIVE_FIELDS;
|
|
1790
|
+
var init_fact_registry = __esm({
|
|
1791
|
+
"src/utils/fact-registry.ts"() {
|
|
1819
1792
|
"use strict";
|
|
1820
1793
|
init_query();
|
|
1821
|
-
init_query();
|
|
1822
1794
|
DERIVE_FIELDS = {
|
|
1823
1795
|
hasrealobjective: { kind: "bool", get: (i) => i["hasRealObjective"] },
|
|
1824
1796
|
acrealtotal: { kind: "number", get: (i) => i["acRealTotal"] },
|
|
@@ -1835,7 +1807,6 @@ var init_derive = __esm({
|
|
|
1835
1807
|
reviewrequested: { kind: "bool", get: (i) => i["reviewRequested"] },
|
|
1836
1808
|
pinned: { kind: "bool" }
|
|
1837
1809
|
};
|
|
1838
|
-
conditionCache = /* @__PURE__ */ new WeakMap();
|
|
1839
1810
|
}
|
|
1840
1811
|
});
|
|
1841
1812
|
|
|
@@ -2018,6 +1989,7 @@ function cloneDefaultConfig() {
|
|
|
2018
1989
|
...DEFAULT_CONFIG,
|
|
2019
1990
|
onboarding: { ...DEFAULT_CONFIG.onboarding },
|
|
2020
1991
|
agentDefaults: { ...DEFAULT_CONFIG.agentDefaults },
|
|
1992
|
+
session: { ...DEFAULT_CONFIG.session },
|
|
2021
1993
|
integrations: { ...DEFAULT_CONFIG.integrations },
|
|
2022
1994
|
backup: DEFAULT_CONFIG.backup ? { ...DEFAULT_CONFIG.backup } : null,
|
|
2023
1995
|
statuses: DEFAULT_CONFIG.statuses ? {
|
|
@@ -3367,6 +3339,11 @@ async function readConfig() {
|
|
|
3367
3339
|
fm["agentDefaults.autoCreateWorktree"]
|
|
3368
3340
|
) ? fm["agentDefaults.autoCreateWorktree"] : DEFAULT_CONFIG.agentDefaults.autoCreateWorktree
|
|
3369
3341
|
},
|
|
3342
|
+
session: {
|
|
3343
|
+
autoTrack: SESSION_AUTO_TRACK_VALUES.includes(
|
|
3344
|
+
fm["session.autoTrack"]
|
|
3345
|
+
) ? fm["session.autoTrack"] : DEFAULT_CONFIG.session.autoTrack
|
|
3346
|
+
},
|
|
3370
3347
|
integrations: {
|
|
3371
3348
|
claudePluginDir: parseOptionalAbsolutePath(
|
|
3372
3349
|
fm["integrations.claudePluginDir"],
|
|
@@ -3466,7 +3443,7 @@ async function updateAgentsConfig(mutation, options = {}) {
|
|
|
3466
3443
|
await writeAgentsConfig(next);
|
|
3467
3444
|
return { previous, next, written: true };
|
|
3468
3445
|
}
|
|
3469
|
-
var DEFAULT_DERIVE_CONFIG, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
3446
|
+
var DEFAULT_DERIVE_CONFIG, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
3470
3447
|
var init_config2 = __esm({
|
|
3471
3448
|
"src/utils/config.ts"() {
|
|
3472
3449
|
"use strict";
|
|
@@ -3478,7 +3455,7 @@ var init_config2 = __esm({
|
|
|
3478
3455
|
init_hotkeysCatalog();
|
|
3479
3456
|
init_agents_schema();
|
|
3480
3457
|
init_slug();
|
|
3481
|
-
|
|
3458
|
+
init_fact_registry();
|
|
3482
3459
|
init_query();
|
|
3483
3460
|
init_terminal_schema();
|
|
3484
3461
|
init_workspace_visibility_schema();
|
|
@@ -3532,6 +3509,9 @@ var init_config2 = __esm({
|
|
|
3532
3509
|
autoApprove: false,
|
|
3533
3510
|
autoCreateWorktree: "ask"
|
|
3534
3511
|
},
|
|
3512
|
+
session: {
|
|
3513
|
+
autoTrack: "all"
|
|
3514
|
+
},
|
|
3535
3515
|
integrations: {
|
|
3536
3516
|
claudePluginDir: null,
|
|
3537
3517
|
codexPluginDir: null,
|
|
@@ -3552,6 +3532,7 @@ var init_config2 = __esm({
|
|
|
3552
3532
|
}
|
|
3553
3533
|
};
|
|
3554
3534
|
AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
|
|
3535
|
+
SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
|
|
3555
3536
|
AgentConfigError = class extends Error {
|
|
3556
3537
|
};
|
|
3557
3538
|
DEFAULT_STATUS_COLORS = {
|
|
@@ -3656,6 +3637,103 @@ var init_assignment_resolver = __esm({
|
|
|
3656
3637
|
}
|
|
3657
3638
|
});
|
|
3658
3639
|
|
|
3640
|
+
// src/lifecycle/derive.ts
|
|
3641
|
+
var derive_exports = {};
|
|
3642
|
+
__export(derive_exports, {
|
|
3643
|
+
DERIVE_FIELDS: () => DERIVE_FIELDS,
|
|
3644
|
+
acceptFactDeclarations: () => acceptFactDeclarations,
|
|
3645
|
+
addFactFields: () => addFactFields,
|
|
3646
|
+
buildDeriveRegistry: () => buildDeriveRegistry,
|
|
3647
|
+
buildQueryRegistry: () => buildQueryRegistry,
|
|
3648
|
+
deriveDimensions: () => deriveDimensions,
|
|
3649
|
+
factFieldNames: () => factFieldNames,
|
|
3650
|
+
queryFieldNames: () => queryFieldNames,
|
|
3651
|
+
validateDeriveCondition: () => validateDeriveCondition
|
|
3652
|
+
});
|
|
3653
|
+
function validateDeriveCondition(when, registry = DERIVE_FIELDS) {
|
|
3654
|
+
if (when === "*") return null;
|
|
3655
|
+
const parsed = parseQuery(when);
|
|
3656
|
+
if (!parsed.ast) return parsed.errors[0]?.message ?? "unparseable condition";
|
|
3657
|
+
try {
|
|
3658
|
+
compileNode(parsed.ast, registry);
|
|
3659
|
+
return null;
|
|
3660
|
+
} catch (err) {
|
|
3661
|
+
if (err instanceof CompileError) return err.errors[0]?.message ?? "invalid condition";
|
|
3662
|
+
throw err;
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
function compiledWhen(registry, when) {
|
|
3666
|
+
let cache = conditionCache.get(registry);
|
|
3667
|
+
if (!cache) {
|
|
3668
|
+
cache = /* @__PURE__ */ new Map();
|
|
3669
|
+
conditionCache.set(registry, cache);
|
|
3670
|
+
}
|
|
3671
|
+
let pred = cache.get(when);
|
|
3672
|
+
if (!pred) {
|
|
3673
|
+
if (when === "*") {
|
|
3674
|
+
pred = () => true;
|
|
3675
|
+
} else {
|
|
3676
|
+
const parsed = parseQuery(when);
|
|
3677
|
+
if (!parsed.ast) {
|
|
3678
|
+
throw new CompileError(parsed.errors);
|
|
3679
|
+
}
|
|
3680
|
+
pred = compileNode(parsed.ast, registry);
|
|
3681
|
+
}
|
|
3682
|
+
cache.set(when, pred);
|
|
3683
|
+
}
|
|
3684
|
+
return pred;
|
|
3685
|
+
}
|
|
3686
|
+
function deriveDimensions(input) {
|
|
3687
|
+
const { facts, derive, currentStatus, terminalStatuses, knownStatusIds, override } = input;
|
|
3688
|
+
const registry = input.registry ?? DERIVE_FIELDS;
|
|
3689
|
+
if (terminalStatuses.has(currentStatus)) return null;
|
|
3690
|
+
const ctx = { now: 0 };
|
|
3691
|
+
const item = facts;
|
|
3692
|
+
let phase = derive.phaseLadder[0]?.phase ?? currentStatus;
|
|
3693
|
+
let nextAction = derive.phaseLadder[0]?.next ?? null;
|
|
3694
|
+
for (let i = derive.phaseLadder.length - 1; i >= 0; i--) {
|
|
3695
|
+
const rung = derive.phaseLadder[i];
|
|
3696
|
+
if (compiledWhen(registry, rung.when)(item, ctx)) {
|
|
3697
|
+
phase = rung.phase;
|
|
3698
|
+
nextAction = rung.next ?? null;
|
|
3699
|
+
break;
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
let disposition = "active";
|
|
3703
|
+
for (const rule of derive.disposition) {
|
|
3704
|
+
if (rule.when === null || compiledWhen(registry, rule.when)(item, ctx)) {
|
|
3705
|
+
disposition = rule.is;
|
|
3706
|
+
break;
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
let derivedStatus;
|
|
3710
|
+
switch (disposition) {
|
|
3711
|
+
case "parked":
|
|
3712
|
+
derivedStatus = knownStatusIds.has(derive.headline.parked) ? derive.headline.parked : phase;
|
|
3713
|
+
break;
|
|
3714
|
+
case "blocked":
|
|
3715
|
+
derivedStatus = knownStatusIds.has(derive.headline.blocked) ? derive.headline.blocked : phase;
|
|
3716
|
+
break;
|
|
3717
|
+
default:
|
|
3718
|
+
derivedStatus = phase;
|
|
3719
|
+
}
|
|
3720
|
+
let status = derivedStatus;
|
|
3721
|
+
if (override && override.status && !terminalStatuses.has(override.status) && knownStatusIds.has(override.status)) {
|
|
3722
|
+
status = override.status;
|
|
3723
|
+
}
|
|
3724
|
+
return { phase, disposition, derivedStatus, status, nextAction };
|
|
3725
|
+
}
|
|
3726
|
+
var conditionCache;
|
|
3727
|
+
var init_derive = __esm({
|
|
3728
|
+
"src/lifecycle/derive.ts"() {
|
|
3729
|
+
"use strict";
|
|
3730
|
+
init_query();
|
|
3731
|
+
init_fact_registry();
|
|
3732
|
+
init_fact_registry();
|
|
3733
|
+
conditionCache = /* @__PURE__ */ new WeakMap();
|
|
3734
|
+
}
|
|
3735
|
+
});
|
|
3736
|
+
|
|
3659
3737
|
// src/utils/playbooks.ts
|
|
3660
3738
|
import { resolve as resolve8 } from "path";
|
|
3661
3739
|
import { readdir as readdir4, readFile as readFile7, unlink } from "fs/promises";
|
|
@@ -3878,9 +3956,162 @@ var init_help = __esm({
|
|
|
3878
3956
|
});
|
|
3879
3957
|
|
|
3880
3958
|
// src/dashboard/session-db.ts
|
|
3959
|
+
var session_db_exports = {};
|
|
3960
|
+
__export(session_db_exports, {
|
|
3961
|
+
closeSessionDb: () => closeSessionDb,
|
|
3962
|
+
getSessionDb: () => getSessionDb,
|
|
3963
|
+
initSessionDb: () => initSessionDb,
|
|
3964
|
+
isSessionDbInitialized: () => isSessionDbInitialized,
|
|
3965
|
+
migrateFromMarkdown: () => migrateFromMarkdown,
|
|
3966
|
+
resetSessionDb: () => resetSessionDb
|
|
3967
|
+
});
|
|
3881
3968
|
import Database from "better-sqlite3";
|
|
3882
3969
|
import { resolve as resolve9 } from "path";
|
|
3883
3970
|
import { readdir as readdir5 } from "fs/promises";
|
|
3971
|
+
function initSessionDb(dbPath) {
|
|
3972
|
+
if (db) return db;
|
|
3973
|
+
const finalPath = dbPath ?? resolve9(syntaurRoot(), "syntaur.db");
|
|
3974
|
+
db = new Database(finalPath);
|
|
3975
|
+
db.pragma("journal_mode = WAL");
|
|
3976
|
+
db.exec(SCHEMA_SQL);
|
|
3977
|
+
db.prepare("INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)").run(
|
|
3978
|
+
"schema_version",
|
|
3979
|
+
SCHEMA_VERSION
|
|
3980
|
+
);
|
|
3981
|
+
const database = db;
|
|
3982
|
+
const runMigrations = database.transaction(() => {
|
|
3983
|
+
const vBeforeV2 = database.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get()?.value;
|
|
3984
|
+
if (vBeforeV2 === "1") {
|
|
3985
|
+
database.exec(`
|
|
3986
|
+
CREATE TABLE sessions_v2 (
|
|
3987
|
+
session_id TEXT PRIMARY KEY,
|
|
3988
|
+
project_slug TEXT,
|
|
3989
|
+
assignment_slug TEXT,
|
|
3990
|
+
agent TEXT NOT NULL,
|
|
3991
|
+
started TEXT NOT NULL,
|
|
3992
|
+
ended TEXT,
|
|
3993
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
3994
|
+
path TEXT,
|
|
3995
|
+
description TEXT,
|
|
3996
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3997
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
3998
|
+
);
|
|
3999
|
+
INSERT INTO sessions_v2 SELECT session_id, project_slug, assignment_slug, agent, started, ended, status, path, NULL, created_at, updated_at FROM sessions;
|
|
4000
|
+
DROP TABLE sessions;
|
|
4001
|
+
ALTER TABLE sessions_v2 RENAME TO sessions;
|
|
4002
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
|
|
4003
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
|
|
4004
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
4005
|
+
UPDATE meta SET value = '2' WHERE key = 'schema_version';
|
|
4006
|
+
`);
|
|
4007
|
+
}
|
|
4008
|
+
const vBeforeV3 = database.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get()?.value;
|
|
4009
|
+
if (vBeforeV3 === "2") {
|
|
4010
|
+
const v2Columns = database.prepare("PRAGMA table_info(sessions)").all();
|
|
4011
|
+
const v2ColNames = v2Columns.map((c) => c.name);
|
|
4012
|
+
const hasProject = v2ColNames.includes("project_slug");
|
|
4013
|
+
const hasMission = v2ColNames.includes("mission_slug");
|
|
4014
|
+
const projectSlugExpr = hasProject && hasMission ? "COALESCE(project_slug, mission_slug)" : hasProject ? "project_slug" : hasMission ? "mission_slug" : null;
|
|
4015
|
+
if (!projectSlugExpr) {
|
|
4016
|
+
throw new Error(
|
|
4017
|
+
"sessions table has neither project_slug nor mission_slug; cannot migrate from v2 to v3"
|
|
4018
|
+
);
|
|
4019
|
+
}
|
|
4020
|
+
database.exec(`
|
|
4021
|
+
CREATE TABLE sessions_v3 (
|
|
4022
|
+
session_id TEXT PRIMARY KEY,
|
|
4023
|
+
project_slug TEXT,
|
|
4024
|
+
assignment_slug TEXT,
|
|
4025
|
+
agent TEXT NOT NULL,
|
|
4026
|
+
started TEXT NOT NULL,
|
|
4027
|
+
ended TEXT,
|
|
4028
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
4029
|
+
path TEXT,
|
|
4030
|
+
description TEXT,
|
|
4031
|
+
transcript_path TEXT,
|
|
4032
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4033
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4034
|
+
);
|
|
4035
|
+
INSERT INTO sessions_v3
|
|
4036
|
+
SELECT session_id, ${projectSlugExpr}, assignment_slug, agent, started, ended, status, path, description, NULL, created_at, updated_at
|
|
4037
|
+
FROM sessions;
|
|
4038
|
+
DROP TABLE sessions;
|
|
4039
|
+
ALTER TABLE sessions_v3 RENAME TO sessions;
|
|
4040
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
|
|
4041
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
|
|
4042
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
4043
|
+
UPDATE meta SET value = '3' WHERE key = 'schema_version';
|
|
4044
|
+
`);
|
|
4045
|
+
}
|
|
4046
|
+
const vBeforeV4 = database.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get()?.value;
|
|
4047
|
+
if (vBeforeV4 === "3") {
|
|
4048
|
+
database.exec(`
|
|
4049
|
+
CREATE TABLE sessions_v4 (
|
|
4050
|
+
session_id TEXT PRIMARY KEY,
|
|
4051
|
+
project_slug TEXT,
|
|
4052
|
+
assignment_slug TEXT,
|
|
4053
|
+
agent TEXT NOT NULL,
|
|
4054
|
+
started TEXT NOT NULL,
|
|
4055
|
+
ended TEXT,
|
|
4056
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
4057
|
+
path TEXT,
|
|
4058
|
+
description TEXT,
|
|
4059
|
+
transcript_path TEXT,
|
|
4060
|
+
pid INTEGER,
|
|
4061
|
+
pid_started_at TEXT,
|
|
4062
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4063
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4064
|
+
);
|
|
4065
|
+
INSERT INTO sessions_v4
|
|
4066
|
+
SELECT session_id, project_slug, assignment_slug, agent, started, ended, status, path, description, transcript_path, NULL, NULL, created_at, updated_at
|
|
4067
|
+
FROM sessions;
|
|
4068
|
+
DROP TABLE sessions;
|
|
4069
|
+
ALTER TABLE sessions_v4 RENAME TO sessions;
|
|
4070
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
|
|
4071
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
|
|
4072
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
4073
|
+
UPDATE meta SET value = '4' WHERE key = 'schema_version';
|
|
4074
|
+
`);
|
|
4075
|
+
}
|
|
4076
|
+
const vBeforeV5 = database.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get()?.value;
|
|
4077
|
+
if (vBeforeV5 === "4") {
|
|
4078
|
+
database.exec(`
|
|
4079
|
+
CREATE TABLE sessions_v5 (
|
|
4080
|
+
session_id TEXT PRIMARY KEY,
|
|
4081
|
+
project_slug TEXT,
|
|
4082
|
+
assignment_slug TEXT,
|
|
4083
|
+
agent TEXT NOT NULL,
|
|
4084
|
+
started TEXT NOT NULL,
|
|
4085
|
+
ended TEXT,
|
|
4086
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
4087
|
+
path TEXT,
|
|
4088
|
+
description TEXT,
|
|
4089
|
+
transcript_path TEXT,
|
|
4090
|
+
pid INTEGER,
|
|
4091
|
+
pid_started_at TEXT,
|
|
4092
|
+
original_head_sha TEXT,
|
|
4093
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4094
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4095
|
+
);
|
|
4096
|
+
INSERT INTO sessions_v5
|
|
4097
|
+
SELECT session_id, project_slug, assignment_slug, agent, started, ended, status, path, description, transcript_path, pid, pid_started_at, NULL, created_at, updated_at
|
|
4098
|
+
FROM sessions;
|
|
4099
|
+
DROP TABLE sessions;
|
|
4100
|
+
ALTER TABLE sessions_v5 RENAME TO sessions;
|
|
4101
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
|
|
4102
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
|
|
4103
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
4104
|
+
UPDATE meta SET value = '5' WHERE key = 'schema_version';
|
|
4105
|
+
`);
|
|
4106
|
+
}
|
|
4107
|
+
});
|
|
4108
|
+
runMigrations.exclusive();
|
|
4109
|
+
db.exec(POST_MIGRATION_INDEXES_SQL);
|
|
4110
|
+
return db;
|
|
4111
|
+
}
|
|
4112
|
+
function isSessionDbInitialized() {
|
|
4113
|
+
return db !== null;
|
|
4114
|
+
}
|
|
3884
4115
|
function getSessionDb() {
|
|
3885
4116
|
if (!db) {
|
|
3886
4117
|
throw new Error(
|
|
@@ -3889,17 +4120,129 @@ function getSessionDb() {
|
|
|
3889
4120
|
}
|
|
3890
4121
|
return db;
|
|
3891
4122
|
}
|
|
3892
|
-
|
|
4123
|
+
function closeSessionDb() {
|
|
4124
|
+
if (db) {
|
|
4125
|
+
db.close();
|
|
4126
|
+
db = null;
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
function resetSessionDb() {
|
|
4130
|
+
db = null;
|
|
4131
|
+
}
|
|
4132
|
+
async function migrateFromMarkdown(projectsDir) {
|
|
4133
|
+
const database = getSessionDb();
|
|
4134
|
+
const count = database.prepare("SELECT COUNT(*) as count FROM sessions").get();
|
|
4135
|
+
if (count.count > 0) return 0;
|
|
4136
|
+
if (!await fileExists(projectsDir)) return 0;
|
|
4137
|
+
const entries = await readdir5(projectsDir, { withFileTypes: true });
|
|
4138
|
+
const allSessions = [];
|
|
4139
|
+
for (const entry of entries) {
|
|
4140
|
+
if (!entry.isDirectory()) continue;
|
|
4141
|
+
const projectDir = resolve9(projectsDir, entry.name);
|
|
4142
|
+
const indexPath = resolve9(projectDir, "_index-sessions.md");
|
|
4143
|
+
if (!await fileExists(indexPath)) continue;
|
|
4144
|
+
const sessions = await parseMarkdownSessionsIndex(indexPath, entry.name);
|
|
4145
|
+
allSessions.push(...sessions);
|
|
4146
|
+
}
|
|
4147
|
+
if (allSessions.length === 0) return 0;
|
|
4148
|
+
const insert = database.prepare(`
|
|
4149
|
+
INSERT OR IGNORE INTO sessions (session_id, project_slug, assignment_slug, agent, started, status, path)
|
|
4150
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
4151
|
+
`);
|
|
4152
|
+
const insertAll = database.transaction((sessions) => {
|
|
4153
|
+
for (const s of sessions) {
|
|
4154
|
+
insert.run(s.sessionId, s.projectSlug, s.assignmentSlug, s.agent, s.started, s.status, s.path);
|
|
4155
|
+
}
|
|
4156
|
+
});
|
|
4157
|
+
insertAll(allSessions);
|
|
4158
|
+
console.log(`Migrated ${allSessions.length} sessions from markdown to SQLite.`);
|
|
4159
|
+
return allSessions.length;
|
|
4160
|
+
}
|
|
4161
|
+
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
4162
|
+
const { readFile: readFile12 } = await import("fs/promises");
|
|
4163
|
+
const raw = await readFile12(filePath, "utf-8");
|
|
4164
|
+
const sessions = [];
|
|
4165
|
+
const lines = raw.split("\n");
|
|
4166
|
+
let inTable = false;
|
|
4167
|
+
let headerSeen = false;
|
|
4168
|
+
for (const line of lines) {
|
|
4169
|
+
const trimmed = line.trim();
|
|
4170
|
+
if (!trimmed) continue;
|
|
4171
|
+
if (trimmed.startsWith("| Assignment") || trimmed.startsWith("|Assignment")) {
|
|
4172
|
+
inTable = true;
|
|
4173
|
+
headerSeen = false;
|
|
4174
|
+
continue;
|
|
4175
|
+
}
|
|
4176
|
+
if (inTable && !headerSeen && trimmed.match(/^\|[-\s|]+\|$/)) {
|
|
4177
|
+
headerSeen = true;
|
|
4178
|
+
continue;
|
|
4179
|
+
}
|
|
4180
|
+
if (inTable && headerSeen && trimmed.startsWith("|")) {
|
|
4181
|
+
const cells = trimmed.split("|").slice(1, -1).map((c) => c.trim());
|
|
4182
|
+
if (cells.length >= 6) {
|
|
4183
|
+
sessions.push({
|
|
4184
|
+
assignmentSlug: cells[0],
|
|
4185
|
+
agent: cells[1],
|
|
4186
|
+
sessionId: cells[2],
|
|
4187
|
+
started: cells[3],
|
|
4188
|
+
status: cells[4] || "active",
|
|
4189
|
+
path: cells[5],
|
|
4190
|
+
projectSlug
|
|
4191
|
+
});
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
return sessions;
|
|
4196
|
+
}
|
|
4197
|
+
var db, SCHEMA_VERSION, SCHEMA_SQL, POST_MIGRATION_INDEXES_SQL;
|
|
3893
4198
|
var init_session_db = __esm({
|
|
3894
4199
|
"src/dashboard/session-db.ts"() {
|
|
3895
4200
|
"use strict";
|
|
3896
4201
|
init_paths();
|
|
3897
4202
|
init_fs();
|
|
3898
4203
|
db = null;
|
|
4204
|
+
SCHEMA_VERSION = "5";
|
|
4205
|
+
SCHEMA_SQL = `
|
|
4206
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
4207
|
+
session_id TEXT PRIMARY KEY,
|
|
4208
|
+
project_slug TEXT,
|
|
4209
|
+
assignment_slug TEXT,
|
|
4210
|
+
agent TEXT NOT NULL,
|
|
4211
|
+
started TEXT NOT NULL,
|
|
4212
|
+
ended TEXT,
|
|
4213
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
4214
|
+
path TEXT,
|
|
4215
|
+
description TEXT,
|
|
4216
|
+
transcript_path TEXT,
|
|
4217
|
+
pid INTEGER,
|
|
4218
|
+
pid_started_at TEXT,
|
|
4219
|
+
original_head_sha TEXT,
|
|
4220
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4221
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4222
|
+
);
|
|
4223
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
4224
|
+
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
4225
|
+
`;
|
|
4226
|
+
POST_MIGRATION_INDEXES_SQL = `
|
|
4227
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
|
|
4228
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
|
|
4229
|
+
`;
|
|
3899
4230
|
}
|
|
3900
4231
|
});
|
|
3901
4232
|
|
|
3902
4233
|
// src/dashboard/agent-sessions.ts
|
|
4234
|
+
var agent_sessions_exports = {};
|
|
4235
|
+
__export(agent_sessions_exports, {
|
|
4236
|
+
appendSession: () => appendSession,
|
|
4237
|
+
deleteSessions: () => deleteSessions,
|
|
4238
|
+
getSessionById: () => getSessionById,
|
|
4239
|
+
listAllSessions: () => listAllSessions,
|
|
4240
|
+
listProjectSessions: () => listProjectSessions,
|
|
4241
|
+
listSessionsByAssignment: () => listSessionsByAssignment,
|
|
4242
|
+
parseSessionsIndex: () => parseSessionsIndex,
|
|
4243
|
+
reconcileActiveSessions: () => reconcileActiveSessions,
|
|
4244
|
+
updateSessionStatus: () => updateSessionStatus
|
|
4245
|
+
});
|
|
3903
4246
|
import { readFile as readFile8 } from "fs/promises";
|
|
3904
4247
|
import { resolve as resolve10 } from "path";
|
|
3905
4248
|
function rowToSession(row) {
|
|
@@ -3919,16 +4262,151 @@ function rowToSession(row) {
|
|
|
3919
4262
|
originalHeadSha: row.original_head_sha ?? null
|
|
3920
4263
|
};
|
|
3921
4264
|
}
|
|
4265
|
+
async function parseSessionsIndex(_projectDir, projectSlug) {
|
|
4266
|
+
const db2 = getSessionDb();
|
|
4267
|
+
const rows = db2.prepare("SELECT * FROM sessions WHERE project_slug = ? ORDER BY started DESC").all(projectSlug);
|
|
4268
|
+
return rows.map(rowToSession);
|
|
4269
|
+
}
|
|
4270
|
+
async function appendSession(_projectDir, session, opts) {
|
|
4271
|
+
const db2 = getSessionDb();
|
|
4272
|
+
db2.prepare(`
|
|
4273
|
+
INSERT INTO sessions (session_id, project_slug, assignment_slug, agent, started, status, path, description, transcript_path, pid, pid_started_at, original_head_sha)
|
|
4274
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4275
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
4276
|
+
project_slug = COALESCE(NULLIF(excluded.project_slug, ''), project_slug),
|
|
4277
|
+
assignment_slug = COALESCE(NULLIF(excluded.assignment_slug, ''), assignment_slug),
|
|
4278
|
+
agent = excluded.agent,
|
|
4279
|
+
status = CASE
|
|
4280
|
+
WHEN status = 'completed' THEN status
|
|
4281
|
+
WHEN status = 'stopped' AND NOT (? AND excluded.status = 'active') THEN status
|
|
4282
|
+
ELSE excluded.status
|
|
4283
|
+
END,
|
|
4284
|
+
path = COALESCE(NULLIF(excluded.path, ''), path),
|
|
4285
|
+
description = COALESCE(NULLIF(excluded.description, ''), description),
|
|
4286
|
+
transcript_path = COALESCE(NULLIF(excluded.transcript_path, ''), transcript_path),
|
|
4287
|
+
pid = COALESCE(excluded.pid, pid),
|
|
4288
|
+
pid_started_at = COALESCE(NULLIF(excluded.pid_started_at, ''), pid_started_at),
|
|
4289
|
+
original_head_sha = COALESCE(NULLIF(original_head_sha, ''), NULLIF(excluded.original_head_sha, '')),
|
|
4290
|
+
updated_at = datetime('now')
|
|
4291
|
+
`).run(
|
|
4292
|
+
session.sessionId,
|
|
4293
|
+
session.projectSlug ?? null,
|
|
4294
|
+
session.assignmentSlug ?? null,
|
|
4295
|
+
session.agent,
|
|
4296
|
+
session.started,
|
|
4297
|
+
session.status,
|
|
4298
|
+
session.path,
|
|
4299
|
+
session.description ?? null,
|
|
4300
|
+
session.transcriptPath ?? null,
|
|
4301
|
+
session.pid ?? null,
|
|
4302
|
+
session.pidStartedAt ?? null,
|
|
4303
|
+
session.originalHeadSha ?? null,
|
|
4304
|
+
opts?.reviveStopped ? 1 : 0
|
|
4305
|
+
);
|
|
4306
|
+
}
|
|
4307
|
+
async function updateSessionStatus(_projectDir, sessionId, status, endedAt) {
|
|
4308
|
+
const db2 = getSessionDb();
|
|
4309
|
+
const isTerminal = status === "completed" || status === "stopped";
|
|
4310
|
+
const result = isTerminal ? db2.prepare(
|
|
4311
|
+
"UPDATE sessions SET status = ?, ended = COALESCE(?, datetime('now')), updated_at = datetime('now') WHERE session_id = ?"
|
|
4312
|
+
).run(status, endedAt ?? null, sessionId) : db2.prepare(
|
|
4313
|
+
"UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE session_id = ?"
|
|
4314
|
+
).run(status, sessionId);
|
|
4315
|
+
return result.changes > 0;
|
|
4316
|
+
}
|
|
4317
|
+
async function listAllSessions(_projectsDir) {
|
|
4318
|
+
const db2 = getSessionDb();
|
|
4319
|
+
const rows = db2.prepare("SELECT * FROM sessions ORDER BY started DESC").all();
|
|
4320
|
+
return rows.map(rowToSession);
|
|
4321
|
+
}
|
|
3922
4322
|
function getSessionById(sessionId) {
|
|
3923
4323
|
const db2 = getSessionDb();
|
|
3924
4324
|
const row = db2.prepare("SELECT * FROM sessions WHERE session_id = ? LIMIT 1").get(sessionId);
|
|
3925
4325
|
return row ? rowToSession(row) : null;
|
|
3926
4326
|
}
|
|
4327
|
+
async function listProjectSessions(_projectsDir, projectSlug, assignmentSlug) {
|
|
4328
|
+
const db2 = getSessionDb();
|
|
4329
|
+
if (assignmentSlug) {
|
|
4330
|
+
const rows2 = db2.prepare(
|
|
4331
|
+
"SELECT * FROM sessions WHERE project_slug = ? AND assignment_slug = ? ORDER BY started DESC"
|
|
4332
|
+
).all(projectSlug, assignmentSlug);
|
|
4333
|
+
return rows2.map(rowToSession);
|
|
4334
|
+
}
|
|
4335
|
+
const rows = db2.prepare("SELECT * FROM sessions WHERE project_slug = ? ORDER BY started DESC").all(projectSlug);
|
|
4336
|
+
return rows.map(rowToSession);
|
|
4337
|
+
}
|
|
4338
|
+
async function deleteSessions(sessionIds) {
|
|
4339
|
+
if (sessionIds.length === 0) return 0;
|
|
4340
|
+
const db2 = getSessionDb();
|
|
4341
|
+
const placeholders = sessionIds.map(() => "?").join(", ");
|
|
4342
|
+
const result = db2.prepare(`DELETE FROM sessions WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
|
4343
|
+
return result.changes;
|
|
4344
|
+
}
|
|
4345
|
+
async function readAssignmentStatusFromPath(assignmentMdPath) {
|
|
4346
|
+
if (!await fileExists(assignmentMdPath)) return null;
|
|
4347
|
+
const raw = await readFile8(assignmentMdPath, "utf-8");
|
|
4348
|
+
const match = raw.match(/^status:\s*(.+)$/m);
|
|
4349
|
+
return match ? match[1].trim() : null;
|
|
4350
|
+
}
|
|
4351
|
+
async function readAssignmentStatus(projectDir, assignmentSlug) {
|
|
4352
|
+
return readAssignmentStatusFromPath(
|
|
4353
|
+
resolve10(projectDir, "assignments", assignmentSlug, "assignment.md")
|
|
4354
|
+
);
|
|
4355
|
+
}
|
|
4356
|
+
async function reconcileActiveSessions(projectsDir, assignmentsDir) {
|
|
4357
|
+
const db2 = getSessionDb();
|
|
4358
|
+
const activeSessions = db2.prepare("SELECT * FROM sessions WHERE status = 'active' AND assignment_slug IS NOT NULL").all();
|
|
4359
|
+
if (activeSessions.length === 0) return 0;
|
|
4360
|
+
const assignmentStatuses = /* @__PURE__ */ new Map();
|
|
4361
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4362
|
+
for (const session of activeSessions) {
|
|
4363
|
+
const aslug = session.assignment_slug;
|
|
4364
|
+
if (!aslug) continue;
|
|
4365
|
+
const projectKey = session.project_slug ?? "__standalone__";
|
|
4366
|
+
const key = `${projectKey}/${aslug}`;
|
|
4367
|
+
if (seen.has(key)) continue;
|
|
4368
|
+
seen.add(key);
|
|
4369
|
+
if (session.project_slug) {
|
|
4370
|
+
const status = await readAssignmentStatus(
|
|
4371
|
+
resolve10(projectsDir, session.project_slug),
|
|
4372
|
+
aslug
|
|
4373
|
+
);
|
|
4374
|
+
if (status) assignmentStatuses.set(key, status);
|
|
4375
|
+
} else if (assignmentsDir) {
|
|
4376
|
+
const status = await readAssignmentStatusFromPath(
|
|
4377
|
+
resolve10(assignmentsDir, aslug, "assignment.md")
|
|
4378
|
+
);
|
|
4379
|
+
if (status) assignmentStatuses.set(key, status);
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
let totalUpdated = 0;
|
|
4383
|
+
for (const session of activeSessions) {
|
|
4384
|
+
const projectKey = session.project_slug ?? "__standalone__";
|
|
4385
|
+
const key = `${projectKey}/${session.assignment_slug}`;
|
|
4386
|
+
const assignmentStatus = assignmentStatuses.get(key);
|
|
4387
|
+
if (!assignmentStatus || !DONE_ASSIGNMENT_STATUSES.has(assignmentStatus)) continue;
|
|
4388
|
+
const newStatus = assignmentStatus === "failed" ? "stopped" : "completed";
|
|
4389
|
+
await updateSessionStatus("", session.session_id, newStatus);
|
|
4390
|
+
totalUpdated++;
|
|
4391
|
+
}
|
|
4392
|
+
return totalUpdated;
|
|
4393
|
+
}
|
|
4394
|
+
async function listSessionsByAssignment(projectSlug, assignmentSlug) {
|
|
4395
|
+
const db2 = getSessionDb();
|
|
4396
|
+
const rows = projectSlug === null ? db2.prepare(
|
|
4397
|
+
"SELECT * FROM sessions WHERE assignment_slug = ? AND project_slug IS NULL ORDER BY started DESC"
|
|
4398
|
+
).all(assignmentSlug) : db2.prepare(
|
|
4399
|
+
"SELECT * FROM sessions WHERE project_slug = ? AND assignment_slug = ? ORDER BY started DESC"
|
|
4400
|
+
).all(projectSlug, assignmentSlug);
|
|
4401
|
+
return rows.map(rowToSession);
|
|
4402
|
+
}
|
|
4403
|
+
var DONE_ASSIGNMENT_STATUSES;
|
|
3927
4404
|
var init_agent_sessions = __esm({
|
|
3928
4405
|
"src/dashboard/agent-sessions.ts"() {
|
|
3929
4406
|
"use strict";
|
|
3930
4407
|
init_fs();
|
|
3931
4408
|
init_session_db();
|
|
4409
|
+
DONE_ASSIGNMENT_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "review"]);
|
|
3932
4410
|
}
|
|
3933
4411
|
});
|
|
3934
4412
|
|
|
@@ -4315,7 +4793,8 @@ async function getStatusConfig() {
|
|
|
4315
4793
|
derive: config.statuses.derive ?? null,
|
|
4316
4794
|
facts: config.statuses.facts ?? null,
|
|
4317
4795
|
factDeclarations: accepted,
|
|
4318
|
-
deriveRegistry: buildDeriveRegistry(accepted)
|
|
4796
|
+
deriveRegistry: buildDeriveRegistry(accepted),
|
|
4797
|
+
queryRegistry: buildQueryRegistry(accepted)
|
|
4319
4798
|
};
|
|
4320
4799
|
} else {
|
|
4321
4800
|
const def = buildDefaultStatusConfig();
|
|
@@ -4329,7 +4808,8 @@ async function getStatusConfig() {
|
|
|
4329
4808
|
derive: null,
|
|
4330
4809
|
facts: null,
|
|
4331
4810
|
factDeclarations: [],
|
|
4332
|
-
deriveRegistry: buildDeriveRegistry([])
|
|
4811
|
+
deriveRegistry: buildDeriveRegistry([]),
|
|
4812
|
+
queryRegistry: buildQueryRegistry([])
|
|
4333
4813
|
};
|
|
4334
4814
|
}
|
|
4335
4815
|
return _cachedConfig;
|
|
@@ -5285,12 +5765,6 @@ function parseOpenUrl(input) {
|
|
|
5285
5765
|
let prompt;
|
|
5286
5766
|
if (promptVals.length === 1) {
|
|
5287
5767
|
const value = promptVals[0];
|
|
5288
|
-
if (/[\r\n]/.test(value)) {
|
|
5289
|
-
throw new OpenUrlError(
|
|
5290
|
-
"invalid-prompt",
|
|
5291
|
-
"`prompt` query param must be a single line (no newlines)"
|
|
5292
|
-
);
|
|
5293
|
-
}
|
|
5294
5768
|
if (value.length > MAX_OPEN_PROMPT_LENGTH) {
|
|
5295
5769
|
throw new OpenUrlError(
|
|
5296
5770
|
"invalid-prompt",
|
|
@@ -5656,13 +6130,16 @@ async function resolveSessionPlan(input, terminal) {
|
|
|
5656
6130
|
env: process.env,
|
|
5657
6131
|
agentId: agent.id,
|
|
5658
6132
|
fallbackWarning,
|
|
5659
|
-
shellFallbackWarning
|
|
6133
|
+
shellFallbackWarning,
|
|
6134
|
+
// Resume continues the SAME session id; fork mints a new one in-agent.
|
|
6135
|
+
session: { sessionId: (input.mode ?? "resume") === "resume" ? session.sessionId : null }
|
|
5660
6136
|
};
|
|
5661
6137
|
}
|
|
5662
6138
|
|
|
5663
6139
|
// src/launch/execute.ts
|
|
5664
6140
|
import { spawn as spawn3 } from "child_process";
|
|
5665
|
-
import {
|
|
6141
|
+
import { homedir as homedir5 } from "os";
|
|
6142
|
+
import { basename as basename2, join as join5, resolve as resolve14 } from "path";
|
|
5666
6143
|
|
|
5667
6144
|
// src/utils/terminal-probe.ts
|
|
5668
6145
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -5734,6 +6211,52 @@ function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunn
|
|
|
5734
6211
|
return null;
|
|
5735
6212
|
}
|
|
5736
6213
|
|
|
6214
|
+
// src/launch/execute.ts
|
|
6215
|
+
init_fs();
|
|
6216
|
+
init_config2();
|
|
6217
|
+
|
|
6218
|
+
// src/utils/session-id.ts
|
|
6219
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
6220
|
+
import { mkdirSync, readFileSync, statSync as statSync2, writeFileSync } from "fs";
|
|
6221
|
+
import { homedir as homedir4 } from "os";
|
|
6222
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
6223
|
+
|
|
6224
|
+
// src/utils/process-info.ts
|
|
6225
|
+
import { execFileSync } from "child_process";
|
|
6226
|
+
function captureProcessStartedAt(pid) {
|
|
6227
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
6228
|
+
try {
|
|
6229
|
+
const out = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
6230
|
+
encoding: "utf8",
|
|
6231
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6232
|
+
});
|
|
6233
|
+
const trimmed = out.trim();
|
|
6234
|
+
return trimmed === "" ? null : trimmed;
|
|
6235
|
+
} catch {
|
|
6236
|
+
return null;
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
|
|
6240
|
+
// src/usage/cwd-extractor.ts
|
|
6241
|
+
init_paths();
|
|
6242
|
+
import { open as open2, readdir as readdir8, stat } from "fs/promises";
|
|
6243
|
+
import { join as join3 } from "path";
|
|
6244
|
+
import { homedir as homedir3 } from "os";
|
|
6245
|
+
|
|
6246
|
+
// src/utils/transcript.ts
|
|
6247
|
+
import { open } from "fs/promises";
|
|
6248
|
+
|
|
6249
|
+
// src/usage/cwd-extractor.ts
|
|
6250
|
+
var TAIL_READ_BYTES = 8 * 1024;
|
|
6251
|
+
var TAIL_READ_BYTES_MAX = 64 * 1024;
|
|
6252
|
+
|
|
6253
|
+
// src/utils/session-id.ts
|
|
6254
|
+
function writeRuntimeMarker(pid, marker, dir) {
|
|
6255
|
+
const path = join4(dir, `${pid}.json`);
|
|
6256
|
+
mkdirSync(dirname3(path), { recursive: true });
|
|
6257
|
+
writeFileSync(path, JSON.stringify(marker));
|
|
6258
|
+
}
|
|
6259
|
+
|
|
5737
6260
|
// src/launch/execute.ts
|
|
5738
6261
|
var TerminalNotFoundError = class extends Error {
|
|
5739
6262
|
terminal;
|
|
@@ -5777,7 +6300,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
5777
6300
|
`Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
|
|
5778
6301
|
);
|
|
5779
6302
|
}
|
|
5780
|
-
await new Promise((
|
|
6303
|
+
await new Promise((resolve16, reject) => {
|
|
5781
6304
|
let settled = false;
|
|
5782
6305
|
let stderr = "";
|
|
5783
6306
|
const finishOk = () => {
|
|
@@ -5787,7 +6310,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
5787
6310
|
child.unref();
|
|
5788
6311
|
} catch {
|
|
5789
6312
|
}
|
|
5790
|
-
|
|
6313
|
+
resolve16();
|
|
5791
6314
|
};
|
|
5792
6315
|
const finishErr = (remediation) => {
|
|
5793
6316
|
if (settled) return;
|
|
@@ -5823,6 +6346,57 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
5823
6346
|
});
|
|
5824
6347
|
}
|
|
5825
6348
|
});
|
|
6349
|
+
await registerLaunchAtBirth(plan, child);
|
|
6350
|
+
}
|
|
6351
|
+
async function registerLaunchAtBirth(plan, child) {
|
|
6352
|
+
try {
|
|
6353
|
+
const pid = child.pid;
|
|
6354
|
+
if (!pid) return;
|
|
6355
|
+
const autoTrack = (await readConfig()).session.autoTrack;
|
|
6356
|
+
if (autoTrack === "off") return;
|
|
6357
|
+
const sessionId = plan.session?.sessionId ?? null;
|
|
6358
|
+
const procStart = captureProcessStartedAt(pid);
|
|
6359
|
+
const envDir = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
|
|
6360
|
+
const markerDir = envDir && envDir.length > 0 ? envDir : join5(homedir5(), ".syntaur", "runtime", "sessions");
|
|
6361
|
+
writeRuntimeMarker(
|
|
6362
|
+
pid,
|
|
6363
|
+
{
|
|
6364
|
+
...sessionId ? { sessionId } : {},
|
|
6365
|
+
agent: plan.agentId,
|
|
6366
|
+
cwd: plan.cwd,
|
|
6367
|
+
...procStart ? { procStart } : {},
|
|
6368
|
+
writtenAt: Date.now()
|
|
6369
|
+
},
|
|
6370
|
+
markerDir
|
|
6371
|
+
);
|
|
6372
|
+
if (!sessionId) return;
|
|
6373
|
+
if (autoTrack === "workspaces-only" && !await fileExists(resolve14(plan.cwd, ".syntaur", "context.json"))) {
|
|
6374
|
+
return;
|
|
6375
|
+
}
|
|
6376
|
+
const { initSessionDb: initSessionDb2 } = await Promise.resolve().then(() => (init_session_db(), session_db_exports));
|
|
6377
|
+
const { appendSession: appendSession2 } = await Promise.resolve().then(() => (init_agent_sessions(), agent_sessions_exports));
|
|
6378
|
+
initSessionDb2();
|
|
6379
|
+
await appendSession2(
|
|
6380
|
+
"",
|
|
6381
|
+
{
|
|
6382
|
+
sessionId,
|
|
6383
|
+
projectSlug: null,
|
|
6384
|
+
assignmentSlug: null,
|
|
6385
|
+
agent: plan.agentId,
|
|
6386
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6387
|
+
status: "active",
|
|
6388
|
+
path: plan.cwd,
|
|
6389
|
+
description: null,
|
|
6390
|
+
transcriptPath: null,
|
|
6391
|
+
pid,
|
|
6392
|
+
pidStartedAt: procStart,
|
|
6393
|
+
originalHeadSha: null
|
|
6394
|
+
},
|
|
6395
|
+
// Resuming a stopped session IS live-process evidence — we just spawned it.
|
|
6396
|
+
{ reviveStopped: true }
|
|
6397
|
+
);
|
|
6398
|
+
} catch {
|
|
6399
|
+
}
|
|
5826
6400
|
}
|
|
5827
6401
|
var CMUX_BUNDLE_ID = "com.cmuxterm.app";
|
|
5828
6402
|
var CMUX_READINESS_MAX_MS = 20 * 250;
|
|
@@ -5972,8 +6546,8 @@ function appleScriptString(value) {
|
|
|
5972
6546
|
init_paths();
|
|
5973
6547
|
init_fs();
|
|
5974
6548
|
import { fileURLToPath } from "url";
|
|
5975
|
-
import { dirname as
|
|
5976
|
-
import { realpathSync, readFileSync, mkdirSync } from "fs";
|
|
6549
|
+
import { dirname as dirname4, resolve as resolve15, join as join6 } from "path";
|
|
6550
|
+
import { realpathSync, readFileSync as readFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
5977
6551
|
var NPX_PATTERNS = [
|
|
5978
6552
|
{ kind: "npm", re: /\/_npx\/([^/]+)\/node_modules(?:\/|$)/ },
|
|
5979
6553
|
{ kind: "pnpm", re: /\/pnpm\/dlx\/([^/]+)\/node_modules(?:\/|$)/ },
|
|
@@ -5998,7 +6572,7 @@ function normalizeSlashes(p) {
|
|
|
5998
6572
|
}
|
|
5999
6573
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
6000
6574
|
const realpath = opts.realpath ?? realpathSync.native;
|
|
6001
|
-
const readFile12 = opts.readFile ?? ((p) =>
|
|
6575
|
+
const readFile12 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
|
|
6002
6576
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
6003
6577
|
const resolved = resolveScriptPath(scriptUrl, realpath);
|
|
6004
6578
|
if (resolved === null) {
|
|
@@ -6014,14 +6588,14 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
6014
6588
|
if (GLOBAL_PATTERN.test(norm)) {
|
|
6015
6589
|
return "global";
|
|
6016
6590
|
}
|
|
6017
|
-
let dir =
|
|
6591
|
+
let dir = dirname4(resolved);
|
|
6018
6592
|
for (let depth = 0; depth < 8; depth++) {
|
|
6019
|
-
const pkgJsonPath =
|
|
6593
|
+
const pkgJsonPath = join6(dir, "package.json");
|
|
6020
6594
|
let raw;
|
|
6021
6595
|
try {
|
|
6022
6596
|
raw = readFile12(pkgJsonPath);
|
|
6023
6597
|
} catch {
|
|
6024
|
-
const parent2 =
|
|
6598
|
+
const parent2 = dirname4(dir);
|
|
6025
6599
|
if (parent2 === dir) break;
|
|
6026
6600
|
dir = parent2;
|
|
6027
6601
|
continue;
|
|
@@ -6033,7 +6607,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
6033
6607
|
}
|
|
6034
6608
|
} catch {
|
|
6035
6609
|
}
|
|
6036
|
-
const parent =
|
|
6610
|
+
const parent = dirname4(dir);
|
|
6037
6611
|
if (parent === dir) break;
|
|
6038
6612
|
dir = parent;
|
|
6039
6613
|
}
|
|
@@ -6051,20 +6625,20 @@ function extractNpxHash(scriptUrl, opts = {}) {
|
|
|
6051
6625
|
return null;
|
|
6052
6626
|
}
|
|
6053
6627
|
function nudgeStampDir() {
|
|
6054
|
-
return
|
|
6628
|
+
return resolve15(syntaurRoot(), "npx-handler-nudge");
|
|
6055
6629
|
}
|
|
6056
6630
|
function sanitizeHash(hash) {
|
|
6057
6631
|
return hash.replace(/[^A-Za-z0-9_-]/g, "_") || "_";
|
|
6058
6632
|
}
|
|
6059
6633
|
function nudgeStampPath(hash) {
|
|
6060
|
-
return
|
|
6634
|
+
return join6(nudgeStampDir(), sanitizeHash(hash));
|
|
6061
6635
|
}
|
|
6062
6636
|
async function hasNudgedHash(hash) {
|
|
6063
6637
|
return fileExists(nudgeStampPath(hash));
|
|
6064
6638
|
}
|
|
6065
6639
|
async function recordNudge(hash) {
|
|
6066
6640
|
try {
|
|
6067
|
-
|
|
6641
|
+
mkdirSync2(nudgeStampDir(), { recursive: true });
|
|
6068
6642
|
} catch {
|
|
6069
6643
|
}
|
|
6070
6644
|
try {
|