wherdr 1.3.1 → 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.
Files changed (180) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
  3. package/.output/public/_nuxt/B4Yk2tB1.js +23 -0
  4. package/.output/public/_nuxt/BBw_0a4M.js +6 -0
  5. package/.output/public/_nuxt/{B6qBWHWB.js → BSy6OCIY.js} +1 -1
  6. package/.output/public/_nuxt/BX5PM2JQ.js +1 -0
  7. package/.output/public/_nuxt/BgPDnf2S.js +1 -0
  8. package/.output/public/_nuxt/BkOxQzbS.js +2 -0
  9. package/.output/public/_nuxt/{WLQjK8uJ.js → CKhgznq4.js} +45 -46
  10. package/.output/public/_nuxt/{C2T9fQ4f.js → CP8o925U.js} +1 -1
  11. package/.output/public/_nuxt/CkbmecwS.js +1 -0
  12. package/.output/public/_nuxt/Cr3SrGky.js +1 -0
  13. package/.output/public/_nuxt/DAg9fiVh.js +89 -0
  14. package/.output/public/_nuxt/DT8qZ0Wv.js +1 -0
  15. package/.output/public/_nuxt/DaaJIRB3.js +1 -0
  16. package/.output/public/_nuxt/DcJi16XA.js +1 -0
  17. package/.output/public/_nuxt/VpjCgSXu.js +36 -0
  18. package/.output/public/_nuxt/builds/latest.json +1 -1
  19. package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +1 -0
  20. package/.output/public/_nuxt/dPxB2L94.js +89 -0
  21. package/.output/public/_nuxt/{entry.cKR16VqG.css → entry.CGWKmYU3.css} +1 -1
  22. package/.output/public/_nuxt/hePW80VL.js +1 -0
  23. package/.output/public/_nuxt/xYX85SPI.js +1 -0
  24. package/.output/public/sw.js +1 -1
  25. package/.output/server/chunks/_/branding.mjs +1 -1
  26. package/.output/server/chunks/_/filePaths.mjs +25 -0
  27. package/.output/server/chunks/_/files.mjs +204 -0
  28. package/.output/server/chunks/_/machineOrder.mjs +1 -1
  29. package/.output/server/chunks/_/onboarding.mjs +1 -1
  30. package/.output/server/chunks/_/package.mjs +97 -0
  31. package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
  32. package/.output/server/chunks/_/plugins.mjs +4 -94
  33. package/.output/server/chunks/_/projectBoard.mjs +120 -29
  34. package/.output/server/chunks/_/projectDocs.mjs +106 -0
  35. package/.output/server/chunks/_/projectInbox.mjs +135 -0
  36. package/.output/server/chunks/_/projectThreads.mjs +46 -0
  37. package/.output/server/chunks/_/projectThreads2.mjs +61 -0
  38. package/.output/server/chunks/_/projectsActions.mjs +1 -1
  39. package/.output/server/chunks/_/projectsPlugin.mjs +1 -1
  40. package/.output/server/chunks/_/restart.mjs +2 -312
  41. package/.output/server/chunks/_/reveal.mjs +2 -24
  42. package/.output/server/chunks/_/selfupdate.mjs +83 -0
  43. package/.output/server/chunks/_/updates.mjs +91 -0
  44. package/.output/server/chunks/_/worktrees.mjs +1 -1
  45. package/.output/server/chunks/_/ws.mjs +1 -1
  46. package/.output/server/chunks/nitro/nitro.mjs +2660 -1061
  47. package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
  48. package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
  49. package/.output/server/chunks/raw/release-ready.mjs +5 -0
  50. package/.output/server/chunks/routes/api/_..._.mjs +6 -2
  51. package/.output/server/chunks/routes/api/agents.post.mjs +6 -2
  52. package/.output/server/chunks/routes/api/ask-tab.post.mjs +6 -2
  53. package/.output/server/chunks/routes/api/attach.post.mjs +6 -2
  54. package/.output/server/chunks/routes/api/auth/disable.post.mjs +6 -2
  55. package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +6 -2
  56. package/.output/server/chunks/routes/api/auth/lock.post.mjs +6 -2
  57. package/.output/server/chunks/routes/api/auth/login/options.post.mjs +6 -2
  58. package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +6 -2
  59. package/.output/server/chunks/routes/api/auth/max-session.post.mjs +6 -2
  60. package/.output/server/chunks/routes/api/auth/register/options.post.mjs +6 -2
  61. package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +6 -2
  62. package/.output/server/chunks/routes/api/auth/status.get.mjs +6 -2
  63. package/.output/server/chunks/routes/api/board.get.mjs +147 -0
  64. package/.output/server/chunks/routes/api/changes.get.mjs +7 -3
  65. package/.output/server/chunks/routes/api/chat/image.get.mjs +7 -3
  66. package/.output/server/chunks/routes/api/chat.get.mjs +7 -3
  67. package/.output/server/chunks/routes/api/choose.post.mjs +34 -4
  68. package/.output/server/chunks/routes/api/claude-statusline.post.mjs +7 -3
  69. package/.output/server/chunks/routes/api/close/status.get.mjs +7 -3
  70. package/.output/server/chunks/routes/api/close.post.mjs +6 -2
  71. package/.output/server/chunks/routes/api/codex-update.post.mjs +7 -3
  72. package/.output/server/chunks/routes/api/commands.get.mjs +7 -3
  73. package/.output/server/chunks/routes/api/config.get.mjs +8 -3
  74. package/.output/server/chunks/routes/api/dirs.get.mjs +7 -3
  75. package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
  76. package/.output/server/chunks/routes/api/dismiss.post.mjs +7 -3
  77. package/.output/server/chunks/routes/api/effort.post.mjs +7 -3
  78. package/.output/server/chunks/routes/api/efforts.get.mjs +7 -3
  79. package/.output/server/chunks/routes/api/file.post.mjs +7 -3
  80. package/.output/server/chunks/routes/api/files/download.get.mjs +53 -0
  81. package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
  82. package/.output/server/chunks/routes/api/files.get.mjs +44 -0
  83. package/.output/server/chunks/routes/api/gitroot.get.mjs +7 -3
  84. package/.output/server/chunks/routes/api/health.get.mjs +36 -0
  85. package/.output/server/chunks/routes/api/index.get.mjs +6 -2
  86. package/.output/server/chunks/routes/api/input.post.mjs +18 -4
  87. package/.output/server/chunks/routes/api/interrupt.post.mjs +7 -3
  88. package/.output/server/chunks/routes/api/isgit.get.mjs +7 -3
  89. package/.output/server/chunks/routes/api/machine/awake.get.mjs +7 -3
  90. package/.output/server/chunks/routes/api/machine/awake.post.mjs +7 -3
  91. package/.output/server/chunks/routes/api/machine/order.get.mjs +6 -2
  92. package/.output/server/chunks/routes/api/machine/order.post.mjs +6 -2
  93. package/.output/server/chunks/routes/api/machine/rename.post.mjs +7 -3
  94. package/.output/server/chunks/routes/api/menu.post.mjs +7 -3
  95. package/.output/server/chunks/routes/api/model.post.mjs +7 -3
  96. package/.output/server/chunks/routes/api/models.get.mjs +7 -3
  97. package/.output/server/chunks/routes/api/nav.post.mjs +10 -5
  98. package/.output/server/chunks/routes/api/onboarding.get.mjs +6 -2
  99. package/.output/server/chunks/routes/api/onboarding.post.mjs +6 -2
  100. package/.output/server/chunks/routes/api/phone.get.mjs +6 -7
  101. package/.output/server/chunks/routes/api/phone.post.mjs +6 -7
  102. package/.output/server/chunks/routes/api/plugins/actions.get.mjs +7 -2
  103. package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +7 -2
  104. package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +7 -2
  105. package/.output/server/chunks/routes/api/plugins/projects.get.mjs +7 -3
  106. package/.output/server/chunks/routes/api/plugins/projects.post.mjs +7 -3
  107. package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +7 -3
  108. package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +7 -3
  109. package/.output/server/chunks/routes/api/project/inbox.get.mjs +43 -0
  110. package/.output/server/chunks/routes/api/project/inbox.post.mjs +43 -0
  111. package/.output/server/chunks/routes/api/project/memory.get.mjs +42 -0
  112. package/.output/server/chunks/routes/api/project/report.get.mjs +7 -3
  113. package/.output/server/chunks/routes/api/project/routines.get.mjs +43 -0
  114. package/.output/server/chunks/routes/api/project/settings.get.mjs +41 -0
  115. package/.output/server/chunks/routes/api/project/thread.get.mjs +44 -0
  116. package/.output/server/chunks/routes/api/project/thread.post.mjs +45 -0
  117. package/.output/server/chunks/routes/api/prompt.post.mjs +26 -4
  118. package/.output/server/chunks/routes/api/push/quiet.post.mjs +7 -3
  119. package/.output/server/chunks/routes/api/push/subscribe.post.mjs +7 -3
  120. package/.output/server/chunks/routes/api/push/test.post.mjs +7 -3
  121. package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +7 -3
  122. package/.output/server/chunks/routes/api/quotas.get.mjs +7 -3
  123. package/.output/server/chunks/routes/api/rename.post.mjs +6 -2
  124. package/.output/server/chunks/routes/api/requeue.post.mjs +7 -3
  125. package/.output/server/chunks/routes/api/restart.get.mjs +6 -2
  126. package/.output/server/chunks/routes/api/restart.post.mjs +6 -2
  127. package/.output/server/chunks/routes/api/reveal.post.mjs +7 -2
  128. package/.output/server/chunks/routes/api/screen-key.post.mjs +7 -3
  129. package/.output/server/chunks/routes/api/screen.get.mjs +10 -4
  130. package/.output/server/chunks/routes/api/search.get.mjs +7 -3
  131. package/.output/server/chunks/routes/api/seen.post.mjs +7 -3
  132. package/.output/server/chunks/routes/api/sessions.get.mjs +7 -3
  133. package/.output/server/chunks/routes/api/sessions.post.mjs +7 -3
  134. package/.output/server/chunks/routes/api/space.post.mjs +19 -15
  135. package/.output/server/chunks/routes/api/state.get.mjs +7 -3
  136. package/.output/server/chunks/routes/api/unqueue.post.mjs +7 -3
  137. package/.output/server/chunks/routes/api/update/status.get.mjs +38 -0
  138. package/.output/server/chunks/routes/api/update.get.mjs +12 -51
  139. package/.output/server/chunks/routes/api/update.post.mjs +41 -0
  140. package/.output/server/chunks/routes/api/upload.post.mjs +7 -3
  141. package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +6 -2
  142. package/.output/server/chunks/routes/api/worktrees.get.mjs +6 -2
  143. package/.output/server/chunks/routes/favicon.ico.get.mjs +6 -2
  144. package/.output/server/chunks/routes/icons/_name_.get.mjs +7 -3
  145. package/.output/server/chunks/routes/renderer.mjs +8 -4
  146. package/.output/server/chunks/routes/uploads/_...name_.get.mjs +7 -3
  147. package/.output/server/chunks/routes/ws/_..._.mjs +7 -3
  148. package/.output/server/chunks/routes/ws/events.mjs +7 -3
  149. package/.output/server/chunks/routes/ws/term.mjs +9 -5
  150. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  151. package/.output/server/index.mjs +7 -3
  152. package/.output/server/package.json +1 -1
  153. package/README.md +440 -46
  154. package/bin/install-docker-skill.sh +20 -0
  155. package/bin/lib/commands.mjs +47 -9
  156. package/bin/lib/core.mjs +18 -2
  157. package/bin/lib/install-skill.sh +100 -0
  158. package/bin/lib/skill.mjs +16 -0
  159. package/bin/lib/tailnet.d.mts +12 -3
  160. package/bin/lib/tailnet.mjs +103 -25
  161. package/bin/lib/updater.mjs +320 -0
  162. package/bin/lib/verify-release.mjs +20 -0
  163. package/package.json +4 -2
  164. package/skills/wherdr/SKILL.md +180 -0
  165. package/.output/public/_nuxt/BDNMzG2s.js +0 -1
  166. package/.output/public/_nuxt/BMIe8p_3.js +0 -2
  167. package/.output/public/_nuxt/BpQH8Ho1.js +0 -1
  168. package/.output/public/_nuxt/BrAeJIZR.js +0 -30
  169. package/.output/public/_nuxt/C12eXl5z.js +0 -1
  170. package/.output/public/_nuxt/CXG-cOdh.js +0 -1
  171. package/.output/public/_nuxt/CYxHnMNH.js +0 -83
  172. package/.output/public/_nuxt/CctS2Xn6.js +0 -65
  173. package/.output/public/_nuxt/CiYar6hg.js +0 -6
  174. package/.output/public/_nuxt/DLLQgP0d.js +0 -1
  175. package/.output/public/_nuxt/Dwh-l2rQ.js +0 -17
  176. package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
  177. package/.output/public/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json +0 -1
  178. package/.output/public/_nuxt/whFLy_Ne.js +0 -23
  179. package/.output/server/chunks/_/phone.mjs +0 -343
  180. package/.output/server/chunks/routes/ws/mirror.mjs +0 -157
@@ -1,6 +1,73 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import path from 'node:path';
3
- import { aF as currentMachineSlots, aG as paneBaseMachine, H as HerdrError, m as machineFor, aD as HERDR_CHILD_ENV, aE as HERDR_BIN, aB as herdrOn, aH as projectOf, aI as isProjectThread } from '../nitro/nitro.mjs';
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 = /\s*\(([^()]{1,48})\)\s*$/;
25
- const REFS = /\s*\[\s*t-\d{4,}(?:\s*[,;\s]\s*t-\d{4,})*\s*\]/gi;
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, target) => {
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(target) || {} });
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
- const m = TASK_LINE.exec(line.trim());
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 && !(text[o.index] === "(" && text[o.index - 1] === "]")) {
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
- const cause = /\s+—\s*(?:bloqu[eé]e? par|blocked by)\s*:\s*(.+)$/i.exec(text);
73
- if ((_a = cause == null ? void 0 : cause[1]) == null ? void 0 : _a.trim()) {
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, cause.index).trim();
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 h = /^##\s+(.+?)\s*#*\s*$/.exec(raw);
94
- if (h) {
95
- cur = { title: h[1], kind: listKind(h[1]), tasks: [] };
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 top = m[1].split(/^\s*\[/m)[0];
160
- const v = /^\s*max_parallel_threads\s*=\s*(\d+)\s*(?:#.*)?$/m.exec(top);
161
- if (!v) return 10;
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
- function threadList(t) {
214
- const root = path.posix.dirname(t.dir);
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('HERDR_BIN_PATH="$1" HERDR_SESSION="$2" exec "$3" --root "$4" thread list "$5" --json', [herdrBin, session, t.bin, root, t.slug], { timeoutMs: 15e3 }).then((r) => {
220
- if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `code ${r.code}`);
221
- if (r.stdout.length > THREADS_BYTES) throw new Error("thread list too long");
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, reject) => {
228
- execFile(t.bin, ["--root", root, "thread", "list", t.slug, "--json"], { env, timeout: 15e3, maxBuffer: THREADS_BYTES }, (err, out, stderr) => {
229
- if (err) reject(new Error(String(stderr || err.message).trim().split("\n").pop()));
230
- else resolve(String(out));
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 { readThreadReport as a, binaryOn as b, readProjectBoard as r };
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,4 +1,4 @@
1
- import { o as fmt } from '../nitro/nitro.mjs';
1
+ import { E as fmt } from '../nitro/nitro.mjs';
2
2
 
3
3
  const PROJECT_INPUTS = {
4
4
  new: ["name"],
@@ -1,6 +1,6 @@
1
1
  import { promises, constants } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { H as HerdrError, o as fmt, aB as herdrOn } from '../nitro/nitro.mjs';
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"];