syntaur 0.64.0 → 0.66.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-CwwFEBj8.js → _basePickBy-CrrGp7iQ.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-BoxAvlar.js → _baseUniq-CR-L9Z4l.js} +1 -1
- package/dashboard/dist/assets/{arc-DohD31yM.js → arc-Bplhanol.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-J4SdvLJs.js → architectureDiagram-2XIMDMQ5-hcuAyqPz.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BHuFRK4q.js → blockDiagram-WCTKOSBZ-BkGOQX1p.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-C5jiXpiB.js → c4Diagram-IC4MRINW-CyhVhe9Z.js} +1 -1
- package/dashboard/dist/assets/channel-DKDXpYJX.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-CcShTIhb.js → chunk-4BX2VUAB-DWT4Kb7h.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-BJDZp3Ih.js → chunk-55IACEB6-DabzdGiV.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-DgX8V3qh.js → chunk-FMBD7UC4-9u9EbTn2.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-DnYWrOFd.js → chunk-JSJVCQXG-qNUjUgoq.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-CvsjbfkS.js → chunk-KX2RTZJC-Iw8btz_i.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-DVLwGTNn.js → chunk-NQ4KR5QH-DuWW63Ax.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-CFNsM5VV.js → chunk-QZHKN3VN-CquL_I3V.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-BgvnUwmv.js → chunk-WL4C6EOR-CmAQtVO_.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-U7OD7LGq.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-U7OD7LGq.js +1 -0
- package/dashboard/dist/assets/clone-CZUxWIoY.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-C4aLmHe0.js → cose-bilkent-S5V4N54A-D_vWs01G.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-BN-9NtEU.js → dagre-KLK3FWXG-DRSd99h1.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-CeYAeYg6.js → diagram-E7M64L7V-CYIlPZb0.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-DwKlGh9_.js → diagram-IFDJBPK2-D1Mk78S1.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-Brl0MDaZ.js → diagram-P4PSJMXO-cK1mot2F.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-DUyLXkUI.js → erDiagram-INFDFZHY-CKAce3P6.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DBgZTa3b.js → flowDiagram-PKNHOUZH-B8UyrAbG.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CTMXhGer.js → ganttDiagram-A5KZAMGK-DFlWro9k.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-CMeLom6N.js → gitGraphDiagram-K3NZZRJ6-CfvsGSZk.js} +1 -1
- package/dashboard/dist/assets/{graph-BQZrNogB.js → graph-DTnszQr1.js} +1 -1
- package/dashboard/dist/assets/{index-tUNHiaW4.js → index-BukhcQ51.js} +127 -127
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-CfiYCGUc.js → infoDiagram-LFFYTUFH-B4CQ1F6_.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-WSdBFOoI.js → ishikawaDiagram-PHBUUO56-BJf9J-fu.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-u-S5-YRq.js → journeyDiagram-4ABVD52K-CDt3Ieap.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-B5sU56Sm.js → kanban-definition-K7BYSVSG-DvenMiPw.js} +1 -1
- package/dashboard/dist/assets/{layout-CH9z9Vzx.js → layout-B_3cS6Wx.js} +1 -1
- package/dashboard/dist/assets/{linear-C-SiDLxL.js → linear-VVB-1jRr.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-CGgij72_.js → mermaid.core-BR4E6mM3.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-yu7UCSjb.js → mindmap-definition-YRQLILUH-BSMVANTC.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-BGSUgFeI.js → pieDiagram-SKSYHLDU-Bc1KLLbU.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-XnJqntVQ.js → quadrantDiagram-337W2JSQ-DCxPDJLj.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CZ5tl3qr.js → requirementDiagram-Z7DCOOCP-CIR7dfaq.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CgcGiKDB.js → sankeyDiagram-WA2Y5GQK-BSmLrby0.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE--GDQSqiF.js → sequenceDiagram-2WXFIKYE-D8SKlaHf.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-sbaNsQ3O.js → stateDiagram-RAJIS63D-P-weHmzQ.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DgvK3I0Y.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DieNq8qp.js → timeline-definition-YZTLITO2-AWMvjwea.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-CND0AGzG.js → treemap-KZPCXAKY-ByTswXsP.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-C32CbTtv.js → vennDiagram-LZ73GAT5-FhcL4LCg.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-I-8Tu4ki.js → xychartDiagram-JWTSCODW-Aql6py8q.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/dashboard/server.js +90 -20
- package/dist/dashboard/server.js.map +1 -1
- package/dist/db/leases-db.d.ts +4 -3
- package/dist/db/leases-db.js +6 -4
- package/dist/db/leases-db.js.map +1 -1
- package/dist/index.js +148 -27
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +20 -7
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- 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/dashboard/dist/assets/channel-3-4_gf6X.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-CrATshpz.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-CrATshpz.js +0 -1
- package/dashboard/dist/assets/clone-CfV5MG52.js +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-Cr-Lm_NG.js +0 -1
package/dist/index.js
CHANGED
|
@@ -516,7 +516,9 @@ function parseDecisionRecord(fileContent) {
|
|
|
516
516
|
function parseComments(fileContent) {
|
|
517
517
|
const [fm, body] = extractFrontmatter(fileContent);
|
|
518
518
|
const entries = [];
|
|
519
|
-
const sections = body.split(
|
|
519
|
+
const sections = body.split(
|
|
520
|
+
/^## (?=[^\n]*\n\s*\*\*Recorded:\*\*[^\n]*\n\*\*Author:\*\*[^\n]*\n\*\*Type:\*\*\s*(?:question|note|feedback)\b)/m
|
|
521
|
+
).slice(1);
|
|
520
522
|
for (const section of sections) {
|
|
521
523
|
const newlineIdx = section.indexOf("\n");
|
|
522
524
|
if (newlineIdx === -1) continue;
|
|
@@ -1198,7 +1200,7 @@ function formatYamlValue(value) {
|
|
|
1198
1200
|
if (/^(null|~|true|false|-?\d+(\.\d+)?)$/i.test(value)) {
|
|
1199
1201
|
return `"${value}"`;
|
|
1200
1202
|
}
|
|
1201
|
-
if (/[:#{}[\],&*?|>!%@\`]/.test(value) || /^\s|\s$/.test(value) || value === "") {
|
|
1203
|
+
if (/[:#{}[\],&*?|>!%@\`]/.test(value) || /^\s|\s$/.test(value) || /^["']|["']$/.test(value) || value === "") {
|
|
1202
1204
|
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1203
1205
|
return `"${escaped}"`;
|
|
1204
1206
|
}
|
|
@@ -1225,7 +1227,7 @@ ${key}: ${formatted}${result.slice(closeIdx)}`;
|
|
|
1225
1227
|
function findWorkspaceBlock(fmBlock) {
|
|
1226
1228
|
const headerMatch = fmBlock.match(/^workspace:\s*$/m);
|
|
1227
1229
|
if (!headerMatch) return null;
|
|
1228
|
-
const headerStart = fmBlock.indexOf(headerMatch[0]);
|
|
1230
|
+
const headerStart = headerMatch.index ?? fmBlock.indexOf(headerMatch[0]);
|
|
1229
1231
|
const bodyStart = headerStart + headerMatch[0].length + 1;
|
|
1230
1232
|
const after = fmBlock.slice(bodyStart);
|
|
1231
1233
|
const lines = after.split("\n");
|
|
@@ -1283,7 +1285,7 @@ function renameStatusInHistory(content, oldId, newId) {
|
|
|
1283
1285
|
if (!fmMatch) return content;
|
|
1284
1286
|
const esc = oldId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1285
1287
|
const re = new RegExp(`^(\\s+(?:from|to|phaseFrom|phaseTo):[ \\t]*)("?)${esc}\\2[ \\t]*$`, "gm");
|
|
1286
|
-
const newFm = fmMatch[2].replace(re, (_m, prefix
|
|
1288
|
+
const newFm = fmMatch[2].replace(re, (_m, prefix) => `${prefix}${formatYamlValue(newId)}`);
|
|
1287
1289
|
return `${fmMatch[1]}${newFm}${fmMatch[3]}${content.slice(fmMatch[0].length)}`;
|
|
1288
1290
|
}
|
|
1289
1291
|
function findStatusHistoryBlock(fmBlock) {
|
|
@@ -1639,12 +1641,14 @@ var parser_exports = {};
|
|
|
1639
1641
|
__export(parser_exports, {
|
|
1640
1642
|
appendLogEntry: () => appendLogEntry,
|
|
1641
1643
|
archivePath: () => archivePath,
|
|
1644
|
+
assertValidTags: () => assertValidTags,
|
|
1642
1645
|
checklistPath: () => checklistPath,
|
|
1643
1646
|
computeCounts: () => computeCounts,
|
|
1644
1647
|
decodeMetaValue: () => decodeMetaValue,
|
|
1645
1648
|
encodeMetaValue: () => encodeMetaValue,
|
|
1646
1649
|
generateShortId: () => generateShortId,
|
|
1647
1650
|
generateUniqueId: () => generateUniqueId,
|
|
1651
|
+
isValidTag: () => isValidTag,
|
|
1648
1652
|
logPath: () => logPath,
|
|
1649
1653
|
parseChecklist: () => parseChecklist,
|
|
1650
1654
|
parseChecklistItem: () => parseChecklistItem,
|
|
@@ -1674,6 +1678,18 @@ function generateUniqueId(existingIds) {
|
|
|
1674
1678
|
}
|
|
1675
1679
|
return id;
|
|
1676
1680
|
}
|
|
1681
|
+
function isValidTag(tag) {
|
|
1682
|
+
return typeof tag === "string" && VALID_TAG_REGEX.test(tag);
|
|
1683
|
+
}
|
|
1684
|
+
function assertValidTags(tags) {
|
|
1685
|
+
for (const t of tags) {
|
|
1686
|
+
if (!isValidTag(t)) {
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
`Invalid tag ${JSON.stringify(t)}: tags may contain only letters, digits, '-' and '_' (no spaces, newlines, or '#').`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1677
1693
|
function encodeMetaValue(value) {
|
|
1678
1694
|
let out = "";
|
|
1679
1695
|
for (const ch of value) {
|
|
@@ -1865,6 +1881,7 @@ function parseChecklistItem(line) {
|
|
|
1865
1881
|
}
|
|
1866
1882
|
function serializeChecklistItem(item) {
|
|
1867
1883
|
const marker = statusToMarker(item);
|
|
1884
|
+
assertValidTags(item.tags);
|
|
1868
1885
|
const tagStr = item.tags.map((t) => `#${t}`).join(" ");
|
|
1869
1886
|
const parts = [`- [${marker}] ${escapeDescription(item.description)}`];
|
|
1870
1887
|
if (tagStr) parts.push(tagStr);
|
|
@@ -2054,7 +2071,7 @@ function computeCounts(items) {
|
|
|
2054
2071
|
}
|
|
2055
2072
|
return counts;
|
|
2056
2073
|
}
|
|
2057
|
-
var ITEM_REGEX, ID_REGEX, TAG_REGEX, META_TOKEN_REGEX, META_ENCODE_CHARS;
|
|
2074
|
+
var ITEM_REGEX, ID_REGEX, TAG_REGEX, META_TOKEN_REGEX, META_ENCODE_CHARS, VALID_TAG_REGEX;
|
|
2058
2075
|
var init_parser2 = __esm({
|
|
2059
2076
|
"src/todos/parser.ts"() {
|
|
2060
2077
|
"use strict";
|
|
@@ -2065,6 +2082,7 @@ var init_parser2 = __esm({
|
|
|
2065
2082
|
TAG_REGEX = /#([a-zA-Z0-9_-]+)/g;
|
|
2066
2083
|
META_TOKEN_REGEX = /\[t:[a-f0-9]{4}\]\s+<([^>]*)>\s*$/;
|
|
2067
2084
|
META_ENCODE_CHARS = ["%", "<", ">", "[", "]", "=", ";", "\n", "\r"];
|
|
2085
|
+
VALID_TAG_REGEX = /^[a-zA-Z0-9_-]+$/;
|
|
2068
2086
|
}
|
|
2069
2087
|
});
|
|
2070
2088
|
|
|
@@ -4072,6 +4090,16 @@ function parseStatusConfig(content) {
|
|
|
4072
4090
|
}
|
|
4073
4091
|
return t;
|
|
4074
4092
|
};
|
|
4093
|
+
const unquoteAql = (v) => {
|
|
4094
|
+
const t = v.trim();
|
|
4095
|
+
if (t.startsWith('"') && t.endsWith('"') && t.length >= 2) {
|
|
4096
|
+
return t.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
4097
|
+
}
|
|
4098
|
+
if (t.startsWith("'") && t.endsWith("'") && t.length >= 2) {
|
|
4099
|
+
return t.slice(1, -1);
|
|
4100
|
+
}
|
|
4101
|
+
return t;
|
|
4102
|
+
};
|
|
4075
4103
|
let currentSection = null;
|
|
4076
4104
|
const lines = remaining.split("\n");
|
|
4077
4105
|
function parseListEntry(lineIdx, baseIndent) {
|
|
@@ -4151,8 +4179,8 @@ function parseStatusConfig(content) {
|
|
|
4151
4179
|
if (entry["phase"] && entry["when"] !== void 0) {
|
|
4152
4180
|
phaseLadder.push({
|
|
4153
4181
|
phase: unquote(entry["phase"]),
|
|
4154
|
-
when:
|
|
4155
|
-
next: entry["next"] !== void 0 ?
|
|
4182
|
+
when: unquoteAql(entry["when"]),
|
|
4183
|
+
next: entry["next"] !== void 0 ? unquoteAql(entry["next"]) : void 0
|
|
4156
4184
|
});
|
|
4157
4185
|
}
|
|
4158
4186
|
lineIdx += consumed - 1;
|
|
@@ -4163,7 +4191,7 @@ function parseStatusConfig(content) {
|
|
|
4163
4191
|
if (entry["else"] !== void 0) {
|
|
4164
4192
|
disposition.push({ when: null, is: unquote(entry["else"]) });
|
|
4165
4193
|
} else if (entry["when"] !== void 0 && entry["is"]) {
|
|
4166
|
-
disposition.push({ when:
|
|
4194
|
+
disposition.push({ when: unquoteAql(entry["when"]), is: unquote(entry["is"]) });
|
|
4167
4195
|
}
|
|
4168
4196
|
lineIdx += consumed - 1;
|
|
4169
4197
|
continue;
|
|
@@ -4227,6 +4255,7 @@ function buildDefaultStatusConfig() {
|
|
|
4227
4255
|
}
|
|
4228
4256
|
function serializeStatusConfig(statuses) {
|
|
4229
4257
|
const lines = [];
|
|
4258
|
+
const escapeAql = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4230
4259
|
lines.push("statuses:");
|
|
4231
4260
|
lines.push(" definitions:");
|
|
4232
4261
|
for (const s of statuses.statuses) {
|
|
@@ -4267,15 +4296,15 @@ function serializeStatusConfig(statuses) {
|
|
|
4267
4296
|
lines.push(" phaseLadder:");
|
|
4268
4297
|
for (const rung of d.phaseLadder) {
|
|
4269
4298
|
lines.push(` - phase: ${rung.phase}`);
|
|
4270
|
-
lines.push(` when: "${rung.when
|
|
4271
|
-
if (rung.next) lines.push(` next: "${rung.next
|
|
4299
|
+
lines.push(` when: "${escapeAql(rung.when)}"`);
|
|
4300
|
+
if (rung.next !== void 0) lines.push(` next: "${escapeAql(rung.next)}"`);
|
|
4272
4301
|
}
|
|
4273
4302
|
lines.push(" disposition:");
|
|
4274
4303
|
for (const rule of d.disposition) {
|
|
4275
4304
|
if (rule.when === null) {
|
|
4276
4305
|
lines.push(` - else: ${rule.is}`);
|
|
4277
4306
|
} else {
|
|
4278
|
-
lines.push(` - when: "${rule.when
|
|
4307
|
+
lines.push(` - when: "${escapeAql(rule.when)}"`);
|
|
4279
4308
|
lines.push(` is: ${rule.is}`);
|
|
4280
4309
|
}
|
|
4281
4310
|
}
|
|
@@ -17365,6 +17394,14 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
|
17365
17394
|
res.status(400).json({ error: "body is required" });
|
|
17366
17395
|
return;
|
|
17367
17396
|
}
|
|
17397
|
+
if (typeof author === "string" && /[\r\n]/.test(author)) {
|
|
17398
|
+
res.status(400).json({ error: "author must not contain newlines" });
|
|
17399
|
+
return;
|
|
17400
|
+
}
|
|
17401
|
+
if (typeof replyTo === "string" && /[\r\n]/.test(replyTo)) {
|
|
17402
|
+
res.status(400).json({ error: "replyTo must not contain newlines" });
|
|
17403
|
+
return;
|
|
17404
|
+
}
|
|
17368
17405
|
const commentType = type && ["question", "note", "feedback"].includes(type) ? type : "note";
|
|
17369
17406
|
const timestamp = nowTimestamp();
|
|
17370
17407
|
const entryAuthor = typeof author === "string" && author.trim() ? author.trim() : "human";
|
|
@@ -18974,6 +19011,14 @@ async function appendCommentTo(assignmentDir, assignmentRef, req2, res, reloadDe
|
|
|
18974
19011
|
}
|
|
18975
19012
|
const commentType = type && ["question", "note", "feedback"].includes(type) ? type : "note";
|
|
18976
19013
|
const timestamp = nowTimestamp();
|
|
19014
|
+
if (typeof author === "string" && /[\r\n]/.test(author)) {
|
|
19015
|
+
res.status(400).json({ error: "author must not contain newlines" });
|
|
19016
|
+
return;
|
|
19017
|
+
}
|
|
19018
|
+
if (typeof replyTo === "string" && /[\r\n]/.test(replyTo)) {
|
|
19019
|
+
res.status(400).json({ error: "replyTo must not contain newlines" });
|
|
19020
|
+
return;
|
|
19021
|
+
}
|
|
18977
19022
|
const entryAuthor = typeof author === "string" && author.trim() ? author.trim() : "human";
|
|
18978
19023
|
let currentContent;
|
|
18979
19024
|
let currentCount = 0;
|
|
@@ -22111,10 +22156,12 @@ function getLeaseEvents(lease_id, limit = 50) {
|
|
|
22111
22156
|
const database = getLeasesDb();
|
|
22112
22157
|
if (lease_id) {
|
|
22113
22158
|
return database.prepare(
|
|
22114
|
-
`SELECT id, lease_id, event, at, detail_json
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22159
|
+
`SELECT id, lease_id, event, at, detail_json FROM (
|
|
22160
|
+
SELECT id, lease_id, event, at, detail_json
|
|
22161
|
+
FROM lease_events WHERE lease_id = ?
|
|
22162
|
+
ORDER BY at DESC, id DESC
|
|
22163
|
+
LIMIT ?
|
|
22164
|
+
) ORDER BY at ASC, id ASC`
|
|
22118
22165
|
).all(lease_id, limit);
|
|
22119
22166
|
}
|
|
22120
22167
|
return database.prepare(
|
|
@@ -22619,19 +22666,18 @@ function evaluateKind(trigger, job, ctx) {
|
|
|
22619
22666
|
}
|
|
22620
22667
|
}
|
|
22621
22668
|
function evaluateCron(trigger, now, createdAtMs) {
|
|
22622
|
-
let cron;
|
|
22623
22669
|
try {
|
|
22624
|
-
cron = new Cron(trigger.expr, trigger.tz ? { timezone: trigger.tz } : {});
|
|
22670
|
+
const cron = new Cron(trigger.expr, trigger.tz ? { timezone: trigger.tz } : {});
|
|
22671
|
+
const prevs = cron.previousRuns(1, new Date(now.getTime() + 1e3));
|
|
22672
|
+
let prev = prevs.length > 0 ? prevs[0] : null;
|
|
22673
|
+
if (prev && prev.getTime() > now.getTime()) prev = null;
|
|
22674
|
+
if (prev && !Number.isNaN(createdAtMs) && prev.getTime() < createdAtMs) prev = null;
|
|
22675
|
+
const next = cron.nextRun(now);
|
|
22676
|
+
if (!prev) return { due: false, nextFireIso: next ? iso(next) : null };
|
|
22677
|
+
return { due: true, dedupeKey: `cron:${iso(prev)}`, nextFireIso: next ? iso(next) : null };
|
|
22625
22678
|
} catch {
|
|
22626
22679
|
return notDue;
|
|
22627
22680
|
}
|
|
22628
|
-
const prevs = cron.previousRuns(1, new Date(now.getTime() + 1e3));
|
|
22629
|
-
let prev = prevs.length > 0 ? prevs[0] : null;
|
|
22630
|
-
if (prev && prev.getTime() > now.getTime()) prev = null;
|
|
22631
|
-
if (prev && !Number.isNaN(createdAtMs) && prev.getTime() < createdAtMs) prev = null;
|
|
22632
|
-
const next = cron.nextRun(now);
|
|
22633
|
-
if (!prev) return { due: false, nextFireIso: next ? iso(next) : null };
|
|
22634
|
-
return { due: true, dedupeKey: `cron:${iso(prev)}`, nextFireIso: next ? iso(next) : null };
|
|
22635
22681
|
}
|
|
22636
22682
|
function evaluateAfterReset(trigger, now) {
|
|
22637
22683
|
const v = verifyReset(trigger.provider, trigger.anchor, now);
|
|
@@ -24951,6 +24997,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
24951
24997
|
res.status(400).json({ error: "description is required" });
|
|
24952
24998
|
return;
|
|
24953
24999
|
}
|
|
25000
|
+
if (tags !== void 0 && (!Array.isArray(tags) || !tags.every(isValidTag))) {
|
|
25001
|
+
res.status(400).json({ error: "tags must be an array of [a-zA-Z0-9_-] strings (no spaces, newlines, or '#')" });
|
|
25002
|
+
return;
|
|
25003
|
+
}
|
|
24954
25004
|
const item = await wsLock(workspace, async () => {
|
|
24955
25005
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
24956
25006
|
const existingIds = new Set(checklist.items.map((i) => i.id));
|
|
@@ -25057,6 +25107,7 @@ workspace: ${workspace}
|
|
|
25057
25107
|
}
|
|
25058
25108
|
const completedItems = checklist.items.filter((i) => completedIds.has(i.id));
|
|
25059
25109
|
for (const item of completedItems) {
|
|
25110
|
+
assertValidTags(item.tags);
|
|
25060
25111
|
archContent += `- [x] ${item.description} ${item.tags.map((t) => `#${t}`).join(" ")} [t:${item.id}]
|
|
25061
25112
|
`;
|
|
25062
25113
|
}
|
|
@@ -25122,6 +25173,14 @@ workspace: ${workspace}
|
|
|
25122
25173
|
router.patch("/:workspace/:id", async (req2, res) => {
|
|
25123
25174
|
try {
|
|
25124
25175
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
25176
|
+
if (req2.body.tags !== void 0 && (!Array.isArray(req2.body.tags) || !req2.body.tags.every(isValidTag))) {
|
|
25177
|
+
res.status(400).json({ error: "tags must be an array of [a-zA-Z0-9_-] strings (no spaces, newlines, or '#')" });
|
|
25178
|
+
return;
|
|
25179
|
+
}
|
|
25180
|
+
if (req2.body.description !== void 0 && typeof req2.body.description !== "string") {
|
|
25181
|
+
res.status(400).json({ error: "description must be a string" });
|
|
25182
|
+
return;
|
|
25183
|
+
}
|
|
25125
25184
|
const result = await wsLock(workspace, async () => {
|
|
25126
25185
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
25127
25186
|
const item = checklist.items.find((i) => i.id === req2.params.id);
|
|
@@ -25660,6 +25719,10 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
|
|
|
25660
25719
|
res.status(400).json({ error: "description is required" });
|
|
25661
25720
|
return;
|
|
25662
25721
|
}
|
|
25722
|
+
if (tags !== void 0 && (!Array.isArray(tags) || !tags.every(isValidTag))) {
|
|
25723
|
+
res.status(400).json({ error: "tags must be an array of [a-zA-Z0-9_-] strings (no spaces, newlines, or '#')" });
|
|
25724
|
+
return;
|
|
25725
|
+
}
|
|
25663
25726
|
if (!await projectExists(projectsDir2, slug)) {
|
|
25664
25727
|
notFound(res, slug);
|
|
25665
25728
|
return;
|
|
@@ -25804,6 +25867,7 @@ workspace: ${slug}
|
|
|
25804
25867
|
}
|
|
25805
25868
|
const completedItems = checklist.items.filter((i) => completedIds.has(i.id));
|
|
25806
25869
|
for (const item of completedItems) {
|
|
25870
|
+
assertValidTags(item.tags);
|
|
25807
25871
|
archContent += `- [x] ${item.description} ${item.tags.map((t) => `#${t}`).join(" ")} [t:${item.id}]
|
|
25808
25872
|
`;
|
|
25809
25873
|
}
|
|
@@ -25889,6 +25953,14 @@ workspace: ${slug}
|
|
|
25889
25953
|
router.patch("/:id", async (req2, res) => {
|
|
25890
25954
|
try {
|
|
25891
25955
|
const slug = getProjectIdParam(params(req2).projectId);
|
|
25956
|
+
if (req2.body.tags !== void 0 && (!Array.isArray(req2.body.tags) || !req2.body.tags.every(isValidTag))) {
|
|
25957
|
+
res.status(400).json({ error: "tags must be an array of [a-zA-Z0-9_-] strings (no spaces, newlines, or '#')" });
|
|
25958
|
+
return;
|
|
25959
|
+
}
|
|
25960
|
+
if (req2.body.description !== void 0 && typeof req2.body.description !== "string") {
|
|
25961
|
+
res.status(400).json({ error: "description must be a string" });
|
|
25962
|
+
return;
|
|
25963
|
+
}
|
|
25892
25964
|
if (!await projectExists(projectsDir2, slug)) {
|
|
25893
25965
|
notFound(res, slug);
|
|
25894
25966
|
return;
|
|
@@ -34014,6 +34086,11 @@ todoCommand.command("add").description("Add a new todo item").argument("<descrip
|
|
|
34014
34086
|
const existingIds = new Set(checklist.items.map((i) => i.id));
|
|
34015
34087
|
const id = generateUniqueId(existingIds);
|
|
34016
34088
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
34089
|
+
const badTag = tags.find((t) => !isValidTag(t));
|
|
34090
|
+
if (badTag !== void 0) {
|
|
34091
|
+
console.error(`Invalid tag ${JSON.stringify(badTag)}: tags may contain only letters, digits, '-' and '_' (no spaces, newlines, or '#').`);
|
|
34092
|
+
process.exit(1);
|
|
34093
|
+
}
|
|
34017
34094
|
const now = nowISO2();
|
|
34018
34095
|
const item = {
|
|
34019
34096
|
id,
|
|
@@ -34247,6 +34324,11 @@ todoCommand.command("tag").description("Modify tags on a todo").argument("<id>",
|
|
|
34247
34324
|
}
|
|
34248
34325
|
if (options.add) {
|
|
34249
34326
|
const toAdd = options.add.split(",").map((t) => t.trim());
|
|
34327
|
+
const badTag = toAdd.find((t) => !isValidTag(t));
|
|
34328
|
+
if (badTag !== void 0) {
|
|
34329
|
+
console.error(`Invalid tag ${JSON.stringify(badTag)}: tags may contain only letters, digits, '-' and '_' (no spaces, newlines, or '#').`);
|
|
34330
|
+
process.exit(1);
|
|
34331
|
+
}
|
|
34250
34332
|
for (const tag of toAdd) {
|
|
34251
34333
|
if (!item.tags.includes(tag)) item.tags.push(tag);
|
|
34252
34334
|
}
|
|
@@ -34325,6 +34407,7 @@ workspace: ${workspace}
|
|
|
34325
34407
|
}
|
|
34326
34408
|
const completedItems = checklist.items.filter((i) => completedIds.has(i.id));
|
|
34327
34409
|
for (const item of completedItems) {
|
|
34410
|
+
assertValidTags(item.tags);
|
|
34328
34411
|
archContent += `- [x] ${item.description} ${item.tags.map((t) => `#${t}`).join(" ")} [t:${item.id}]
|
|
34329
34412
|
`;
|
|
34330
34413
|
}
|
|
@@ -34617,6 +34700,11 @@ async function moveTodo(id, options) {
|
|
|
34617
34700
|
throw new Error(`Todo [t:${id}] not found in scope ${describeScope(sourceScope)}.`);
|
|
34618
34701
|
}
|
|
34619
34702
|
const item = sourceChecklist.items[idx];
|
|
34703
|
+
if (item.bundleId !== null) {
|
|
34704
|
+
throw new Error(
|
|
34705
|
+
`Todo [t:${id}] is part of bundle b:${item.bundleId}; run \`syntaur todo bundle remove b:${item.bundleId} ${id}\` first.`
|
|
34706
|
+
);
|
|
34707
|
+
}
|
|
34620
34708
|
if (targetChecklist.items.some((i) => i.id === id)) {
|
|
34621
34709
|
throw new Error(`Todo id [t:${id}] already exists in target scope ${describeScope(targetScope)}; refusing to move (collision).`);
|
|
34622
34710
|
}
|
|
@@ -39752,7 +39840,14 @@ function parseDuration(input4, fallbackSeconds) {
|
|
|
39752
39840
|
const n = Number.parseInt(match[1], 10);
|
|
39753
39841
|
const unit = (match[2] ?? "s").toLowerCase();
|
|
39754
39842
|
const mult = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
39755
|
-
|
|
39843
|
+
const seconds = n * mult[unit];
|
|
39844
|
+
const MAX_DURATION_SECONDS = 100 * 365 * 86400;
|
|
39845
|
+
if (seconds > MAX_DURATION_SECONDS) {
|
|
39846
|
+
throw new Error(
|
|
39847
|
+
`invalid duration "${input4}" \u2014 too large (max ${MAX_DURATION_SECONDS}s \u2248 100 years)`
|
|
39848
|
+
);
|
|
39849
|
+
}
|
|
39850
|
+
return seconds;
|
|
39756
39851
|
}
|
|
39757
39852
|
function parseMetadataFlags(values) {
|
|
39758
39853
|
if (!values || values.length === 0) return void 0;
|
|
@@ -39842,6 +39937,11 @@ leaseCommand.command("claim").description("Claim an idle member of an inventory.
|
|
|
39842
39937
|
return;
|
|
39843
39938
|
}
|
|
39844
39939
|
const ttl_s = parseDuration(opts.ttl, detail.inventory.default_ttl_s);
|
|
39940
|
+
if (ttl_s <= 0) {
|
|
39941
|
+
console.error("Error: --ttl must be positive");
|
|
39942
|
+
process.exit(1);
|
|
39943
|
+
return;
|
|
39944
|
+
}
|
|
39845
39945
|
const waitBudgetMs = opts.wait !== void 0 ? parseDuration(opts.wait, 0) * 1e3 : 0;
|
|
39846
39946
|
const tryClaim = () => claimLease(inventory, ttl_s, opts.for);
|
|
39847
39947
|
let result;
|
|
@@ -40213,6 +40313,7 @@ memberCommand.command("list").description("List all members of an inventory (pur
|
|
|
40213
40313
|
// src/commands/schedule.ts
|
|
40214
40314
|
init_config2();
|
|
40215
40315
|
import { Command as Command8 } from "commander";
|
|
40316
|
+
import { Cron as Cron2 } from "croner";
|
|
40216
40317
|
init_agent_sessions();
|
|
40217
40318
|
init_session_db();
|
|
40218
40319
|
init_terminal_schema();
|
|
@@ -40388,7 +40489,21 @@ function buildTrigger(opts) {
|
|
|
40388
40489
|
const chosen = [];
|
|
40389
40490
|
if (opts.at) chosen.push({ kind: "at", at: opts.at });
|
|
40390
40491
|
if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs(opts.in), anchorIso: nowTimestamp() });
|
|
40391
|
-
if (opts.cron)
|
|
40492
|
+
if (opts.cron) {
|
|
40493
|
+
try {
|
|
40494
|
+
new Cron2(opts.cron).nextRun();
|
|
40495
|
+
} catch {
|
|
40496
|
+
throw new Error(`invalid --cron expression: ${JSON.stringify(opts.cron)}`);
|
|
40497
|
+
}
|
|
40498
|
+
if (opts.tz) {
|
|
40499
|
+
try {
|
|
40500
|
+
new Cron2(opts.cron, { timezone: opts.tz }).nextRun();
|
|
40501
|
+
} catch {
|
|
40502
|
+
throw new Error(`invalid --tz timezone: ${JSON.stringify(opts.tz)}`);
|
|
40503
|
+
}
|
|
40504
|
+
}
|
|
40505
|
+
chosen.push({ kind: "cron", expr: opts.cron, ...opts.tz ? { tz: opts.tz } : {} });
|
|
40506
|
+
}
|
|
40392
40507
|
if (opts.afterReset) {
|
|
40393
40508
|
const provider = opts.afterReset;
|
|
40394
40509
|
if (provider !== "claude" && provider !== "codex") {
|
|
@@ -40442,7 +40557,13 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40442
40557
|
if (unattended) assertUnattendedTerminalSupported(terminal);
|
|
40443
40558
|
const limits = defaultLimits();
|
|
40444
40559
|
if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs(opts.maxRuntime);
|
|
40445
|
-
if (opts.maxLaunchesPerDay)
|
|
40560
|
+
if (opts.maxLaunchesPerDay) {
|
|
40561
|
+
const n = Number(opts.maxLaunchesPerDay);
|
|
40562
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
40563
|
+
throw new Error("--max-launches-per-day must be a positive integer");
|
|
40564
|
+
}
|
|
40565
|
+
limits.maxLaunchesPerDay = n;
|
|
40566
|
+
}
|
|
40446
40567
|
if (opts.cooldown) limits.cooldownMs = parseDurationMs(opts.cooldown);
|
|
40447
40568
|
const now = nowTimestamp();
|
|
40448
40569
|
const job = {
|