@tablation/crew 0.79.8 → 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 +107 -34
  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) {
@@ -9650,6 +9679,35 @@ function rememberSession(stateDir, host, workspaceSlug) {
9650
9679
  } catch {
9651
9680
  }
9652
9681
  }
9682
+ function forgetWorkspace(stateDir, host, workspaceSlug) {
9683
+ try {
9684
+ const all = readPointers(stateDir);
9685
+ const left = (all[host] ?? []).filter((w) => w !== workspaceSlug);
9686
+ const { [host]: _old, ...rest } = all;
9687
+ const next = left.length ? { ...rest, [host]: left } : rest;
9688
+ if (Object.keys(next).length) writePointers(stateDir, next);
9689
+ else rmSync10(lastSessionFile(stateDir), { force: true });
9690
+ } catch {
9691
+ }
9692
+ }
9693
+ async function adoptSessionAddress(o) {
9694
+ if (o.from === o.to) return false;
9695
+ try {
9696
+ const stored = await o.store.get(o.host, o.from);
9697
+ if (!stored?.apiKey) return false;
9698
+ if (!(await o.store.get(o.host, o.to))?.apiKey) await o.store.set(o.host, o.to, stored);
9699
+ await o.store.clear(o.host, o.from);
9700
+ } catch {
9701
+ return false;
9702
+ }
9703
+ forgetWorkspace(o.stateDir, o.host, o.from);
9704
+ rememberSession(o.stateDir, o.host, o.to);
9705
+ return true;
9706
+ }
9707
+ function rememberedAddressFor(stateDir, host, workspace) {
9708
+ if (workspace.includes(":")) return workspace;
9709
+ return (readPointers(stateDir)[host] ?? []).find((w) => w.endsWith(`:${workspace}`)) ?? workspace;
9710
+ }
9653
9711
  async function resumableSessions(o) {
9654
9712
  const out = [];
9655
9713
  for (const workspace of readPointers(o.stateDir)[o.host] ?? []) {
@@ -9696,7 +9754,7 @@ async function signOutSessions(o) {
9696
9754
  }
9697
9755
 
9698
9756
  // src/reset.ts
9699
- 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";
9700
9758
  import { dirname as dirname9, join as join22 } from "path";
9701
9759
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
9702
9760
  function clonedCheckoutsFile(stateDir) {
@@ -9791,7 +9849,7 @@ function removeState(stateDir, dryRun) {
9791
9849
  const out = [];
9792
9850
  const targets = [join22(stateDir, "resolved"), join22(stateDir, "keys"), clonedCheckoutsFile(stateDir), join22(stateDir, "connect-session.json"), join22(stateDir, "reload-requested"), join22(stateDir, "daemon.started")];
9793
9851
  try {
9794
- for (const name of readdirSync7(stateDir)) {
9852
+ for (const name of readdirSync8(stateDir)) {
9795
9853
  if (name.startsWith(".") && statSync6(join22(stateDir, name)).isFile()) targets.push(join22(stateDir, name));
9796
9854
  }
9797
9855
  } catch {
@@ -15477,7 +15535,7 @@ function schedulerInstalled(plan, exists, crontab) {
15477
15535
  }
15478
15536
 
15479
15537
  // src/worktree-sweep.ts
15480
- 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";
15481
15539
  import { basename as basename11, dirname as dirname18, join as join30, resolve as resolve4 } from "path";
15482
15540
  function planWorktreeSweep(target, tickets, contract) {
15483
15541
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -15537,7 +15595,7 @@ function siblingWorktreeDirs(target) {
15537
15595
  const registered = new Set(worktrees(target.dir).map((w) => resolve4(w.path)));
15538
15596
  let names;
15539
15597
  try {
15540
- names = readdirSync8(parent);
15598
+ names = readdirSync9(parent);
15541
15599
  } catch {
15542
15600
  return [];
15543
15601
  }
@@ -15753,14 +15811,14 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
15753
15811
  }
15754
15812
 
15755
15813
  // src/stream-sweep.ts
15756
- 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";
15757
15815
  import { join as join31 } from "path";
15758
15816
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
15759
15817
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
15760
15818
  const dir = join31(stateDir, "streams");
15761
15819
  if (!existsSync28(dir)) return [];
15762
15820
  const cutoff = now - retentionDays * DAY_MS2;
15763
- return readdirSync9(dir).map((name) => join31(dir, name)).filter((path) => {
15821
+ return readdirSync10(dir).map((name) => join31(dir, name)).filter((path) => {
15764
15822
  try {
15765
15823
  return statSync8(path).mtimeMs < cutoff;
15766
15824
  } catch {
@@ -15788,7 +15846,7 @@ function applyStreamSweep(paths, dryRun, log) {
15788
15846
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15789
15847
  if (!existsSync28(stateDir)) return [];
15790
15848
  const cutoff = now - retentionDays * DAY_MS2;
15791
- 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) => {
15792
15850
  try {
15793
15851
  return statSync8(path).mtimeMs < cutoff;
15794
15852
  } catch {
@@ -15798,7 +15856,7 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15798
15856
  }
15799
15857
 
15800
15858
  // src/cli.ts
15801
- 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";
15802
15860
  import { createInterface } from "readline/promises";
15803
15861
  import { dirname as dirOf, resolve as resolvePath } from "path";
15804
15862
 
@@ -16055,7 +16113,7 @@ function syncAllPassengerCheckouts(cfg) {
16055
16113
 
16056
16114
  // src/tunnel.ts
16057
16115
  import { spawn as spawn5 } from "child_process";
16058
- 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";
16059
16117
  import { randomInt } from "crypto";
16060
16118
  import { join as join33 } from "path";
16061
16119
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -16237,7 +16295,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
16237
16295
  function readAllPersistedWorkspaceIds(stateDir) {
16238
16296
  const dir = join33(stateDir, "passengers");
16239
16297
  if (!existsSync30(dir)) return [];
16240
- 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));
16241
16299
  }
16242
16300
 
16243
16301
  // src/mcp-endpoint-registry.ts
@@ -16357,7 +16415,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
16357
16415
  import { createServer as createHttpServer } from "http";
16358
16416
  import { spawn as spawn6 } from "child_process";
16359
16417
  import { randomBytes } from "crypto";
16360
- 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";
16361
16419
  import { join as join34 } from "path";
16362
16420
 
16363
16421
  // src/secret-compare.ts
@@ -16484,7 +16542,7 @@ function readAllPersistedSyncWorkspaceIds(stateDir) {
16484
16542
  const dir = join34(stateDir, "passengers");
16485
16543
  if (!existsSync31(dir)) return [];
16486
16544
  const suffix = "-sync.json";
16487
- 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));
16488
16546
  }
16489
16547
  function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
16490
16548
  const alive = deps.isPidAlive ?? isPidAlive;
@@ -16570,11 +16628,11 @@ function describeCause(err) {
16570
16628
  }
16571
16629
 
16572
16630
  // src/daemon.ts
16573
- 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";
16574
16632
  import { join as join36 } from "path";
16575
16633
 
16576
16634
  // src/interrupted.ts
16577
- 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";
16578
16636
  import { join as join35 } from "path";
16579
16637
  var INTERRUPTED_MARKER = calloutHeader("NOTE", "Interrupted");
16580
16638
  function interruptedComment(b) {
@@ -16606,7 +16664,7 @@ function orphanedStreams(o) {
16606
16664
  if (!existsSync32(dir)) return [];
16607
16665
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
16608
16666
  const out = [];
16609
- for (const name of readdirSync12(dir)) {
16667
+ for (const name of readdirSync13(dir)) {
16610
16668
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
16611
16669
  const streamPath = join35(dir, name);
16612
16670
  if (runFinished(streamPath)) continue;
@@ -16727,7 +16785,7 @@ function latestMtime(path) {
16727
16785
  if (stat.isFile()) return stat.mtimeMs;
16728
16786
  if (!stat.isDirectory()) return 0;
16729
16787
  let latest = stat.mtimeMs;
16730
- for (const entry of readdirSync13(path, { withFileTypes: true })) {
16788
+ for (const entry of readdirSync14(path, { withFileTypes: true })) {
16731
16789
  latest = Math.max(latest, latestMtime(join36(path, entry.name)));
16732
16790
  }
16733
16791
  return latest;
@@ -17502,6 +17560,7 @@ if (command === "passenger-sync-daemon") {
17502
17560
  }, releaseRefused = function() {
17503
17561
  const why = state.releaseSkipReason();
17504
17562
  if (!why) return false;
17563
+ if (!dryRun) state.releaseHeartbeat().touch();
17505
17564
  emit.emit(`${why} \u2014 release skipped`, { step: "release" });
17506
17565
  return true;
17507
17566
  }, doctorMachineId = function(stateDir) {
@@ -19252,7 +19311,7 @@ ${changed2.map((c) => ` - ${c}
19252
19311
  if (!existsSync34(dir)) throw new ReposError(`${dir} does not exist on disk`);
19253
19312
  const files = { exists: (r) => existsSync34(resolvePath(dir, r)), list: (r) => {
19254
19313
  try {
19255
- return readdirSync14(resolvePath(dir, r));
19314
+ return readdirSync15(resolvePath(dir, r));
19256
19315
  } catch {
19257
19316
  return [];
19258
19317
  }
@@ -19449,7 +19508,7 @@ ${changed2.map((c) => ` - ${c}
19449
19508
  ` : `${human}
19450
19509
  `);
19451
19510
  const plan2 = planRepoClone({ text, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
19452
- 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)) {
19453
19512
  throw new ReposError(
19454
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`
19455
19514
  );
@@ -19680,12 +19739,22 @@ ${reconstruction.prompt}
19680
19739
  store = void 0;
19681
19740
  }
19682
19741
  let keychainBacked = false;
19742
+ let sessionAddress;
19683
19743
  if (!apiKey && workspace && store) {
19684
19744
  try {
19685
- const existing = await store.get(hostFromUrl4(baseUrl), workspace);
19745
+ let existing = await store.get(hostFromUrl4(baseUrl), workspace);
19746
+ let found = workspace;
19747
+ if (!existing?.apiKey) {
19748
+ const full = rememberedAddressFor(cfg.ship.stateDir, hostFromUrl4(baseUrl), workspace);
19749
+ if (full !== workspace) {
19750
+ existing = await store.get(hostFromUrl4(baseUrl), full);
19751
+ found = full;
19752
+ }
19753
+ }
19686
19754
  if (existing?.apiKey) {
19687
19755
  apiKey = existing.apiKey;
19688
19756
  keychainBacked = true;
19757
+ sessionAddress = found;
19689
19758
  process.stderr.write(`Reusing the existing keychain session for ${hostFromUrl4(baseUrl)}/${workspace}.
19690
19759
  `);
19691
19760
  }
@@ -19719,6 +19788,7 @@ ${reconstruction.prompt}
19719
19788
  apiKey = resumed.apiKey;
19720
19789
  workspace = resumed.workspace;
19721
19790
  keychainBacked = true;
19791
+ sessionAddress = resumed.workspace;
19722
19792
  signInSource = "keychain";
19723
19793
  process.stderr.write(`Reusing the existing keychain session for ${hostFromUrl4(baseUrl)}/${workspace}.
19724
19794
  `);
@@ -19773,13 +19843,6 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19773
19843
  );
19774
19844
  apiKey = result.apiKey.key;
19775
19845
  mintedFromDevice = true;
19776
- signedInEmitted = true;
19777
- connectEvents.emit("signed_in", `signed in as ${result.identity.email}`, {
19778
- identity: { id: result.identity.id, email: result.identity.email },
19779
- workspaces: [{ slug: result.workspace.slug, name: result.workspace.name }],
19780
- session: "device",
19781
- host: hostFromUrl4(baseUrl)
19782
- });
19783
19846
  const deviceWorkspace = "path" in result.workspace && typeof result.workspace.path === "string" ? result.workspace.path : result.workspace.slug;
19784
19847
  if (!workspace) workspace = deviceWorkspace;
19785
19848
  if (store) {
@@ -19791,6 +19854,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19791
19854
  createdAt: result.apiKey.createdAt
19792
19855
  });
19793
19856
  keychainBacked = true;
19857
+ sessionAddress = deviceWorkspace;
19794
19858
  rememberSession(cfg.ship.stateDir, hostFromUrl4(baseUrl), deviceWorkspace);
19795
19859
  process.stderr.write(
19796
19860
  `API key stored in the OS keychain for ${hostFromUrl4(baseUrl)}/${deviceWorkspace}.
@@ -19834,9 +19898,18 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19834
19898
  };
19835
19899
  const runConnect = async (ws, proj) => {
19836
19900
  let found = await discover({ ...authOpts, workspace: ws, project: proj, area: value("area"), ship, dryRun, interactive: canAsk || connectEvents.json });
19901
+ if (store && keychainBacked && sessionAddress && sessionAddress !== found.workspaceSlug) {
19902
+ if (await adoptSessionAddress({
19903
+ store,
19904
+ stateDir: cfg.ship.stateDir,
19905
+ host: hostFromUrl4(baseUrl),
19906
+ from: sessionAddress,
19907
+ to: found.workspaceSlug
19908
+ })) sessionAddress = found.workspaceSlug;
19909
+ }
19837
19910
  if (!signedInEmitted && found.meId) {
19838
19911
  signedInEmitted = true;
19839
- connectEvents.emit("signed_in", `key belongs to ${found.meEmail ?? found.meId}`, {
19912
+ connectEvents.emit("signed_in", mintedFromDevice ? `signed in as ${found.meEmail ?? found.meId}` : `key belongs to ${found.meEmail ?? found.meId}`, {
19840
19913
  identity: { id: found.meId, email: found.meEmail, name: found.meName },
19841
19914
  workspaces: [{ slug: found.workspaceSlug, name: found.workspaceName, role: found.role }],
19842
19915
  session: signInSource,
@@ -20987,7 +21060,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20987
21060
  }
20988
21061
  const projectId = route.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
20989
21062
  const fresh = !existsSync34(join37(dir, ".git"));
20990
- 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`);
20991
21064
  if (dryRun) {
20992
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`);
20993
21066
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.79.8",
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",