agent-dealer 0.2.0 → 1.0.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.
Files changed (276) hide show
  1. package/bundle/server/dist/adapters/agent-deck-bind.js +443 -0
  2. package/bundle/server/dist/adapters/agent-deck-bind.test.js +454 -0
  3. package/bundle/server/dist/adapters/agent-deck.js +38 -50
  4. package/bundle/server/dist/adapters/agent-deck.test.js +63 -0
  5. package/bundle/server/dist/adapters/agent-health.js +230 -33
  6. package/bundle/server/dist/adapters/agent-health.test.js +237 -0
  7. package/bundle/server/dist/adapters/authority-lifecycle.js +289 -0
  8. package/bundle/server/dist/adapters/codex-scoped-config.js +103 -0
  9. package/bundle/server/dist/adapters/execution-authority.js +103 -0
  10. package/bundle/server/dist/adapters/execution-authority.test.js +166 -0
  11. package/bundle/server/dist/adapters/external.js +0 -1
  12. package/bundle/server/dist/adapters/git-worktree.js +514 -0
  13. package/bundle/server/dist/adapters/git-worktree.test.js +502 -0
  14. package/bundle/server/dist/adapters/github.js +170 -0
  15. package/bundle/server/dist/adapters/github.test.js +205 -0
  16. package/bundle/server/dist/adapters/linear-inbox.js +199 -30
  17. package/bundle/server/dist/adapters/linear-inbox.test.js +35 -0
  18. package/bundle/server/dist/adapters/linear-sync.js +3 -101
  19. package/bundle/server/dist/adapters/managed-repo.js +168 -0
  20. package/bundle/server/dist/adapters/managed-repo.test.js +63 -0
  21. package/bundle/server/dist/adapters/outbound-delivery.js +83 -0
  22. package/bundle/server/dist/adapters/outbound-delivery.test.js +53 -0
  23. package/bundle/server/dist/adapters/reflect-authority.js +43 -0
  24. package/bundle/server/dist/cli-env.js +26 -0
  25. package/bundle/server/dist/cli-env.test.js +14 -1
  26. package/bundle/server/dist/coordinator/abort.integration.test.js +74 -0
  27. package/bundle/server/dist/coordinator/admission.js +330 -0
  28. package/bundle/server/dist/coordinator/admission.test.js +338 -0
  29. package/bundle/server/dist/coordinator/args.js +128 -0
  30. package/bundle/server/dist/coordinator/args.test.js +132 -0
  31. package/bundle/server/dist/coordinator/authority-recovery.integration.test.js +661 -0
  32. package/bundle/server/dist/coordinator/auto-merge.integration.test.js +363 -0
  33. package/bundle/server/dist/coordinator/auto-merge.js +286 -0
  34. package/bundle/server/dist/coordinator/auto-merge.timeout.test.js +14 -0
  35. package/bundle/server/dist/coordinator/branch-progress.js +81 -0
  36. package/bundle/server/dist/coordinator/clock-jump.js +98 -0
  37. package/bundle/server/dist/coordinator/commands.js +1088 -0
  38. package/bundle/server/dist/coordinator/commands.test.js +554 -0
  39. package/bundle/server/dist/coordinator/deck-outage-config.js +32 -0
  40. package/bundle/server/dist/coordinator/deck-outage.integration.test.js +200 -0
  41. package/bundle/server/dist/coordinator/dependencies.js +242 -0
  42. package/bundle/server/dist/coordinator/dependency-readiness.integration.test.js +535 -0
  43. package/bundle/server/dist/coordinator/developer-effect.js +959 -0
  44. package/bundle/server/dist/coordinator/developer-effect.test.js +1143 -0
  45. package/bundle/server/dist/coordinator/dispatcher.js +58 -0
  46. package/bundle/server/dist/coordinator/dispatcher.test.js +81 -0
  47. package/bundle/server/dist/coordinator/effect-registry.js +17 -0
  48. package/bundle/server/dist/coordinator/execution-env.integration.test.js +122 -0
  49. package/bundle/server/dist/coordinator/failure-reason.js +273 -0
  50. package/bundle/server/dist/coordinator/failure-reason.test.js +234 -0
  51. package/bundle/server/dist/coordinator/guidance.js +33 -0
  52. package/bundle/server/dist/coordinator/guidance.test.js +110 -0
  53. package/bundle/server/dist/coordinator/human-resolution.js +88 -0
  54. package/bundle/server/dist/coordinator/human-resolution.test.js +72 -0
  55. package/bundle/server/dist/coordinator/intent-forecast.js +13 -0
  56. package/bundle/server/dist/coordinator/intent-forecast.test.js +34 -0
  57. package/bundle/server/dist/coordinator/latest-failure.js +100 -0
  58. package/bundle/server/dist/coordinator/latest-failure.test.js +125 -0
  59. package/bundle/server/dist/coordinator/metrics.js +35 -0
  60. package/bundle/server/dist/coordinator/metrics.test.js +52 -0
  61. package/bundle/server/dist/coordinator/permissions.js +127 -0
  62. package/bundle/server/dist/coordinator/permissions.test.js +184 -0
  63. package/bundle/server/dist/coordinator/presumed-dead-budget.integration.test.js +146 -0
  64. package/bundle/server/dist/coordinator/process-liveness.js +191 -0
  65. package/bundle/server/dist/coordinator/profile-snapshot.js +20 -0
  66. package/bundle/server/dist/coordinator/profile-snapshot.test.js +118 -0
  67. package/bundle/server/dist/coordinator/projection.js +147 -0
  68. package/bundle/server/dist/coordinator/projection.test.js +121 -0
  69. package/bundle/server/dist/coordinator/prompts.js +164 -0
  70. package/bundle/server/dist/coordinator/prompts.test.js +177 -0
  71. package/bundle/server/dist/coordinator/queue-by-default.integration.test.js +271 -0
  72. package/bundle/server/dist/coordinator/reclaim-republish.integration.test.js +372 -0
  73. package/bundle/server/dist/coordinator/recovery.js +466 -0
  74. package/bundle/server/dist/coordinator/recovery.test.js +164 -0
  75. package/bundle/server/dist/coordinator/reflect-trigger.js +218 -0
  76. package/bundle/server/dist/coordinator/reflect-trigger.test.js +324 -0
  77. package/bundle/server/dist/coordinator/repair-cycle.integration.test.js +234 -0
  78. package/bundle/server/dist/coordinator/restart-recovery.integration.test.js +363 -0
  79. package/bundle/server/dist/coordinator/reviewer-effect.js +507 -0
  80. package/bundle/server/dist/coordinator/reviewer-effect.test.js +612 -0
  81. package/bundle/server/dist/coordinator/reviewer-result.js +38 -0
  82. package/bundle/server/dist/coordinator/reviewer-result.test.js +45 -0
  83. package/bundle/server/dist/coordinator/routing.js +160 -0
  84. package/bundle/server/dist/coordinator/routing.test.js +336 -0
  85. package/bundle/server/dist/coordinator/session-lifecycle.integration.test.js +127 -0
  86. package/bundle/server/dist/coordinator/session-lifecycle.js +280 -0
  87. package/bundle/server/dist/coordinator/session-lifecycle.test.js +121 -0
  88. package/bundle/server/dist/coordinator/session-progress.js +351 -0
  89. package/bundle/server/dist/coordinator/session-progress.test.js +190 -0
  90. package/bundle/server/dist/coordinator/session-timeouts.js +33 -0
  91. package/bundle/server/dist/coordinator/sleep-recovery.integration.test.js +233 -0
  92. package/bundle/server/dist/coordinator/spawn.js +49 -0
  93. package/bundle/server/dist/coordinator/sync-issue-base-branch.js +18 -0
  94. package/bundle/server/dist/coordinator/usage-cap-config.js +10 -0
  95. package/bundle/server/dist/coordinator/usage-cap-defer.js +189 -0
  96. package/bundle/server/dist/coordinator/usage-cap.integration.test.js +193 -0
  97. package/bundle/server/dist/coordinator/usage.js +40 -0
  98. package/bundle/server/dist/coordinator/usage.test.js +69 -0
  99. package/bundle/server/dist/coordinator/verification-receipt.js +333 -0
  100. package/bundle/server/dist/coordinator/verification-receipt.test.js +382 -0
  101. package/bundle/server/dist/coordinator/worker-loop.js +363 -0
  102. package/bundle/server/dist/coordinator/worker-loop.test.js +327 -0
  103. package/bundle/server/dist/coordinator/workflow-instances.test.js +45 -0
  104. package/bundle/server/dist/coordinator/worktree-owner-liveness.js +82 -0
  105. package/bundle/server/dist/coordinator/worktree-owner-liveness.test.js +199 -0
  106. package/bundle/server/dist/db/artifacts-issue-columns.test.js +18 -0
  107. package/bundle/server/dist/db/authority-attempts-stale-at-migration.test.js +65 -0
  108. package/bundle/server/dist/db/idempotency-index-migration.test.js +57 -0
  109. package/bundle/server/dist/db/index.js +359 -7
  110. package/bundle/server/dist/db/linear-inbox-unlock.test.js +67 -0
  111. package/bundle/server/dist/db/migrate-to-issues.js +824 -0
  112. package/bundle/server/dist/db/migrate-to-issues.test.js +842 -0
  113. package/bundle/server/dist/db/schema.sql +311 -1
  114. package/bundle/server/dist/db/schema.test.js +66 -0
  115. package/bundle/server/dist/db/worker-session-pid-migration.test.js +112 -0
  116. package/bundle/server/dist/dev-review-cli-happy-path.integration.test.js +338 -0
  117. package/bundle/server/dist/direct-start-liveness.integration.test.js +350 -0
  118. package/bundle/server/dist/index.js +58 -5
  119. package/bundle/server/dist/paths.js +40 -4
  120. package/bundle/server/dist/paths.test.js +42 -0
  121. package/bundle/server/dist/pid-marker-claim-child.js +18 -0
  122. package/bundle/server/dist/pid-marker-hold-arbiter-child.js +24 -0
  123. package/bundle/server/dist/pid-marker.js +141 -0
  124. package/bundle/server/dist/pid-marker.test.js +208 -0
  125. package/bundle/server/dist/queue/approve-deliver.js +128 -28
  126. package/bundle/server/dist/queue/reflect-schedule.js +25 -0
  127. package/bundle/server/dist/queue/result-qa.js +1 -1
  128. package/bundle/server/dist/queue/send-gate.test.js +172 -36
  129. package/bundle/server/dist/repository/agents.js +55 -18
  130. package/bundle/server/dist/repository/artifacts-for-issue.js +42 -0
  131. package/bundle/server/dist/repository/artifacts-for-issue.test.js +48 -0
  132. package/bundle/server/dist/repository/artifacts.js +47 -0
  133. package/bundle/server/dist/repository/artifacts.test.js +48 -0
  134. package/bundle/server/dist/repository/authority-attempts.js +189 -0
  135. package/bundle/server/dist/repository/findings.js +70 -0
  136. package/bundle/server/dist/repository/findings.test.js +68 -0
  137. package/bundle/server/dist/repository/human-actions.js +142 -0
  138. package/bundle/server/dist/repository/human-actions.test.js +123 -0
  139. package/bundle/server/dist/repository/intake-settings.js +5 -59
  140. package/bundle/server/dist/repository/issues.js +305 -0
  141. package/bundle/server/dist/repository/issues.test.js +69 -0
  142. package/bundle/server/dist/repository/outbound-drafts.js +28 -0
  143. package/bundle/server/dist/repository/queue-entries.js +162 -0
  144. package/bundle/server/dist/repository/review-publications.js +153 -0
  145. package/bundle/server/dist/repository/review-publications.test.js +78 -0
  146. package/bundle/server/dist/repository/runs-external-id.test.js +5 -3
  147. package/bundle/server/dist/repository/runs.js +4 -211
  148. package/bundle/server/dist/repository/runtime-availability.js +54 -0
  149. package/bundle/server/dist/repository/usage-events.js +60 -0
  150. package/bundle/server/dist/repository/usage-events.test.js +56 -0
  151. package/bundle/server/dist/repository/work-items.js +305 -0
  152. package/bundle/server/dist/repository/work-items.test.js +142 -0
  153. package/bundle/server/dist/repository/worker-sessions.js +219 -0
  154. package/bundle/server/dist/repository/worker-sessions.test.js +60 -0
  155. package/bundle/server/dist/repository/workflow-events.js +193 -0
  156. package/bundle/server/dist/repository/workflow-events.test.js +89 -0
  157. package/bundle/server/dist/resolve-tsx-bin.js +19 -0
  158. package/bundle/server/dist/routes/human-actions.js +68 -0
  159. package/bundle/server/dist/routes/human-actions.test.js +284 -0
  160. package/bundle/server/dist/routes/index.js +29 -496
  161. package/bundle/server/dist/routes/issues.js +240 -0
  162. package/bundle/server/dist/routes/issues.test.js +451 -0
  163. package/bundle/server/dist/routes/queue.js +41 -0
  164. package/bundle/server/dist/routes/queue.test.js +78 -0
  165. package/bundle/server/dist/runners/claude-args.js +18 -30
  166. package/bundle/server/dist/runners/claude.js +19 -163
  167. package/bundle/server/dist/runners/claude.test.js +15 -70
  168. package/bundle/server/dist/runners/codex-args.js +59 -0
  169. package/bundle/server/dist/runners/codex-args.test.js +72 -0
  170. package/bundle/server/dist/runners/codex-jsonl.js +150 -0
  171. package/bundle/server/dist/runners/codex-jsonl.test.js +36 -0
  172. package/bundle/server/dist/runners/codex-resume.test.js +19 -0
  173. package/bundle/server/dist/runners/codex.js +46 -0
  174. package/bundle/server/dist/runners/models-codex.test.js +14 -0
  175. package/bundle/server/dist/runners/models.js +53 -1
  176. package/bundle/server/dist/runners/persist.js +4 -1
  177. package/bundle/server/dist/runners/process-registry.js +11 -0
  178. package/bundle/server/dist/runners/prompts.js +13 -280
  179. package/bundle/server/dist/runners/prompts.test.js +22 -153
  180. package/bundle/server/dist/runners/qa.js +17 -1
  181. package/bundle/server/dist/runners/reflect.js +4 -16
  182. package/bundle/server/dist/runners/spawn-cli.js +197 -0
  183. package/bundle/server/dist/runners/spawn-cli.test.js +130 -0
  184. package/bundle/server/dist/runners/stream-json.js +0 -141
  185. package/bundle/server/dist/runners/usage-cap.js +242 -0
  186. package/bundle/server/dist/runners/usage-cap.test.js +97 -0
  187. package/bundle/server/dist/server-liveness.js +37 -0
  188. package/bundle/server/dist/server-liveness.test.js +82 -0
  189. package/bundle/server/dist/static-ui.test.js +44 -0
  190. package/bundle/server/package.json +4 -2
  191. package/bundle/server/static-ui/assets/index-BTtPGYpY.js +60 -0
  192. package/bundle/server/static-ui/assets/index-BxTx4b1d.css +1 -0
  193. package/bundle/server/static-ui/index.html +2 -2
  194. package/bundle/shared/dist/agents.d.ts +262 -133
  195. package/bundle/shared/dist/agents.js +85 -19
  196. package/bundle/shared/dist/cursor-auth-health.d.ts +12 -0
  197. package/bundle/shared/dist/cursor-auth-health.js +36 -0
  198. package/bundle/shared/dist/cursor-auth-health.test.d.ts +1 -0
  199. package/bundle/shared/dist/cursor-auth-health.test.js +40 -0
  200. package/bundle/shared/dist/findings.d.ts +46 -0
  201. package/bundle/shared/dist/findings.js +17 -0
  202. package/bundle/shared/dist/findings.test.d.ts +1 -0
  203. package/bundle/shared/dist/findings.test.js +20 -0
  204. package/bundle/shared/dist/github-repo.d.ts +28 -0
  205. package/bundle/shared/dist/github-repo.js +98 -0
  206. package/bundle/shared/dist/github-repo.test.d.ts +1 -0
  207. package/bundle/shared/dist/github-repo.test.js +32 -0
  208. package/bundle/shared/dist/human-actions.d.ts +66 -0
  209. package/bundle/shared/dist/human-actions.js +51 -0
  210. package/bundle/shared/dist/human-actions.test.d.ts +1 -0
  211. package/bundle/shared/dist/human-actions.test.js +57 -0
  212. package/bundle/shared/dist/index.d.ts +567 -757
  213. package/bundle/shared/dist/index.js +22 -36
  214. package/bundle/shared/dist/issues.d.ts +385 -0
  215. package/bundle/shared/dist/issues.js +169 -0
  216. package/bundle/shared/dist/issues.test.d.ts +1 -0
  217. package/bundle/shared/dist/issues.test.js +72 -0
  218. package/bundle/shared/dist/outbound-draft.d.ts +6 -0
  219. package/bundle/shared/dist/outbound-draft.js +4 -0
  220. package/bundle/shared/dist/plan-triage.d.ts +6 -6
  221. package/bundle/shared/dist/profile-snapshot.d.ts +154 -0
  222. package/bundle/shared/dist/profile-snapshot.js +145 -0
  223. package/bundle/shared/dist/profile-snapshot.test.d.ts +1 -0
  224. package/bundle/shared/dist/profile-snapshot.test.js +67 -0
  225. package/bundle/shared/dist/queue-entries.d.ts +89 -0
  226. package/bundle/shared/dist/queue-entries.js +19 -0
  227. package/bundle/shared/dist/result-qa.d.ts +2 -2
  228. package/bundle/shared/dist/runtime-auth-health.d.ts +66 -0
  229. package/bundle/shared/dist/runtime-auth-health.js +186 -0
  230. package/bundle/shared/dist/runtime-auth-health.test.d.ts +1 -0
  231. package/bundle/shared/dist/runtime-auth-health.test.js +200 -0
  232. package/bundle/shared/dist/runtime-availability.d.ts +51 -0
  233. package/bundle/shared/dist/runtime-availability.js +13 -0
  234. package/bundle/shared/dist/runtime.d.ts +1 -1
  235. package/bundle/shared/dist/runtime.js +1 -1
  236. package/bundle/shared/dist/usage-events.d.ts +36 -0
  237. package/bundle/shared/dist/usage-events.js +13 -0
  238. package/bundle/shared/dist/worker-sessions.d.ts +140 -0
  239. package/bundle/shared/dist/worker-sessions.js +70 -0
  240. package/bundle/shared/dist/worker-sessions.test.d.ts +1 -0
  241. package/bundle/shared/dist/worker-sessions.test.js +39 -0
  242. package/bundle/shared/dist/workflow.d.ts +78 -0
  243. package/bundle/shared/dist/workflow.js +59 -0
  244. package/bundle/shared/dist/workflow.test.d.ts +1 -0
  245. package/bundle/shared/dist/workflow.test.js +61 -0
  246. package/bundle/shared/package.json +1 -1
  247. package/dist/action.d.ts +1 -0
  248. package/dist/action.js +50 -0
  249. package/dist/action.test.d.ts +1 -0
  250. package/dist/action.test.js +70 -0
  251. package/dist/agent.d.ts +1 -0
  252. package/dist/agent.js +17 -0
  253. package/dist/agent.test.d.ts +1 -0
  254. package/dist/agent.test.js +19 -0
  255. package/dist/cli-check.d.ts +7 -0
  256. package/dist/cli-check.js +40 -0
  257. package/dist/doctor.js +59 -1
  258. package/dist/http.d.ts +5 -0
  259. package/dist/http.js +19 -0
  260. package/dist/index.js +28 -0
  261. package/dist/issue.d.ts +35 -0
  262. package/dist/issue.js +143 -0
  263. package/dist/issue.test.d.ts +1 -0
  264. package/dist/issue.test.js +240 -0
  265. package/dist/lifecycle.contract.test.d.ts +1 -0
  266. package/dist/lifecycle.contract.test.js +135 -0
  267. package/dist/queue.d.ts +11 -0
  268. package/dist/queue.js +60 -0
  269. package/dist/queue.test.d.ts +1 -0
  270. package/dist/queue.test.js +46 -0
  271. package/dist/test-fetch-stub.d.ts +6 -0
  272. package/dist/test-fetch-stub.js +31 -0
  273. package/package.json +2 -1
  274. package/templates/prod.env.example +1 -1
  275. package/bundle/server/static-ui/assets/index-B_7S7xJb.js +0 -79
  276. package/bundle/server/static-ui/assets/index-QYv3zmE6.css +0 -1
package/dist/cli-check.js CHANGED
@@ -19,6 +19,33 @@ export function resolveClaudeBin() {
19
19
  }
20
20
  return "claude";
21
21
  }
22
+ /** Match server cli-env resolution for Cursor Agent CLI. */
23
+ export function resolveCursorBin() {
24
+ const home = process.env.HOME ?? os.homedir();
25
+ if (process.env.CURSOR_CLI)
26
+ return process.env.CURSOR_CLI;
27
+ const candidates = [
28
+ path.join(home, ".local/bin/cursor-agent"),
29
+ path.join(home, ".cursor/bin/cursor-agent"),
30
+ "/opt/homebrew/bin/cursor-agent",
31
+ "/usr/local/bin/cursor-agent",
32
+ path.join(home, ".local/bin/cursor"),
33
+ path.join(home, ".cursor/bin/cursor"),
34
+ "/opt/homebrew/bin/cursor",
35
+ "/usr/local/bin/cursor",
36
+ ];
37
+ for (const candidate of candidates) {
38
+ if (fs.existsSync(candidate))
39
+ return candidate;
40
+ }
41
+ return "cursor-agent";
42
+ }
43
+ export function cursorInvokeArgs(args) {
44
+ const bin = resolveCursorBin();
45
+ if (path.basename(bin) === "cursor")
46
+ return ["agent", ...args];
47
+ return args;
48
+ }
22
49
  export async function claudeAvailable() {
23
50
  const bin = resolveClaudeBin();
24
51
  if (bin !== "claude")
@@ -27,3 +54,16 @@ export async function claudeAvailable() {
27
54
  spawn("which", ["claude"]).on("close", (code) => resolve({ ok: code === 0, bin }));
28
55
  });
29
56
  }
57
+ export async function cursorAvailable() {
58
+ const bin = resolveCursorBin();
59
+ if (bin !== "cursor-agent" && bin !== "cursor")
60
+ return { ok: true, bin };
61
+ return new Promise((resolve) => {
62
+ spawn("which", ["cursor-agent"]).on("close", (code) => {
63
+ if (code === 0)
64
+ resolve({ ok: true, bin: "cursor-agent" });
65
+ else
66
+ spawn("which", ["cursor"]).on("close", (c2) => resolve({ ok: c2 === 0, bin: "cursor" }));
67
+ });
68
+ });
69
+ }
package/dist/doctor.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import net from "node:net";
2
2
  import fs from "node:fs";
3
- import { claudeAvailable } from "./cli-check.js";
3
+ import path from "node:path";
4
+ import { cursorAuthIssueFromOutput } from "@agent-dealer/shared";
5
+ import { claudeAvailable, cursorAvailable } from "./cli-check.js";
4
6
  import { loadProdEnvFile, prodEnvFilePath, prodHomeDir, resolveBundledListenPort, shortenHome, } from "./env.js";
5
7
  import { probeAgentDealer } from "./ports.js";
6
8
  import { resolveServerEntry, resolveUiDist } from "./paths.js";
@@ -23,6 +25,62 @@ export async function runDoctor() {
23
25
  console.error(" https://docs.anthropic.com/en/docs/claude-code");
24
26
  failed = true;
25
27
  }
28
+ {
29
+ const cursor = await cursorAvailable();
30
+ if (!cursor.ok) {
31
+ console.warn("⚠ Cursor Agent CLI not found (optional for cursor_local agents)");
32
+ }
33
+ else {
34
+ const { spawnSync } = await import("node:child_process");
35
+ const bin = cursor.bin;
36
+ const statusArgs = path.basename(bin) === "cursor" ? ["agent", "status"] : ["status"];
37
+ const status = spawnSync(bin, statusArgs, {
38
+ encoding: "utf8",
39
+ timeout: 8000,
40
+ env: process.env,
41
+ });
42
+ const output = `${status.stdout ?? ""}${status.stderr ?? ""}${status.error?.message ?? ""}`;
43
+ const authIssue = cursorAuthIssueFromOutput(output);
44
+ if (authIssue) {
45
+ console.error(`✗ Cursor auth — ${authIssue.message}`);
46
+ console.error(" See docs/TROUBLESHOOTING.md#cursor-macos-keychain-auth");
47
+ failed = true;
48
+ }
49
+ else if (status.error || status.status !== 0) {
50
+ const detail = (status.error?.message ?? output.trim()) || `exit ${status.status}`;
51
+ console.error(`✗ Cursor auth — status failed (${detail})`);
52
+ console.error(" See docs/TROUBLESHOOTING.md#cursor-macos-keychain-auth");
53
+ failed = true;
54
+ }
55
+ else {
56
+ console.log(`✓ Cursor Agent CLI auth (${bin})`);
57
+ }
58
+ }
59
+ }
60
+ {
61
+ const { spawnSync } = await import("node:child_process");
62
+ const ghVer = spawnSync("gh", ["--version"], { encoding: "utf8" });
63
+ if (ghVer.error || ghVer.status !== 0) {
64
+ console.error("✗ GitHub CLI (`gh`) not found — install gh (required to open draft PRs)");
65
+ failed = true;
66
+ }
67
+ else {
68
+ const auth = spawnSync("gh", ["auth", "status"], { encoding: "utf8" });
69
+ const out = `${auth.stdout ?? ""}${auth.stderr ?? ""}`.toLowerCase();
70
+ if (auth.status !== 0 ||
71
+ out.includes("not logged in") ||
72
+ out.includes("failed to log in") ||
73
+ out.includes("token in keyring is invalid") ||
74
+ out.includes("re-authenticate")) {
75
+ console.error("✗ GitHub CLI auth — run: gh auth login -h github.com");
76
+ console.error(" (Issue workflows open draft PRs via gh; invalid auth wastes agent runs.)");
77
+ failed = true;
78
+ }
79
+ else {
80
+ console.log("✓ GitHub CLI auth");
81
+ }
82
+ }
83
+ }
26
84
  try {
27
85
  resolveServerEntry();
28
86
  console.log("✓ @agent-dealer/server entry");
package/dist/http.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare function resolveApiBase(): string;
2
+ export declare function apiFetch(path: string, opts?: {
3
+ method?: string;
4
+ body?: unknown;
5
+ }): Promise<unknown>;
package/dist/http.js ADDED
@@ -0,0 +1,19 @@
1
+ import { readRunState } from "./runtime-state.js";
2
+ import { loadProdEnvFile, resolveBundledListenPort } from "./env.js";
3
+ export function resolveApiBase() {
4
+ loadProdEnvFile();
5
+ const state = readRunState();
6
+ const port = state?.port ?? resolveBundledListenPort();
7
+ return `http://127.0.0.1:${port}`;
8
+ }
9
+ export async function apiFetch(path, opts) {
10
+ const res = await fetch(`${resolveApiBase()}${path}`, {
11
+ method: opts?.method ?? "GET",
12
+ headers: opts?.body ? { "content-type": "application/json" } : undefined,
13
+ body: opts?.body ? JSON.stringify(opts.body) : undefined,
14
+ });
15
+ const json = await res.json().catch(() => null);
16
+ if (!res.ok)
17
+ throw new Error(`API error ${res.status}: ${JSON.stringify(json)}`);
18
+ return json;
19
+ }
package/dist/index.js CHANGED
@@ -16,6 +16,18 @@ function printUsage() {
16
16
  agent-dealer doctor
17
17
  agent-dealer install [--to VERSION] [--migrate-cli] [--purge-global]
18
18
  agent-dealer upgrade [--to VERSION] [--yes] [--check]
19
+ agent-dealer agent list
20
+ agent-dealer issue create --title T --repo owner/repo --developer-agent ID --reviewer-agent ID [--description D] [--acceptance-criteria A] [--base-branch B] [--no-enqueue]
21
+ agent-dealer issue import --external-id ID --title T --repo owner/repo --developer-agent ID --reviewer-agent ID [--external-label L]
22
+ agent-dealer issue list [--status S1,S2]
23
+ agent-dealer issue show <id> [--include evidence]
24
+ agent-dealer issue start <id>
25
+ agent-dealer issue guide <id> --message M
26
+ agent-dealer queue add <issueId>
27
+ agent-dealer queue remove <issueId>
28
+ agent-dealer queue list
29
+ agent-dealer action list
30
+ agent-dealer action resolve <id> --choice C --by NAME
19
31
  agent-dealer --version
20
32
 
21
33
  Human control plane for agent execution.`);
@@ -123,6 +135,22 @@ export async function runCli(argv) {
123
135
  }
124
136
  return runUpgrade({ toVersion, yes, check });
125
137
  }
138
+ if (cmd === "agent") {
139
+ const { runAgentCommand } = await import("./agent.js");
140
+ return runAgentCommand(args.slice(1));
141
+ }
142
+ if (cmd === "issue") {
143
+ const { runIssueCommand } = await import("./issue.js");
144
+ return runIssueCommand(args.slice(1));
145
+ }
146
+ if (cmd === "queue") {
147
+ const { runQueueCommand } = await import("./queue.js");
148
+ return runQueueCommand(args.slice(1));
149
+ }
150
+ if (cmd === "action") {
151
+ const { runActionCommand } = await import("./action.js");
152
+ return runActionCommand(args.slice(1));
153
+ }
126
154
  console.error(`Unknown command: ${cmd}`);
127
155
  printUsage();
128
156
  return 1;
@@ -0,0 +1,35 @@
1
+ export type ParsedIssueArgs = {
2
+ subcommand: "create";
3
+ title: string;
4
+ repo: string;
5
+ developerAgentId: string;
6
+ reviewerAgentId: string;
7
+ description?: string;
8
+ acceptanceCriteria?: string;
9
+ baseBranch?: string;
10
+ enqueue: boolean;
11
+ } | {
12
+ subcommand: "import";
13
+ externalId: string;
14
+ externalLabel?: string;
15
+ title: string;
16
+ repo: string;
17
+ developerAgentId: string;
18
+ reviewerAgentId: string;
19
+ } | {
20
+ subcommand: "list";
21
+ status?: string;
22
+ } | {
23
+ subcommand: "show";
24
+ id: string;
25
+ includeEvidence: boolean;
26
+ } | {
27
+ subcommand: "start";
28
+ id: string;
29
+ } | {
30
+ subcommand: "guide";
31
+ id: string;
32
+ message: string;
33
+ };
34
+ export declare function parseIssueArgs(args: string[]): ParsedIssueArgs;
35
+ export declare function runIssueCommand(args: string[]): Promise<number>;
package/dist/issue.js ADDED
@@ -0,0 +1,143 @@
1
+ import { apiFetch } from "./http.js";
2
+ /**
3
+ * NOT-141: the hint reports what the server *did*, never what was requested. Printing
4
+ * "Queued for admission" off the request flag told operators a queue happened when a
5
+ * re-import had matched an existing issue and queued nothing.
6
+ */
7
+ function createOutcomeHint(result) {
8
+ if (!result.created) {
9
+ const matched = `Matched existing issue ${result.id} (${result.status}) — nothing new was created`;
10
+ switch (result.queue) {
11
+ // Enqueue is idempotent: an issue already waiting is not a queue action, so the hint
12
+ // must not claim one. `queue list` is the honest next step.
13
+ case "already_queued":
14
+ return `${matched}, and it was already in the admission queue — nothing was queued. \`agent-dealer queue list\` shows its position and wait reason.`;
15
+ case "enqueued":
16
+ return `${matched}; it was put back in the admission queue.`;
17
+ case "not_queued":
18
+ return `${matched} and nothing was queued.`;
19
+ }
20
+ }
21
+ const pass = result.priorPasses > 0
22
+ ? ` This is pass ${result.priorPasses + 1} on ${result.externalLabel ?? result.externalId} — ${result.priorPasses} earlier pass(es) already finished.`
23
+ : "";
24
+ return result.queue === "enqueued"
25
+ ? `Queued for admission — \`agent-dealer queue list\` shows position and wait reason.${pass}`
26
+ : `Created as a draft (not queued) — \`agent-dealer queue add <id>\` when it is ready.${pass}`;
27
+ }
28
+ function flag(args, name) {
29
+ const idx = args.indexOf(name);
30
+ return idx >= 0 ? args[idx + 1] : undefined;
31
+ }
32
+ export function parseIssueArgs(args) {
33
+ const [subcommand, ...rest] = args;
34
+ switch (subcommand) {
35
+ case "create":
36
+ case "import": {
37
+ const title = flag(rest, "--title");
38
+ const repo = flag(rest, "--repo");
39
+ const developerAgentId = flag(rest, "--developer-agent");
40
+ const reviewerAgentId = flag(rest, "--reviewer-agent");
41
+ if (!title || !repo || !developerAgentId || !reviewerAgentId) {
42
+ throw new Error(`${subcommand} requires --title, --repo, --developer-agent, --reviewer-agent`);
43
+ }
44
+ if (subcommand === "import") {
45
+ const externalId = flag(rest, "--external-id");
46
+ if (!externalId)
47
+ throw new Error("import requires --external-id");
48
+ return { subcommand: "import", externalId, externalLabel: flag(rest, "--external-label"), title, repo, developerAgentId, reviewerAgentId };
49
+ }
50
+ // NOT-118: create enqueues for admission by default; --no-enqueue leaves a draft out.
51
+ return { subcommand: "create", title, repo, developerAgentId, reviewerAgentId, description: flag(rest, "--description"), acceptanceCriteria: flag(rest, "--acceptance-criteria"), baseBranch: flag(rest, "--base-branch"), enqueue: !rest.includes("--no-enqueue") };
52
+ }
53
+ case "list": {
54
+ return { subcommand: "list", status: flag(rest, "--status") };
55
+ }
56
+ case "show": {
57
+ const id = rest[0];
58
+ if (!id)
59
+ throw new Error("show requires an issue id");
60
+ return { subcommand: "show", id, includeEvidence: rest.includes("--include") && rest[rest.indexOf("--include") + 1] === "evidence" };
61
+ }
62
+ case "start": {
63
+ const id = rest[0];
64
+ if (!id)
65
+ throw new Error("start requires an issue id");
66
+ return { subcommand: "start", id };
67
+ }
68
+ case "guide": {
69
+ const id = rest[0];
70
+ const message = flag(rest, "--message");
71
+ if (!id || !message)
72
+ throw new Error("guide requires an issue id and --message");
73
+ return { subcommand: "guide", id, message };
74
+ }
75
+ default:
76
+ throw new Error(`Unknown issue subcommand: ${subcommand}`);
77
+ }
78
+ }
79
+ export async function runIssueCommand(args) {
80
+ let parsed;
81
+ try {
82
+ parsed = parseIssueArgs(args);
83
+ }
84
+ catch (err) {
85
+ console.error(err.message);
86
+ return 1;
87
+ }
88
+ try {
89
+ switch (parsed.subcommand) {
90
+ case "create": {
91
+ const result = (await apiFetch("/api/issues", { method: "POST", body: { title: parsed.title, repo: parsed.repo, developerAgentId: parsed.developerAgentId, reviewerAgentId: parsed.reviewerAgentId, description: parsed.description, acceptanceCriteria: parsed.acceptanceCriteria, baseBranch: parsed.baseBranch, source: "agent", enqueue: parsed.enqueue } }));
92
+ console.log(JSON.stringify(result, null, 2));
93
+ // stdout stays pure JSON for agents that pipe it — the hint goes to stderr.
94
+ console.error(createOutcomeHint(result));
95
+ return 0;
96
+ }
97
+ case "import": {
98
+ // A re-import of a ticket already in flight answers 409 (apiFetch throws) — the
99
+ // operator sees the conflicting issue id instead of a 200 they cannot interpret.
100
+ const result = (await apiFetch("/api/issues", { method: "POST", body: { title: parsed.title, repo: parsed.repo, developerAgentId: parsed.developerAgentId, reviewerAgentId: parsed.reviewerAgentId, source: "linear", externalId: parsed.externalId, externalLabel: parsed.externalLabel } }));
101
+ console.log(JSON.stringify(result, null, 2));
102
+ console.error(createOutcomeHint(result));
103
+ return 0;
104
+ }
105
+ case "list": {
106
+ const query = parsed.status ? `?status=${encodeURIComponent(parsed.status)}` : "";
107
+ const result = await apiFetch(`/api/issues${query}`);
108
+ console.log(JSON.stringify(result, null, 2));
109
+ return 0;
110
+ }
111
+ case "show": {
112
+ const result = await apiFetch(`/api/issues/${parsed.id}`);
113
+ if (parsed.includeEvidence) {
114
+ const evidence = await apiFetch(`/api/issues/${parsed.id}/evidence`);
115
+ console.log(JSON.stringify({ ...result, evidence }, null, 2));
116
+ }
117
+ else {
118
+ console.log(JSON.stringify(result, null, 2));
119
+ }
120
+ return 0;
121
+ }
122
+ case "start": {
123
+ // NOT-118: start moves the issue to the front of the admission queue. It either
124
+ // admits it right away or leaves it waiting at position 1 — never a queue bypass.
125
+ const result = (await apiFetch(`/api/issues/${parsed.id}/start`, { method: "POST" }));
126
+ console.log(JSON.stringify(result, null, 2));
127
+ console.error(result.state === "admitted"
128
+ ? "Admitted — the workflow started."
129
+ : `Queued at position ${result.position}${result.waitReason ? ` — ${result.waitReason}` : ""}.`);
130
+ return 0;
131
+ }
132
+ case "guide": {
133
+ const result = await apiFetch(`/api/issues/${parsed.id}/guidance`, { method: "POST", body: { markdown: parsed.message } });
134
+ console.log(JSON.stringify(result, null, 2));
135
+ return 0;
136
+ }
137
+ }
138
+ }
139
+ catch (err) {
140
+ console.error(err.message);
141
+ return 1;
142
+ }
143
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,240 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { parseIssueArgs, runIssueCommand } from "./issue.js";
4
+ import { stubFetch } from "./test-fetch-stub.js";
5
+ test("parseIssueArgs: create requires --title and --repo", () => {
6
+ const parsed = parseIssueArgs(["create", "--title", "Fix bug", "--repo", "/repo", "--developer-agent", "a1", "--reviewer-agent", "a2"]);
7
+ assert.equal(parsed.subcommand, "create");
8
+ assert.equal(parsed.title, "Fix bug");
9
+ // NOT-118: queue by default.
10
+ assert.equal(parsed.enqueue, true);
11
+ });
12
+ test("parseIssueArgs: create --no-enqueue asks for a draft outside the queue", () => {
13
+ const parsed = parseIssueArgs(["create", "--title", "Draft", "--repo", "/repo", "--developer-agent", "a1", "--reviewer-agent", "a2", "--no-enqueue"]);
14
+ assert.equal(parsed.enqueue, false);
15
+ });
16
+ test("parseIssueArgs: show requires an id", () => {
17
+ const parsed = parseIssueArgs(["show", "issue-123"]);
18
+ assert.equal(parsed.subcommand, "show");
19
+ assert.equal(parsed.id, "issue-123");
20
+ });
21
+ test("parseIssueArgs: unknown subcommand throws", () => {
22
+ assert.throws(() => parseIssueArgs(["bogus"]));
23
+ });
24
+ test("parseIssueArgs: guide requires an id and --message", () => {
25
+ const parsed = parseIssueArgs(["guide", "issue-123", "--message", "prioritize this"]);
26
+ assert.equal(parsed.subcommand, "guide");
27
+ assert.equal(parsed.message, "prioritize this");
28
+ });
29
+ test("parseIssueArgs: list accepts an optional --status", () => {
30
+ assert.deepEqual(parseIssueArgs(["list"]), { subcommand: "list", status: undefined });
31
+ assert.deepEqual(parseIssueArgs(["list", "--status", "ready,needs_human"]), {
32
+ subcommand: "list",
33
+ status: "ready,needs_human",
34
+ });
35
+ });
36
+ test("parseIssueArgs: start requires an id", () => {
37
+ const parsed = parseIssueArgs(["start", "issue-123"]);
38
+ assert.equal(parsed.subcommand, "start");
39
+ assert.equal(parsed.id, "issue-123");
40
+ assert.throws(() => parseIssueArgs(["start"]));
41
+ });
42
+ test("issue list calls GET /api/issues and exits 0", async () => {
43
+ const stub = stubFetch("/api/issues", "GET", [{ id: "i1", status: "ready" }]);
44
+ try {
45
+ const code = await runIssueCommand(["list"]);
46
+ assert.equal(code, 0);
47
+ stub.assertCalled();
48
+ }
49
+ finally {
50
+ stub.restore();
51
+ }
52
+ });
53
+ test("issue list forwards --status as a query param", async () => {
54
+ const stub = stubFetch(/\/api\/issues\?status=ready%2Cneeds_human$/, "GET", []);
55
+ try {
56
+ await runIssueCommand(["list", "--status", "ready,needs_human"]);
57
+ stub.assertCalled();
58
+ }
59
+ finally {
60
+ stub.restore();
61
+ }
62
+ });
63
+ test("issue create sends enqueue:true by default and enqueue:false with --no-enqueue", async () => {
64
+ const enqueued = stubFetch("/api/issues", "POST", { id: "i1", status: "ready" });
65
+ try {
66
+ assert.equal(await runIssueCommand(["create", "--title", "T", "--repo", "/r", "--developer-agent", "a1", "--reviewer-agent", "a2"]), 0);
67
+ assert.equal(enqueued.assertCalled().enqueue, true);
68
+ }
69
+ finally {
70
+ enqueued.restore();
71
+ }
72
+ const draft = stubFetch("/api/issues", "POST", { id: "i2", status: "ready" });
73
+ try {
74
+ await runIssueCommand(["create", "--title", "T", "--repo", "/r", "--developer-agent", "a1", "--reviewer-agent", "a2", "--no-enqueue"]);
75
+ assert.equal(draft.assertCalled().enqueue, false);
76
+ }
77
+ finally {
78
+ draft.restore();
79
+ }
80
+ });
81
+ /** Runs the command with stderr captured — the hints go to stderr, stdout stays JSON. */
82
+ async function captureStderr(args) {
83
+ const errs = [];
84
+ const logs = [];
85
+ const originalError = console.error;
86
+ const originalLog = console.log;
87
+ console.error = (message) => errs.push(String(message));
88
+ console.log = (message) => logs.push(String(message));
89
+ try {
90
+ const code = await runIssueCommand(args);
91
+ return { code, err: errs.join("\n"), out: logs.join("\n") };
92
+ }
93
+ finally {
94
+ console.error = originalError;
95
+ console.log = originalLog;
96
+ }
97
+ }
98
+ const createArgs = ["--title", "T", "--repo", "/r", "--developer-agent", "a1", "--reviewer-agent", "a2"];
99
+ test("NOT-141: the create hint reports what the server did, not what was requested", async () => {
100
+ const matched = stubFetch("/api/issues", "POST", { id: "i1", status: "developing", created: false, queue: "not_queued" });
101
+ try {
102
+ const { code, err } = await captureStderr(["create", ...createArgs]);
103
+ assert.equal(code, 0);
104
+ assert.doesNotMatch(err, /Queued for admission/);
105
+ assert.match(err, /Matched existing issue i1 \(developing\)/);
106
+ assert.match(err, /nothing was queued/);
107
+ }
108
+ finally {
109
+ matched.restore();
110
+ }
111
+ // The issue was already waiting: enqueue changed nothing, so the hint must not report a
112
+ // queue action of any kind — the bug this ticket is about, one step down the line.
113
+ const alreadyQueued = stubFetch("/api/issues", "POST", { id: "i2", status: "ready", created: false, queue: "already_queued" });
114
+ try {
115
+ const { err } = await captureStderr(["create", ...createArgs]);
116
+ assert.doesNotMatch(err, /Queued for admission/);
117
+ assert.doesNotMatch(err, /re-queued|was put back/);
118
+ assert.match(err, /already in the admission queue — nothing was queued/);
119
+ }
120
+ finally {
121
+ alreadyQueued.restore();
122
+ }
123
+ const requeued = stubFetch("/api/issues", "POST", { id: "i6", status: "ready", created: false, queue: "enqueued" });
124
+ try {
125
+ const { err } = await captureStderr(["create", ...createArgs]);
126
+ assert.doesNotMatch(err, /Queued for admission/);
127
+ assert.match(err, /it was put back in the admission queue/);
128
+ }
129
+ finally {
130
+ requeued.restore();
131
+ }
132
+ const fresh = stubFetch("/api/issues", "POST", { id: "i3", status: "ready", created: true, queue: "enqueued", priorPasses: 0 });
133
+ try {
134
+ const { err } = await captureStderr(["create", ...createArgs]);
135
+ assert.match(err, /Queued for admission/);
136
+ assert.doesNotMatch(err, /pass/);
137
+ }
138
+ finally {
139
+ fresh.restore();
140
+ }
141
+ });
142
+ test("NOT-141: a second pass on a ticket is named as such, not printed like a first import", async () => {
143
+ const secondPass = stubFetch("/api/issues", "POST", {
144
+ id: "i5",
145
+ status: "ready",
146
+ created: true,
147
+ queue: "enqueued",
148
+ priorPasses: 1,
149
+ externalId: "lin-uuid",
150
+ externalLabel: "NOT-128",
151
+ });
152
+ try {
153
+ const { err } = await captureStderr(["import", "--external-id", "lin-uuid", ...createArgs]);
154
+ assert.match(err, /Queued for admission/);
155
+ assert.match(err, /This is pass 2 on NOT-128 — 1 earlier pass\(es\) already finished\./);
156
+ }
157
+ finally {
158
+ secondPass.restore();
159
+ }
160
+ });
161
+ test("NOT-141: import reports a fresh row, and surfaces the 409 conflict instead of exiting 0", async () => {
162
+ const created = stubFetch("/api/issues", "POST", { id: "i4", status: "ready", created: true, queue: "enqueued" });
163
+ try {
164
+ const { code, err } = await captureStderr(["import", "--external-id", "NOT-1", ...createArgs]);
165
+ assert.equal(code, 0);
166
+ assert.match(err, /Queued for admission/);
167
+ }
168
+ finally {
169
+ created.restore();
170
+ }
171
+ const conflict = stubFetch("/api/issues", "POST", { error: "Issue i4 is already tracking linear NOT-1 (developing)", existingIssueId: "i4", existingIssueStatus: "developing" }, 409);
172
+ try {
173
+ const { code, err } = await captureStderr(["import", "--external-id", "NOT-1", ...createArgs]);
174
+ assert.equal(code, 1);
175
+ assert.match(err, /already tracking linear NOT-1/);
176
+ assert.doesNotMatch(err, /Queued for admission/);
177
+ }
178
+ finally {
179
+ conflict.restore();
180
+ }
181
+ });
182
+ test("issue start prints the admitted response shape and exits 0", async () => {
183
+ const stub = stubFetch("/api/issues/issue-123/start", "POST", {
184
+ state: "admitted",
185
+ instance: { id: "wf-1" },
186
+ workItem: { id: "work-1", kind: "developer" },
187
+ });
188
+ const logs = [];
189
+ const originalLog = console.log;
190
+ console.log = (message) => logs.push(String(message));
191
+ try {
192
+ const code = await runIssueCommand(["start", "issue-123"]);
193
+ assert.equal(code, 0);
194
+ stub.assertCalled();
195
+ const printed = JSON.parse(logs.join("\n"));
196
+ assert.equal(printed.state, "admitted");
197
+ assert.equal(printed.instance.id, "wf-1");
198
+ }
199
+ finally {
200
+ console.log = originalLog;
201
+ stub.restore();
202
+ }
203
+ });
204
+ test("issue start prints the queued response shape (position + wait reason) and still exits 0", async () => {
205
+ const stub = stubFetch("/api/issues/issue-123/start", "POST", {
206
+ state: "queued",
207
+ position: 1,
208
+ waitReason: "waiting for slot — running: Other issue",
209
+ });
210
+ const logs = [];
211
+ const errs = [];
212
+ const originalLog = console.log;
213
+ const originalError = console.error;
214
+ console.log = (message) => logs.push(String(message));
215
+ console.error = (message) => errs.push(String(message));
216
+ try {
217
+ const code = await runIssueCommand(["start", "issue-123"]);
218
+ assert.equal(code, 0);
219
+ const printed = JSON.parse(logs.join("\n"));
220
+ assert.equal(printed.state, "queued");
221
+ assert.equal(printed.position, 1);
222
+ assert.match(printed.waitReason, /waiting for slot/);
223
+ assert.match(errs.join("\n"), /Queued at position 1 — waiting for slot/);
224
+ }
225
+ finally {
226
+ console.log = originalLog;
227
+ console.error = originalError;
228
+ stub.restore();
229
+ }
230
+ });
231
+ test("issue start returns nonzero on API failure", async () => {
232
+ const stub = stubFetch("/api/issues/issue-123/start", "POST", { error: "not ready" }, 400);
233
+ try {
234
+ const code = await runIssueCommand(["start", "issue-123"]);
235
+ assert.equal(code, 1);
236
+ }
237
+ finally {
238
+ stub.restore();
239
+ }
240
+ });
@@ -0,0 +1 @@
1
+ export {};