@tablation/crew 0.79.9 → 0.79.10

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 (2) hide show
  1. package/dist/cli.js +55 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4326,7 +4326,7 @@ function reconcileHeldWorktrees(state, repo, log, stillRefused) {
4326
4326
  }
4327
4327
  }
4328
4328
  function openShipAttention(state, nowMs = Date.now()) {
4329
- const stale = staleRelease(state.releaseHeartbeat().at(), nowMs);
4329
+ const stale = state.releaseInProgress() ? null : staleRelease(state.releaseHeartbeat().at(), nowMs);
4330
4330
  const stored = state.shipAttention().previous();
4331
4331
  return stale ? [...stored, stale] : stored;
4332
4332
  }
@@ -5465,7 +5465,7 @@ var HandoffTranscriptTracker = class {
5465
5465
  };
5466
5466
 
5467
5467
  // src/pending-handoff.ts
5468
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync13, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
5468
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync6, readFileSync as readFileSync13, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
5469
5469
  import { join as join16 } from "path";
5470
5470
 
5471
5471
  // src/ticket-branch.ts
@@ -5476,7 +5476,7 @@ import { hostname } from "os";
5476
5476
  import { TablationClient } from "@tablation/client";
5477
5477
 
5478
5478
  // src/state.ts
5479
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync7, rmSync as rmSync7, statSync as statSync4 } from "fs";
5479
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync7, rmSync as rmSync7, statSync as statSync4, readdirSync as readdirSync5 } from "fs";
5480
5480
  import { join as join13 } from "path";
5481
5481
  var EPOCH = "1970-01-01T00:00:00.000Z";
5482
5482
  var State = class _State {
@@ -5605,6 +5605,35 @@ var State = class _State {
5605
5605
  }
5606
5606
  };
5607
5607
  }
5608
+ /**
5609
+ * Whether a release is running right now: any `.release-<scope>.lock` in the
5610
+ * state directory naming a live process (the locks `releasePhase` takes per
5611
+ * scope). A lock naming a dead pid is ignored, as `acquire` ignores it.
5612
+ */
5613
+ releaseInProgress() {
5614
+ let names;
5615
+ try {
5616
+ names = readdirSync5(this.dir);
5617
+ } catch {
5618
+ return false;
5619
+ }
5620
+ for (const n of names) {
5621
+ if (!/^\.release-.+\.lock$/.test(n)) continue;
5622
+ let pid = 0;
5623
+ try {
5624
+ pid = Number.parseInt(readFileSync12(this.path(n), "utf8").trim(), 10);
5625
+ } catch {
5626
+ continue;
5627
+ }
5628
+ if (!Number.isFinite(pid) || pid <= 0) continue;
5629
+ try {
5630
+ process.kill(pid, 0);
5631
+ return true;
5632
+ } catch {
5633
+ }
5634
+ }
5635
+ return false;
5636
+ }
5608
5637
  /**
5609
5638
  * Like `acquire`, but for a resource with `max` interchangeable slots
5610
5639
  * rather than one exclusive holder — this machine's overall concurrency
@@ -7519,7 +7548,7 @@ function listPendingHandoffs(stateDir, route) {
7519
7548
  const dir = pendingDirFor(stateDir);
7520
7549
  if (!existsSync15(dir)) return [];
7521
7550
  const out = [];
7522
- for (const f of readdirSync5(dir).filter((n) => n.endsWith(".json")).sort()) {
7551
+ for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7523
7552
  try {
7524
7553
  const rec = JSON.parse(readFileSync13(join16(dir, f), "utf8"));
7525
7554
  if (rec.version === 1 && rec.ticketRecordId && (!route || rec.route === route)) out.push(rec);
@@ -7639,7 +7668,7 @@ function expandSkillMarkers(text, lookup, opts = {}) {
7639
7668
  }
7640
7669
 
7641
7670
  // src/skills.ts
7642
- import { readFileSync as readFileSync15, readdirSync as readdirSync6, existsSync as existsSync17 } from "fs";
7671
+ import { readFileSync as readFileSync15, readdirSync as readdirSync7, existsSync as existsSync17 } from "fs";
7643
7672
  import { join as join18 } from "path";
7644
7673
  import { parse as parseYaml } from "yaml";
7645
7674
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
@@ -8138,7 +8167,7 @@ function parseSkillFile(path) {
8138
8167
  function listLocalSkills(promptsDir) {
8139
8168
  const dir = join18(promptsDir, "skills");
8140
8169
  if (!existsSync17(dir)) return [];
8141
- return readdirSync6(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join18(dir, f)));
8170
+ return readdirSync7(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join18(dir, f)));
8142
8171
  }
8143
8172
  async function syncSkills(route, opts) {
8144
8173
  if (!route.resolved) {
@@ -9725,7 +9754,7 @@ async function signOutSessions(o) {
9725
9754
  }
9726
9755
 
9727
9756
  // src/reset.ts
9728
- import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync19, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
9757
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync19, readdirSync as readdirSync8, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
9729
9758
  import { dirname as dirname9, join as join22 } from "path";
9730
9759
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
9731
9760
  function clonedCheckoutsFile(stateDir) {
@@ -9820,7 +9849,7 @@ function removeState(stateDir, dryRun) {
9820
9849
  const out = [];
9821
9850
  const targets = [join22(stateDir, "resolved"), join22(stateDir, "keys"), clonedCheckoutsFile(stateDir), join22(stateDir, "connect-session.json"), join22(stateDir, "reload-requested"), join22(stateDir, "daemon.started")];
9822
9851
  try {
9823
- for (const name of readdirSync7(stateDir)) {
9852
+ for (const name of readdirSync8(stateDir)) {
9824
9853
  if (name.startsWith(".") && statSync6(join22(stateDir, name)).isFile()) targets.push(join22(stateDir, name));
9825
9854
  }
9826
9855
  } catch {
@@ -15506,7 +15535,7 @@ function schedulerInstalled(plan, exists, crontab) {
15506
15535
  }
15507
15536
 
15508
15537
  // src/worktree-sweep.ts
15509
- import { existsSync as existsSync27, readFileSync as readFileSync25, readdirSync as readdirSync8 } from "fs";
15538
+ import { existsSync as existsSync27, readFileSync as readFileSync25, readdirSync as readdirSync9 } from "fs";
15510
15539
  import { basename as basename11, dirname as dirname18, join as join30, resolve as resolve4 } from "path";
15511
15540
  function planWorktreeSweep(target, tickets, contract) {
15512
15541
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -15566,7 +15595,7 @@ function siblingWorktreeDirs(target) {
15566
15595
  const registered = new Set(worktrees(target.dir).map((w) => resolve4(w.path)));
15567
15596
  let names;
15568
15597
  try {
15569
- names = readdirSync8(parent);
15598
+ names = readdirSync9(parent);
15570
15599
  } catch {
15571
15600
  return [];
15572
15601
  }
@@ -15782,14 +15811,14 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
15782
15811
  }
15783
15812
 
15784
15813
  // src/stream-sweep.ts
15785
- import { existsSync as existsSync28, readdirSync as readdirSync9, rmSync as rmSync13, statSync as statSync8 } from "fs";
15814
+ import { existsSync as existsSync28, readdirSync as readdirSync10, rmSync as rmSync13, statSync as statSync8 } from "fs";
15786
15815
  import { join as join31 } from "path";
15787
15816
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
15788
15817
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
15789
15818
  const dir = join31(stateDir, "streams");
15790
15819
  if (!existsSync28(dir)) return [];
15791
15820
  const cutoff = now - retentionDays * DAY_MS2;
15792
- return readdirSync9(dir).map((name) => join31(dir, name)).filter((path) => {
15821
+ return readdirSync10(dir).map((name) => join31(dir, name)).filter((path) => {
15793
15822
  try {
15794
15823
  return statSync8(path).mtimeMs < cutoff;
15795
15824
  } catch {
@@ -15817,7 +15846,7 @@ function applyStreamSweep(paths, dryRun, log) {
15817
15846
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15818
15847
  if (!existsSync28(stateDir)) return [];
15819
15848
  const cutoff = now - retentionDays * DAY_MS2;
15820
- return readdirSync9(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join31(stateDir, name)).filter((path) => {
15849
+ return readdirSync10(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join31(stateDir, name)).filter((path) => {
15821
15850
  try {
15822
15851
  return statSync8(path).mtimeMs < cutoff;
15823
15852
  } catch {
@@ -15827,7 +15856,7 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15827
15856
  }
15828
15857
 
15829
15858
  // src/cli.ts
15830
- import { realpathSync as realpathSync3, readFileSync as readFileSync31, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
15859
+ import { realpathSync as realpathSync3, readFileSync as readFileSync31, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync15 } from "fs";
15831
15860
  import { createInterface } from "readline/promises";
15832
15861
  import { dirname as dirOf, resolve as resolvePath } from "path";
15833
15862
 
@@ -16084,7 +16113,7 @@ function syncAllPassengerCheckouts(cfg) {
16084
16113
 
16085
16114
  // src/tunnel.ts
16086
16115
  import { spawn as spawn5 } from "child_process";
16087
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
16116
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync11 } from "fs";
16088
16117
  import { randomInt } from "crypto";
16089
16118
  import { join as join33 } from "path";
16090
16119
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -16266,7 +16295,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
16266
16295
  function readAllPersistedWorkspaceIds(stateDir) {
16267
16296
  const dir = join33(stateDir, "passengers");
16268
16297
  if (!existsSync30(dir)) return [];
16269
- return readdirSync10(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
16298
+ return readdirSync11(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
16270
16299
  }
16271
16300
 
16272
16301
  // src/mcp-endpoint-registry.ts
@@ -16386,7 +16415,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
16386
16415
  import { createServer as createHttpServer } from "http";
16387
16416
  import { spawn as spawn6 } from "child_process";
16388
16417
  import { randomBytes } from "crypto";
16389
- import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
16418
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync12 } from "fs";
16390
16419
  import { join as join34 } from "path";
16391
16420
 
16392
16421
  // src/secret-compare.ts
@@ -16513,7 +16542,7 @@ function readAllPersistedSyncWorkspaceIds(stateDir) {
16513
16542
  const dir = join34(stateDir, "passengers");
16514
16543
  if (!existsSync31(dir)) return [];
16515
16544
  const suffix = "-sync.json";
16516
- return readdirSync11(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
16545
+ return readdirSync12(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
16517
16546
  }
16518
16547
  function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
16519
16548
  const alive = deps.isPidAlive ?? isPidAlive;
@@ -16599,11 +16628,11 @@ function describeCause(err) {
16599
16628
  }
16600
16629
 
16601
16630
  // src/daemon.ts
16602
- import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync30, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
16631
+ import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync30, readdirSync as readdirSync14, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
16603
16632
  import { join as join36 } from "path";
16604
16633
 
16605
16634
  // src/interrupted.ts
16606
- import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync29 } from "fs";
16635
+ import { existsSync as existsSync32, readdirSync as readdirSync13, readFileSync as readFileSync29 } from "fs";
16607
16636
  import { join as join35 } from "path";
16608
16637
  var INTERRUPTED_MARKER = calloutHeader("NOTE", "Interrupted");
16609
16638
  function interruptedComment(b) {
@@ -16635,7 +16664,7 @@ function orphanedStreams(o) {
16635
16664
  if (!existsSync32(dir)) return [];
16636
16665
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
16637
16666
  const out = [];
16638
- for (const name of readdirSync12(dir)) {
16667
+ for (const name of readdirSync13(dir)) {
16639
16668
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
16640
16669
  const streamPath = join35(dir, name);
16641
16670
  if (runFinished(streamPath)) continue;
@@ -16756,7 +16785,7 @@ function latestMtime(path) {
16756
16785
  if (stat.isFile()) return stat.mtimeMs;
16757
16786
  if (!stat.isDirectory()) return 0;
16758
16787
  let latest = stat.mtimeMs;
16759
- for (const entry of readdirSync13(path, { withFileTypes: true })) {
16788
+ for (const entry of readdirSync14(path, { withFileTypes: true })) {
16760
16789
  latest = Math.max(latest, latestMtime(join36(path, entry.name)));
16761
16790
  }
16762
16791
  return latest;
@@ -17531,6 +17560,7 @@ if (command === "passenger-sync-daemon") {
17531
17560
  }, releaseRefused = function() {
17532
17561
  const why = state.releaseSkipReason();
17533
17562
  if (!why) return false;
17563
+ if (!dryRun) state.releaseHeartbeat().touch();
17534
17564
  emit.emit(`${why} \u2014 release skipped`, { step: "release" });
17535
17565
  return true;
17536
17566
  }, doctorMachineId = function(stateDir) {
@@ -19281,7 +19311,7 @@ ${changed2.map((c) => ` - ${c}
19281
19311
  if (!existsSync34(dir)) throw new ReposError(`${dir} does not exist on disk`);
19282
19312
  const files = { exists: (r) => existsSync34(resolvePath(dir, r)), list: (r) => {
19283
19313
  try {
19284
- return readdirSync14(resolvePath(dir, r));
19314
+ return readdirSync15(resolvePath(dir, r));
19285
19315
  } catch {
19286
19316
  return [];
19287
19317
  }
@@ -19478,7 +19508,7 @@ ${changed2.map((c) => ` - ${c}
19478
19508
  ` : `${human}
19479
19509
  `);
19480
19510
  const plan2 = planRepoClone({ text, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
19481
- if (existsSync34(plan2.target) && (existsSync34(`${plan2.target}/.git`) || readdirSync14(plan2.target).length > 0)) {
19511
+ if (existsSync34(plan2.target) && (existsSync34(`${plan2.target}/.git`) || readdirSync15(plan2.target).length > 0)) {
19482
19512
  throw new ReposError(
19483
19513
  existsSync34(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
19484
19514
  );
@@ -21030,7 +21060,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
21030
21060
  }
21031
21061
  const projectId = route.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
21032
21062
  const fresh = !existsSync34(join37(dir, ".git"));
21033
- if (existsSync34(dir) && fresh && readdirSync14(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
21063
+ if (existsSync34(dir) && fresh && readdirSync15(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
21034
21064
  if (dryRun) {
21035
21065
  say("dry-run", { dir, newCheckout: fresh, repoRow: row?.id ?? null }, `would ${fresh ? "create" : "reuse"} ${dir}${row ? "" : ", create the Repos row"}, and file one ${tracker.contract.statuses.approved} ticket`);
21036
21066
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.79.9",
3
+ "version": "0.79.10",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",