@tablation/crew 0.79.7 → 0.79.9

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 +52 -9
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9650,6 +9650,35 @@ function rememberSession(stateDir, host, workspaceSlug) {
9650
9650
  } catch {
9651
9651
  }
9652
9652
  }
9653
+ function forgetWorkspace(stateDir, host, workspaceSlug) {
9654
+ try {
9655
+ const all = readPointers(stateDir);
9656
+ const left = (all[host] ?? []).filter((w) => w !== workspaceSlug);
9657
+ const { [host]: _old, ...rest } = all;
9658
+ const next = left.length ? { ...rest, [host]: left } : rest;
9659
+ if (Object.keys(next).length) writePointers(stateDir, next);
9660
+ else rmSync10(lastSessionFile(stateDir), { force: true });
9661
+ } catch {
9662
+ }
9663
+ }
9664
+ async function adoptSessionAddress(o) {
9665
+ if (o.from === o.to) return false;
9666
+ try {
9667
+ const stored = await o.store.get(o.host, o.from);
9668
+ if (!stored?.apiKey) return false;
9669
+ if (!(await o.store.get(o.host, o.to))?.apiKey) await o.store.set(o.host, o.to, stored);
9670
+ await o.store.clear(o.host, o.from);
9671
+ } catch {
9672
+ return false;
9673
+ }
9674
+ forgetWorkspace(o.stateDir, o.host, o.from);
9675
+ rememberSession(o.stateDir, o.host, o.to);
9676
+ return true;
9677
+ }
9678
+ function rememberedAddressFor(stateDir, host, workspace) {
9679
+ if (workspace.includes(":")) return workspace;
9680
+ return (readPointers(stateDir)[host] ?? []).find((w) => w.endsWith(`:${workspace}`)) ?? workspace;
9681
+ }
9653
9682
  async function resumableSessions(o) {
9654
9683
  const out = [];
9655
9684
  for (const workspace of readPointers(o.stateDir)[o.host] ?? []) {
@@ -19680,12 +19709,22 @@ ${reconstruction.prompt}
19680
19709
  store = void 0;
19681
19710
  }
19682
19711
  let keychainBacked = false;
19712
+ let sessionAddress;
19683
19713
  if (!apiKey && workspace && store) {
19684
19714
  try {
19685
- const existing = await store.get(hostFromUrl4(baseUrl), workspace);
19715
+ let existing = await store.get(hostFromUrl4(baseUrl), workspace);
19716
+ let found = workspace;
19717
+ if (!existing?.apiKey) {
19718
+ const full = rememberedAddressFor(cfg.ship.stateDir, hostFromUrl4(baseUrl), workspace);
19719
+ if (full !== workspace) {
19720
+ existing = await store.get(hostFromUrl4(baseUrl), full);
19721
+ found = full;
19722
+ }
19723
+ }
19686
19724
  if (existing?.apiKey) {
19687
19725
  apiKey = existing.apiKey;
19688
19726
  keychainBacked = true;
19727
+ sessionAddress = found;
19689
19728
  process.stderr.write(`Reusing the existing keychain session for ${hostFromUrl4(baseUrl)}/${workspace}.
19690
19729
  `);
19691
19730
  }
@@ -19719,6 +19758,7 @@ ${reconstruction.prompt}
19719
19758
  apiKey = resumed.apiKey;
19720
19759
  workspace = resumed.workspace;
19721
19760
  keychainBacked = true;
19761
+ sessionAddress = resumed.workspace;
19722
19762
  signInSource = "keychain";
19723
19763
  process.stderr.write(`Reusing the existing keychain session for ${hostFromUrl4(baseUrl)}/${workspace}.
19724
19764
  `);
@@ -19773,13 +19813,6 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19773
19813
  );
19774
19814
  apiKey = result.apiKey.key;
19775
19815
  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
19816
  const deviceWorkspace = "path" in result.workspace && typeof result.workspace.path === "string" ? result.workspace.path : result.workspace.slug;
19784
19817
  if (!workspace) workspace = deviceWorkspace;
19785
19818
  if (store) {
@@ -19791,6 +19824,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19791
19824
  createdAt: result.apiKey.createdAt
19792
19825
  });
19793
19826
  keychainBacked = true;
19827
+ sessionAddress = deviceWorkspace;
19794
19828
  rememberSession(cfg.ship.stateDir, hostFromUrl4(baseUrl), deviceWorkspace);
19795
19829
  process.stderr.write(
19796
19830
  `API key stored in the OS keychain for ${hostFromUrl4(baseUrl)}/${deviceWorkspace}.
@@ -19834,9 +19868,18 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19834
19868
  };
19835
19869
  const runConnect = async (ws, proj) => {
19836
19870
  let found = await discover({ ...authOpts, workspace: ws, project: proj, area: value("area"), ship, dryRun, interactive: canAsk || connectEvents.json });
19871
+ if (store && keychainBacked && sessionAddress && sessionAddress !== found.workspaceSlug) {
19872
+ if (await adoptSessionAddress({
19873
+ store,
19874
+ stateDir: cfg.ship.stateDir,
19875
+ host: hostFromUrl4(baseUrl),
19876
+ from: sessionAddress,
19877
+ to: found.workspaceSlug
19878
+ })) sessionAddress = found.workspaceSlug;
19879
+ }
19837
19880
  if (!signedInEmitted && found.meId) {
19838
19881
  signedInEmitted = true;
19839
- connectEvents.emit("signed_in", `key belongs to ${found.meEmail ?? found.meId}`, {
19882
+ connectEvents.emit("signed_in", mintedFromDevice ? `signed in as ${found.meEmail ?? found.meId}` : `key belongs to ${found.meEmail ?? found.meId}`, {
19840
19883
  identity: { id: found.meId, email: found.meEmail, name: found.meName },
19841
19884
  workspaces: [{ slug: found.workspaceSlug, name: found.workspaceName, role: found.role }],
19842
19885
  session: signInSource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.79.7",
3
+ "version": "0.79.9",
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",