@archme/cli 0.14.7 → 0.14.8

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/dist/cli.js CHANGED
@@ -19366,6 +19366,31 @@ var init_imageManifest = __esm({
19366
19366
  }
19367
19367
  });
19368
19368
 
19369
+ // src/daemon/claimStance.ts
19370
+ function claimStance(configured, gate) {
19371
+ if (!gate || gate.allowsClaims()) return { kind: "claim", mode: configured };
19372
+ if (gate.isFailing?.()) return { kind: "presence_only", mode: "required_unavailable" };
19373
+ return { kind: "hold" };
19374
+ }
19375
+ function claimRpcParams(a2) {
19376
+ return {
19377
+ p_device_id: a2.deviceId,
19378
+ p_worker_id: a2.workerId,
19379
+ p_lease_seconds: a2.leaseSeconds,
19380
+ // Stamps presence + gates against daemon_min_version (migration 067);
19381
+ // NULL (read failure) is below any floor server-side.
19382
+ p_daemon_version: a2.daemonVersion,
19383
+ // Recorded on presence only; never gates (R3 §3.4).
19384
+ p_claude_version: a2.claudeVersion,
19385
+ p_sandbox_mode: a2.sandboxMode
19386
+ };
19387
+ }
19388
+ var init_claimStance = __esm({
19389
+ "src/daemon/claimStance.ts"() {
19390
+ "use strict";
19391
+ }
19392
+ });
19393
+
19369
19394
  // src/daemon/sandboxJob/result.ts
19370
19395
  import { readFile as readFile13 } from "node:fs/promises";
19371
19396
  function isValidNonce(nonce) {
@@ -19666,6 +19691,10 @@ var init_confinement = __esm({
19666
19691
  allowsClaims() {
19667
19692
  return this.opts.mode !== "required" || !this.failing && !this.probing;
19668
19693
  }
19694
+ /** A probe has failed and no re-probe has passed since (re-probes included). */
19695
+ isFailing() {
19696
+ return this.failing;
19697
+ }
19669
19698
  schedule(ms) {
19670
19699
  if (this.stopped) return;
19671
19700
  if (this.handle !== null) this.timers.clearTimeout(this.handle);
@@ -21841,18 +21870,18 @@ The sub-agent returns its findings; **you then apply** the fixes yourself \u2014
21841
21870
  ${patchSection}
21842
21871
  ${DIFF_RULES}`;
21843
21872
  }
21844
- async function rpcClaim(ctx, deviceId) {
21873
+ async function rpcClaim(ctx, deviceId, mode) {
21874
+ const claudeVersion = await claudeCodeVersion();
21845
21875
  const { data, error } = await withAuthRetry(
21846
21876
  "claim",
21847
- () => ctx.supabase.rpc("claim_archme_job", {
21848
- p_device_id: deviceId,
21849
- p_worker_id: ctx.workerId,
21850
- p_lease_seconds: ctx.leaseSeconds,
21851
- // Stamps daemon_presence.daemon_version + gates against
21852
- // daemon_min_version (migration 067). NULL — read failure — is
21853
- // treated as below any floor server-side.
21854
- p_daemon_version: daemonVersion
21855
- }),
21877
+ () => ctx.supabase.rpc("claim_archme_job", claimRpcParams({
21878
+ deviceId,
21879
+ workerId: ctx.workerId,
21880
+ leaseSeconds: ctx.leaseSeconds,
21881
+ daemonVersion,
21882
+ claudeVersion,
21883
+ sandboxMode: mode
21884
+ })),
21856
21885
  ctx.reLogin
21857
21886
  );
21858
21887
  if (error) {
@@ -21984,11 +22013,14 @@ async function rpcDefer(ctx, jobId, deviceId, reason, detail) {
21984
22013
  }
21985
22014
  return data != null;
21986
22015
  }
21987
- async function rpcFail(ctx, jobId, deviceId, message2, metrics, logs) {
22016
+ async function rpcFail(ctx, jobId, deviceId, message2, metrics, logs, opts = {}) {
21988
22017
  const result = await callJobCompletionEndpoint(ctx, "fail", jobId, deviceId, {
21989
22018
  error: message2.slice(0, 4e3),
21990
22019
  metrics,
21991
- logs
22020
+ logs,
22021
+ // retry:false fails the job for good now (migration 141); absent, it
22022
+ // requeues while attempts remain.
22023
+ ...opts
21992
22024
  });
21993
22025
  if (!result.ok) console.error(`[${jobId}] fail error:`, result.error);
21994
22026
  }
@@ -22797,7 +22829,7 @@ ${logs.stderr}`, sentPrompt);
22797
22829
  );
22798
22830
  if (route2.kind === "terminal") {
22799
22831
  console.log(`[${job.id}] failed (sandbox request cap, not deferred): ${route2.message}`);
22800
- await rpcFail(ctx, job.id, deviceId, route2.message, metrics, logs);
22832
+ await rpcFail(ctx, job.id, deviceId, route2.message, metrics, logs, { retry: false });
22801
22833
  return;
22802
22834
  }
22803
22835
  const plan = route2.kind === "defer" ? route2.plan : null;
@@ -22865,7 +22897,18 @@ async function dispatch(sched) {
22865
22897
  }
22866
22898
  pauseUntilMs = null;
22867
22899
  }
22868
- if (confinementGate && !confinementGate.allowsClaims()) return;
22900
+ const stance = claimStance(sandboxMode, confinementGate);
22901
+ if (stance.kind === "hold") return;
22902
+ if (stance.kind === "presence_only") {
22903
+ sched.dispatching = true;
22904
+ try {
22905
+ for (const { ctx, deviceId } of claimUnits(sched)) await sched.claim(ctx, deviceId, stance.mode);
22906
+ } finally {
22907
+ sched.dispatching = false;
22908
+ }
22909
+ return;
22910
+ }
22911
+ const mode = stance.mode;
22869
22912
  sched.dispatching = true;
22870
22913
  try {
22871
22914
  let atCap = false;
@@ -22877,7 +22920,7 @@ async function dispatch(sched) {
22877
22920
  atCap = true;
22878
22921
  break;
22879
22922
  }
22880
- const claimed = await claimRoundRobin(sched);
22923
+ const claimed = await claimRoundRobin(sched, mode);
22881
22924
  if (!claimed) {
22882
22925
  slot();
22883
22926
  break;
@@ -22898,16 +22941,20 @@ async function dispatch(sched) {
22898
22941
  sched.dispatching = false;
22899
22942
  }
22900
22943
  }
22901
- async function claimRoundRobin(sched) {
22944
+ function claimUnits(sched) {
22902
22945
  const units = [];
22903
22946
  for (const ctx of sched.sessions) {
22904
22947
  if (ctx.auth && ctx.auth.getRefreshToken() === null && ctx.auth.getAccessToken() === null) continue;
22905
22948
  for (const d of ctx.devices) units.push({ ctx, deviceId: d.deviceId });
22906
22949
  }
22950
+ return units;
22951
+ }
22952
+ async function claimRoundRobin(sched, mode) {
22953
+ const units = claimUnits(sched);
22907
22954
  const n2 = units.length;
22908
22955
  for (let i2 = 0; i2 < n2; i2++) {
22909
22956
  const u2 = units[(sched.rrIndex + i2) % n2];
22910
- const job = await sched.claim(u2.ctx, u2.deviceId);
22957
+ const job = await sched.claim(u2.ctx, u2.deviceId, mode);
22911
22958
  if (job && job.id) {
22912
22959
  sched.rrIndex = (sched.rrIndex + i2 + 1) % n2;
22913
22960
  return { job, ctx: u2.ctx, deviceId: u2.deviceId };
@@ -23056,6 +23103,7 @@ async function runDaemon() {
23056
23103
  }
23057
23104
  exitDaemon(1);
23058
23105
  }
23106
+ sandboxMode = sandboxSettings.mode;
23059
23107
  if (process.env.ARCHME_SANDBOX_SLOTS !== void 0) {
23060
23108
  console.log(" ARCHME_SANDBOX_SLOTS is no longer read; sandbox slots follow MAX_CONCURRENT_JOBS");
23061
23109
  }
@@ -23322,7 +23370,7 @@ ${signal} received, shutting down`);
23322
23370
  process.on("SIGINT", () => shutdown("SIGINT"));
23323
23371
  process.on("SIGTERM", () => shutdown("SIGTERM"));
23324
23372
  }
23325
- var HEARTBEAT_INTERVAL_MS, CLAUDE_TIMEOUT_MS, CLAUDE_LARGE_DIFF_TIMEOUT_MS, SAFETY_SWEEP_MS, LOG_STDOUT_CAP_BYTES, LOG_STDERR_CAP_BYTES, FAILURE_DETAIL_MAX, AUTH_PAUSE_MS, ClaudeRunError, PRE_SPAWN_TOKEN_MIN_LIFETIME_MS, SUPABASE_URL, SUPABASE_ANON_KEY, ARCHME_DAEMON_CLIENT_ID, ARCHME_MCP_BASE_URL, CLONE_BASE_DIR, DAEMON_ID, LEASE_SECONDS, inFlight, cancelled, killReasons, activeCleanups, cloneMutex, shuttingDown, daemonVersion, pauseUntilMs, resumeTimer, currentSched, MAX_CONCURRENT_JOBS, ctxByJob, deviceByJob, sandboxHost, confinementGate, boxedJobs, leaseLost, prefetchOverride, DEFAULT_CLONE_BASE_DIR, JOB_TOKEN_PREFIX, INLINE_PATCH_BYTE_CAP, GIT_SHA_RE, CURATOR_PREAMBLE, CORE_PRINCIPLES, STORY_COVERAGE_GATE, NEW_STORY_GUIDANCE, STORY_DESCRIPTION_EDIT_RULE, PHASE_GUIDANCE, BASE_RULES, DIFF_RULES, finite, channelSeq, MultipleAccountsError, __testDispatch;
23373
+ var HEARTBEAT_INTERVAL_MS, CLAUDE_TIMEOUT_MS, CLAUDE_LARGE_DIFF_TIMEOUT_MS, SAFETY_SWEEP_MS, LOG_STDOUT_CAP_BYTES, LOG_STDERR_CAP_BYTES, FAILURE_DETAIL_MAX, AUTH_PAUSE_MS, ClaudeRunError, PRE_SPAWN_TOKEN_MIN_LIFETIME_MS, SUPABASE_URL, SUPABASE_ANON_KEY, ARCHME_DAEMON_CLIENT_ID, ARCHME_MCP_BASE_URL, CLONE_BASE_DIR, DAEMON_ID, LEASE_SECONDS, inFlight, cancelled, killReasons, activeCleanups, cloneMutex, shuttingDown, daemonVersion, pauseUntilMs, resumeTimer, currentSched, MAX_CONCURRENT_JOBS, ctxByJob, deviceByJob, sandboxHost, confinementGate, sandboxMode, boxedJobs, leaseLost, prefetchOverride, DEFAULT_CLONE_BASE_DIR, JOB_TOKEN_PREFIX, INLINE_PATCH_BYTE_CAP, GIT_SHA_RE, CURATOR_PREAMBLE, CORE_PRINCIPLES, STORY_COVERAGE_GATE, NEW_STORY_GUIDANCE, STORY_DESCRIPTION_EDIT_RULE, PHASE_GUIDANCE, BASE_RULES, DIFF_RULES, finite, channelSeq, MultipleAccountsError, __testDispatch;
23326
23374
  var init_run = __esm({
23327
23375
  "src/daemon/run.ts"() {
23328
23376
  "use strict";
@@ -23366,6 +23414,8 @@ var init_run = __esm({
23366
23414
  init_localPoller();
23367
23415
  init_setting();
23368
23416
  init_imageManifest();
23417
+ init_claimStance();
23418
+ init_claudeVersion();
23369
23419
  init_host();
23370
23420
  init_runSandboxedJob();
23371
23421
  init_inputDir();
@@ -23403,6 +23453,7 @@ var init_run = __esm({
23403
23453
  deviceByJob = /* @__PURE__ */ new Map();
23404
23454
  sandboxHost = null;
23405
23455
  confinementGate = null;
23456
+ sandboxMode = "off";
23406
23457
  boxedJobs = /* @__PURE__ */ new Set();
23407
23458
  leaseLost = /* @__PURE__ */ new Set();
23408
23459
  prefetchOverride = null;
@@ -23586,6 +23637,9 @@ ${ANCHOR_VERIFICATION}`;
23586
23637
  setConfinementGate(gate) {
23587
23638
  confinementGate = gate;
23588
23639
  },
23640
+ setSandboxMode(mode) {
23641
+ sandboxMode = mode;
23642
+ },
23589
23643
  // Daemon-version hooks for tests / introspection. The real value is
23590
23644
  // populated at runDaemon boot from package.json; tests can stub.
23591
23645
  setDaemonVersion(v) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archme/cli",
3
- "version": "0.14.7",
3
+ "version": "0.14.8",
4
4
  "description": "Map your codebase into a living architecture diagram: run `archme init` in a repo to scan it, `archme daemon start` to keep it current on every commit.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "image": "ghcr.io/architectme/archme-job",
3
- "digest": "sha256:1b84fd7691c5d22c289985083532f5642c7c6d028d76ef2e350757d87d7cb3e6"
3
+ "digest": "sha256:9e4d5cd98c8d72bd14fadcf5063b3cd8c45dc2f65e2fe2ad44e141f5bfe0edbf"
4
4
  }