svamp-cli 0.2.182 → 0.2.183

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-CDRs-TTe.mjs} +5 -5
  2. package/dist/{auth-CL9Qjhq6.mjs → auth-BwjvUcKt.mjs} +1 -1
  3. package/dist/cli.mjs +60 -60
  4. package/dist/{commands-HXmFkc9h.mjs → commands-Bv7vbi-z.mjs} +1 -1
  5. package/dist/{commands-f-gIOx_m.mjs → commands-C2mXJNwA.mjs} +20 -3
  6. package/dist/{commands-CM0JkQlm.mjs → commands-D4hGdv1B.mjs} +5 -5
  7. package/dist/{commands-D8uYJqvJ.mjs → commands-DQ_kpWqH.mjs} +1 -1
  8. package/dist/{commands-B_P2D4A3.mjs → commands-D_wD9qIr.mjs} +2 -2
  9. package/dist/{commands-BUg7EHp0.mjs → commands-ix9L1jaQ.mjs} +1 -1
  10. package/dist/{commands-Dxr3C5Y4.mjs → commands-pMwoqGpe.mjs} +2 -2
  11. package/dist/{fleet-wmEx5X-B.mjs → fleet-B4hfyHp4.mjs} +1 -1
  12. package/dist/{frpc-Clge8vu4.mjs → frpc-BzkAtKLy.mjs} +1 -1
  13. package/dist/{headlessCli-Bb3_TKKD.mjs → headlessCli-ohRvD7CT.mjs} +2 -2
  14. package/dist/index.mjs +1 -1
  15. package/dist/{package-C35XarmI.mjs → package-CcFzcCql.mjs} +1 -1
  16. package/dist/{rpc-oZAY4e3e.mjs → rpc-BwqRLir7.mjs} +1 -1
  17. package/dist/{rpc-BnH7Ljou.mjs → rpc-degyMl0V.mjs} +8 -2
  18. package/dist/{run-C_2jxzE5.mjs → run-68xel1pO.mjs} +168 -96
  19. package/dist/{run-CFgCIth9.mjs → run-B2aWNd0R.mjs} +1 -1
  20. package/dist/{scheduler-CYu4tzBf.mjs → scheduler-D0FGGxti.mjs} +3 -2
  21. package/dist/{serveCommands-CRntd5Tp.mjs → serveCommands-Bo2xZPIZ.mjs} +5 -5
  22. package/dist/{serveManager-CvlOyKBf.mjs → serveManager-BKbhWnMy.mjs} +2 -2
  23. package/dist/{sideband-Bqe-7HxJ.mjs → sideband-B1jzlbuf.mjs} +1 -1
  24. package/dist/{store-BPL__e4D.mjs → store-D-fJ9ANL.mjs} +17 -2
  25. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import os from 'os';
2
2
  import fs__default from 'fs';
3
3
  import { resolve, join, relative } from 'path';
4
- import { C as parseFrontmatter, D as getSkillsServer, E as getSkillsWorkspaceName, F as getSkillsCollectionName, G as fetchWithTimeout, H as searchSkills, I as SKILLS_DIR, J as getSkillInfo, K as downloadSkillFile, L as listSkillFiles } from './run-C_2jxzE5.mjs';
4
+ import { C as parseFrontmatter, D as getSkillsServer, E as getSkillsWorkspaceName, F as getSkillsCollectionName, G as fetchWithTimeout, H as searchSkills, I as SKILLS_DIR, J as getSkillInfo, K as downloadSkillFile, L as listSkillFiles } from './run-68xel1pO.mjs';
5
5
  import 'fs/promises';
6
6
  import 'url';
7
7
  import 'child_process';
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-D8uYJqvJ.mjs';
2
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-DQ_kpWqH.mjs';
3
3
  import { execSync } from 'node:child_process';
4
- import { u as updateIssue, o as addComment, p as addIssue, x as shortId } from './run-C_2jxzE5.mjs';
4
+ import { u as updateIssue, o as addComment, p as addIssue, x as shortId } from './run-68xel1pO.mjs';
5
5
  import 'node:path';
6
6
  import 'node:os';
7
7
  import 'os';
@@ -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-68xel1pO.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-68xel1pO.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-68xel1pO.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-B1jzlbuf.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-68xel1pO.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.183";
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,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-68xel1pO.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -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-68xel1pO.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}`);
@@ -89,7 +89,10 @@ 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
+ return ws === ownerWorkspace;
93
96
  }
94
97
  function resolveRoleLevel(sharing, userEmail) {
95
98
  let level = -1;
@@ -2712,7 +2715,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
2712
2715
  const tunnels = handlers.tunnels;
2713
2716
  if (!tunnels) throw new Error("Tunnel management not available");
2714
2717
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
2715
- const { FrpcTunnel } = await import('./frpc-Clge8vu4.mjs');
2718
+ const { FrpcTunnel } = await import('./frpc-BzkAtKLy.mjs');
2716
2719
  const tunnel = new FrpcTunnel({
2717
2720
  name: params.name,
2718
2721
  ports: params.ports,
@@ -3159,7 +3162,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3159
3162
  }
3160
3163
  const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
3161
3164
  const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
3162
- const { toolsForRole } = await import('./sideband-Bqe-7HxJ.mjs');
3165
+ const { toolsForRole } = await import('./sideband-B1jzlbuf.mjs');
3163
3166
  const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
3164
3167
  return fmt(r2);
3165
3168
  }
@@ -3258,7 +3261,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
3258
3261
  if (r.error || !r.sender) return { error: r.error || "unauthorized" };
3259
3262
  const callId = "call_" + Math.random().toString(16).slice(2, 12);
3260
3263
  const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
3261
- const { queryCore } = await import('./commands-D8uYJqvJ.mjs');
3264
+ const { queryCore } = await import('./commands-DQ_kpWqH.mjs');
3262
3265
  const timeout = c.reply?.timeout_sec || 120;
3263
3266
  let result;
3264
3267
  try {
@@ -3528,6 +3531,35 @@ function applyInboxClear(inbox, opts) {
3528
3531
  return { kept, removed: inbox.length - kept.length };
3529
3532
  }
3530
3533
 
3534
+ function envInt(name, fallback) {
3535
+ const raw = process.env[name];
3536
+ if (raw === void 0 || raw === "") return fallback;
3537
+ const n = parseInt(raw, 10);
3538
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
3539
+ }
3540
+ function getRateLimitRetryConfig() {
3541
+ return {
3542
+ maxRetries: envInt("SVAMP_RATELIMIT_MAX_RETRIES", 10),
3543
+ baseMs: envInt("SVAMP_RATELIMIT_BASE_MS", 5e3),
3544
+ capMs: envInt("SVAMP_RATELIMIT_CAP_MS", 12e4)
3545
+ };
3546
+ }
3547
+ function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Math.random) {
3548
+ const a = Math.max(0, Math.floor(attempt));
3549
+ const expo = Math.min(a, 30);
3550
+ const nominal = Math.min(cfg.capMs, cfg.baseMs * Math.pow(2, expo));
3551
+ const half = nominal / 2;
3552
+ return Math.round(half + rng() * half);
3553
+ }
3554
+ function isRetryableRateLimit(text, apiErrorStatus) {
3555
+ const t = (text || "").toLowerCase();
3556
+ 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");
3557
+ if (hardLimit) return false;
3558
+ const authIssue = t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
3559
+ if (authIssue) return false;
3560
+ 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");
3561
+ }
3562
+
3531
3563
  const PARTICIPANTS_CHANNEL_ID = "sys-participants";
3532
3564
  function channelPublicView(c) {
3533
3565
  return { id: c.id, name: c.name, description: c.description, identity: { mode: c.identity?.mode }, action: c.action?.kind, bind: c.bind };
@@ -3858,7 +3890,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3858
3890
  }
3859
3891
  };
3860
3892
  ensureParticipantsChannel(initialMetadata.sharing);
3861
- const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6, timeoutMs = 6e4) => {
3893
+ const forkClaudeOnce = async (prompt, claudeSessionId, cwd, maxTurns, timeoutMs) => {
3862
3894
  const { spawn } = await import('child_process');
3863
3895
  return new Promise((resolve) => {
3864
3896
  const child = spawn("claude", [
@@ -3895,14 +3927,37 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
3895
3927
  }
3896
3928
  try {
3897
3929
  const result = JSON.parse(stdout);
3930
+ if (result && result.is_error) {
3931
+ resolve({ success: false, error: String(result.result || result.error || stderr || "error") });
3932
+ return;
3933
+ }
3898
3934
  resolve({ success: true, text: result.result || result.text || stdout });
3899
3935
  } catch {
3936
+ if (code !== 0) {
3937
+ resolve({ success: false, error: stderr || stdout.trim() || `claude exited with code ${code}` });
3938
+ return;
3939
+ }
3900
3940
  resolve({ success: true, text: stdout.trim() });
3901
3941
  }
3902
3942
  });
3903
3943
  child.on("error", (err) => resolve({ success: false, error: err.message }));
3904
3944
  });
3905
3945
  };
3946
+ const forkClaudePrint = async (prompt, claudeSessionId, cwd, maxTurns = 6, timeoutMs = 6e4) => {
3947
+ const cfg2 = getRateLimitRetryConfig();
3948
+ let last = { success: false, error: "" };
3949
+ for (let attempt = 0; ; attempt++) {
3950
+ last = await forkClaudeOnce(prompt, claudeSessionId, cwd, maxTurns, timeoutMs);
3951
+ if (last.success) return last;
3952
+ if (!isRetryableRateLimit(last.error || "") || attempt >= cfg2.maxRetries) return last;
3953
+ const delay = computeRetryDelayMs(attempt, cfg2);
3954
+ try {
3955
+ console.error(`[btw] rate-limited \u2014 holding ${Math.round(delay / 1e3)}s then retrying (attempt ${attempt + 1}/${cfg2.maxRetries})`);
3956
+ } catch {
3957
+ }
3958
+ await new Promise((r) => setTimeout(r, delay));
3959
+ }
3960
+ };
3906
3961
  const performEdit = async (target, newText) => {
3907
3962
  if (!callbacks.onEditTranscript) return { success: false, message: "Editing history is not supported for this session." };
3908
3963
  const info = editableInfo(target);
@@ -10214,35 +10269,6 @@ function resolveContextWindow(opts) {
10214
10269
  return candidate;
10215
10270
  }
10216
10271
 
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
10272
  const SVAMP_HOME$1 = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
10247
10273
  function generateHookSettings(portOrOptions = {}) {
10248
10274
  const opts = typeof portOrOptions === "number" ? { sessionStartPort: portOrOptions } : portOrOptions;
@@ -11581,7 +11607,7 @@ async function startDaemon(options) {
11581
11607
  saveExposedTunnels(list);
11582
11608
  }
11583
11609
  async function createExposedTunnel(spec) {
11584
- const { FrpcTunnel } = await import('./frpc-Clge8vu4.mjs');
11610
+ const { FrpcTunnel } = await import('./frpc-BzkAtKLy.mjs');
11585
11611
  const tunnel = new FrpcTunnel({
11586
11612
  name: spec.name,
11587
11613
  ports: spec.ports,
@@ -11601,7 +11627,7 @@ async function startDaemon(options) {
11601
11627
  return tunnel;
11602
11628
  }
11603
11629
  const tunnelRecreateState = /* @__PURE__ */ new Map();
11604
- const { ServeManager } = await import('./serveManager-CvlOyKBf.mjs');
11630
+ const { ServeManager } = await import('./serveManager-BKbhWnMy.mjs');
11605
11631
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
11606
11632
  ensureAutoInstalledSkills(logger).catch(() => {
11607
11633
  });
@@ -12955,73 +12981,119 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
12955
12981
  return;
12956
12982
  }
12957
12983
  if (msgMeta?.btw && claudeResumeId) {
12984
+ const btwResumeId = claudeResumeId;
12958
12985
  logger.log(`[Session ${sessionId}] /btw side-channel: "${text.substring(0, 80)}..."`);
12959
12986
  sessionService.pushMessage(
12960
12987
  { type: "message", message: `/btw ${text}`, level: "btw-question" },
12961
12988
  "event"
12962
12989
  );
12963
12990
  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;
12991
+ const runBtwOnce = () => new Promise((resolve2, reject) => {
12992
+ const btwArgs = [
12993
+ "--print",
12994
+ text,
12995
+ "--resume",
12996
+ btwResumeId,
12997
+ "--fork-session",
12998
+ "--no-session-persistence",
12999
+ // /btw is non-interactive; without bypass the forked Claude
13000
+ // pauses on tool prompts and the user sees a hanging side-channel.
13001
+ "--permission-mode",
13002
+ "bypassPermissions",
13003
+ "--output-format",
13004
+ "stream-json",
13005
+ "--verbose"
13006
+ ];
13007
+ const btwProcess = spawn$1("claude", btwArgs, {
13008
+ cwd: directory,
13009
+ env: { ...process.env },
13010
+ stdio: ["pipe", "pipe", "pipe"]
13011
+ });
13012
+ let btwResult = "";
13013
+ let btwBuffer = "";
13014
+ let btwStderr = "";
13015
+ let settled = false;
13016
+ btwProcess.stderr?.on("data", (chunk) => {
13017
+ btwStderr += chunk.toString();
13018
+ });
13019
+ btwProcess.stdout.on("data", (chunk) => {
13020
+ btwBuffer += chunk.toString();
13021
+ let newlineIdx;
13022
+ while ((newlineIdx = btwBuffer.indexOf("\n")) !== -1) {
13023
+ const line = btwBuffer.substring(0, newlineIdx);
13024
+ btwBuffer = btwBuffer.substring(newlineIdx + 1);
13025
+ if (!line.trim()) continue;
13026
+ try {
13027
+ const msg = JSON.parse(line);
13028
+ if (msg.type === "assistant" && msg.message?.content) {
13029
+ for (const block of msg.message.content) {
13030
+ if (block.type === "text" && block.text) {
13031
+ btwResult += block.text;
13032
+ }
13033
+ }
13034
+ } else if (msg.type === "result") {
13035
+ if (settled) continue;
13036
+ settled = true;
13037
+ if (msg.is_error) {
13038
+ const err = new Error(String(msg.result || "error"));
13039
+ err.retryable = isRetryableRateLimit(String(msg.result || ""), msg.api_error_status);
13040
+ reject(err);
13041
+ } else {
13042
+ resolve2(btwResult || msg.result || "(no response)");
13000
13043
  }
13001
13044
  }
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)`);
13045
+ } catch {
13011
13046
  }
13012
- } catch {
13013
13047
  }
13014
- }
13048
+ });
13049
+ btwProcess.on("exit", (code) => {
13050
+ if (settled) return;
13051
+ settled = true;
13052
+ if (code === 0) {
13053
+ resolve2(btwResult || "(no response)");
13054
+ return;
13055
+ }
13056
+ const detail = btwStderr.trim() || btwResult.trim();
13057
+ const err = new Error(detail || `claude exited with code ${code}`);
13058
+ err.retryable = isRetryableRateLimit(detail || "");
13059
+ reject(err);
13060
+ });
13061
+ btwProcess.on("error", (e) => {
13062
+ if (settled) return;
13063
+ settled = true;
13064
+ const err = new Error(e.message);
13065
+ err.retryable = false;
13066
+ reject(err);
13067
+ });
13015
13068
  });
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();
13069
+ (async () => {
13070
+ let lastErr = null;
13071
+ for (let attempt = 0; ; attempt++) {
13072
+ try {
13073
+ const finalText = await runBtwOnce();
13074
+ sessionService.pushMessage(
13075
+ { type: "message", message: finalText, level: "btw-answer" },
13076
+ "event"
13077
+ );
13078
+ logger.log(`[Session ${sessionId}] /btw complete (${finalText.length} chars)`);
13079
+ return;
13080
+ } catch (e) {
13081
+ lastErr = e;
13082
+ if (!e?.retryable || attempt >= RATELIMIT_CFG.maxRetries) break;
13083
+ const delayMs = computeRetryDelayMs(attempt, RATELIMIT_CFG);
13084
+ logger.log(`[Session ${sessionId}] /btw rate-limited \u2014 holding ${Math.round(delayMs / 1e3)}s then retrying (attempt ${attempt + 1}/${RATELIMIT_CFG.maxRetries})`);
13085
+ await new Promise((r) => setTimeout(r, delayMs));
13086
+ }
13024
13087
  }
13088
+ const detail = (lastErr?.message || "unknown error").toString().slice(0, 500);
13089
+ sessionService.pushMessage(
13090
+ { type: "message", message: `/btw failed: ${detail}`, level: "error" },
13091
+ "event"
13092
+ );
13093
+ logger.log(`[Session ${sessionId}] /btw failed: ${detail}`);
13094
+ })().finally(() => {
13095
+ signalProcessing(false);
13096
+ sessionService.sendSessionEnd();
13025
13097
  });
13026
13098
  return;
13027
13099
  }
@@ -13392,11 +13464,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13392
13464
  });
13393
13465
  },
13394
13466
  onIssue: async (params) => {
13395
- const { issueRpc } = await import('./rpc-oZAY4e3e.mjs');
13467
+ const { issueRpc } = await import('./rpc-BwqRLir7.mjs');
13396
13468
  return issueRpc(params?.cwd || directory, params || {});
13397
13469
  },
13398
13470
  onWorkflow: async (params) => {
13399
- const { workflowRpc } = await import('./rpc-BnH7Ljou.mjs');
13471
+ const { workflowRpc } = await import('./rpc-degyMl0V.mjs');
13400
13472
  return workflowRpc(params?.cwd || directory, params || {});
13401
13473
  },
13402
13474
  onRipgrep: async (args, cwd) => {
@@ -13902,11 +13974,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
13902
13974
  });
13903
13975
  },
13904
13976
  onIssue: async (params) => {
13905
- const { issueRpc } = await import('./rpc-oZAY4e3e.mjs');
13977
+ const { issueRpc } = await import('./rpc-BwqRLir7.mjs');
13906
13978
  return issueRpc(params?.cwd || directory, params || {});
13907
13979
  },
13908
13980
  onWorkflow: async (params) => {
13909
- const { workflowRpc } = await import('./rpc-BnH7Ljou.mjs');
13981
+ const { workflowRpc } = await import('./rpc-degyMl0V.mjs');
13910
13982
  return workflowRpc(params?.cwd || directory, params || {});
13911
13983
  },
13912
13984
  onRipgrep: async (args, cwd) => {
@@ -14659,7 +14731,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
14659
14731
  const PING_TIMEOUT_MS = 15e3;
14660
14732
  const POST_RECONNECT_GRACE_MS = 2e4;
14661
14733
  const RECONNECT_JITTER_MS = 2500;
14662
- const { WorkflowScheduler } = await import('./scheduler-CYu4tzBf.mjs');
14734
+ const { WorkflowScheduler } = await import('./scheduler-D0FGGxti.mjs');
14663
14735
  const workflowScheduler = new WorkflowScheduler({
14664
14736
  projectRoots: () => {
14665
14737
  const dirs = /* @__PURE__ */ new Set();
@@ -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-68xel1pO.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';
@@ -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-68xel1pO.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-DQ_kpWqH.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-DQ_kpWqH.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-DQ_kpWqH.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-DQ_kpWqH.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-DQ_kpWqH.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-68xel1pO.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-BzkAtKLy.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-68xel1pO.mjs';
2
2
  import 'node:child_process';
3
3
  import 'os';
4
4
  import 'fs/promises';
@@ -2,6 +2,9 @@ import { existsSync, unlinkSync, readFileSync, readdirSync, mkdirSync, writeFile
2
2
  import { join } from 'node:path';
3
3
  import { parse, stringify } from 'yaml';
4
4
 
5
+ function isWorkflowEnabled(wf) {
6
+ return wf.enabled !== false;
7
+ }
5
8
  function workflowSteps(wf) {
6
9
  return Object.values(wf.jobs || {}).flatMap((j) => j?.steps || []);
7
10
  }
@@ -54,7 +57,9 @@ function parseWorkflow(content) {
54
57
  name: String(o.name),
55
58
  on: normalizeOn(o.on),
56
59
  jobs: normalizeJobs(o.jobs),
57
- session: o.session ?? null
60
+ session: o.session ?? null,
61
+ // Only `enabled: false` disables; anything else (absent/true) is active.
62
+ ...o.enabled === false ? { enabled: false } : {}
58
63
  };
59
64
  } catch {
60
65
  return null;
@@ -63,6 +68,7 @@ function parseWorkflow(content) {
63
68
  function serializeWorkflow(wf) {
64
69
  const clean = { name: wf.name };
65
70
  if (wf.session) clean.session = wf.session;
71
+ if (wf.enabled === false) clean.enabled = false;
66
72
  const on = {};
67
73
  if (wf.on?.schedule?.length) on.schedule = wf.on.schedule.map((s) => ({ cron: s.cron }));
68
74
  if (wf.on?.workflow_dispatch) on.workflow_dispatch = {};
@@ -111,6 +117,15 @@ function saveWorkflow(projectRoot, wf) {
111
117
  writeFileSync(tmp, serializeWorkflow(wf));
112
118
  renameSync(tmp, path);
113
119
  }
120
+ function setWorkflowEnabled(projectRoot, name, enabled) {
121
+ const wf = getWorkflow(projectRoot, name);
122
+ if (!wf) return null;
123
+ const next = { ...wf };
124
+ if (enabled) delete next.enabled;
125
+ else next.enabled = false;
126
+ saveWorkflow(projectRoot, next);
127
+ return next;
128
+ }
114
129
  function removeWorkflow(projectRoot, name) {
115
130
  const p = workflowPath(projectRoot, name);
116
131
  if (!existsSync(p)) return false;
@@ -122,4 +137,4 @@ function removeWorkflow(projectRoot, name) {
122
137
  }
123
138
  }
124
139
 
125
- export { rawWorkflow as a, workflowCrons as b, getWorkflow as g, listWorkflows as l, removeWorkflow as r, saveWorkflow as s, workflowSteps as w };
140
+ export { saveWorkflow as a, rawWorkflow as b, workflowCrons as c, getWorkflow as g, isWorkflowEnabled as i, listWorkflows as l, removeWorkflow as r, setWorkflowEnabled as s, workflowSteps as w };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.2.182",
3
+ "version": "0.2.183",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",