dahrk-node 0.1.19 → 0.1.20
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/main.js +259 -17
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1510,6 +1510,14 @@ function createGitService(opts = {}) {
|
|
|
1510
1510
|
};
|
|
1511
1511
|
const netEnv = (authEnv) => authEnv ?? { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
1512
1512
|
const withTokenUser = (gitUrl) => /^https:\/\/[^@/]+@/.test(gitUrl) ? gitUrl : gitUrl.replace(/^https:\/\//, "https://x-access-token@");
|
|
1513
|
+
const resolveRemoteAuth = (gitUrl, credentialToken) => {
|
|
1514
|
+
const auth = credentialToken ? setupAuth(credentialToken) : void 0;
|
|
1515
|
+
return {
|
|
1516
|
+
remote: credentialToken ? withTokenUser(gitUrl) : gitUrl,
|
|
1517
|
+
authEnv: auth?.env,
|
|
1518
|
+
cleanup: () => auth?.cleanup()
|
|
1519
|
+
};
|
|
1520
|
+
};
|
|
1513
1521
|
const mirrorPathFor = (repoId) => join3(mirrorsDir, sanitizeBranchName(repoId));
|
|
1514
1522
|
const listWorktrees = (mirror) => {
|
|
1515
1523
|
let out2;
|
|
@@ -1689,15 +1697,14 @@ function createGitService(opts = {}) {
|
|
|
1689
1697
|
} catch {
|
|
1690
1698
|
}
|
|
1691
1699
|
}
|
|
1692
|
-
const
|
|
1693
|
-
const remote = opts2.credentialToken ? withTokenUser(ref.gitUrl) : ref.gitUrl;
|
|
1700
|
+
const { remote, authEnv, cleanup } = resolveRemoteAuth(ref.gitUrl, opts2.credentialToken);
|
|
1694
1701
|
let pushed = false;
|
|
1695
1702
|
let integration;
|
|
1696
1703
|
try {
|
|
1697
1704
|
let fetched = false;
|
|
1698
1705
|
if (opts2.base) {
|
|
1699
1706
|
try {
|
|
1700
|
-
git(worktreePath, ["fetch", remote, opts2.base], netEnv(
|
|
1707
|
+
git(worktreePath, ["fetch", remote, opts2.base], netEnv(authEnv));
|
|
1701
1708
|
fetched = true;
|
|
1702
1709
|
} catch (e) {
|
|
1703
1710
|
log.warn(`base fetch failed for ${opts2.base}; skipping push-time integration: ${e.message}`);
|
|
@@ -1721,7 +1728,7 @@ function createGitService(opts = {}) {
|
|
|
1721
1728
|
"merge",
|
|
1722
1729
|
"--no-edit",
|
|
1723
1730
|
"FETCH_HEAD"
|
|
1724
|
-
],
|
|
1731
|
+
], authEnv);
|
|
1725
1732
|
integration = "clean";
|
|
1726
1733
|
headSha = git(worktreePath, ["rev-parse", "HEAD"]).trim();
|
|
1727
1734
|
} catch (mergeErr) {
|
|
@@ -1738,10 +1745,10 @@ function createGitService(opts = {}) {
|
|
|
1738
1745
|
throw mergeErr;
|
|
1739
1746
|
}
|
|
1740
1747
|
}
|
|
1741
|
-
git(worktreePath, ["push", remote, `HEAD:refs/heads/${branch}`], netEnv(
|
|
1748
|
+
git(worktreePath, ["push", remote, `HEAD:refs/heads/${branch}`], netEnv(authEnv));
|
|
1742
1749
|
pushed = true;
|
|
1743
1750
|
} finally {
|
|
1744
|
-
|
|
1751
|
+
cleanup();
|
|
1745
1752
|
}
|
|
1746
1753
|
return { headSha, pushed, nothingToCommit: !dirty, commitsAhead, ...integration ? { integration } : {} };
|
|
1747
1754
|
},
|
|
@@ -1752,12 +1759,11 @@ function createGitService(opts = {}) {
|
|
|
1752
1759
|
}
|
|
1753
1760
|
const wipRef = sanitizeBranchName(opts2.branch);
|
|
1754
1761
|
const { headSha, dirty } = commitPending(worktreePath, opts2.message);
|
|
1755
|
-
const
|
|
1756
|
-
const remote = opts2.credentialToken ? withTokenUser(ref.gitUrl) : ref.gitUrl;
|
|
1762
|
+
const { remote, authEnv, cleanup } = resolveRemoteAuth(ref.gitUrl, opts2.credentialToken);
|
|
1757
1763
|
try {
|
|
1758
|
-
git(worktreePath, ["push", "--force", remote, `HEAD:refs/heads/${wipRef}`], netEnv(
|
|
1764
|
+
git(worktreePath, ["push", "--force", remote, `HEAD:refs/heads/${wipRef}`], netEnv(authEnv));
|
|
1759
1765
|
} finally {
|
|
1760
|
-
|
|
1766
|
+
cleanup();
|
|
1761
1767
|
}
|
|
1762
1768
|
return { headSha, pushed: true, nothingToCommit: !dirty, wipRef };
|
|
1763
1769
|
},
|
|
@@ -1772,15 +1778,14 @@ function createGitService(opts = {}) {
|
|
|
1772
1778
|
if (!dirty) return { dirty: false, headSha, pushed: false };
|
|
1773
1779
|
git(worktreePath, ["branch", "--force", wipRef, tailSha]);
|
|
1774
1780
|
let pushed = false;
|
|
1775
|
-
const
|
|
1776
|
-
const remote = opts2.credentialToken ? withTokenUser(ref.gitUrl) : ref.gitUrl;
|
|
1781
|
+
const { remote, authEnv, cleanup } = resolveRemoteAuth(ref.gitUrl, opts2.credentialToken);
|
|
1777
1782
|
try {
|
|
1778
|
-
git(worktreePath, ["push", "--force", remote, `${tailSha}:refs/heads/${wipRef}`], netEnv(
|
|
1783
|
+
git(worktreePath, ["push", "--force", remote, `${tailSha}:refs/heads/${wipRef}`], netEnv(authEnv));
|
|
1779
1784
|
pushed = true;
|
|
1780
1785
|
} catch (e) {
|
|
1781
1786
|
log.warn(`could not push the preserved tail to ${wipRef}: ${e.message}`);
|
|
1782
1787
|
} finally {
|
|
1783
|
-
|
|
1788
|
+
cleanup();
|
|
1784
1789
|
}
|
|
1785
1790
|
git(worktreePath, ["reset", "--hard", headSha]);
|
|
1786
1791
|
git(worktreePath, ["clean", "-fd", "--exclude", SCRATCH_DIR]);
|
|
@@ -4596,6 +4601,7 @@ var COMMANDS = /* @__PURE__ */ new Set([
|
|
|
4596
4601
|
"logs",
|
|
4597
4602
|
"diagnose",
|
|
4598
4603
|
"run",
|
|
4604
|
+
"repo",
|
|
4599
4605
|
"service",
|
|
4600
4606
|
"doctor",
|
|
4601
4607
|
"status",
|
|
@@ -4619,6 +4625,7 @@ function parseCli(argv) {
|
|
|
4619
4625
|
flagArgs = rest;
|
|
4620
4626
|
}
|
|
4621
4627
|
if (command === "run") return parseRun(flagArgs);
|
|
4628
|
+
if (command === "repo") return parseRepo(flagArgs);
|
|
4622
4629
|
if (command === "service") return parseService(flagArgs);
|
|
4623
4630
|
if (command === "update") return parseUpdate(flagArgs);
|
|
4624
4631
|
if (command === "logs") return parseLogs(flagArgs);
|
|
@@ -4633,6 +4640,8 @@ function parseCli(argv) {
|
|
|
4633
4640
|
"hub-url": { type: "string" },
|
|
4634
4641
|
ephemeral: { type: "boolean", default: false },
|
|
4635
4642
|
foreground: { type: "boolean", default: false },
|
|
4643
|
+
// `start`: enrol but do not install the service (the operator supervises the node themselves).
|
|
4644
|
+
"no-service": { type: "boolean", default: false },
|
|
4636
4645
|
// `stop` / `restart`: take the node down even though it has work in flight.
|
|
4637
4646
|
force: { type: "boolean", default: false },
|
|
4638
4647
|
// `status`: machine-readable output.
|
|
@@ -4654,7 +4663,8 @@ function parseCli(argv) {
|
|
|
4654
4663
|
ephemeral,
|
|
4655
4664
|
// An ephemeral node mints a throwaway id and persists nothing, so there is nothing coherent to hand to
|
|
4656
4665
|
// a supervisor that restarts it on boot. It is a foreground node by definition.
|
|
4657
|
-
foreground: (values.foreground ?? false) || ephemeral
|
|
4666
|
+
foreground: (values.foreground ?? false) || ephemeral,
|
|
4667
|
+
...values["no-service"] ? { noService: true } : {}
|
|
4658
4668
|
};
|
|
4659
4669
|
if (command === "stop") return { kind: "stop", force };
|
|
4660
4670
|
if (command === "restart") return { kind: "restart", flags, force };
|
|
@@ -4786,6 +4796,43 @@ function parseService(flagArgs) {
|
|
|
4786
4796
|
};
|
|
4787
4797
|
return { kind: "service", flags };
|
|
4788
4798
|
}
|
|
4799
|
+
var REPO_ACTIONS = /* @__PURE__ */ new Set(["add"]);
|
|
4800
|
+
var isRepoAction = (s) => REPO_ACTIONS.has(s);
|
|
4801
|
+
function parseRepo(flagArgs) {
|
|
4802
|
+
let values;
|
|
4803
|
+
let positionals;
|
|
4804
|
+
try {
|
|
4805
|
+
({ values, positionals } = parseArgs({
|
|
4806
|
+
args: flagArgs,
|
|
4807
|
+
options: {
|
|
4808
|
+
name: { type: "string" },
|
|
4809
|
+
"hub-url": { type: "string" },
|
|
4810
|
+
token: { type: "string" },
|
|
4811
|
+
help: { type: "boolean", default: false }
|
|
4812
|
+
},
|
|
4813
|
+
allowPositionals: true
|
|
4814
|
+
}));
|
|
4815
|
+
} catch (e) {
|
|
4816
|
+
return { kind: "error", message: e.message };
|
|
4817
|
+
}
|
|
4818
|
+
if (values.help) return { kind: "help", command: "repo" };
|
|
4819
|
+
if (positionals.length === 0) {
|
|
4820
|
+
return { kind: "error", message: "repo: missing action (`dahrk repo add`)" };
|
|
4821
|
+
}
|
|
4822
|
+
if (positionals.length > 1) {
|
|
4823
|
+
return { kind: "error", message: `repo: unexpected argument "${positionals[1]}" (one action at a time)` };
|
|
4824
|
+
}
|
|
4825
|
+
const action = positionals[0];
|
|
4826
|
+
if (!isRepoAction(action)) {
|
|
4827
|
+
return { kind: "error", message: `repo: unknown action "${action}" (expected add)` };
|
|
4828
|
+
}
|
|
4829
|
+
const flags = {
|
|
4830
|
+
...values.name ? { name: values.name } : {},
|
|
4831
|
+
...values["hub-url"] ? { hubUrl: values["hub-url"] } : {},
|
|
4832
|
+
...values.token ? { token: values.token } : {}
|
|
4833
|
+
};
|
|
4834
|
+
return { kind: "repo", flags };
|
|
4835
|
+
}
|
|
4789
4836
|
function parseUpdate(flagArgs) {
|
|
4790
4837
|
let values;
|
|
4791
4838
|
try {
|
|
@@ -4830,6 +4877,8 @@ function usage(bin, command) {
|
|
|
4830
4877
|
" setting DAHRK_FOREGROUND=1, which is easier to set in a Dockerfile or pm2 config.",
|
|
4831
4878
|
" --ephemeral Do not persist (or read) node id and token; mint a throwaway id (CI / one-shot).",
|
|
4832
4879
|
" Implies --foreground: a node with no persistent identity has nothing to daemonise.",
|
|
4880
|
+
" --no-service Enrol (cache the token) but do not install the always-on service, for a node you",
|
|
4881
|
+
" supervise yourself. Run it later with `dahrk start --foreground` (or your own supervisor).",
|
|
4833
4882
|
"",
|
|
4834
4883
|
"Only one node may run at a time on a host - they would share this machine's node id and race each",
|
|
4835
4884
|
"other for Jobs - so a second `start` refuses rather than dialling the hub twice."
|
|
@@ -4930,6 +4979,30 @@ function usage(bin, command) {
|
|
|
4930
4979
|
" --token <token> Enrolment token to verify against the hub (or set DAHRK_ENROL_TOKEN)."
|
|
4931
4980
|
].join("\n");
|
|
4932
4981
|
}
|
|
4982
|
+
if (command === "repo") {
|
|
4983
|
+
return [
|
|
4984
|
+
`Usage: ${bin} repo add [options]`,
|
|
4985
|
+
"",
|
|
4986
|
+
"Register the current git repository with the hub, so it can run workflows. Run it from inside the",
|
|
4987
|
+
"repo - `cd your-repo && dahrk repo add` - and the node reads the `origin` remote and the current",
|
|
4988
|
+
"branch itself: no form, no pasted git URL, because the node already sits next to the code.",
|
|
4989
|
+
"",
|
|
4990
|
+
"The git URL is registered in the form the host can authenticate. An HTTPS origin is kept as-is; an",
|
|
4991
|
+
"SSH origin is kept when this host has an SSH key, and otherwise normalised to HTTPS with a warning.",
|
|
4992
|
+
"Re-running on an already-registered repo is a no-op, not an error or a duplicate.",
|
|
4993
|
+
"",
|
|
4994
|
+
"This uses the node's existing enrolment - run `dahrk start --token <token>` first if it is not yet",
|
|
4995
|
+
"enrolled. It dials the hub itself, so the daemon need not be running.",
|
|
4996
|
+
"",
|
|
4997
|
+
"Actions:",
|
|
4998
|
+
" add Register the repository in the current directory.",
|
|
4999
|
+
"",
|
|
5000
|
+
"Options:",
|
|
5001
|
+
" --name <name> Display name for the repo (default: the slug from the origin URL).",
|
|
5002
|
+
" --hub-url <url> Hub URL to register with (or set DAHRK_HUB_URL).",
|
|
5003
|
+
" --token <token> Enrolment token to authenticate with (default: the cached one)."
|
|
5004
|
+
].join("\n");
|
|
5005
|
+
}
|
|
4933
5006
|
if (command === "service") {
|
|
4934
5007
|
return [
|
|
4935
5008
|
`Usage: ${bin} service install|uninstall [options]`,
|
|
@@ -5006,6 +5079,7 @@ function usage(bin, command) {
|
|
|
5006
5079
|
" diagnose Write a support bundle you can read, and send on if you choose. Uploads nothing.",
|
|
5007
5080
|
" status Is it up, is it enrolled, what is it working on? (local, dials nothing)",
|
|
5008
5081
|
" run Run a workflow locally (engine-backed), e.g. `run preflight`.",
|
|
5082
|
+
" repo Register the current repository with the hub (`repo add`).",
|
|
5009
5083
|
" doctor Preflight checks: Node, runtimes, hub reachability, token validity.",
|
|
5010
5084
|
" service Install/uninstall the always-on service by hand (`start` does this for you).",
|
|
5011
5085
|
" update Update the client to the latest release (or print how for your channel).",
|
|
@@ -5335,6 +5409,91 @@ async function runDoctor(inputs, deps = {}) {
|
|
|
5335
5409
|
return checks.some((c) => c.status === "fail") ? 1 : 0;
|
|
5336
5410
|
}
|
|
5337
5411
|
|
|
5412
|
+
// src/repo-add.ts
|
|
5413
|
+
import { createHash as createHash4 } from "crypto";
|
|
5414
|
+
var stripRepoSuffix = (s) => s.replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
5415
|
+
function splitOwnerRepo(path) {
|
|
5416
|
+
const parts = stripRepoSuffix(path).split("/").filter(Boolean);
|
|
5417
|
+
if (parts.length < 2) return void 0;
|
|
5418
|
+
const repo = parts[parts.length - 1];
|
|
5419
|
+
const owner = parts.slice(0, -1).join("/");
|
|
5420
|
+
return { owner, repo };
|
|
5421
|
+
}
|
|
5422
|
+
function parseGitRemote(url) {
|
|
5423
|
+
const trimmed = url.trim();
|
|
5424
|
+
if (!trimmed) return void 0;
|
|
5425
|
+
const urlMatch = /^(?:ssh|https?):\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/(.+)$/i.exec(trimmed);
|
|
5426
|
+
if (urlMatch) {
|
|
5427
|
+
const parts = splitOwnerRepo(urlMatch[2]);
|
|
5428
|
+
return parts ? { host: urlMatch[1], ...parts } : void 0;
|
|
5429
|
+
}
|
|
5430
|
+
const scpMatch = /^(?:[^@\s]+@)?([^/:\s]+):(.+)$/.exec(trimmed);
|
|
5431
|
+
if (scpMatch) {
|
|
5432
|
+
const parts = splitOwnerRepo(scpMatch[2]);
|
|
5433
|
+
return parts ? { host: scpMatch[1], ...parts } : void 0;
|
|
5434
|
+
}
|
|
5435
|
+
return void 0;
|
|
5436
|
+
}
|
|
5437
|
+
var httpsFormOf = (r) => `https://${r.host}/${r.owner}/${r.repo}.git`;
|
|
5438
|
+
var isHttpsRemote = (url) => /^https?:\/\//i.test(url.trim());
|
|
5439
|
+
function chooseGitUrl(input) {
|
|
5440
|
+
const originUrl = input.originUrl.trim();
|
|
5441
|
+
if (isHttpsRemote(originUrl)) return { gitUrl: originUrl, converted: false };
|
|
5442
|
+
if (input.sshKeyPresent) return { gitUrl: originUrl, converted: false };
|
|
5443
|
+
const parsed = parseGitRemote(originUrl);
|
|
5444
|
+
if (!parsed) return { gitUrl: originUrl, converted: false };
|
|
5445
|
+
return { gitUrl: httpsFormOf(parsed), converted: true };
|
|
5446
|
+
}
|
|
5447
|
+
var deriveRepoName = (remote) => remote.repo;
|
|
5448
|
+
function deriveRepoId(gitUrl) {
|
|
5449
|
+
const parsed = parseGitRemote(gitUrl);
|
|
5450
|
+
const canonical2 = parsed ? `${parsed.host}/${parsed.owner}/${parsed.repo}`.toLowerCase() : gitUrl.trim().toLowerCase();
|
|
5451
|
+
const hash = createHash4("sha256").update(canonical2).digest("hex").slice(0, 12);
|
|
5452
|
+
const slug = (parsed ? parsed.repo : "repo").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
5453
|
+
return `${slug}-${hash}`;
|
|
5454
|
+
}
|
|
5455
|
+
function hubHttpBase(url) {
|
|
5456
|
+
const trimmed = url.trim().replace(/\/+$/, "");
|
|
5457
|
+
if (/^wss:\/\//i.test(trimmed)) return trimmed.replace(/^wss:\/\//i, "https://");
|
|
5458
|
+
if (/^ws:\/\//i.test(trimmed)) return trimmed.replace(/^ws:\/\//i, "http://");
|
|
5459
|
+
return trimmed;
|
|
5460
|
+
}
|
|
5461
|
+
async function readJson(res) {
|
|
5462
|
+
try {
|
|
5463
|
+
const body = await res.json();
|
|
5464
|
+
return body && typeof body === "object" ? body : void 0;
|
|
5465
|
+
} catch {
|
|
5466
|
+
return void 0;
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
function driftOf(stored, repo) {
|
|
5470
|
+
if (!stored) return void 0;
|
|
5471
|
+
const drift = {};
|
|
5472
|
+
if (typeof stored.defaultBranch === "string" && stored.defaultBranch !== repo.defaultBranch) drift.branch = stored.defaultBranch;
|
|
5473
|
+
if (typeof stored.name === "string" && stored.name !== repo.name) drift.name = stored.name;
|
|
5474
|
+
return drift.branch || drift.name ? drift : void 0;
|
|
5475
|
+
}
|
|
5476
|
+
async function registerRepo(deps, args) {
|
|
5477
|
+
const url = `${args.base}/config/api/repositories`;
|
|
5478
|
+
let res;
|
|
5479
|
+
try {
|
|
5480
|
+
res = await deps.fetch(url, {
|
|
5481
|
+
method: "POST",
|
|
5482
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${args.token}` },
|
|
5483
|
+
body: JSON.stringify(args.repo)
|
|
5484
|
+
});
|
|
5485
|
+
} catch (e) {
|
|
5486
|
+
return { kind: "error", message: `could not reach the hub at ${args.base}: ${e.message}` };
|
|
5487
|
+
}
|
|
5488
|
+
const stored = await readJson(res);
|
|
5489
|
+
if (res.status === 200 || res.status === 409) {
|
|
5490
|
+
const drift = driftOf(stored, args.repo);
|
|
5491
|
+
return { kind: "already", ...drift ? { drift } : {} };
|
|
5492
|
+
}
|
|
5493
|
+
if (res.ok) return { kind: "registered" };
|
|
5494
|
+
return { kind: "error", message: `the hub rejected the repo (HTTP ${res.status})` };
|
|
5495
|
+
}
|
|
5496
|
+
|
|
5338
5497
|
// src/lock.ts
|
|
5339
5498
|
import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync7, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
5340
5499
|
import { dirname as dirname6 } from "path";
|
|
@@ -5744,7 +5903,18 @@ function probeRepo(repoPath) {
|
|
|
5744
5903
|
baseBranch = git(["rev-parse", "--abbrev-ref", "HEAD"]) || void 0;
|
|
5745
5904
|
} catch {
|
|
5746
5905
|
}
|
|
5747
|
-
|
|
5906
|
+
let remoteUrl;
|
|
5907
|
+
try {
|
|
5908
|
+
remoteUrl = git(["remote", "get-url", "origin"]) || void 0;
|
|
5909
|
+
} catch {
|
|
5910
|
+
}
|
|
5911
|
+
return {
|
|
5912
|
+
path: repoPath,
|
|
5913
|
+
isGitRepo: true,
|
|
5914
|
+
headResolves,
|
|
5915
|
+
...baseBranch ? { baseBranch } : {},
|
|
5916
|
+
...remoteUrl ? { remoteUrl } : {}
|
|
5917
|
+
};
|
|
5748
5918
|
}
|
|
5749
5919
|
function gatherHostFacts(repoPath) {
|
|
5750
5920
|
const root = worktreeRoot(process.env);
|
|
@@ -6749,7 +6919,7 @@ async function runStatus(inputs, deps) {
|
|
|
6749
6919
|
}
|
|
6750
6920
|
|
|
6751
6921
|
// src/main.ts
|
|
6752
|
-
var CLIENT_VERSION = "0.1.
|
|
6922
|
+
var CLIENT_VERSION = "0.1.20";
|
|
6753
6923
|
var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
|
|
6754
6924
|
var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
6755
6925
|
var RUNTIMES2 = ["claude-code", "codex", "pi"];
|
|
@@ -6836,6 +7006,12 @@ async function start(flags) {
|
|
|
6836
7006
|
if (wantsForeground(env, flags)) return startForeground(env, flags);
|
|
6837
7007
|
const enrolled = await enrolToDisk(env);
|
|
6838
7008
|
if (enrolled !== 0) return enrolled;
|
|
7009
|
+
if (flags.noService) {
|
|
7010
|
+
out("");
|
|
7011
|
+
out(verdict("ok", "Enrolled. Not installing a service (--no-service)."));
|
|
7012
|
+
out(hint("Run the node with `dahrk start --foreground` (or your own supervisor), or `dahrk start` to install the service."));
|
|
7013
|
+
return 0;
|
|
7014
|
+
}
|
|
6839
7015
|
await offerUpdate(env);
|
|
6840
7016
|
const outcome = await runNodeStart(serviceInputs(env));
|
|
6841
7017
|
if (outcome.kind === "error") return outcome.code;
|
|
@@ -7081,6 +7257,69 @@ async function runWorkflow(flags) {
|
|
|
7081
7257
|
clientVersion: CLIENT_VERSION
|
|
7082
7258
|
});
|
|
7083
7259
|
}
|
|
7260
|
+
async function runRepoAdd(flags) {
|
|
7261
|
+
const env = applyEnvAliases(process.env);
|
|
7262
|
+
if (flags.hubUrl) env.DAHRK_HUB_URL = flags.hubUrl;
|
|
7263
|
+
if (flags.token) env.DAHRK_ENROL_TOKEN = flags.token;
|
|
7264
|
+
const cwd = process.cwd();
|
|
7265
|
+
const probe2 = probeRepo(cwd);
|
|
7266
|
+
if (!probe2.isGitRepo) {
|
|
7267
|
+
out("");
|
|
7268
|
+
out(verdict("fail", `${cwd} is not a git repository.`));
|
|
7269
|
+
out(hint("Run `dahrk repo add` from inside the repository you want to register."));
|
|
7270
|
+
return 1;
|
|
7271
|
+
}
|
|
7272
|
+
if (!probe2.remoteUrl) {
|
|
7273
|
+
out("");
|
|
7274
|
+
out(verdict("fail", "This repository has no `origin` remote, so there is nothing to register."));
|
|
7275
|
+
out(hint("Add one with `git remote add origin <url>`, then run `dahrk repo add` again."));
|
|
7276
|
+
return 1;
|
|
7277
|
+
}
|
|
7278
|
+
const token = resolveEnrolToken(env);
|
|
7279
|
+
if (!token) {
|
|
7280
|
+
out("");
|
|
7281
|
+
out(verdict("fail", "This node is not enrolled, so it cannot register a repository."));
|
|
7282
|
+
out(hint("Enrol it first with `dahrk start --token <token>`."));
|
|
7283
|
+
return 1;
|
|
7284
|
+
}
|
|
7285
|
+
const { gitUrl, converted } = chooseGitUrl({ originUrl: probe2.remoteUrl, sshKeyPresent: sshKeyPresent() });
|
|
7286
|
+
if (converted) {
|
|
7287
|
+
out("");
|
|
7288
|
+
out(verdict("warn", `No SSH key found on this host, so the SSH remote was registered as HTTPS: ${gitUrl}`));
|
|
7289
|
+
out(hint("Add an SSH key (or set up brokered credentials) if this repo must be cloned over SSH - see DHK-252."));
|
|
7290
|
+
}
|
|
7291
|
+
const parsed = parseGitRemote(gitUrl);
|
|
7292
|
+
const name = flags.name ?? (parsed ? deriveRepoName(parsed) : "repo");
|
|
7293
|
+
const repo = {
|
|
7294
|
+
id: deriveRepoId(gitUrl),
|
|
7295
|
+
name,
|
|
7296
|
+
gitUrl,
|
|
7297
|
+
// HEAD's branch is the run's base. An empty repo (no commits) has none yet; default to `main`.
|
|
7298
|
+
defaultBranch: probe2.baseBranch ?? "main"
|
|
7299
|
+
};
|
|
7300
|
+
const base = hubHttpBase(env.DAHRK_HUB_URL ?? DEFAULT_HUB_URL);
|
|
7301
|
+
const result = await registerRepo({ fetch }, { base, token, repo });
|
|
7302
|
+
out("");
|
|
7303
|
+
switch (result.kind) {
|
|
7304
|
+
case "registered":
|
|
7305
|
+
out(verdict("ok", `Registered ${name} with the hub.`));
|
|
7306
|
+
out(kv("URL", gitUrl));
|
|
7307
|
+
out(kv("Branch", repo.defaultBranch));
|
|
7308
|
+
return 0;
|
|
7309
|
+
case "already":
|
|
7310
|
+
out(verdict("ok", `${name} is already registered - nothing to do.`));
|
|
7311
|
+
if (result.drift) {
|
|
7312
|
+
const bits = [];
|
|
7313
|
+
if (result.drift.branch) bits.push(`branch ${result.drift.branch} (this repo is on ${repo.defaultBranch})`);
|
|
7314
|
+
if (result.drift.name) bits.push(`name ${result.drift.name}`);
|
|
7315
|
+
out(hint(`The hub's stored record differs - ${bits.join(", ")} - and was left unchanged.`));
|
|
7316
|
+
}
|
|
7317
|
+
return 0;
|
|
7318
|
+
case "error":
|
|
7319
|
+
out(verdict("fail", `Could not register the repository: ${result.message}`));
|
|
7320
|
+
return 1;
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7084
7323
|
async function main() {
|
|
7085
7324
|
const invoked = basename2(process.argv[1] ?? "");
|
|
7086
7325
|
const bin = !invoked || invoked.startsWith("main.") ? "dahrk" : invoked;
|
|
@@ -7127,6 +7366,9 @@ async function main() {
|
|
|
7127
7366
|
case "run":
|
|
7128
7367
|
process.exitCode = await runWorkflow(parsed.flags);
|
|
7129
7368
|
break;
|
|
7369
|
+
case "repo":
|
|
7370
|
+
process.exitCode = await runRepoAdd(parsed.flags);
|
|
7371
|
+
break;
|
|
7130
7372
|
case "service": {
|
|
7131
7373
|
if (parsed.flags.action === "uninstall") {
|
|
7132
7374
|
process.exitCode = await runServiceUninstall();
|