wherdr 1.3.2 → 1.3.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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
- package/.output/public/_nuxt/B4Yk2tB1.js +23 -0
- package/.output/public/_nuxt/BBw_0a4M.js +6 -0
- package/.output/public/_nuxt/{D7sxNXx1.js → BSy6OCIY.js} +1 -1
- package/.output/public/_nuxt/BX5PM2JQ.js +1 -0
- package/.output/public/_nuxt/BgPDnf2S.js +1 -0
- package/.output/public/_nuxt/BkOxQzbS.js +2 -0
- package/.output/public/_nuxt/{8ksjcuDu.js → CKhgznq4.js} +45 -56
- package/.output/public/_nuxt/{Cd8j9O5w.js → CP8o925U.js} +1 -1
- package/.output/public/_nuxt/CkbmecwS.js +1 -0
- package/.output/public/_nuxt/Cr3SrGky.js +1 -0
- package/.output/public/_nuxt/DAg9fiVh.js +89 -0
- package/.output/public/_nuxt/DT8qZ0Wv.js +1 -0
- package/.output/public/_nuxt/DaaJIRB3.js +1 -0
- package/.output/public/_nuxt/{DViwOclv.js → DcJi16XA.js} +1 -1
- package/.output/public/_nuxt/VpjCgSXu.js +36 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +1 -0
- package/.output/public/_nuxt/dPxB2L94.js +89 -0
- package/.output/public/_nuxt/{entry.Crt1r4l3.css → entry.CGWKmYU3.css} +1 -1
- package/.output/public/_nuxt/xYX85SPI.js +1 -0
- package/.output/public/sw.js +1 -1
- package/.output/server/chunks/_/branding.mjs +1 -1
- package/.output/server/chunks/_/filePaths.mjs +25 -0
- package/.output/server/chunks/_/files.mjs +204 -0
- package/.output/server/chunks/_/machineOrder.mjs +1 -1
- package/.output/server/chunks/_/onboarding.mjs +1 -1
- package/.output/server/chunks/_/package.mjs +3 -2
- package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
- package/.output/server/chunks/_/plugins.mjs +4 -94
- package/.output/server/chunks/_/projectBoard.mjs +120 -29
- package/.output/server/chunks/_/projectDocs.mjs +106 -0
- package/.output/server/chunks/_/projectInbox.mjs +135 -0
- package/.output/server/chunks/_/projectThreads.mjs +46 -0
- package/.output/server/chunks/_/projectThreads2.mjs +61 -0
- package/.output/server/chunks/_/projectsActions.mjs +1 -1
- package/.output/server/chunks/_/projectsPlugin.mjs +1 -1
- package/.output/server/chunks/_/restart.mjs +2 -312
- package/.output/server/chunks/_/reveal.mjs +2 -24
- package/.output/server/chunks/_/selfupdate.mjs +3 -2
- package/.output/server/chunks/_/updates.mjs +51 -10
- package/.output/server/chunks/_/worktrees.mjs +1 -1
- package/.output/server/chunks/_/ws.mjs +1 -1
- package/.output/server/chunks/nitro/nitro.mjs +1096 -268
- package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
- package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
- package/.output/server/chunks/raw/release-ready.mjs +5 -0
- package/.output/server/chunks/routes/api/_..._.mjs +1 -1
- package/.output/server/chunks/routes/api/agents.post.mjs +1 -1
- package/.output/server/chunks/routes/api/ask-tab.post.mjs +1 -1
- package/.output/server/chunks/routes/api/attach.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/lock.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/login/options.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/max-session.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/register/options.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +1 -1
- package/.output/server/chunks/routes/api/auth/status.get.mjs +1 -1
- package/.output/server/chunks/routes/api/board.get.mjs +147 -0
- package/.output/server/chunks/routes/api/changes.get.mjs +2 -2
- package/.output/server/chunks/routes/api/chat/image.get.mjs +2 -2
- package/.output/server/chunks/routes/api/chat.get.mjs +2 -2
- package/.output/server/chunks/routes/api/choose.post.mjs +29 -3
- package/.output/server/chunks/routes/api/claude-statusline.post.mjs +2 -2
- package/.output/server/chunks/routes/api/close/status.get.mjs +2 -2
- package/.output/server/chunks/routes/api/close.post.mjs +1 -1
- package/.output/server/chunks/routes/api/codex-update.post.mjs +2 -2
- package/.output/server/chunks/routes/api/commands.get.mjs +2 -2
- package/.output/server/chunks/routes/api/config.get.mjs +3 -2
- package/.output/server/chunks/routes/api/dirs.get.mjs +2 -2
- package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
- package/.output/server/chunks/routes/api/dismiss.post.mjs +2 -2
- package/.output/server/chunks/routes/api/effort.post.mjs +2 -2
- package/.output/server/chunks/routes/api/efforts.get.mjs +2 -2
- package/.output/server/chunks/routes/api/file.post.mjs +2 -2
- package/.output/server/chunks/routes/api/files/download.get.mjs +53 -0
- package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
- package/.output/server/chunks/routes/api/files.get.mjs +44 -0
- package/.output/server/chunks/routes/api/gitroot.get.mjs +2 -2
- package/.output/server/chunks/routes/api/health.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/input.post.mjs +13 -3
- package/.output/server/chunks/routes/api/interrupt.post.mjs +2 -2
- package/.output/server/chunks/routes/api/isgit.get.mjs +2 -2
- package/.output/server/chunks/routes/api/machine/awake.get.mjs +2 -2
- package/.output/server/chunks/routes/api/machine/awake.post.mjs +2 -2
- package/.output/server/chunks/routes/api/machine/order.get.mjs +1 -1
- package/.output/server/chunks/routes/api/machine/order.post.mjs +1 -1
- package/.output/server/chunks/routes/api/machine/rename.post.mjs +2 -2
- package/.output/server/chunks/routes/api/menu.post.mjs +2 -2
- package/.output/server/chunks/routes/api/model.post.mjs +2 -2
- package/.output/server/chunks/routes/api/models.get.mjs +2 -2
- package/.output/server/chunks/routes/api/nav.post.mjs +5 -4
- package/.output/server/chunks/routes/api/onboarding.get.mjs +1 -1
- package/.output/server/chunks/routes/api/onboarding.post.mjs +1 -1
- package/.output/server/chunks/routes/api/phone.get.mjs +2 -2
- package/.output/server/chunks/routes/api/phone.post.mjs +2 -2
- package/.output/server/chunks/routes/api/plugins/actions.get.mjs +2 -1
- package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +2 -1
- package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +2 -1
- package/.output/server/chunks/routes/api/plugins/projects.get.mjs +2 -2
- package/.output/server/chunks/routes/api/plugins/projects.post.mjs +2 -2
- package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +2 -2
- package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +2 -2
- package/.output/server/chunks/routes/api/project/inbox.get.mjs +43 -0
- package/.output/server/chunks/routes/api/project/inbox.post.mjs +43 -0
- package/.output/server/chunks/routes/api/project/memory.get.mjs +42 -0
- package/.output/server/chunks/routes/api/project/report.get.mjs +2 -2
- package/.output/server/chunks/routes/api/project/routines.get.mjs +43 -0
- package/.output/server/chunks/routes/api/project/settings.get.mjs +41 -0
- package/.output/server/chunks/routes/api/project/thread.get.mjs +44 -0
- package/.output/server/chunks/routes/api/project/thread.post.mjs +45 -0
- package/.output/server/chunks/routes/api/prompt.post.mjs +21 -3
- package/.output/server/chunks/routes/api/push/quiet.post.mjs +2 -2
- package/.output/server/chunks/routes/api/push/subscribe.post.mjs +2 -2
- package/.output/server/chunks/routes/api/push/test.post.mjs +2 -2
- package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +2 -2
- package/.output/server/chunks/routes/api/quotas.get.mjs +2 -2
- package/.output/server/chunks/routes/api/rename.post.mjs +1 -1
- package/.output/server/chunks/routes/api/requeue.post.mjs +2 -2
- package/.output/server/chunks/routes/api/restart.get.mjs +1 -1
- package/.output/server/chunks/routes/api/restart.post.mjs +1 -1
- package/.output/server/chunks/routes/api/reveal.post.mjs +2 -1
- package/.output/server/chunks/routes/api/screen-key.post.mjs +2 -2
- package/.output/server/chunks/routes/api/screen.get.mjs +5 -3
- package/.output/server/chunks/routes/api/search.get.mjs +2 -2
- package/.output/server/chunks/routes/api/seen.post.mjs +2 -2
- package/.output/server/chunks/routes/api/sessions.get.mjs +2 -2
- package/.output/server/chunks/routes/api/sessions.post.mjs +2 -2
- package/.output/server/chunks/routes/api/space.post.mjs +14 -14
- package/.output/server/chunks/routes/api/state.get.mjs +2 -2
- package/.output/server/chunks/routes/api/unqueue.post.mjs +2 -2
- package/.output/server/chunks/routes/api/update/status.get.mjs +1 -1
- package/.output/server/chunks/routes/api/update.get.mjs +2 -2
- package/.output/server/chunks/routes/api/update.post.mjs +2 -2
- package/.output/server/chunks/routes/api/upload.post.mjs +2 -2
- package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +1 -1
- package/.output/server/chunks/routes/api/worktrees.get.mjs +1 -1
- package/.output/server/chunks/routes/favicon.ico.get.mjs +1 -1
- package/.output/server/chunks/routes/icons/_name_.get.mjs +2 -2
- package/.output/server/chunks/routes/renderer.mjs +3 -3
- package/.output/server/chunks/routes/uploads/_...name_.get.mjs +2 -2
- package/.output/server/chunks/routes/ws/_..._.mjs +2 -2
- package/.output/server/chunks/routes/ws/events.mjs +2 -2
- package/.output/server/chunks/routes/ws/term.mjs +2 -2
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/index.mjs +2 -2
- package/.output/server/package.json +1 -1
- package/README.md +332 -25
- package/bin/install-docker-skill.sh +20 -0
- package/bin/lib/commands.mjs +5 -1
- package/bin/lib/core.mjs +1 -0
- package/bin/lib/install-skill.sh +100 -0
- package/bin/lib/skill.mjs +16 -0
- package/bin/lib/updater.mjs +65 -12
- package/bin/lib/verify-release.mjs +20 -0
- package/package.json +3 -2
- package/skills/wherdr/SKILL.md +180 -0
- package/.output/public/_nuxt/B7QP1TFs.js +0 -23
- package/.output/public/_nuxt/BDNMzG2s.js +0 -1
- package/.output/public/_nuxt/BOpBIziZ.js +0 -6
- package/.output/public/_nuxt/BWXdFfeg.js +0 -17
- package/.output/public/_nuxt/COFCNFpG.js +0 -83
- package/.output/public/_nuxt/CXG-cOdh.js +0 -1
- package/.output/public/_nuxt/CkZ8t6uB.js +0 -1
- package/.output/public/_nuxt/D4Yz4rat.js +0 -65
- package/.output/public/_nuxt/DoD7Qb5P.js +0 -30
- package/.output/public/_nuxt/DyBZDSCV.js +0 -2
- package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
- package/.output/public/_nuxt/builds/meta/7afbf062-5369-4002-b20d-7a582ff6e0f1.json +0 -1
- package/.output/server/chunks/routes/ws/mirror.mjs +0 -161
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { u as currentMachineSlots, q as paneBaseMachine, H as HerdrError, v as machineFor, x as herdrOn, y as HERDR_CHILD_ENV, z as HERDR_BIN, n as projectOf, B as isProjectThread } from '../nitro/nitro.mjs';
|
|
4
|
+
|
|
5
|
+
const ESCAPES = { b: "\b", t: " ", n: "\n", f: "\f", r: "\r", e: "\x1B", '"': '"', "\\": "\\" };
|
|
6
|
+
function unescapeBasic(s) {
|
|
7
|
+
return s.replace(/\\[ \t]*\n\s*/g, "").replace(/\\(u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[\s\S])/g, (all, x) => {
|
|
8
|
+
var _a;
|
|
9
|
+
if (x.length > 1) {
|
|
10
|
+
const code = Number.parseInt(x.slice(1), 16);
|
|
11
|
+
return code <= 1114111 ? String.fromCodePoint(code) : all;
|
|
12
|
+
}
|
|
13
|
+
return (_a = ESCAPES[x]) != null ? _a : all;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function closing(text, from, quote, basic) {
|
|
17
|
+
for (let i = from; i < text.length; i++) {
|
|
18
|
+
if (basic && text[i] === "\\") {
|
|
19
|
+
i++;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (quote.length === 1 && text[i] === "\n") return -1;
|
|
23
|
+
if (text.startsWith(quote, i)) {
|
|
24
|
+
if (quote.length === 3) while (text[i + 3] === quote[0]) i++;
|
|
25
|
+
return i;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
const HEADER = /^[ \t]*\[\[?[ \t]*([A-Za-z0-9_.-]+)[ \t]*\]\]?[ \t]*(?:#.*)?$/;
|
|
31
|
+
const KEY = /^[ \t]*([A-Za-z0-9_-]+)[ \t]*=[ \t]*/;
|
|
32
|
+
function tomlTables(input) {
|
|
33
|
+
const text = String(input || "").replace(/\r\n?/g, "\n");
|
|
34
|
+
let table = { name: "", strings: {}, bare: {} };
|
|
35
|
+
const tables = [table];
|
|
36
|
+
let at = 0;
|
|
37
|
+
while (at < text.length) {
|
|
38
|
+
const eol = text.indexOf("\n", at);
|
|
39
|
+
const line = text.slice(at, eol < 0 ? text.length : eol);
|
|
40
|
+
let next = eol < 0 ? text.length : eol + 1;
|
|
41
|
+
const header = HEADER.exec(line);
|
|
42
|
+
const m = header ? null : KEY.exec(line);
|
|
43
|
+
if (header) {
|
|
44
|
+
table = { name: header[1], strings: {}, bare: {} };
|
|
45
|
+
tables.push(table);
|
|
46
|
+
} else if (m) {
|
|
47
|
+
const start = at + m[0].length;
|
|
48
|
+
const quote = ['"""', "'''", '"', "'"].find((q) => text.startsWith(q, start));
|
|
49
|
+
if (quote) {
|
|
50
|
+
const basic = quote[0] === '"';
|
|
51
|
+
const end = closing(text, start + quote.length, quote, basic);
|
|
52
|
+
if (end >= 0) {
|
|
53
|
+
let raw = text.slice(start + quote.length, end);
|
|
54
|
+
if (quote.length === 3 && raw.startsWith("\n")) raw = raw.slice(1);
|
|
55
|
+
table.strings[m[1]] = basic ? unescapeBasic(raw) : raw;
|
|
56
|
+
const after = text.indexOf("\n", end + quote.length);
|
|
57
|
+
next = after < 0 ? text.length : after + 1;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
const value = line.slice(m[0].length).split("#")[0].trim();
|
|
61
|
+
if (value) table.bare[m[1]] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
at = next;
|
|
65
|
+
}
|
|
66
|
+
return tables;
|
|
67
|
+
}
|
|
68
|
+
function tomlStrings(front) {
|
|
69
|
+
return Object.assign({}, ...tomlTables(front).map((t) => t.strings));
|
|
70
|
+
}
|
|
4
71
|
|
|
5
72
|
const fold = (s) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
6
73
|
const KINDS = [
|
|
@@ -21,8 +88,12 @@ function listKind(title) {
|
|
|
21
88
|
}
|
|
22
89
|
const THREAD_ID = /\bt-\d{4,}\b/i;
|
|
23
90
|
const TASK_LINE = /^[-*+]\s+(?:\[([ xX])\](?:\s+|$))?(.*)$/;
|
|
24
|
-
const OWNER = /\
|
|
25
|
-
const REFS = /\s*\[\s*t-\d{4,}(?:\s*[
|
|
91
|
+
const OWNER = /\(([^()]{1,48})\)\s*$/;
|
|
92
|
+
const REFS = /\s*\[\s*t-\d{4,}(?:(?:\s+|\s*[,;]\s*)t-\d{4,})*\s*\]/gi;
|
|
93
|
+
const CAUSE = /^\s*(?:bloqu[eé]e? par|blocked by)\s*:\s*(.+)$/i;
|
|
94
|
+
const ANSI = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
95
|
+
const CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
96
|
+
const MAX_LINE = 2e3;
|
|
26
97
|
const BADGE_COLORS = ["red", "orange", "amber", "green", "teal", "blue", "violet", "pink", "gray"];
|
|
27
98
|
const BADGE = /\[b:\s*([#\w-]*)\s*\((.*?)\)\](?:\(\s*((?:[^\s()]|\([^\s()]*\))*)\s*\))?/gi;
|
|
28
99
|
function badgeColor(raw) {
|
|
@@ -47,32 +118,36 @@ function badgeTarget(raw) {
|
|
|
47
118
|
}
|
|
48
119
|
function takeBadges(input) {
|
|
49
120
|
const badges = [];
|
|
50
|
-
const text = input.replace(BADGE, (all, color, label,
|
|
121
|
+
const text = input.replace(BADGE, (all, color, label, target2) => {
|
|
51
122
|
const t = label.replace(/\s+/g, " ").trim();
|
|
52
123
|
if (!t) return all;
|
|
53
|
-
badges.push({ text: t, color: badgeColor(color), ...badgeTarget(
|
|
124
|
+
badges.push({ text: t, color: badgeColor(color), ...badgeTarget(target2) || {} });
|
|
54
125
|
return " ";
|
|
55
126
|
});
|
|
56
127
|
return badges.length ? { text: text.replace(/\s{2,}/g, " ").trim(), badges } : { text: input, badges };
|
|
57
128
|
}
|
|
58
129
|
function parseTaskLine(line, kind = null) {
|
|
59
130
|
var _a;
|
|
60
|
-
|
|
131
|
+
if (line.length > MAX_LINE) return null;
|
|
132
|
+
const m = TASK_LINE.exec(line.replace(ANSI, "").replace(/\t/g, " ").replace(CONTROL, "").trim());
|
|
61
133
|
if (!m) return null;
|
|
62
134
|
const free = takeBadges(m[2].trim());
|
|
63
135
|
let text = free.text.replace(REFS, "").trim();
|
|
64
136
|
let owner = null;
|
|
65
137
|
const o = OWNER.exec(text);
|
|
66
|
-
if (o && o.index > 0 &&
|
|
138
|
+
if (o && o.index > 0 && text[o.index - 1] !== "]") {
|
|
67
139
|
owner = o[1].trim();
|
|
68
140
|
text = text.slice(0, o.index).trim();
|
|
69
141
|
}
|
|
70
142
|
let reason;
|
|
71
143
|
if (kind === "blocked") {
|
|
72
|
-
|
|
73
|
-
|
|
144
|
+
for (let at = text.indexOf("\u2014"); at > 0; at = text.indexOf("\u2014", at + 1)) {
|
|
145
|
+
if (!/\s/.test(text[at - 1])) continue;
|
|
146
|
+
const cause = CAUSE.exec(text.slice(at + 1));
|
|
147
|
+
if (!((_a = cause == null ? void 0 : cause[1]) == null ? void 0 : _a.trim())) continue;
|
|
74
148
|
reason = cause[1].trim();
|
|
75
|
-
text = text.slice(0,
|
|
149
|
+
text = text.slice(0, at).trim();
|
|
150
|
+
break;
|
|
76
151
|
}
|
|
77
152
|
}
|
|
78
153
|
if (!text) text = free.badges.map((b) => b.text).join(" \xB7 ");
|
|
@@ -80,19 +155,27 @@ function parseTaskLine(line, kind = null) {
|
|
|
80
155
|
const ref = owner && THREAD_ID.exec(owner);
|
|
81
156
|
return { text, done: m[1] === "x" || m[1] === "X", owner, thread: ref ? ref[0].toLowerCase() : null, ...reason ? { reason } : {}, ...free.badges.length ? { badges: free.badges } : {} };
|
|
82
157
|
}
|
|
158
|
+
function headingTitle(raw) {
|
|
159
|
+
var _a;
|
|
160
|
+
if (!raw.startsWith("##") || !/\s/.test((_a = raw[2]) != null ? _a : "")) return null;
|
|
161
|
+
const body = raw.slice(2).trim();
|
|
162
|
+
if (!body) return null;
|
|
163
|
+
return body.replace(/#+$/, "").trim() || body[0];
|
|
164
|
+
}
|
|
83
165
|
function parseTasks(md) {
|
|
84
166
|
const lists = [];
|
|
85
167
|
let cur = null;
|
|
86
168
|
let fence = false;
|
|
87
169
|
for (const raw of String(md || "").replace(/\r\n?/g, "\n").split("\n")) {
|
|
170
|
+
if (raw.length > MAX_LINE) continue;
|
|
88
171
|
if (/^\s*(```|~~~)/.test(raw)) {
|
|
89
172
|
fence = !fence;
|
|
90
173
|
continue;
|
|
91
174
|
}
|
|
92
175
|
if (fence) continue;
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
cur = { title
|
|
176
|
+
const title = headingTitle(raw);
|
|
177
|
+
if (title) {
|
|
178
|
+
cur = { title, kind: listKind(title), tasks: [] };
|
|
96
179
|
lists.push(cur);
|
|
97
180
|
continue;
|
|
98
181
|
}
|
|
@@ -128,6 +211,8 @@ function normalizeThreads(raw) {
|
|
|
128
211
|
created: str(r.created),
|
|
129
212
|
agentName: str(r.agent_name) || null,
|
|
130
213
|
machine: str(r.machine),
|
|
214
|
+
profile: str(r.profile) || str(r.agent),
|
|
215
|
+
stateLine: str(r.state_line).trim(),
|
|
131
216
|
activity: str(r.activity).trim(),
|
|
132
217
|
percent: typeof r.percent === "number" && Number.isFinite(r.percent) ? Math.max(0, Math.min(100, Math.round(r.percent))) : null,
|
|
133
218
|
pr: /^https:\/\//.test(str(r.pr)) ? str(r.pr) : "",
|
|
@@ -156,10 +241,9 @@ function splitThreads(list) {
|
|
|
156
241
|
function maxParallelThreads(projectMd) {
|
|
157
242
|
const m = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+/.exec(String(projectMd || ""));
|
|
158
243
|
if (!m) return null;
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const n = Number(v[1]);
|
|
244
|
+
const v = tomlTables(m[1])[0].bare.max_parallel_threads;
|
|
245
|
+
if (v === void 0 || !/^\d+$/.test(v)) return 10;
|
|
246
|
+
const n = Number(v);
|
|
163
247
|
return n >= 1 ? n : null;
|
|
164
248
|
}
|
|
165
249
|
|
|
@@ -210,27 +294,34 @@ async function version(t) {
|
|
|
210
294
|
const v = `${tasks ? `${tasks.size}.${tasks.mtimeMs}` : "-"}|${threads ? threads.mtimeMs : "-"}|${project ? project.mtimeMs : "-"}`;
|
|
211
295
|
return { v, tasks: (tasks == null ? void 0 : tasks.isFile) ? tasks : null, project: (project == null ? void 0 : project.isFile) ? project : null };
|
|
212
296
|
}
|
|
213
|
-
|
|
214
|
-
|
|
297
|
+
const REMOTE_RUN = 'export HERDR_BIN_PATH="$1" HERDR_SESSION="$2"; bin="$3"; shift 3; exec "$bin" "$@"';
|
|
298
|
+
function runProjectCommand(t, args, timeoutMs = 15e3, maxBytes = 1024 * 1024) {
|
|
215
299
|
const session = t.m.session && t.m.session !== "default" ? t.m.session : "";
|
|
216
300
|
const remote = t.m.exec;
|
|
217
301
|
if (remote) {
|
|
218
302
|
const herdrBin = t.m.bin || "herdr";
|
|
219
|
-
return remote(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return r.stdout.toString("utf8");
|
|
303
|
+
return remote(REMOTE_RUN, [herdrBin, session, t.bin, ...args], { timeoutMs }).then((r) => {
|
|
304
|
+
var _a;
|
|
305
|
+
return r.stdout.length > maxBytes ? { code: 1, stdout: "", stderr: "output too long" } : { code: (_a = r.code) != null ? _a : 1, stdout: r.stdout.toString("utf8"), stderr: r.stderr };
|
|
223
306
|
});
|
|
224
307
|
}
|
|
225
308
|
const env = { ...HERDR_CHILD_ENV, HERDR_BIN_PATH: HERDR_BIN };
|
|
226
309
|
if (session) env.HERDR_SESSION = session;
|
|
227
|
-
return new Promise((resolve
|
|
228
|
-
execFile(t.bin,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
execFile(t.bin, args, { env, timeout: timeoutMs, maxBuffer: maxBytes }, (err, out, stderr) => resolve({
|
|
312
|
+
code: err ? typeof err.code === "number" ? err.code : 1 : 0,
|
|
313
|
+
stdout: String(out),
|
|
314
|
+
stderr: String(stderr || (err ? err.message : ""))
|
|
315
|
+
}));
|
|
232
316
|
});
|
|
233
317
|
}
|
|
318
|
+
const projectsRoot = (t) => path.posix.dirname(t.dir);
|
|
319
|
+
async function threadList(t) {
|
|
320
|
+
const r = await runProjectCommand(t, ["--root", projectsRoot(t), "thread", "list", t.slug, "--json"], 15e3, THREADS_BYTES);
|
|
321
|
+
if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `code ${r.code}`);
|
|
322
|
+
if (r.stdout.length > THREADS_BYTES) throw new Error("thread list too long");
|
|
323
|
+
return r.stdout;
|
|
324
|
+
}
|
|
234
325
|
const lastThreads = /* @__PURE__ */ new Map();
|
|
235
326
|
async function readProjectBoard(pane, since) {
|
|
236
327
|
const t = await target(pane);
|
|
@@ -271,5 +362,5 @@ async function readThreadReport(pane, id) {
|
|
|
271
362
|
return { id, text, truncated: st.size > REPORT_BYTES };
|
|
272
363
|
}
|
|
273
364
|
|
|
274
|
-
export {
|
|
365
|
+
export { target as a, binaryOn as b, runProjectCommand as c, tomlTables as d, readThreadReport as e, normalizeThreads as n, projectsRoot as p, readProjectBoard as r, tomlStrings as t };
|
|
275
366
|
//# sourceMappingURL=projectBoard.mjs.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { d as tomlTables, a as target } from './projectBoard.mjs';
|
|
2
|
+
import { H as HerdrError } from '../nitro/nitro.mjs';
|
|
3
|
+
|
|
4
|
+
const front = (md) => {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
return (_b = (_a = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+/.exec(String(md || ""))) == null ? void 0 : _a[1]) != null ? _b : null;
|
|
7
|
+
};
|
|
8
|
+
const cut = (s, max) => (s || "").replace(/\s+/g, " ").trim().slice(0, max);
|
|
9
|
+
function parseProjectSettings(projectMd) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const f = front(projectMd);
|
|
12
|
+
if (f === null) return null;
|
|
13
|
+
const tables = tomlTables(f);
|
|
14
|
+
const strings = tables[0].strings;
|
|
15
|
+
const raw = (key) => tables[0].bare[key];
|
|
16
|
+
const defaults = [];
|
|
17
|
+
const pick = (key, value, fallback) => {
|
|
18
|
+
if (value !== void 0) return value;
|
|
19
|
+
defaults.push(key);
|
|
20
|
+
return fallback;
|
|
21
|
+
};
|
|
22
|
+
const int = (key) => {
|
|
23
|
+
const v = raw(key);
|
|
24
|
+
return v !== void 0 && /^\d{1,6}$/.test(v) ? Number(v) : void 0;
|
|
25
|
+
};
|
|
26
|
+
const bool = (key) => {
|
|
27
|
+
const v = raw(key);
|
|
28
|
+
return v === "true" ? true : v === "false" ? false : void 0;
|
|
29
|
+
};
|
|
30
|
+
const settings = {
|
|
31
|
+
name: cut(pick("name", strings.name, ""), 200),
|
|
32
|
+
goal: cut(pick("goal", strings.goal, ""), 2e3),
|
|
33
|
+
// `*_agent`: the keys' names before profiles.
|
|
34
|
+
coordinatorProfile: cut(pick("coordinatorProfile", (_a = strings.coordinator_profile) != null ? _a : strings.coordinator_agent, "claude"), 60),
|
|
35
|
+
threadProfile: cut(pick("threadProfile", (_b = strings.thread_profile) != null ? _b : strings.thread_agent, "claude"), 60),
|
|
36
|
+
maxParallelThreads: pick("maxParallelThreads", int("max_parallel_threads"), 10),
|
|
37
|
+
autoResolveDays: pick("autoResolveDays", int("auto_resolve_days"), 7),
|
|
38
|
+
nudge: pick("nudge", bool("nudge"), true),
|
|
39
|
+
mute: pick("mute", bool("mute"), false),
|
|
40
|
+
repos: tables.filter((t) => t.name === "repos").slice(0, 50).map((t) => ({ path: cut(t.strings.path, 1024), machine: cut(t.strings.machine, 120) })).filter((r) => r.path),
|
|
41
|
+
defaults
|
|
42
|
+
};
|
|
43
|
+
return settings;
|
|
44
|
+
}
|
|
45
|
+
const MEMORY_INDEX = "MEMORY.md";
|
|
46
|
+
const MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,119}\.md$/;
|
|
47
|
+
const memoryFileOk = (name) => typeof name === "string" && MEMORY_FILE_RE.test(name) && !name.includes("..");
|
|
48
|
+
function parseMemoryIndex(indexMd) {
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const line of String(indexMd || "").replace(/\r\n?/g, "\n").split("\n")) {
|
|
51
|
+
const m = /^\s*[-*+]\s+\[([^\]]+)\]\((?:\.\/)?memory\/([^)\s]+)\)\s*(?:[:—–-]\s*)?(.*)$/.exec(line);
|
|
52
|
+
if (!m || !memoryFileOk(m[2]) || out.some((x) => x.name === m[2])) continue;
|
|
53
|
+
out.push({ name: m[2], title: cut(m[1], 160), hook: cut(m[3], 400) });
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
function memoryFiles(indexMd, names) {
|
|
58
|
+
const files = names.filter(memoryFileOk);
|
|
59
|
+
const listed = parseMemoryIndex(indexMd || "");
|
|
60
|
+
const out = [];
|
|
61
|
+
if (indexMd !== null) out.push({ name: MEMORY_INDEX, title: MEMORY_INDEX, hook: "", index: true });
|
|
62
|
+
for (const l of listed) out.push(files.includes(l.name) ? l : { ...l, missing: true });
|
|
63
|
+
for (const name of files.filter((n) => !listed.some((l) => l.name === n)).sort()) out.push({ name, title: name, hook: "" });
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const PROJECT_HEAD_BYTES = 32 * 1024;
|
|
68
|
+
const INDEX_BYTES = 128 * 1024;
|
|
69
|
+
const MEMORY_BYTES = 256 * 1024;
|
|
70
|
+
const MEMORY_FILES_MAX = 300;
|
|
71
|
+
async function head(t, file, max) {
|
|
72
|
+
const st = await t.m.fs.stat(file).catch(() => null);
|
|
73
|
+
if (!st || !st.isFile) return null;
|
|
74
|
+
const text = (await t.m.fs.read(file, 0, Math.min(st.size, max))).toString("utf8");
|
|
75
|
+
return { text, truncated: st.size > max };
|
|
76
|
+
}
|
|
77
|
+
async function settingsOn(t) {
|
|
78
|
+
const file = await head(t, `${t.dir}/PROJECT.md`, PROJECT_HEAD_BYTES);
|
|
79
|
+
const settings = file && parseProjectSettings(file.text);
|
|
80
|
+
if (!settings) throw new HerdrError("no_settings", "project settings not found");
|
|
81
|
+
return { settings };
|
|
82
|
+
}
|
|
83
|
+
async function memoryOn(t) {
|
|
84
|
+
const [index, names] = await Promise.all([
|
|
85
|
+
head(t, `${t.dir}/${MEMORY_INDEX}`, INDEX_BYTES),
|
|
86
|
+
t.m.fs.readdir(`${t.dir}/memory`).catch(() => [])
|
|
87
|
+
]);
|
|
88
|
+
return { files: memoryFiles(index ? index.text : null, names.slice(0, MEMORY_FILES_MAX * 4)).slice(0, MEMORY_FILES_MAX) };
|
|
89
|
+
}
|
|
90
|
+
async function memoryFileOn(t, name) {
|
|
91
|
+
if (name !== MEMORY_INDEX && !memoryFileOk(name)) throw new HerdrError("bad_memory", "invalid memory file");
|
|
92
|
+
const file = await head(t, name === MEMORY_INDEX ? `${t.dir}/${MEMORY_INDEX}` : `${t.dir}/memory/${name}`, MEMORY_BYTES);
|
|
93
|
+
if (!file) throw new HerdrError("no_memory", "memory file not found");
|
|
94
|
+
return { name, ...file };
|
|
95
|
+
}
|
|
96
|
+
async function need(pane) {
|
|
97
|
+
const t = await target(pane);
|
|
98
|
+
if (!t) throw new HerdrError("no_project", "project not found");
|
|
99
|
+
return t;
|
|
100
|
+
}
|
|
101
|
+
const readProjectSettings = async (pane) => settingsOn(await need(pane));
|
|
102
|
+
const readProjectMemory = async (pane) => memoryOn(await need(pane));
|
|
103
|
+
const readMemoryFile = async (pane, name) => memoryFileOn(await need(pane), name);
|
|
104
|
+
|
|
105
|
+
export { readMemoryFile as a, readProjectSettings as b, readProjectMemory as r };
|
|
106
|
+
//# sourceMappingURL=projectDocs.mjs.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { p as projectSlugOk, l as lastLine } from './projectThreads.mjs';
|
|
2
|
+
import { t as tomlStrings, a as target, p as projectsRoot, c as runProjectCommand } from './projectBoard.mjs';
|
|
3
|
+
import { H as HerdrError } from '../nitro/nitro.mjs';
|
|
4
|
+
import { s as stripAnsi } from './pluginPolicy.mjs';
|
|
5
|
+
|
|
6
|
+
const INBOX_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
|
|
7
|
+
const inboxIdOk = (id) => typeof id === "string" && INBOX_ID_RE.test(id) && !id.includes("..");
|
|
8
|
+
const INBOX_BODY_MAX = 2e3;
|
|
9
|
+
function parseInboxItem(fileText) {
|
|
10
|
+
const text = String(fileText || "").replace(/\r\n?/g, "\n");
|
|
11
|
+
if (!text.startsWith("+++\n")) return null;
|
|
12
|
+
const rest = text.slice(4);
|
|
13
|
+
const cut = rest.indexOf("\n+++\n");
|
|
14
|
+
const front = cut >= 0 ? rest.slice(0, cut) : rest.endsWith("\n+++") ? rest.slice(0, -4) : null;
|
|
15
|
+
if (front === null) return null;
|
|
16
|
+
const f = tomlStrings(front);
|
|
17
|
+
if (!inboxIdOk(f.id)) return null;
|
|
18
|
+
const oneLine = (s, max) => (s || "").replace(/\s+/g, " ").trim().slice(0, max);
|
|
19
|
+
const body = cut >= 0 ? rest.slice(cut + 5).replace(/^\n+|\n+$/g, "") : "";
|
|
20
|
+
return {
|
|
21
|
+
id: f.id,
|
|
22
|
+
kind: oneLine(f.kind, 40),
|
|
23
|
+
subject: oneLine(f.subject, 120),
|
|
24
|
+
event: oneLine(f.event, 120),
|
|
25
|
+
summary: oneLine(f.summary, 1e3),
|
|
26
|
+
created: oneLine(f.created, 40),
|
|
27
|
+
body: body.length > INBOX_BODY_MAX ? `${body.slice(0, INBOX_BODY_MAX)}\u2026` : body
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const sortInbox = (items) => [...items].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
31
|
+
function inboxDoneArgs(root, slug, ids) {
|
|
32
|
+
if (!root.startsWith("/")) throw new Error("invalid projects root");
|
|
33
|
+
if (!projectSlugOk(slug)) throw new Error("invalid project");
|
|
34
|
+
if (!ids.length || ids.length > 50 || !ids.every(inboxIdOk)) throw new Error("invalid inbox item");
|
|
35
|
+
return ["--root", root, "inbox", "done", slug, ...ids];
|
|
36
|
+
}
|
|
37
|
+
function routineListArgs(root, slug) {
|
|
38
|
+
if (!root.startsWith("/")) throw new Error("invalid projects root");
|
|
39
|
+
if (!projectSlugOk(slug)) throw new Error("invalid project");
|
|
40
|
+
return ["--root", root, "routine", "list", slug];
|
|
41
|
+
}
|
|
42
|
+
function parseRoutineList(output) {
|
|
43
|
+
const routines = [];
|
|
44
|
+
const broken = [];
|
|
45
|
+
for (const line of String(output || "").replace(/\r\n?/g, "\n").split("\n")) {
|
|
46
|
+
const cols = line.split(" ");
|
|
47
|
+
if (cols.length === 2 && cols[1].startsWith("config-error:")) {
|
|
48
|
+
broken.push({ file: cols[0].trim().slice(0, 120), error: cols[1].slice("config-error:".length).trim().slice(0, 300) });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (cols.length !== 5 || !cols[0].trim() || cols[2] !== "enabled" && cols[2] !== "disabled") continue;
|
|
52
|
+
routines.push({
|
|
53
|
+
name: cols[0].trim().slice(0, 120),
|
|
54
|
+
schedule: cols[1].trim().slice(0, 120),
|
|
55
|
+
enabled: cols[2] === "enabled",
|
|
56
|
+
kind: cols[3].trim().slice(0, 160),
|
|
57
|
+
when: cols[4].split(" \xB7 ").map((s) => s.trim().slice(0, 160)).filter(Boolean)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return { routines, broken };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const INBOX_MAX = 100;
|
|
64
|
+
const ITEM_BYTES = 8 * 1024;
|
|
65
|
+
const READS_AT_ONCE = 8;
|
|
66
|
+
const seen = /* @__PURE__ */ new Map();
|
|
67
|
+
const SEEN_MAX = 2e3;
|
|
68
|
+
async function inboxOn(t) {
|
|
69
|
+
var _a;
|
|
70
|
+
const dir = `${t.dir}/inbox`;
|
|
71
|
+
const names = await t.m.fs.readdir(dir).catch(() => []);
|
|
72
|
+
const ids = names.filter((n) => n.endsWith(".md")).map((n) => n.slice(0, -3)).filter(inboxIdOk).sort();
|
|
73
|
+
const items = [];
|
|
74
|
+
const wanted = ids.slice(0, INBOX_MAX);
|
|
75
|
+
const stats = await t.m.fs.statMany(wanted.map((id) => `${dir}/${id}.md`));
|
|
76
|
+
const base = `${(_a = t.m.key) != null ? _a : ""}|${dir}/`;
|
|
77
|
+
const todo = [];
|
|
78
|
+
const found = /* @__PURE__ */ new Map();
|
|
79
|
+
wanted.forEach((id, i) => {
|
|
80
|
+
const st = stats[i];
|
|
81
|
+
if (!st || !st.isFile || st.size <= 0) return;
|
|
82
|
+
const known = seen.get(base + id);
|
|
83
|
+
if (known && known.size === st.size && known.mtimeMs === st.mtimeMs) {
|
|
84
|
+
if (known.item) found.set(id, known.item);
|
|
85
|
+
} else todo.push({ id, size: st.size, mtimeMs: st.mtimeMs });
|
|
86
|
+
});
|
|
87
|
+
for (let i = 0; i < todo.length; i += READS_AT_ONCE) {
|
|
88
|
+
await Promise.all(todo.slice(i, i + READS_AT_ONCE).map(async ({ id, size, mtimeMs }) => {
|
|
89
|
+
const text = await t.m.fs.read(`${dir}/${id}.md`, 0, Math.min(size, ITEM_BYTES)).then((b) => b.toString("utf8")).catch(() => null);
|
|
90
|
+
if (text === null) return;
|
|
91
|
+
const parsed = parseInboxItem(text.length >= ITEM_BYTES && !text.includes("\n+++\n") ? "" : text);
|
|
92
|
+
const item = parsed && parsed.id === id ? parsed : null;
|
|
93
|
+
seen.set(base + id, { size, mtimeMs, item });
|
|
94
|
+
if (item) found.set(id, item);
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
for (const item of found.values()) items.push(item);
|
|
98
|
+
const here = new Set(ids);
|
|
99
|
+
for (const key of seen.keys()) if (key.startsWith(base) && !here.has(key.slice(base.length))) seen.delete(key);
|
|
100
|
+
while (seen.size > SEEN_MAX) seen.delete(seen.keys().next().value);
|
|
101
|
+
return { items: sortInbox(items), total: items.length + (ids.length - wanted.length) };
|
|
102
|
+
}
|
|
103
|
+
async function inboxDoneOn(t, id) {
|
|
104
|
+
let args;
|
|
105
|
+
try {
|
|
106
|
+
args = inboxDoneArgs(projectsRoot(t), t.slug, [id]);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
throw new HerdrError("bad_item", e.message);
|
|
109
|
+
}
|
|
110
|
+
const r = await runProjectCommand(t, args, 15e3);
|
|
111
|
+
if (r.code !== 0) throw new HerdrError("inbox_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
|
|
112
|
+
return { ok: true };
|
|
113
|
+
}
|
|
114
|
+
async function routinesOn(t) {
|
|
115
|
+
let args;
|
|
116
|
+
try {
|
|
117
|
+
args = routineListArgs(projectsRoot(t), t.slug);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
throw new HerdrError("bad_project", e.message);
|
|
120
|
+
}
|
|
121
|
+
const r = await runProjectCommand(t, args, 15e3);
|
|
122
|
+
if (r.code !== 0) throw new HerdrError("routines_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
|
|
123
|
+
return parseRoutineList(stripAnsi(r.stdout));
|
|
124
|
+
}
|
|
125
|
+
async function need(pane) {
|
|
126
|
+
const t = await target(pane);
|
|
127
|
+
if (!t) throw new HerdrError("no_project", "project not found");
|
|
128
|
+
return t;
|
|
129
|
+
}
|
|
130
|
+
const readInbox = async (pane) => inboxOn(await need(pane));
|
|
131
|
+
const markInboxDone = async (pane, id) => inboxDoneOn(await need(pane), id);
|
|
132
|
+
const readRoutines = async (pane) => routinesOn(await need(pane));
|
|
133
|
+
|
|
134
|
+
export { readRoutines as a, markInboxDone as m, readInbox as r };
|
|
135
|
+
//# sourceMappingURL=projectInbox.mjs.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { n as normalizeThreads } from './projectBoard.mjs';
|
|
2
|
+
|
|
3
|
+
const THREAD_ID_RE = /^t-\d{4,}$/;
|
|
4
|
+
const PROJECT_SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$/;
|
|
5
|
+
const projectSlugOk = (slug) => PROJECT_SLUG_RE.test(slug) && !slug.includes("..");
|
|
6
|
+
const THREAD_ACTIONS = ["stop", "restart", "ack", "resolve"];
|
|
7
|
+
const isThreadAction = (v) => THREAD_ACTIONS.includes(v);
|
|
8
|
+
const THREAD_TIMEOUT_MS = { show: 15e3, stop: 2e4, ack: 15e3, restart: 9e4, resolve: 18e4 };
|
|
9
|
+
function threadCommandArgs(command, root, slug, id) {
|
|
10
|
+
if (!root.startsWith("/")) throw new Error("invalid projects root");
|
|
11
|
+
if (!projectSlugOk(slug)) throw new Error("invalid project");
|
|
12
|
+
if (!THREAD_ID_RE.test(id)) throw new Error("invalid thread");
|
|
13
|
+
if (command !== "show" && !isThreadAction(command)) throw new Error("invalid action");
|
|
14
|
+
return ["--root", root, "thread", command, slug, id, ...command === "show" ? ["--json"] : []];
|
|
15
|
+
}
|
|
16
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
17
|
+
const NEXT_MAX = 20;
|
|
18
|
+
function normalizeThreadDetail(raw) {
|
|
19
|
+
const base = normalizeThreads([raw])[0];
|
|
20
|
+
if (!base) return null;
|
|
21
|
+
const r = raw;
|
|
22
|
+
const hash = str(r.report_hash);
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
note: str(r.note).trim(),
|
|
26
|
+
error: str(r.error).trim().slice(0, 600),
|
|
27
|
+
kind: str(r.kind),
|
|
28
|
+
agent: str(r.agent),
|
|
29
|
+
branch: str(r.branch),
|
|
30
|
+
base: str(r.base),
|
|
31
|
+
prState: str(r.pr_state),
|
|
32
|
+
prReview: str(r.pr_review),
|
|
33
|
+
next: (Array.isArray(r.next) ? r.next : []).filter((x) => typeof x === "string" && Boolean(x.trim())).slice(0, NEXT_MAX).map((x) => x.trim().slice(0, 300)),
|
|
34
|
+
acked: Boolean(hash) && str(r.acked_report_hash) === hash,
|
|
35
|
+
hasPane: Boolean(str(r.pane_id)),
|
|
36
|
+
keptWorktree: r.kept_worktree === true,
|
|
37
|
+
resolvedReason: str(r.resolved_reason)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function lastLine(text) {
|
|
41
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
42
|
+
return (lines[lines.length - 1] || "").replace(/^error:\s*/i, "").slice(0, 400);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { THREAD_ID_RE as T, THREAD_TIMEOUT_MS as a, isThreadAction as i, lastLine as l, normalizeThreadDetail as n, projectSlugOk as p, threadCommandArgs as t };
|
|
46
|
+
//# sourceMappingURL=projectThreads.mjs.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { i as isThreadAction, T as THREAD_ID_RE, l as lastLine, n as normalizeThreadDetail, a as THREAD_TIMEOUT_MS, t as threadCommandArgs } from './projectThreads.mjs';
|
|
2
|
+
import { H as HerdrError } from '../nitro/nitro.mjs';
|
|
3
|
+
import { s as stripAnsi } from './pluginPolicy.mjs';
|
|
4
|
+
import { a as target, c as runProjectCommand, p as projectsRoot } from './projectBoard.mjs';
|
|
5
|
+
|
|
6
|
+
function argsFor(command, t, id) {
|
|
7
|
+
try {
|
|
8
|
+
return threadCommandArgs(command, projectsRoot(t), t.slug, id);
|
|
9
|
+
} catch (e) {
|
|
10
|
+
throw new HerdrError("bad_thread", e.message);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function threadDetailOn(t, id) {
|
|
14
|
+
const r = await runProjectCommand(t, argsFor("show", t, id), THREAD_TIMEOUT_MS.show);
|
|
15
|
+
if (r.code !== 0) throw new HerdrError("thread_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
|
|
16
|
+
let raw;
|
|
17
|
+
try {
|
|
18
|
+
raw = JSON.parse(r.stdout);
|
|
19
|
+
} catch {
|
|
20
|
+
throw new HerdrError("thread_failed", "unreadable thread record");
|
|
21
|
+
}
|
|
22
|
+
const detail = normalizeThreadDetail(raw);
|
|
23
|
+
if (!detail || detail.id !== id) throw new HerdrError("thread_failed", "unreadable thread record");
|
|
24
|
+
return detail;
|
|
25
|
+
}
|
|
26
|
+
const busy = /* @__PURE__ */ new Set();
|
|
27
|
+
async function threadActionOn(t, id, action) {
|
|
28
|
+
var _a;
|
|
29
|
+
const args = argsFor(action, t, id);
|
|
30
|
+
const key = `${(_a = t.m.key) != null ? _a : ""}|${t.dir}|${id}`;
|
|
31
|
+
if (busy.has(key)) throw new HerdrError("thread_busy", "an action is already running on this thread");
|
|
32
|
+
busy.add(key);
|
|
33
|
+
try {
|
|
34
|
+
const r = await runProjectCommand(t, args, THREAD_TIMEOUT_MS[action]);
|
|
35
|
+
const out = stripAnsi(r.stdout).trim();
|
|
36
|
+
if (r.code !== 0) {
|
|
37
|
+
throw new HerdrError("thread_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
|
|
38
|
+
}
|
|
39
|
+
return { output: out.slice(-2e3) };
|
|
40
|
+
} finally {
|
|
41
|
+
busy.delete(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function where(pane, id) {
|
|
45
|
+
if (typeof id !== "string" || !THREAD_ID_RE.test(id)) throw new HerdrError("bad_thread", "invalid thread");
|
|
46
|
+
const t = await target(pane);
|
|
47
|
+
if (!t) throw new HerdrError("no_project", "project not found");
|
|
48
|
+
return { t, id };
|
|
49
|
+
}
|
|
50
|
+
async function readThreadDetail(pane, rawId) {
|
|
51
|
+
const { t, id } = await where(pane, rawId);
|
|
52
|
+
return { thread: await threadDetailOn(t, id) };
|
|
53
|
+
}
|
|
54
|
+
async function runThreadAction(pane, rawId, action, guard = (_t, run) => run()) {
|
|
55
|
+
if (!isThreadAction(action)) throw new HerdrError("bad_action", "Invalid action");
|
|
56
|
+
const { t, id } = await where(pane, rawId);
|
|
57
|
+
return guard(t, () => threadActionOn(t, id, action));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { runThreadAction as a, readThreadDetail as r };
|
|
61
|
+
//# sourceMappingURL=projectThreads2.mjs.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises, constants } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { H as HerdrError,
|
|
3
|
+
import { H as HerdrError, E as fmt, x as herdrOn } from '../nitro/nitro.mjs';
|
|
4
4
|
|
|
5
5
|
const PROJECTS_REPO = "eliasstravik/herdr-projects";
|
|
6
6
|
const PROJECTS_INSTALL_ARGS = ["plugin", "install", PROJECTS_REPO, "--yes"];
|