@mushi-mushi/cli 0.35.2 → 0.35.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Kenji Sakuramoto / Kensaurus 合同会社
3
+ Copyright (c) 2026 kensaurus LLC
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MUSHI_CLI_VERSION
4
- } from "./chunk-OWUJYMV6.js";
4
+ } from "./chunk-OXHAJNVK.js";
5
5
  import {
6
6
  CLOUD_API_ENDPOINT,
7
7
  normalizeEndpoint
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var MUSHI_CLI_VERSION = true ? "0.35.2" : "0.0.0-dev";
2
+ var MUSHI_CLI_VERSION = true ? "0.35.4" : "0.0.0-dev";
3
3
  var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.30.2";
4
4
 
5
5
  export {
@@ -9,10 +9,10 @@ import {
9
9
  requireUuid,
10
10
  saveConfig,
11
11
  savedSdkKeyFor
12
- } from "./chunk-DBXL4RLE.js";
12
+ } from "./chunk-ED3OCCXS.js";
13
13
  import {
14
14
  MUSHI_MCP_PIN_SPEC
15
- } from "./chunk-OWUJYMV6.js";
15
+ } from "./chunk-OXHAJNVK.js";
16
16
  import {
17
17
  assertEndpoint,
18
18
  normalizeEndpoint
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var MUSHI_CLI_VERSION = true ? "0.35.2" : "0.0.0-dev";
4
+ var MUSHI_CLI_VERSION = true ? "0.35.4" : "0.0.0-dev";
5
5
  var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.30.2";
6
6
 
7
7
  export {
@@ -8,10 +8,10 @@ import {
8
8
  requireConfig,
9
9
  requireUuid,
10
10
  tryLoadWorkerKey
11
- } from "./chunk-DBXL4RLE.js";
11
+ } from "./chunk-ED3OCCXS.js";
12
12
  import {
13
13
  MUSHI_CLI_VERSION
14
- } from "./chunk-OWUJYMV6.js";
14
+ } from "./chunk-OXHAJNVK.js";
15
15
 
16
16
  // src/worker/daemon.ts
17
17
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
@@ -608,9 +608,11 @@ function runProc(cmd, args, opts = {}) {
608
608
  });
609
609
  }
610
610
  async function clearWorktree(repoDir, wt) {
611
- if (!existsSync(wt)) return;
612
- await runProc("git", ["worktree", "remove", "--force", wt], { cwd: repoDir });
613
- if (existsSync(wt)) rmSync(wt, { recursive: true, force: true });
611
+ if (existsSync(wt)) {
612
+ await runProc("git", ["worktree", "remove", "--force", wt], { cwd: repoDir });
613
+ if (existsSync(wt)) rmSync(wt, { recursive: true, force: true });
614
+ }
615
+ await runProc("git", ["worktree", "unlock", wt], { cwd: repoDir });
614
616
  await runProc("git", ["worktree", "prune"], { cwd: repoDir });
615
617
  }
616
618
  var GIT_TIMEOUT_MS = 10 * 6e4;
@@ -983,7 +985,7 @@ function registerWorkCommands(program) {
983
985
  if (opts.repoDir || opts.report || opts.followups) {
984
986
  throw new MushiCliError("E_INVALID_INPUT", "--daemon takes its lanes and clones from the worker config, not from --repo-dir, --report or --followups.", "mushi worker install");
985
987
  }
986
- const { runDaemon: runDaemon2 } = await import("./daemon-STJJNPRM.js");
988
+ const { runDaemon: runDaemon2 } = await import("./daemon-EQPTR6IO.js");
987
989
  await runDaemon2({ configPath: opts.workerConfig });
988
990
  return;
989
991
  }
@@ -2126,8 +2128,18 @@ function loadWorkerConfig(path = WORKER_CONFIG_PATH) {
2126
2128
  var HEARTBEAT_MS = 6e4;
2127
2129
  var IDLE_MS = 15 * 6e4;
2128
2130
  var BACKOFF_MS = 2 * 6e4;
2131
+ var INFRA_BACKOFF_MS = 6e4;
2132
+ var INFRA_BACKOFF_MAX_MS = 30 * 6e4;
2133
+ var AGENT_UNAVAILABLE = /ActionRequiredError|Increase limits|usage limit|out of (?:usage|credits)|Cannot use this model/i;
2129
2134
  var GH_CHECK_MS = 5 * 6e4;
2130
2135
  var CURSOR_CHECK_MS = 20 * 6e4;
2136
+ function infraBackoffMs(streak) {
2137
+ return streak <= 1 ? 0 : Math.min(INFRA_BACKOFF_MS * 2 ** (streak - 2), INFRA_BACKOFF_MAX_MS);
2138
+ }
2139
+ function machineFailed(result, failureKind, detail) {
2140
+ return result === "failed" && failureKind === "infra" || AGENT_UNAVAILABLE.test(detail);
2141
+ }
2142
+ var KEY_REFUSALS_TO_STOP = 3;
2131
2143
  function createDaemon(cfg, deps) {
2132
2144
  const lanes = cfg.lanes.map((local) => ({
2133
2145
  local,
@@ -2137,6 +2149,7 @@ function createDaemon(cfg, deps) {
2137
2149
  followup: false,
2138
2150
  nextClaimAt: 0,
2139
2151
  nextFollowupAt: 0,
2152
+ infraStreak: 0,
2140
2153
  state: deps.notReady?.has(local.name) ? "not-ready" : "starting",
2141
2154
  seenState: "",
2142
2155
  stateAt: deps.now(),
@@ -2167,6 +2180,8 @@ function createDaemon(cfg, deps) {
2167
2180
  return r.data;
2168
2181
  };
2169
2182
  const refusedByKey = (r) => !r.ok && (r.httpStatus === 401 || r.httpStatus === 403 && /REVOKED|INVALID_API_KEY|WORKER_KEY_REQUIRED|INSUFFICIENT_SCOPE/.test(r.error.code));
2183
+ const revokedForSure = (r) => !r.ok && r.httpStatus === 403 && /REVOKED|WORKER_KEY_REQUIRED|INSUFFICIENT_SCOPE/.test(r.error.code);
2184
+ let keyRefusals = 0;
2170
2185
  function stopAll(why) {
2171
2186
  stopped = why;
2172
2187
  for (const l of lanes) for (const ac of l.running.values()) ac.abort();
@@ -2231,6 +2246,17 @@ function createDaemon(cfg, deps) {
2231
2246
  claims: lanes.flatMap((l) => [...l.running.keys()])
2232
2247
  };
2233
2248
  }
2249
+ function afterRun(lane, failedOnMachine) {
2250
+ if (!failedOnMachine) {
2251
+ lane.infraStreak = 0;
2252
+ return;
2253
+ }
2254
+ lane.infraStreak++;
2255
+ const wait = infraBackoffMs(lane.infraStreak);
2256
+ if (wait === 0) return;
2257
+ lane.nextClaimAt = Math.max(lane.nextClaimAt, deps.now() + wait);
2258
+ say(lane, ` the machine or the agent account failed ${lane.infraStreak} time${lane.infraStreak === 1 ? "" : "s"} in a row: next claim in ${Math.round(wait / 6e4)} min`);
2259
+ }
2234
2260
  function startRun(lane, claim) {
2235
2261
  const ac = new AbortController();
2236
2262
  lane.running.set(claim.id, ac);
@@ -2266,6 +2292,7 @@ function createDaemon(cfg, deps) {
2266
2292
  if (out.result === "failed") lane.counters.errors++;
2267
2293
  lane.last = `${claim.report.id.slice(0, 8)} \u2192 ${out.outcome}`.slice(0, 160);
2268
2294
  say(lane, ` \u2192 ${out.outcome}${out.prUrl ? ` ${out.prUrl}` : ""}`);
2295
+ afterRun(lane, machineFailed(out.result, out.failureKind, out.outcome));
2269
2296
  const done = await deps.call(`/v1/worker/claims/${claim.id}/done`, {
2270
2297
  method: "POST",
2271
2298
  body: JSON.stringify({
@@ -2312,6 +2339,7 @@ function createDaemon(cfg, deps) {
2312
2339
  if (out.result === "failed") lane.counters.errors++;
2313
2340
  lane.last = `${out.result}: ${out.detail}`.slice(0, 160);
2314
2341
  say(lane, ` \u2192 ${out.result}: ${out.detail}`);
2342
+ afterRun(lane, machineFailed(out.result, out.failureKind, out.detail));
2315
2343
  const done = await deps.call(`/v1/worker/claims/${claimId}/done`, {
2316
2344
  method: "POST",
2317
2345
  body: JSON.stringify({
@@ -2409,7 +2437,12 @@ function createDaemon(cfg, deps) {
2409
2437
  while (!stopped && lane.running.size < c.concurrency && agents() < g.max_agents && now >= lane.nextClaimAt) {
2410
2438
  const r = await deps.call("/v1/worker/claim", { method: "POST", body: JSON.stringify({ laneId: lane.serverId }) });
2411
2439
  if (refusedByKey(r)) {
2412
- stopAll(`the server refused this worker's key (${r.error.code}); it was revoked or lost access`);
2440
+ if (revokedForSure(r)) stopAll(`the server refused this worker's key (${r.error.code}); it was revoked or lost access`);
2441
+ else {
2442
+ lane.nextClaimAt = now + BACKOFF_MS;
2443
+ lane.state = "backoff";
2444
+ say(lane, `claim refused (${r.error.code}); the next heartbeat checks the key again`);
2445
+ }
2413
2446
  return;
2414
2447
  }
2415
2448
  if (!r.ok) {
@@ -2471,9 +2504,17 @@ function createDaemon(cfg, deps) {
2471
2504
  if (stopped) return;
2472
2505
  const hb = await deps.call("/v1/worker/heartbeat", { method: "POST", body: JSON.stringify(heartbeatBody()) });
2473
2506
  if (refusedByKey(hb)) {
2474
- stopAll(`the server refused this worker's key (${hb.error.code}); it was revoked or lost access`);
2507
+ const code = hb.error.code;
2508
+ keyRefusals++;
2509
+ if (revokedForSure(hb) || keyRefusals >= KEY_REFUSALS_TO_STOP) {
2510
+ stopAll(`the server refused this worker's key (${code}) ${keyRefusals > 1 ? `${keyRefusals} times in a row` : ""}; it was revoked or lost access`.replace(/\s+;/, ";"));
2511
+ return;
2512
+ }
2513
+ hostState = "backoff";
2514
+ say(null, `the server refused this worker's key (${code}); checking again before stopping (${keyRefusals} of ${KEY_REFUSALS_TO_STOP})`);
2475
2515
  return;
2476
2516
  }
2517
+ if (hb.ok) keyRefusals = 0;
2477
2518
  if (!hb.ok) {
2478
2519
  hostState = "backoff";
2479
2520
  say(null, `heartbeat failed: ${hb.error.code} ${hb.error.message}`);
@@ -2759,6 +2800,8 @@ export {
2759
2800
  WORKER_KEY_PATH,
2760
2801
  validateWorkerConfig,
2761
2802
  loadWorkerConfig,
2803
+ infraBackoffMs,
2804
+ machineFailed,
2762
2805
  createDaemon,
2763
2806
  workerHostId,
2764
2807
  readWorkerKey,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION
3
- } from "./chunk-VNGG2M3O.js";
3
+ } from "./chunk-GZYBA5IS.js";
4
4
  import {
5
5
  assertEndpoint,
6
6
  normalizeEndpoint
@@ -6,10 +6,10 @@ import {
6
6
  saveConfig,
7
7
  savedSdkKeyFor,
8
8
  waitForIngestReady
9
- } from "./chunk-ZA2HNXTH.js";
9
+ } from "./chunk-ZQV3NBK7.js";
10
10
  import {
11
11
  MUSHI_MCP_PIN_SPEC
12
- } from "./chunk-VNGG2M3O.js";
12
+ } from "./chunk-GZYBA5IS.js";
13
13
  import {
14
14
  detectFramework,
15
15
  envVarsToWrite,
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  resolveSdkEnvKey,
4
4
  runConnect
5
- } from "./chunk-XUPFPVB2.js";
5
+ } from "./chunk-IZAVMMI6.js";
6
6
  import "./chunk-KOBEOGNF.js";
7
- import "./chunk-DBXL4RLE.js";
8
- import "./chunk-OWUJYMV6.js";
7
+ import "./chunk-ED3OCCXS.js";
8
+ import "./chunk-OXHAJNVK.js";
9
9
  import "./chunk-LGGE3LH7.js";
10
10
  export {
11
11
  resolveSdkEnvKey,
@@ -4,18 +4,22 @@ import {
4
4
  createDaemon,
5
5
  createHostReadings,
6
6
  cursorStateFor,
7
+ infraBackoffMs,
8
+ machineFailed,
7
9
  readWorkerKey,
8
10
  runDaemon,
9
11
  workerHostId
10
- } from "./chunk-VAQTC77F.js";
11
- import "./chunk-DBXL4RLE.js";
12
- import "./chunk-OWUJYMV6.js";
12
+ } from "./chunk-VZIANKUF.js";
13
+ import "./chunk-ED3OCCXS.js";
14
+ import "./chunk-OXHAJNVK.js";
13
15
  import "./chunk-LGGE3LH7.js";
14
16
  export {
15
17
  assertWorkerKey,
16
18
  createDaemon,
17
19
  createHostReadings,
18
20
  cursorStateFor,
21
+ infraBackoffMs,
22
+ machineFailed,
19
23
  readWorkerKey,
20
24
  runDaemon,
21
25
  workerHostId
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  sanitizeEndpoint,
23
23
  sanitizeProjectId,
24
24
  writeMcpServerEntry
25
- } from "./chunk-XUPFPVB2.js";
25
+ } from "./chunk-IZAVMMI6.js";
26
26
  import {
27
27
  FRAMEWORKS,
28
28
  detectFramework,
@@ -49,7 +49,7 @@ import {
49
49
  registerUxCommands,
50
50
  registerWorkCommands,
51
51
  validateWorkerConfig
52
- } from "./chunk-VAQTC77F.js";
52
+ } from "./chunk-VZIANKUF.js";
53
53
  import {
54
54
  CONFIG_PATH,
55
55
  MushiCliError,
@@ -87,11 +87,11 @@ import {
87
87
  tryDeleteWorkerKey,
88
88
  trySaveKeyToKeychain,
89
89
  trySaveWorkerKey
90
- } from "./chunk-DBXL4RLE.js";
90
+ } from "./chunk-ED3OCCXS.js";
91
91
  import {
92
92
  MUSHI_CLI_VERSION,
93
93
  MUSHI_MCP_PIN_SPEC
94
- } from "./chunk-OWUJYMV6.js";
94
+ } from "./chunk-OXHAJNVK.js";
95
95
  import {
96
96
  CLOUD_API_ENDPOINT,
97
97
  CLOUD_MCP_OAUTH_URL,
@@ -1594,7 +1594,7 @@ async function checkOnboardingStatus(config, _consoleBase, cwd, doFetch = global
1594
1594
  }
1595
1595
  async function checkVersionDrift(cwd, doFetch) {
1596
1596
  const checks = [];
1597
- const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-6ZDAJV77.js");
1597
+ const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-237BBW3V.js");
1598
1598
  if (MUSHI_CLI_VERSION2 === "0.0.0-dev") return checks;
1599
1599
  const parse2 = (v) => {
1600
1600
  const m = v.replace(/^[~^]/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
@@ -2567,7 +2567,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2567
2567
  if (p.isCancel(answer) || !answer) return;
2568
2568
  const endpoint = resolveCloudEndpoint(options.endpoint);
2569
2569
  try {
2570
- const { runConnect: runConnect2 } = await import("./connect-SH7D5RJP.js");
2570
+ const { runConnect: runConnect2 } = await import("./connect-XJHTZIHF.js");
2571
2571
  const saved = loadConfig();
2572
2572
  const result = await runConnect2(
2573
2573
  {
package/dist/init.js CHANGED
@@ -25,10 +25,10 @@ import {
25
25
  saveConfig,
26
26
  savedSdkKeyFor,
27
27
  trySaveKeyToKeychain
28
- } from "./chunk-ZA2HNXTH.js";
28
+ } from "./chunk-ZQV3NBK7.js";
29
29
  import {
30
30
  MUSHI_CLI_VERSION
31
- } from "./chunk-VNGG2M3O.js";
31
+ } from "./chunk-GZYBA5IS.js";
32
32
  import {
33
33
  FRAMEWORKS,
34
34
  detectFramework,
@@ -2127,7 +2127,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2127
2127
  if (p.isCancel(answer) || !answer) return;
2128
2128
  const endpoint = resolveCloudEndpoint(options.endpoint);
2129
2129
  try {
2130
- const { runConnect } = await import("./connect-YC6MRVZN.js");
2130
+ const { runConnect } = await import("./connect-Q75QLGAB.js");
2131
2131
  const saved = loadConfig();
2132
2132
  const result = await runConnect(
2133
2133
  {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-OWUJYMV6.js";
5
+ } from "./chunk-OXHAJNVK.js";
6
6
  export {
7
7
  MUSHI_CLI_VERSION,
8
8
  MUSHI_MCP_PIN_SPEC
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION,
3
3
  MUSHI_MCP_PIN_SPEC
4
- } from "./chunk-VNGG2M3O.js";
4
+ } from "./chunk-GZYBA5IS.js";
5
5
  export {
6
6
  MUSHI_CLI_VERSION,
7
7
  MUSHI_MCP_PIN_SPEC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/cli",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "license": "MIT",
5
5
  "description": "Mushi CLI: SDK setup, MCP wiring, bug triage. The bug mediator for AI-built apps: plain-English diagnosis + a ready fix, in your editor.",
6
6
  "bin": {