@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.
- package/dist/cli.js +107 -34
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`) ||
|
|
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
|
-
|
|
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 &&
|
|
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