@tablation/crew 0.98.0 → 0.99.0
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 +56 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1602,6 +1602,28 @@ import { dirname as dirname3, join as join9 } from "path";
|
|
|
1602
1602
|
var ForgeError = class extends Error {
|
|
1603
1603
|
};
|
|
1604
1604
|
var DEFAULT_FORGE_USERNAME = "x-access-token";
|
|
1605
|
+
var DEFAULT_FORGE_HOST = "github.com";
|
|
1606
|
+
var FORGE_DESCRIPTORS = {
|
|
1607
|
+
github: {
|
|
1608
|
+
kind: "github",
|
|
1609
|
+
httpsUrl: (host, remote) => `https://${host}/${remote}.git`,
|
|
1610
|
+
sshUrl: (host, remote) => `git@${host}:${remote}.git`,
|
|
1611
|
+
credentialUsername: DEFAULT_FORGE_USERNAME
|
|
1612
|
+
},
|
|
1613
|
+
gitlab: {
|
|
1614
|
+
kind: "gitlab",
|
|
1615
|
+
httpsUrl: (host, remote) => `https://${host}/${remote}.git`,
|
|
1616
|
+
sshUrl: (host, remote) => `git@${host}:${remote}.git`,
|
|
1617
|
+
credentialUsername: "oauth2"
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
function normalizeForgeHost(host) {
|
|
1621
|
+
const h = host?.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/\/+$/, "");
|
|
1622
|
+
return h || DEFAULT_FORGE_HOST;
|
|
1623
|
+
}
|
|
1624
|
+
function forgeDescriptorFor(host) {
|
|
1625
|
+
return normalizeForgeHost(host).includes("gitlab") ? FORGE_DESCRIPTORS.gitlab : FORGE_DESCRIPTORS.github;
|
|
1626
|
+
}
|
|
1605
1627
|
var DEFAULT_KEYCHAIN_SERVICE = "crew-forge";
|
|
1606
1628
|
function defaultTokenFilePath(stateDir, host) {
|
|
1607
1629
|
return join9(stateDir, "forges", `${host.replace(/[^a-z0-9.-]/gi, "_")}.token`);
|
|
@@ -1814,7 +1836,7 @@ function parseForges(raw) {
|
|
|
1814
1836
|
throw new ForgeError(`${where}.token.source must be one of: ${Object.keys(TOKEN_SOURCES).join(", ")}`);
|
|
1815
1837
|
}
|
|
1816
1838
|
out[host.toLowerCase()] = {
|
|
1817
|
-
username: typeof e.username === "string" && e.username ? e.username :
|
|
1839
|
+
username: typeof e.username === "string" && e.username ? e.username : forgeDescriptorFor(host).credentialUsername,
|
|
1818
1840
|
token
|
|
1819
1841
|
};
|
|
1820
1842
|
}
|
|
@@ -2932,6 +2954,7 @@ function parseResolved(raw, m, where) {
|
|
|
2932
2954
|
reposModelId: raw.reposModelId,
|
|
2933
2955
|
repoNames: raw.repoNames,
|
|
2934
2956
|
repoRemotes: raw.repoRemotes,
|
|
2957
|
+
repoForgeHosts: raw.repoForgeHosts,
|
|
2935
2958
|
models: {
|
|
2936
2959
|
issues: m.req(raw.models?.issues, `${where}.resolved.models.issues`),
|
|
2937
2960
|
comments: m.req(raw.models?.comments, `${where}.resolved.models.comments`),
|
|
@@ -3437,23 +3460,29 @@ function reposOf(r) {
|
|
|
3437
3460
|
if (r.dirIsCloneBase) {
|
|
3438
3461
|
const [workspace2] = splitRoute(r.route);
|
|
3439
3462
|
const remotes2 = r.resolved?.repoRemotes ?? {};
|
|
3463
|
+
const hosts = r.resolved?.repoForgeHosts ?? {};
|
|
3440
3464
|
return Object.entries(r.resolved?.repoNames ?? {}).map(([id, name]) => ({
|
|
3441
3465
|
name,
|
|
3442
3466
|
dir: defaultRepoDir(r.reposBasePath, workspace2, name),
|
|
3443
|
-
remote: remotes2[id]
|
|
3467
|
+
remote: remotes2[id],
|
|
3468
|
+
...hosts[id] ? { forgeHost: hosts[id] } : {}
|
|
3444
3469
|
}));
|
|
3445
3470
|
}
|
|
3446
3471
|
if (trackerNames.length <= 1) return [{ name: basename3(r.dir), dir: r.dir }];
|
|
3447
3472
|
const remotes = r.resolved?.repoRemotes ?? {};
|
|
3448
3473
|
const remoteByName = {};
|
|
3474
|
+
const hostByName = {};
|
|
3449
3475
|
for (const [id, name] of Object.entries(r.resolved.repoNames)) {
|
|
3450
3476
|
if (remotes[id]) remoteByName[name] = remotes[id];
|
|
3477
|
+
const h = r.resolved?.repoForgeHosts?.[id];
|
|
3478
|
+
if (h) hostByName[name] = h;
|
|
3451
3479
|
}
|
|
3452
3480
|
const [workspace] = splitRoute(r.route);
|
|
3453
3481
|
return trackerNames.map((name) => ({
|
|
3454
3482
|
name,
|
|
3455
3483
|
dir: defaultRepoDir(r.reposBasePath, workspace, name),
|
|
3456
|
-
remote: remoteByName[name]
|
|
3484
|
+
remote: remoteByName[name],
|
|
3485
|
+
...hostByName[name] ? { forgeHost: hostByName[name] } : {}
|
|
3457
3486
|
}));
|
|
3458
3487
|
}
|
|
3459
3488
|
function passengerRepoTargets(r) {
|
|
@@ -3469,10 +3498,12 @@ function passengerCheckoutTargets(r) {
|
|
|
3469
3498
|
const remotes = r.resolved?.repoRemotes ?? {};
|
|
3470
3499
|
return passengerRepoTargets(r).map((t) => {
|
|
3471
3500
|
const id = repoIdForName(r, t.name);
|
|
3501
|
+
const forgeHost = t.forgeHost ?? (id ? r.resolved?.repoForgeHosts?.[id] : void 0);
|
|
3472
3502
|
return {
|
|
3473
3503
|
name: t.name,
|
|
3474
3504
|
dir: passengerCheckoutDir(r.reposBasePath, workspace, t.name),
|
|
3475
|
-
remote: t.remote ?? (id ? remotes[id] : void 0)
|
|
3505
|
+
remote: t.remote ?? (id ? remotes[id] : void 0),
|
|
3506
|
+
...forgeHost ? { forgeHost } : {}
|
|
3476
3507
|
};
|
|
3477
3508
|
});
|
|
3478
3509
|
}
|
|
@@ -4780,8 +4811,10 @@ function setForgeTokenHosts(hosts) {
|
|
|
4780
4811
|
}
|
|
4781
4812
|
function cloneUrlFor(remote, opts = {}) {
|
|
4782
4813
|
const origin = opts.originUrl?.trim() ?? "";
|
|
4783
|
-
const
|
|
4784
|
-
|
|
4814
|
+
const host = normalizeForgeHost(opts.host);
|
|
4815
|
+
const scheme = tokenHosts.has(host) ? "https" : /^https?:\/\//i.test(origin) ? "https" : /^(git@|ssh:\/\/)/i.test(origin) ? "ssh" : opts.scheme ?? "ssh";
|
|
4816
|
+
const forge = forgeDescriptorFor(host);
|
|
4817
|
+
return scheme === "https" ? forge.httpsUrl(host, remote) : forge.sshUrl(host, remote);
|
|
4785
4818
|
}
|
|
4786
4819
|
function originUrlOf(dir, remoteName) {
|
|
4787
4820
|
try {
|
|
@@ -5244,7 +5277,7 @@ function probePushAuth(o, run2 = realGit, baseEnv = process.env) {
|
|
|
5244
5277
|
const raw = (err3.stderr ? String(err3.stderr) : err3.message).trim().split("\n").filter(Boolean).slice(-2).join(" / ");
|
|
5245
5278
|
const detail = raw || "push dry-run failed";
|
|
5246
5279
|
const f = o.forge;
|
|
5247
|
-
if (!f) return { state: "failed", detail, hint: pushAuthHint(o.remoteUrl) };
|
|
5280
|
+
if (!f) return { state: "failed", detail: hostOfRemote(o.remoteUrl) ? `${hostOfRemote(o.remoteUrl)}: ${detail}` : detail, hint: pushAuthHint(o.remoteUrl) };
|
|
5248
5281
|
if (!f.hasToken) {
|
|
5249
5282
|
return {
|
|
5250
5283
|
state: "failed",
|
|
@@ -13156,7 +13189,8 @@ function planRepoClone(i) {
|
|
|
13156
13189
|
const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
|
|
13157
13190
|
const target = i.dir ? expandDir(i.dir, i.base) : join29(expandDir(basePath, i.base), i.route.split("/")[0], name);
|
|
13158
13191
|
const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
|
|
13159
|
-
|
|
13192
|
+
const forgeHost = i.resolved?.repoForgeHosts?.[id];
|
|
13193
|
+
return { ...add, remote, target, ...forgeHost ? { forgeHost } : {} };
|
|
13160
13194
|
}
|
|
13161
13195
|
|
|
13162
13196
|
// src/doctor-fix.ts
|
|
@@ -15282,7 +15316,7 @@ function runForge(a, io) {
|
|
|
15282
15316
|
const doc = parseDocument4(io.readFile(io.configFile));
|
|
15283
15317
|
doc.setIn(["ship", "forges", host, "token"], tok);
|
|
15284
15318
|
io.writeFile(io.configFile, doc.toString());
|
|
15285
|
-
io.forges = { ...io.forges ?? {}, [host]: { username:
|
|
15319
|
+
io.forges = { ...io.forges ?? {}, [host]: { username: forgeDescriptorFor(host).credentialUsername, token: tok } };
|
|
15286
15320
|
existing = forgeFor(io.forges, host);
|
|
15287
15321
|
}
|
|
15288
15322
|
if (existing.entry.token.source === "env") {
|
|
@@ -16279,6 +16313,10 @@ async function discover(o) {
|
|
|
16279
16313
|
repos.filter((r) => r.remote).map((r) => [r.id, r.remote])
|
|
16280
16314
|
);
|
|
16281
16315
|
if (Object.keys(remotes).length > 0) out.repoRemotes = remotes;
|
|
16316
|
+
const forgeHosts = Object.fromEntries(
|
|
16317
|
+
repos.filter((r) => r.forge_host?.trim()).map((r) => [r.id, r.forge_host.trim().toLowerCase()])
|
|
16318
|
+
);
|
|
16319
|
+
if (Object.keys(forgeHosts).length > 0) out.repoForgeHosts = forgeHosts;
|
|
16282
16320
|
}
|
|
16283
16321
|
if (o.area) {
|
|
16284
16322
|
if (!out.areaModelId) problems.push(`--area given but this project has no "Projects" table`);
|
|
@@ -18432,7 +18470,8 @@ function passengerCheckoutSyncSpecs(cfg, workspaceId) {
|
|
|
18432
18470
|
gitRemoteName: effective.branch.remote,
|
|
18433
18471
|
base: effective.branch.base,
|
|
18434
18472
|
originUrl: ordinary ? originUrlOf(ordinary.dir, effective.branch.remote) : null,
|
|
18435
|
-
cloneScheme: cfg.ship.cloneScheme
|
|
18473
|
+
cloneScheme: cfg.ship.cloneScheme,
|
|
18474
|
+
...target.forgeHost ? { forgeHost: target.forgeHost } : {}
|
|
18436
18475
|
});
|
|
18437
18476
|
}
|
|
18438
18477
|
}
|
|
@@ -18442,7 +18481,7 @@ function syncAllPassengerCheckouts(cfg) {
|
|
|
18442
18481
|
return passengerCheckoutSyncSpecs(cfg).map((spec) => ({
|
|
18443
18482
|
name: spec.name,
|
|
18444
18483
|
workspaceId: spec.workspaceId,
|
|
18445
|
-
outcome: syncPassengerCheckout(spec.dir, spec.remote, spec.gitRemoteName, spec.base, { originUrl: spec.originUrl, scheme: spec.cloneScheme })
|
|
18484
|
+
outcome: syncPassengerCheckout(spec.dir, spec.remote, spec.gitRemoteName, spec.base, { originUrl: spec.originUrl, scheme: spec.cloneScheme, host: spec.forgeHost })
|
|
18446
18485
|
}));
|
|
18447
18486
|
}
|
|
18448
18487
|
|
|
@@ -18815,7 +18854,7 @@ function createSyncDaemonServer(targets, secret) {
|
|
|
18815
18854
|
try {
|
|
18816
18855
|
const synced = targets.map((t) => ({
|
|
18817
18856
|
name: t.name,
|
|
18818
|
-
outcome: syncPassengerCheckout(t.dir, t.remote, t.gitRemoteName, t.base, { originUrl: t.originUrl, scheme: t.cloneScheme })
|
|
18857
|
+
outcome: syncPassengerCheckout(t.dir, t.remote, t.gitRemoteName, t.base, { originUrl: t.originUrl, scheme: t.cloneScheme, host: t.forgeHost })
|
|
18819
18858
|
}));
|
|
18820
18859
|
res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify({ synced }));
|
|
18821
18860
|
} catch (error) {
|
|
@@ -19511,7 +19550,7 @@ async function runRoleAgentLocked(role, decision, ctx, hooks, held) {
|
|
|
19511
19550
|
const target = repoTargetFor(route2, result.ticket.repo_id);
|
|
19512
19551
|
ticketRepo = target?.name;
|
|
19513
19552
|
try {
|
|
19514
|
-
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: ship.cloneScheme })) {
|
|
19553
|
+
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: ship.cloneScheme, host: target.forgeHost })) {
|
|
19515
19554
|
emit.emit(`cloned ${target.name} into ${target.dir}`, { step: "select", role });
|
|
19516
19555
|
}
|
|
19517
19556
|
} catch (e) {
|
|
@@ -21828,7 +21867,7 @@ ${cu.tail.trimEnd()}
|
|
|
21828
21867
|
const target = repoTargetFor(w.route, result.ticket.repo_id);
|
|
21829
21868
|
fleetTicketRepo = target?.name;
|
|
21830
21869
|
try {
|
|
21831
|
-
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: cfg.ship.cloneScheme })) {
|
|
21870
|
+
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: cfg.ship.cloneScheme, host: target.forgeHost })) {
|
|
21832
21871
|
wemit.emit(`cloned ${target.name} into ${target.dir}`, { step: "select", role: w.role });
|
|
21833
21872
|
}
|
|
21834
21873
|
} catch (e) {
|
|
@@ -22045,7 +22084,7 @@ ${cu.tail.trimEnd()}
|
|
|
22045
22084
|
const target = repoTargetFor(route2, result.ticket.repo_id);
|
|
22046
22085
|
ticketRepo = target?.name;
|
|
22047
22086
|
try {
|
|
22048
|
-
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: cfg.ship.cloneScheme })) {
|
|
22087
|
+
if (target && ensureRepoCheckout(target.dir, target.remote, { scheme: cfg.ship.cloneScheme, host: target.forgeHost })) {
|
|
22049
22088
|
emit.emit(`cloned ${target.name} into ${target.dir}`, { step: "select", role: current });
|
|
22050
22089
|
}
|
|
22051
22090
|
} catch (e) {
|
|
@@ -22785,7 +22824,7 @@ ${cu.tail.trimEnd()}
|
|
|
22785
22824
|
}
|
|
22786
22825
|
say("cloning", { remote: plan2.remote, target: plan2.target }, `cloning ${plan2.remote} into ${plan2.target}`);
|
|
22787
22826
|
try {
|
|
22788
|
-
ensureRepoCheckout(plan2.target, plan2.remote, { scheme: cfg.ship.cloneScheme ?? "https" });
|
|
22827
|
+
ensureRepoCheckout(plan2.target, plan2.remote, { scheme: cfg.ship.cloneScheme ?? "https", host: plan2.forgeHost });
|
|
22789
22828
|
} catch (e) {
|
|
22790
22829
|
if (!(e instanceof GitError)) throw e;
|
|
22791
22830
|
const authHint = /authentication failed|could not read (username|password)|permission denied|terminal prompts disabled|repository not found/i.test(e.message) ? "\n hint: git could not authenticate \u2014 run `gh auth login` (or add an SSH key and set ship.cloneScheme: ssh), then re-run" : "";
|
|
@@ -23375,6 +23414,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
23375
23414
|
reposModelId: found.reposModelId,
|
|
23376
23415
|
repoNames: found.repoNames,
|
|
23377
23416
|
repoRemotes: found.repoRemotes,
|
|
23417
|
+
repoForgeHosts: found.repoForgeHosts,
|
|
23378
23418
|
models: found.models,
|
|
23379
23419
|
seats: found.seats,
|
|
23380
23420
|
holds: found.holds.map((h) => ({ id: h.id, role: h.name })),
|
package/package.json
CHANGED