svamp-cli 0.2.182 → 0.2.184

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 (25) hide show
  1. package/dist/{agentCommands-Cf9VhA8Q.mjs → agentCommands-CMUhRF1u.mjs} +5 -5
  2. package/dist/{auth-CL9Qjhq6.mjs → auth-BLrGgRMn.mjs} +1 -1
  3. package/dist/cli.mjs +60 -60
  4. package/dist/{commands-D8uYJqvJ.mjs → commands-B7KfL975.mjs} +1 -1
  5. package/dist/{commands-BUg7EHp0.mjs → commands-BKn8V28j.mjs} +1 -1
  6. package/dist/{commands-B_P2D4A3.mjs → commands-BRw97I6V.mjs} +2 -2
  7. package/dist/{commands-CM0JkQlm.mjs → commands-BSkCO1ZU.mjs} +5 -5
  8. package/dist/{commands-HXmFkc9h.mjs → commands-CkLF-9Cz.mjs} +1 -1
  9. package/dist/{commands-Dxr3C5Y4.mjs → commands-D4oFWE2l.mjs} +2 -2
  10. package/dist/{commands-f-gIOx_m.mjs → commands-d8OWAyfD.mjs} +20 -3
  11. package/dist/{fleet-wmEx5X-B.mjs → fleet-D3Ffuh1f.mjs} +1 -1
  12. package/dist/{frpc-Clge8vu4.mjs → frpc-CcCnRJRo.mjs} +1 -1
  13. package/dist/{headlessCli-Bb3_TKKD.mjs → headlessCli-B-QKmPuI.mjs} +2 -2
  14. package/dist/index.mjs +1 -1
  15. package/dist/{package-C35XarmI.mjs → package-DrqD4DHH.mjs} +1 -1
  16. package/dist/{rpc-BnH7Ljou.mjs → rpc-B_HEACXE.mjs} +8 -2
  17. package/dist/{rpc-oZAY4e3e.mjs → rpc-DrT7cP5y.mjs} +1 -1
  18. package/dist/{run-CFgCIth9.mjs → run-D-9H4ceY.mjs} +1 -1
  19. package/dist/{run-C_2jxzE5.mjs → run-FQLbrWFm.mjs} +173 -96
  20. package/dist/{scheduler-CYu4tzBf.mjs → scheduler-BSXOwdjt.mjs} +3 -2
  21. package/dist/{serveCommands-CRntd5Tp.mjs → serveCommands-DPGI-Z8a.mjs} +5 -5
  22. package/dist/{serveManager-CvlOyKBf.mjs → serveManager-C53SwFLY.mjs} +2 -2
  23. package/dist/{sideband-Bqe-7HxJ.mjs → sideband-pj_YFwZr.mjs} +1 -1
  24. package/dist/{store-BPL__e4D.mjs → store-D-fJ9ANL.mjs} +17 -2
  25. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { m as resolveProjectRoot } from './run-C_2jxzE5.mjs';
3
- import { w as workflowSteps, r as removeWorkflow, g as getWorkflow, l as listWorkflows, s as saveWorkflow, a as rawWorkflow } from './store-BPL__e4D.mjs';
2
+ import { m as resolveProjectRoot } from './run-FQLbrWFm.mjs';
3
+ import { w as workflowSteps, s as setWorkflowEnabled, i as isWorkflowEnabled, r as removeWorkflow, g as getWorkflow, l as listWorkflows, a as saveWorkflow, b as rawWorkflow } from './store-D-fJ9ANL.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
6
6
  import 'fs';
@@ -92,7 +92,7 @@ async function workflowCommand(args) {
92
92
  }
93
93
  for (const wf of all) {
94
94
  const n = workflowSteps(wf).length;
95
- console.log(`${wf.name} [${describeOn(wf.on)}] ${n} step${n === 1 ? "" : "s"}`);
95
+ console.log(`${wf.name} [${describeOn(wf.on)}] ${n} step${n === 1 ? "" : "s"}${isWorkflowEnabled(wf) ? "" : " (disabled)"}`);
96
96
  }
97
97
  break;
98
98
  }
@@ -117,6 +117,22 @@ async function workflowCommand(args) {
117
117
  console.log(removeWorkflow(root, name) ? `Removed workflow "${name}".` : `Workflow not found: ${name}`);
118
118
  break;
119
119
  }
120
+ case "enable":
121
+ case "disable": {
122
+ const name = positional(rest)[0];
123
+ if (!name) {
124
+ console.error(`usage: svamp workflow ${sub} <name>`);
125
+ process.exit(1);
126
+ }
127
+ const wf = setWorkflowEnabled(root, name, sub === "enable");
128
+ if (!wf) {
129
+ console.error(`Workflow not found: ${name}`);
130
+ process.exit(1);
131
+ }
132
+ if (json) console.log(JSON.stringify(wf));
133
+ else console.log(`Workflow "${name}" is now ${isWorkflowEnabled(wf) ? "active" : "disabled"}.`);
134
+ break;
135
+ }
120
136
  case "run": {
121
137
  const name = positional(rest)[0];
122
138
  const wf = name ? getWorkflow(root, name) : null;
@@ -162,6 +178,7 @@ async function workflowCommand(args) {
162
178
  ' add <name> --run "<cmd>" [--run "<cmd2>"] [--on schedule|dispatch|channel|issue]',
163
179
  ' [--cron "0 9 * * 1-5"] [--channel <name>] [--issue ready,closed,labeled]',
164
180
  " list | show <name> | remove <name>",
181
+ " enable <name> | disable <name> # toggle active/disabled (scheduler skips disabled)",
165
182
  " run <name> # execute the workflow's run-only steps now",
166
183
  ' guide # the "create a workflow from a request" procedure (so triggers can stay short)',
167
184
  "",
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-C_2jxzE5.mjs';
4
+ import { c as connectToHypha } from './run-FQLbrWFm.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
4
4
  import { join } from 'path';
5
5
  import { homedir, platform, arch } from 'os';
6
6
  import { randomUUID, createHash } from 'crypto';
7
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-C_2jxzE5.mjs';
7
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-FQLbrWFm.mjs';
8
8
  import 'fs/promises';
9
9
  import 'url';
10
10
  import 'node:crypto';
@@ -1,5 +1,5 @@
1
- import { M as resolveModel, _ as describeMisconfiguration, $ as buildMachineDeps } from './run-C_2jxzE5.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-Bqe-7HxJ.mjs';
1
+ import { M as resolveModel, _ as describeMisconfiguration, $ as buildMachineDeps } from './run-FQLbrWFm.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-pj_YFwZr.mjs';
3
3
  import { WebSocket } from 'ws';
4
4
  import { execSync, spawn } from 'child_process';
5
5
  import 'os';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-C_2jxzE5.mjs';
1
+ export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-FQLbrWFm.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.182";
2
+ var version = "0.2.184";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { m as resolveProjectRoot } from './run-C_2jxzE5.mjs';
3
- import { g as getWorkflow, w as workflowSteps, r as removeWorkflow, s as saveWorkflow, a as rawWorkflow, l as listWorkflows } from './store-BPL__e4D.mjs';
2
+ import { m as resolveProjectRoot } from './run-FQLbrWFm.mjs';
3
+ import { g as getWorkflow, w as workflowSteps, s as setWorkflowEnabled, r as removeWorkflow, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-D-fJ9ANL.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
6
6
  import 'fs';
@@ -53,6 +53,12 @@ function workflowRpc(cwd, params) {
53
53
  }
54
54
  case "remove":
55
55
  return { removed: removeWorkflow(root, String(params.name)) };
56
+ case "enable":
57
+ case "disable": {
58
+ const wf = setWorkflowEnabled(root, String(params.name), op === "enable");
59
+ if (!wf) throw new Error(`Workflow not found: ${params.name}`);
60
+ return wf;
61
+ }
56
62
  case "run": {
57
63
  const wf = getWorkflow(root, String(params.name));
58
64
  if (!wf) throw new Error(`Workflow not found: ${params.name}`);
@@ -1,4 +1,4 @@
1
- import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues } from './run-C_2jxzE5.mjs';
1
+ import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as addComment, p as addIssue, q as listIssues, t as searchIssues } from './run-FQLbrWFm.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as composeSessionId, a1 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a2 as generateHookSettings } from './run-C_2jxzE5.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a0 as composeSessionId, a1 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a2 as generateHookSettings } from './run-FQLbrWFm.mjs';
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
@@ -89,7 +89,15 @@ function setOwnerWorkspace(ws) {
89
89
  ownerWorkspace = ws || void 0;
90
90
  }
91
91
  function isSameOwnerWorkspace(context) {
92
- return !!ownerWorkspace && !!context?.ws && context.ws === ownerWorkspace;
92
+ const ws = context?.ws;
93
+ if (!ownerWorkspace || ownerWorkspace === "*") return false;
94
+ if (!ws || ws === "*") return false;
95
+ if (ws !== ownerWorkspace) return false;
96
+ const from = context?.from;
97
+ if (!from) return false;
98
+ const fromWs = String(from).split("/")[0];
99
+ if (!fromWs || fromWs === "*") return false;
100
+ return fromWs === ws;
93
101
  }
94
102
  function resolveRoleLevel(sharing, userEmail) {
95
103
  let level = -1;
@@ -2712,7 +2720,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2712
2720
  const tunnels = handlers.tunnels;
2713
2721
  if (!tunnels) throw new Error("Tunnel management not available");
2714
2722
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2715
- const { FrpcTunnel } = await import('./frpc-Clge8vu4.mjs');
2723
+ const { FrpcTunnel } = await import('./frpc-CcCnRJRo.mjs');
2716
2724
  const tunnel = new FrpcTunnel({
2717
2725
  name: params.name,
2718
2726
  ports: params.ports,
@@ -3159,7 +3167,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3159
3167
  }
3160
3168
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3161
3169
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3162
- const { toolsForRole } = await import('./sideband-Bqe-7HxJ.mjs');
3170
+ const { toolsForRole } = await import('./sideband-pj_YFwZr.mjs');
3163
3171
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3164
3172
  return fmt(r2);
3165
3173
  }
@@ -3258,7 +3266,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3258
3266
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3259
3267
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3260
3268
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3261
- const { queryCore } = await import('./commands-D8uYJqvJ.mjs');
3269
+ const { queryCore } = await import('./commands-B7KfL975.mjs');
3262
3270
  const timeout = c.reply?.timeout_sec || 120;
3263
3271
  let result;
3264
3272
  try {
@@ -3528,6 +3536,35 @@ function applyInboxClear(inbox, opts) {
3528
3536
  return { kept, removed: inbox.length - kept.length };
3529
3537
  }
3530
3538
 
3539
+ function envInt(name, fallback) {
3540
+ const raw = process.env[name];
3541
+ if (raw === void 0 || raw === "") return fallback;
3542
+ const n = parseInt(raw, 10);
3543
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
3544
+ }
3545
+ function getRateLimitRetryConfig() {
3546
+ return {
3547
+ maxRetries: envInt("SVAMP_RATELIMIT_MAX_RETRIES", 10),
3548
+ baseMs: envInt("SVAMP_RATELIMIT_BASE_MS", 5e3),
3549
+ capMs: envInt("SVAMP_RATELIMIT_CAP_MS", 12e4)
3550
+ };
3551
+ }
3552
+ function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Math.random) {
3553
+ const a = Math.max(0, Math.floor(attempt));
3554
+ const expo = Math.min(a, 30);
3555
+ const nominal = Math.min(cfg.capMs, cfg.baseMs * Math.pow(2, expo));
3556
+ const half = nominal / 2;
3557
+ return Math.round(half + rng() * half);
3558
+ }
3559
+ function isRetryableRateLimit(text, apiErrorStatus) {
3560
+ const t = (text || "").toLowerCase();
3561
+ const hardLimit = t.includes("quota") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
3562
+ if (hardLimit) return false;
3563
+ const authIssue = t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
3564
+ if (authIssue) return false;
3565
+ return apiErrorStatus === 429 || apiErrorStatus === 529 || apiErrorStatus === 503 || /\b(429|529|503)\b/.test(t) || t.includes("temporarily limiting") || t.includes("not your usage limit") || t.includes("overloaded") || t.includes("overload") || t.includes("too many requests") || t.includes("server is busy") || t.includes("rate limit") || t.includes("rate-limit") || t.includes("rate limited");
3566
+ }
3567
+
3531
3568
  const PARTICIPANTS_CHANNEL_ID = "sys-participants";
3532
3569
  function channelPublicView(c) {
3533
3570
  return { id: c.id, name: c.name, description: c.description, identity: { mode: c.identity?.mode }, action: c.action?.kind, bind: c.bind };
@@ -3858,7 +3895,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3858
3895
  }
3859
3896
  };
3860
3897
  ensureParticipantsChannel(initialMetadata.sharing);
3861
- const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6, timeoutMs = 6e4) => {
3898
+ const forkClaudeOnce = async (prompt, claudeSessionId, cwd, maxTurns, timeoutMs) => {
3862
3899
  const { spawn } = await import('child_process');
3863
3900
  return new Promise((resolve) => {
3864
3901
  const child = spawn("claude", [
@@ -3895,14 +3932,37 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3895
3932
  }
3896
3933
  try {
3897
3934
  const result = JSON.parse(stdout);
3935
+ if (result && result.is_error) {
3936
+ resolve({ success: false, error: String(result.result || result.error || stderr || "error") });
3937
+ return;
3938
+ }
3898
3939
  resolve({ success: true, text: result.result || result.text || stdout });
3899
3940
  } catch {
3941
+ if (code !== 0) {
3942
+ resolve({ success: false, error: stderr || stdout.trim() || `claude exited with code ${code}` });
3943
+ return;
3944
+ }
3900
3945
  resolve({ success: true, text: stdout.trim() });
3901
3946
  }
3902
3947
  });
3903
3948
  child.on("error", (err) => resolve({ success: false, error: err.message }));
3904
3949
  });
3905
3950
  };
3951
+ const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6, timeoutMs = 6e4) => {
3952
+ const cfg2 = getRateLimitRetryConfig();
3953
+ let last = { success: false, error: "" };
3954
+ for (let attempt = 0; ; attempt++) {
3955
+ last = await forkClaudeOnce(prompt, claudeSessionId, cwd, maxTurns, timeoutMs);
3956
+ if (last.success) return last;
3957
+ if (!isRetryableRateLimit(last.error || "") || attempt >= cfg2.maxRetries) return last;
3958
+ const delay = computeRetryDelayMs(attempt, cfg2);
3959
+ try {
3960
+ console.error(`[btw] rate-limited \u2014 holding ${Math.round(delay / 1e3)}s then retrying (attempt ${attempt + 1}/${cfg2.maxRetries})`);
3961
+ } catch {
3962
+ }
3963
+ await new Promise((r) => setTimeout(r, delay));
3964
+ }
3965
+ };
3906
3966
  const performEdit = async (target, newText) => {
3907
3967
  if (!callbacks.onEditTranscript) return { success: false, message: "Editing history is not supported for this session." };
3908
3968
  const info = editableInfo(target);
@@ -10214,35 +10274,6 @@ function resolveContextWindow(opts) {
10214
10274
  return candidate;
10215
10275
  }
10216
10276
 
10217
- function envInt(name, fallback) {
10218
- const raw = process.env[name];
10219
- if (raw === void 0 || raw === "") return fallback;
10220
- const n = parseInt(raw, 10);
10221
- return Number.isFinite(n) && n >= 0 ? n : fallback;
10222
- }
10223
- function getRateLimitRetryConfig() {
10224
- return {
10225
- maxRetries: envInt("SVAMP_RATELIMIT_MAX_RETRIES", 10),
10226
- baseMs: envInt("SVAMP_RATELIMIT_BASE_MS", 5e3),
10227
- capMs: envInt("SVAMP_RATELIMIT_CAP_MS", 12e4)
10228
- };
10229
- }
10230
- function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Math.random) {
10231
- const a = Math.max(0, Math.floor(attempt));
10232
- const expo = Math.min(a, 30);
10233
- const nominal = Math.min(cfg.capMs, cfg.baseMs * Math.pow(2, expo));
10234
- const half = nominal / 2;
10235
- return Math.round(half + rng() * half);
10236
- }
10237
- function isRetryableRateLimit(text, apiErrorStatus) {
10238
- const t = (text || "").toLowerCase();
10239
- const hardLimit = t.includes("quota") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
10240
- if (hardLimit) return false;
10241
- const authIssue = t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
10242
- if (authIssue) return false;
10243
- return apiErrorStatus === 429 || apiErrorStatus === 529 || apiErrorStatus === 503 || /\b(429|529|503)\b/.test(t) || t.includes("temporarily limiting") || t.includes("not your usage limit") || t.includes("overloaded") || t.includes("overload") || t.includes("too many requests") || t.includes("server is busy") || t.includes("rate limit") || t.includes("rate-limit") || t.includes("rate limited");
10244
- }
10245
-
10246
10277
  const SVAMP_HOME$1 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
10247
10278
  function generateHookSettings(portOrOptions = {}) {
10248
10279
  const opts = typeof portOrOptions === "number" ? { sessionStartPort: portOrOptions } : portOrOptions;
@@ -11581,7 +11612,7 @@ async function startDaemon(options) {
11581
11612
  saveExposedTunnels(list);
11582
11613
  }
11583
11614
  async function createExposedTunnel(spec) {
11584
- const { FrpcTunnel } = await import('./frpc-Clge8vu4.mjs');
11615
+ const { FrpcTunnel } = await import('./frpc-CcCnRJRo.mjs');
11585
11616
  const tunnel = new FrpcTunnel({
11586
11617
  name: spec.name,
11587
11618
  ports: spec.ports,
@@ -11601,7 +11632,7 @@ async function startDaemon(options) {
11601
11632
  return tunnel;
11602
11633
  }
11603
11634
  const tunnelRecreateState = /* @__PURE__ */ new Map();
11604
- const { ServeManager } = await import('./serveManager-CvlOyKBf.mjs');
11635
+ const { ServeManager } = await import('./serveManager-C53SwFLY.mjs');
11605
11636
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11606
11637
  ensureAutoInstalledSkills(logger).catch(() => {
11607
11638
  });
@@ -12955,73 +12986,119 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
12955
12986
  return;
12956
12987
  }
12957
12988
  if (msgMeta?.btw && claudeResumeId) {
12989
+ const btwResumeId = claudeResumeId;
12958
12990
  logger.log(`[Session ${sessionId}] /btw side-channel: "${text.substring(0, 80)}..."`);
12959
12991
  sessionService.pushMessage(
12960
12992
  { type: "message", message: `/btw ${text}`, level: "btw-question" },
12961
12993
  "event"
12962
12994
  );
12963
12995
  signalProcessing(true);
12964
- const btwArgs = [
12965
- "--print",
12966
- text,
12967
- "--resume",
12968
- claudeResumeId,
12969
- "--fork-session",
12970
- "--no-session-persistence",
12971
- // /btw is non-interactive; without bypass the
12972
- // forked Claude pauses on tool prompts and the
12973
- // user just sees a hanging side-channel.
12974
- "--permission-mode",
12975
- "bypassPermissions",
12976
- "--output-format",
12977
- "stream-json",
12978
- "--verbose"
12979
- ];
12980
- const btwProcess = spawn$1("claude", btwArgs, {
12981
- cwd: directory,
12982
- env: { ...process.env },
12983
- stdio: ["pipe", "pipe", "pipe"]
12984
- });
12985
- let btwResult = "";
12986
- let btwBuffer = "";
12987
- btwProcess.stdout.on("data", (chunk) => {
12988
- btwBuffer += chunk.toString();
12989
- let newlineIdx;
12990
- while ((newlineIdx = btwBuffer.indexOf("\n")) !== -1) {
12991
- const line = btwBuffer.substring(0, newlineIdx);
12992
- btwBuffer = btwBuffer.substring(newlineIdx + 1);
12993
- if (!line.trim()) continue;
12994
- try {
12995
- const msg = JSON.parse(line);
12996
- if (msg.type === "assistant" && msg.message?.content) {
12997
- for (const block of msg.message.content) {
12998
- if (block.type === "text" && block.text) {
12999
- btwResult += block.text;
12996
+ const runBtwOnce = () => new Promise((resolve2, reject) => {
12997
+ const btwArgs = [
12998
+ "--print",
12999
+ text,
13000
+ "--resume",
13001
+ btwResumeId,
13002
+ "--fork-session",
13003
+ "--no-session-persistence",
13004
+ // /btw is non-interactive; without bypass the forked Claude
13005
+ // pauses on tool prompts and the user sees a hanging side-channel.
13006
+ "--permission-mode",
13007
+ "bypassPermissions",
13008
+ "--output-format",
13009
+ "stream-json",
13010
+ "--verbose"
13011
+ ];
13012
+ const btwProcess = spawn$1("claude", btwArgs, {
13013
+ cwd: directory,
13014
+ env: { ...process.env },
13015
+ stdio: ["pipe", "pipe", "pipe"]
13016
+ });
13017
+ let btwResult = "";
13018
+ let btwBuffer = "";
13019
+ let btwStderr = "";
13020
+ let settled = false;
13021
+ btwProcess.stderr?.on("data", (chunk) => {
13022
+ btwStderr += chunk.toString();
13023
+ });
13024
+ btwProcess.stdout.on("data", (chunk) => {
13025
+ btwBuffer += chunk.toString();
13026
+ let newlineIdx;
13027
+ while ((newlineIdx = btwBuffer.indexOf("\n")) !== -1) {
13028
+ const line = btwBuffer.substring(0, newlineIdx);
13029
+ btwBuffer = btwBuffer.substring(newlineIdx + 1);
13030
+ if (!line.trim()) continue;
13031
+ try {
13032
+ const msg = JSON.parse(line);
13033
+ if (msg.type === "assistant" && msg.message?.content) {
13034
+ for (const block of msg.message.content) {
13035
+ if (block.type === "text" && block.text) {
13036
+ btwResult += block.text;
13037
+ }
13038
+ }
13039
+ } else if (msg.type === "result") {
13040
+ if (settled) continue;
13041
+ settled = true;
13042
+ if (msg.is_error) {
13043
+ const err = new Error(String(msg.result || "error"));
13044
+ err.retryable = isRetryableRateLimit(String(msg.result || ""), msg.api_error_status);
13045
+ reject(err);
13046
+ } else {
13047
+ resolve2(btwResult || msg.result || "(no response)");
13000
13048
  }
13001
13049
  }
13002
- } else if (msg.type === "result") {
13003
- const finalText = btwResult || msg.result || "(no response)";
13004
- sessionService.pushMessage(
13005
- { type: "message", message: finalText, level: "btw-answer" },
13006
- "event"
13007
- );
13008
- signalProcessing(false);
13009
- sessionService.sendSessionEnd();
13010
- logger.log(`[Session ${sessionId}] /btw complete (${finalText.length} chars)`);
13050
+ } catch {
13011
13051
  }
13012
- } catch {
13013
13052
  }
13014
- }
13053
+ });
13054
+ btwProcess.on("exit", (code) => {
13055
+ if (settled) return;
13056
+ settled = true;
13057
+ if (code === 0) {
13058
+ resolve2(btwResult || "(no response)");
13059
+ return;
13060
+ }
13061
+ const detail = btwStderr.trim() || btwResult.trim();
13062
+ const err = new Error(detail || `claude exited with code ${code}`);
13063
+ err.retryable = isRetryableRateLimit(detail || "");
13064
+ reject(err);
13065
+ });
13066
+ btwProcess.on("error", (e) => {
13067
+ if (settled) return;
13068
+ settled = true;
13069
+ const err = new Error(e.message);
13070
+ err.retryable = false;
13071
+ reject(err);
13072
+ });
13015
13073
  });
13016
- btwProcess.on("exit", (code) => {
13017
- if (code !== 0) {
13018
- sessionService.pushMessage(
13019
- { type: "message", message: `/btw failed (exit code ${code})`, level: "error" },
13020
- "event"
13021
- );
13022
- signalProcessing(false);
13023
- sessionService.sendSessionEnd();
13074
+ (async () => {
13075
+ let lastErr = null;
13076
+ for (let attempt = 0; ; attempt++) {
13077
+ try {
13078
+ const finalText = await runBtwOnce();
13079
+ sessionService.pushMessage(
13080
+ { type: "message", message: finalText, level: "btw-answer" },
13081
+ "event"
13082
+ );
13083
+ logger.log(`[Session ${sessionId}] /btw complete (${finalText.length} chars)`);
13084
+ return;
13085
+ } catch (e) {
13086
+ lastErr = e;
13087
+ if (!e?.retryable || attempt >= RATELIMIT_CFG.maxRetries) break;
13088
+ const delayMs = computeRetryDelayMs(attempt, RATELIMIT_CFG);
13089
+ logger.log(`[Session ${sessionId}] /btw rate-limited \u2014 holding ${Math.round(delayMs / 1e3)}s then retrying (attempt ${attempt + 1}/${RATELIMIT_CFG.maxRetries})`);
13090
+ await new Promise((r) => setTimeout(r, delayMs));
13091
+ }
13024
13092
  }
13093
+ const detail = (lastErr?.message || "unknown error").toString().slice(0, 500);
13094
+ sessionService.pushMessage(
13095
+ { type: "message", message: `/btw failed: ${detail}`, level: "error" },
13096
+ "event"
13097
+ );
13098
+ logger.log(`[Session ${sessionId}] /btw failed: ${detail}`);
13099
+ })().finally(() => {
13100
+ signalProcessing(false);
13101
+ sessionService.sendSessionEnd();
13025
13102
  });
13026
13103
  return;
13027
13104
  }
@@ -13392,11 +13469,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13392
13469
  });
13393
13470
  },
13394
13471
  onIssue: async (params) => {
13395
- const { issueRpc } = await import('./rpc-oZAY4e3e.mjs');
13472
+ const { issueRpc } = await import('./rpc-DrT7cP5y.mjs');
13396
13473
  return issueRpc(params?.cwd || directory, params || {});
13397
13474
  },
13398
13475
  onWorkflow: async (params) => {
13399
- const { workflowRpc } = await import('./rpc-BnH7Ljou.mjs');
13476
+ const { workflowRpc } = await import('./rpc-B_HEACXE.mjs');
13400
13477
  return workflowRpc(params?.cwd || directory, params || {});
13401
13478
  },
13402
13479
  onRipgrep: async (args, cwd) => {
@@ -13902,11 +13979,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13902
13979
  });
13903
13980
  },
13904
13981
  onIssue: async (params) => {
13905
- const { issueRpc } = await import('./rpc-oZAY4e3e.mjs');
13982
+ const { issueRpc } = await import('./rpc-DrT7cP5y.mjs');
13906
13983
  return issueRpc(params?.cwd || directory, params || {});
13907
13984
  },
13908
13985
  onWorkflow: async (params) => {
13909
- const { workflowRpc } = await import('./rpc-BnH7Ljou.mjs');
13986
+ const { workflowRpc } = await import('./rpc-B_HEACXE.mjs');
13910
13987
  return workflowRpc(params?.cwd || directory, params || {});
13911
13988
  },
13912
13989
  onRipgrep: async (args, cwd) => {
@@ -14659,7 +14736,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14659
14736
  const PING_TIMEOUT_MS = 15e3;
14660
14737
  const POST_RECONNECT_GRACE_MS = 2e4;
14661
14738
  const RECONNECT_JITTER_MS = 2500;
14662
- const { WorkflowScheduler } = await import('./scheduler-CYu4tzBf.mjs');
14739
+ const { WorkflowScheduler } = await import('./scheduler-BSXOwdjt.mjs');
14663
14740
  const workflowScheduler = new WorkflowScheduler({
14664
14741
  projectRoots: () => {
14665
14742
  const dirs = /* @__PURE__ */ new Set();
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { m as resolveProjectRoot, v as cronMatches } from './run-C_2jxzE5.mjs';
3
- import { l as listWorkflows, b as workflowCrons, w as workflowSteps } from './store-BPL__e4D.mjs';
2
+ import { m as resolveProjectRoot, v as cronMatches } from './run-FQLbrWFm.mjs';
3
+ import { l as listWorkflows, i as isWorkflowEnabled, c as workflowCrons, w as workflowSteps } from './store-D-fJ9ANL.mjs';
4
4
  import 'os';
5
5
  import 'fs/promises';
6
6
  import 'fs';
@@ -73,6 +73,7 @@ class WorkflowScheduler {
73
73
  continue;
74
74
  }
75
75
  for (const wf of workflows) {
76
+ if (!isWorkflowEnabled(wf)) continue;
76
77
  const crons = workflowCrons(wf);
77
78
  if (!crons.length) continue;
78
79
  if (crons.some((c) => cronMatchesSafe(c, now))) {
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-D8uYJqvJ.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-B7KfL975.mjs');
58
58
  const pos = positionalArgs(args);
59
59
  const name = pos[0];
60
60
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  }
95
95
  async function serveApply(args, machineId) {
96
- const { connectAndGetMachine } = await import('./commands-D8uYJqvJ.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-B7KfL975.mjs');
97
97
  const fs = await import('fs');
98
98
  const yaml = await import('yaml');
99
99
  const file = positionalArgs(args)[0];
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
182
182
  }
183
183
  }
184
184
  async function serveRemove(args, machineId) {
185
- const { connectAndGetMachine } = await import('./commands-D8uYJqvJ.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-B7KfL975.mjs');
186
186
  const pos = positionalArgs(args);
187
187
  const name = pos[0];
188
188
  if (!name) {
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
202
202
  }
203
203
  }
204
204
  async function serveList(args, machineId) {
205
- const { connectAndGetMachine } = await import('./commands-D8uYJqvJ.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-B7KfL975.mjs');
206
206
  const all = hasFlag(args, "--all", "-a");
207
207
  const json = hasFlag(args, "--json");
208
208
  const sessionId = getFlag(args, "--session");
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
235
235
  }
236
236
  }
237
237
  async function serveInfo(machineId) {
238
- const { connectAndGetMachine } = await import('./commands-D8uYJqvJ.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-B7KfL975.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -4,7 +4,7 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as net from 'net';
6
6
  import * as path from 'path';
7
- import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-C_2jxzE5.mjs';
7
+ import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-FQLbrWFm.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
@@ -733,7 +733,7 @@ class ServeManager {
733
733
  const mount = this.mounts.get(mountName);
734
734
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
735
735
  try {
736
- const { FrpcTunnel } = await import('./frpc-Clge8vu4.mjs');
736
+ const { FrpcTunnel } = await import('./frpc-CcCnRJRo.mjs');
737
737
  let tunnel;
738
738
  tunnel = new FrpcTunnel({
739
739
  name: tunnelName,
@@ -1,4 +1,4 @@
1
- import { R as READ_ONLY_TOOLS, y as loadMachineContext, z as buildMachineInstructions, A as machineToolsForRole, B as buildMachineTools } from './run-C_2jxzE5.mjs';
1
+ import { R as READ_ONLY_TOOLS, y as loadMachineContext, z as buildMachineInstructions, A as machineToolsForRole, B as buildMachineTools } from './run-FQLbrWFm.mjs';
2
2
  import 'node:child_process';
3
3
  import 'os';
4
4
  import 'fs/promises';