dahrk-node 0.1.6 → 0.1.8

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/main.js +247 -39
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/main.ts
4
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync5, realpathSync as realpathSync3, writeFileSync as writeFileSync7 } from "fs";
4
+ import { execFileSync as execFileSync7 } from "child_process";
5
+ import { existsSync as existsSync7, realpathSync as realpathSync3 } from "fs";
5
6
  import { randomUUID as randomUUID3 } from "crypto";
6
- import { homedir as homedir4 } from "os";
7
- import { basename, join as join9 } from "path";
7
+ import { homedir as homedir5, platform as osPlatform4 } from "os";
8
+ import { basename, join as join10 } from "path";
8
9
  import { pathToFileURL } from "url";
9
10
 
10
11
  // ../../packages/edge/src/ws-client.ts
@@ -640,7 +641,7 @@ function createClaudeRunner() {
640
641
  maxTurns: MAX_TURNS,
641
642
  includePartialMessages: false
642
643
  };
643
- const exit = ctx.config.exit ?? "gate";
644
+ const exit = ctx.config.exit ?? "either";
644
645
  const wantsTool = exit === "tool" || exit === "either";
645
646
  const mailbox = new ManagedMailbox();
646
647
  const q = query({ prompt: mailbox, options });
@@ -914,7 +915,7 @@ function createCodexRunner() {
914
915
  async runInteractive(ctx, turns, onTrace) {
915
916
  const emit = makeEmit("codex", onTrace);
916
917
  const t = openThread(ctx);
917
- const exit = ctx.config.exit ?? "gate";
918
+ const exit = ctx.config.exit ?? "either";
918
919
  if (exit === "tool" || exit === "either") {
919
920
  process.stderr.write("codex-adapter: interactive tool-exit not supported in M4; using gate exit\n");
920
921
  }
@@ -1163,7 +1164,7 @@ function createPiRunner(deps = {}) {
1163
1164
  const emit = makeEmit("pi", onTrace);
1164
1165
  const s = await openSession(ctx);
1165
1166
  const state = newPiBufferState();
1166
- const exit = ctx.config.exit ?? "gate";
1167
+ const exit = ctx.config.exit ?? "either";
1167
1168
  const wantsTool = exit === "tool" || exit === "either";
1168
1169
  let toolFired = false;
1169
1170
  let toolSummary = null;
@@ -2720,6 +2721,15 @@ async function startEdgeNode(opts) {
2720
2721
  startHeartbeat(ws, msg.heartbeatMs);
2721
2722
  }
2722
2723
  log(`EDGE_WELCOMED:${msg.name} tenant=${msg.tenantId} credentialMode=${msg.credentialMode}`);
2724
+ try {
2725
+ opts.onEnrolled?.({
2726
+ name: msg.name,
2727
+ tenantId: msg.tenantId,
2728
+ credentialMode: msg.credentialMode
2729
+ });
2730
+ } catch (e) {
2731
+ log(`EDGE_ENROL_PERSIST_FAILED ${e.message}`);
2732
+ }
2723
2733
  return;
2724
2734
  }
2725
2735
  if (msg.type === "blob-put-url") {
@@ -3009,7 +3019,7 @@ function probeHub(opts) {
3009
3019
 
3010
3020
  // src/cli.ts
3011
3021
  import { parseArgs } from "util";
3012
- var COMMANDS = /* @__PURE__ */ new Set(["start", "run", "service", "doctor", "update"]);
3022
+ var COMMANDS = /* @__PURE__ */ new Set(["start", "run", "service", "doctor", "status", "update"]);
3013
3023
  var isCommand = (s) => COMMANDS.has(s);
3014
3024
  function parseCli(argv) {
3015
3025
  const [first, ...rest] = argv;
@@ -3145,15 +3155,18 @@ function parseUpdate(flagArgs) {
3145
3155
  function usage(bin, command) {
3146
3156
  if (command === "start") {
3147
3157
  return [
3148
- `Usage: ${bin} start --token <token> [options]`,
3158
+ `Usage: ${bin} start [--token <token>] [options]`,
3149
3159
  "",
3150
3160
  "Run the edge node: dial the hub over WebSocket and serve Jobs in git worktrees.",
3151
3161
  "",
3162
+ "A token is needed only to enrol. Once the hub accepts it, it is cached in ~/.dahrk/node.json and",
3163
+ "every later `start` re-attaches without one. Pass --token again to re-enrol (rotated token, new pool).",
3164
+ "",
3152
3165
  "Options:",
3153
- " --token <token> Enrolment token (required; or set DAHRK_ENROL_TOKEN).",
3166
+ " --token <token> Enrolment token (first run only; or set DAHRK_ENROL_TOKEN).",
3154
3167
  " --hub-url <url> Hub WebSocket URL (or set DAHRK_HUB_URL).",
3155
3168
  " --name <name> Display-name override (else the hub assigns one).",
3156
- " --ephemeral Do not persist a node id; mint a throwaway one (CI / one-shot)."
3169
+ " --ephemeral Do not persist (or read) node id and token; mint a throwaway id (CI / one-shot)."
3157
3170
  ].join("\n");
3158
3171
  }
3159
3172
  if (command === "run") {
@@ -3201,6 +3214,18 @@ function usage(bin, command) {
3201
3214
  " --hub-url <url> Hub WebSocket URL to reach (or set DAHRK_HUB_URL)."
3202
3215
  ].join("\n");
3203
3216
  }
3217
+ if (command === "status") {
3218
+ return [
3219
+ `Usage: ${bin} status`,
3220
+ "",
3221
+ "Report this node's local state: whether it is enrolled (and as whom), its node id, the runtimes",
3222
+ "it can serve, and whether the always-on service is installed and actually running.",
3223
+ "",
3224
+ "Local only - it dials nothing, so it is instant and works offline. Use `doctor` to check the hub",
3225
+ "is reachable and the token still valid. Exits non-zero only when the service is installed but",
3226
+ "not running, so it works as a health check in a script."
3227
+ ].join("\n");
3228
+ }
3204
3229
  if (command === "update") {
3205
3230
  return [
3206
3231
  `Usage: ${bin} update [--check]`,
@@ -3217,9 +3242,10 @@ function usage(bin, command) {
3217
3242
  `Usage: ${bin} <command> [options]`,
3218
3243
  "",
3219
3244
  "Commands:",
3220
- " start Run the edge node (default). Needs a --token and a hub URL.",
3245
+ " start Run the edge node (default). Needs a --token to enrol; cached thereafter.",
3221
3246
  " run Run a workflow locally (engine-backed), e.g. `run preflight`.",
3222
3247
  " service Install/uninstall the node as an always-on service (launchd/systemd).",
3248
+ " status Is this node enrolled, and is the service running? (local, dials nothing)",
3223
3249
  " doctor Preflight checks: Node, runtimes, hub reachability, token validity.",
3224
3250
  " update Update the client to the latest release (or print how for your channel).",
3225
3251
  " version Print the client version.",
@@ -3578,9 +3604,10 @@ function gatherHostFacts(repoPath) {
3578
3604
 
3579
3605
  // src/service.ts
3580
3606
  import { execFileSync as execFileSync5 } from "child_process";
3581
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, realpathSync, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
3607
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync6, realpathSync, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
3582
3608
  import { homedir as homedir3, platform as osPlatform3, userInfo } from "os";
3583
3609
  import { join as join8 } from "path";
3610
+ var UNIT_FILE_MODE = 384;
3584
3611
  var LAUNCHD_LABEL = "ai.dahrk.node";
3585
3612
  var SYSTEMD_UNIT = "dahrk-node.service";
3586
3613
  function detectManager(plat) {
@@ -3699,6 +3726,24 @@ function buildPlan(inputs) {
3699
3726
  logHint: `journalctl --user -u ${SYSTEMD_UNIT} -f`
3700
3727
  };
3701
3728
  }
3729
+ function unitPath(manager, homeDir) {
3730
+ return manager === "launchd" ? join8(homeDir, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`) : join8(homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
3731
+ }
3732
+ function statusCommand(manager) {
3733
+ return manager === "launchd" ? ["launchctl", "list", LAUNCHD_LABEL] : ["systemctl", "--user", "show", SYSTEMD_UNIT, "-p", "ActiveState", "-p", "MainPID"];
3734
+ }
3735
+ function parseServiceStatus(manager, unitExists, probe2) {
3736
+ if (!unitExists) return { installed: false, running: false };
3737
+ if (probe2.code !== 0) return { installed: true, running: false };
3738
+ if (manager === "launchd") {
3739
+ const pid2 = Number(/"PID"\s*=\s*(\d+);/.exec(probe2.stdout)?.[1]);
3740
+ return pid2 > 0 ? { installed: true, running: true, pid: pid2 } : { installed: true, running: false };
3741
+ }
3742
+ const active = /^ActiveState=(.*)$/m.exec(probe2.stdout)?.[1]?.trim();
3743
+ const pid = Number(/^MainPID=(\d+)$/m.exec(probe2.stdout)?.[1]);
3744
+ const running = active === "active";
3745
+ return running && pid > 0 ? { installed: true, running: true, pid } : { installed: true, running };
3746
+ }
3702
3747
  function printUnsupported(out) {
3703
3748
  out("dahrk service is supported on macOS (launchd) and Linux (systemd) only.");
3704
3749
  out("On other platforms, run the node under a process manager such as pm2 (see the README).");
@@ -3797,17 +3842,39 @@ function resolveScriptPath() {
3797
3842
  return argv1;
3798
3843
  }
3799
3844
  }
3845
+ function stableNodeBin(execPath, realpath) {
3846
+ const m = /^(?<prefix>.*)\/Cellar\/(?<formula>node(?:@[\d.]+)?)\/[^/]+\/bin\/node$/.exec(execPath);
3847
+ const { prefix, formula } = m?.groups ?? {};
3848
+ if (!prefix || !formula) return execPath;
3849
+ const candidates = [`${prefix}/opt/${formula}/bin/node`, `${prefix}/bin/node`];
3850
+ return candidates.find((c) => realpath(c) === execPath) ?? execPath;
3851
+ }
3852
+ var realpathOrUndefined = (p) => {
3853
+ try {
3854
+ return realpathSync(p);
3855
+ } catch {
3856
+ return void 0;
3857
+ }
3858
+ };
3800
3859
  var defaultDeps3 = () => ({
3801
3860
  platform: osPlatform3(),
3802
3861
  homeDir: homedir3(),
3803
- nodeBin: process.execPath,
3862
+ // Not `process.execPath` raw: that is the versioned Homebrew Cellar path, which the next
3863
+ // `brew upgrade node` deletes out from under the unit. See `stableNodeBin`.
3864
+ nodeBin: stableNodeBin(process.execPath, realpathOrUndefined),
3804
3865
  scriptPath: resolveScriptPath(),
3805
3866
  logDir: join8(process.env.DAHRK_STATE_DIR ?? join8(homedir3(), ".dahrk"), "logs"),
3806
3867
  // Snapshot the operator's PATH at install time so the daemon finds git + the runtime CLIs (Homebrew /
3807
3868
  // npm-global bins) that a supervisor's minimal PATH would otherwise hide.
3808
3869
  pathEnv: process.env.PATH,
3809
3870
  mkdirp: (dir) => void mkdirSync6(dir, { recursive: true }),
3810
- writeFile: (path, content) => writeFileSync6(path, content),
3871
+ // The unit's environment block carries the enrolment token, so the file is a secret: write it
3872
+ // owner-only. `writeFileSync`'s `mode` applies only when it CREATES the file, so chmod explicitly
3873
+ // too - re-installing over a unit an older client left at 0644 must tighten it, not keep it.
3874
+ writeFile: (path, content) => {
3875
+ writeFileSync6(path, content, { mode: UNIT_FILE_MODE });
3876
+ chmodSync(path, UNIT_FILE_MODE);
3877
+ },
3811
3878
  removeFile: (path) => rmSync4(path, { force: true }),
3812
3879
  fileExists: (path) => existsSync5(path),
3813
3880
  run: (argv) => {
@@ -3947,47 +4014,149 @@ var defaultDeps4 = () => ({
3947
4014
  `)
3948
4015
  });
3949
4016
 
3950
- // src/main.ts
3951
- var CLIENT_VERSION = "0.1.6";
3952
- var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
3953
- var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
3954
- var RUNTIMES = ["claude-code", "codex", "pi"];
3955
- var isRuntime = (r) => RUNTIMES.includes(r);
4017
+ // src/state.ts
4018
+ import { chmodSync as chmodSync2, existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
4019
+ import { homedir as homedir4 } from "os";
4020
+ import { join as join9 } from "path";
4021
+ var STRING_FIELDS = ["nodeId", "enrolToken", "name", "tenantId"];
4022
+ var FILE_MODE = 384;
4023
+ var DIR_MODE = 448;
3956
4024
  function stateDir(env) {
3957
4025
  return env.DAHRK_STATE_DIR ?? join9(homedir4(), ".dahrk");
3958
4026
  }
3959
4027
  function legacyStateDir(env) {
3960
4028
  return env.DAHRK_STATE_DIR ? void 0 : join9(homedir4(), ".skakel");
3961
4029
  }
3962
- function readNodeId(file) {
3963
- if (!existsSync6(file)) return void 0;
4030
+ function stateFile(env) {
4031
+ return join9(stateDir(env), "node.json");
4032
+ }
4033
+ function readState(file) {
4034
+ if (!existsSync6(file)) return {};
3964
4035
  try {
3965
4036
  const parsed = JSON.parse(readFileSync5(file, "utf8"));
3966
- if (typeof parsed.nodeId === "string" && parsed.nodeId) return parsed.nodeId;
4037
+ const state = {};
4038
+ for (const key of STRING_FIELDS) {
4039
+ const value = parsed[key];
4040
+ if (typeof value === "string" && value) state[key] = value;
4041
+ }
4042
+ return state;
3967
4043
  } catch {
4044
+ return {};
3968
4045
  }
3969
- return void 0;
3970
4046
  }
4047
+ function writeState(env, patch) {
4048
+ const dir = stateDir(env);
4049
+ const file = stateFile(env);
4050
+ try {
4051
+ mkdirSync7(dir, { recursive: true, mode: DIR_MODE });
4052
+ const next = { ...readState(file), ...patch };
4053
+ writeFileSync7(file, `${JSON.stringify(next, null, 2)}
4054
+ `, { mode: FILE_MODE });
4055
+ chmodSync2(file, FILE_MODE);
4056
+ } catch (e) {
4057
+ console.warn(`could not persist node state to ${file}: ${e.message}`);
4058
+ }
4059
+ }
4060
+ function readPersistedToken(env) {
4061
+ return readState(stateFile(env)).enrolToken;
4062
+ }
4063
+ function persistEnrolment(env, enrolment) {
4064
+ const { token, name, tenantId } = enrolment;
4065
+ if (!token) return;
4066
+ const current = readState(stateFile(env));
4067
+ const unchanged = current.enrolToken === token && (name === void 0 || current.name === name) && (tenantId === void 0 || current.tenantId === tenantId);
4068
+ if (unchanged) return;
4069
+ writeState(env, {
4070
+ enrolToken: token,
4071
+ ...name ? { name } : {},
4072
+ ...tenantId ? { tenantId } : {}
4073
+ });
4074
+ }
4075
+ function resolveEnrolToken(env, opts = {}) {
4076
+ if (env.DAHRK_ENROL_TOKEN) return env.DAHRK_ENROL_TOKEN;
4077
+ if (opts.ephemeral) return void 0;
4078
+ return readPersistedToken(env);
4079
+ }
4080
+
4081
+ // src/status.ts
4082
+ var bullet = (label, value) => ` ${label.padEnd(12)}${value}`;
4083
+ function renderStatus(f) {
4084
+ const lines = ["dahrk status", ""];
4085
+ const { nodeId, name, tenantId, enrolToken } = f.state;
4086
+ if (enrolToken) {
4087
+ lines.push(bullet("Enrolled", name ? `yes, as ${name}` : "yes"));
4088
+ if (tenantId) lines.push(bullet("Tenant", tenantId));
4089
+ } else if (f.envToken) {
4090
+ lines.push(bullet("Enrolled", "yes, via DAHRK_ENROL_TOKEN (caches on the next successful start)"));
4091
+ } else {
4092
+ lines.push(bullet("Enrolled", "no - run `dahrk start --token <token>` once to enrol"));
4093
+ }
4094
+ lines.push(bullet("Node id", nodeId ?? "not yet minted (first `dahrk start` mints one)"));
4095
+ lines.push(bullet("Client", f.clientVersion));
4096
+ lines.push(bullet("Hub", f.hubUrl));
4097
+ lines.push(
4098
+ bullet(
4099
+ "Runtimes",
4100
+ f.runtimes.length > 0 ? f.runtimes.join(", ") : "none detected - this node will serve no Jobs (install claude / codex / pi)"
4101
+ )
4102
+ );
4103
+ if (!f.service) {
4104
+ lines.push(bullet("Service", "not supported on this host (run `dahrk start` under pm2 instead)"));
4105
+ } else if (!f.service.installed) {
4106
+ lines.push(bullet("Service", "not installed - run `dahrk service install` to run on boot"));
4107
+ } else if (f.service.running) {
4108
+ const pid = f.service.pid ? ` (pid ${f.service.pid})` : "";
4109
+ lines.push(bullet("Service", `running${pid}`));
4110
+ } else {
4111
+ lines.push(bullet("Service", "INSTALLED BUT NOT RUNNING - it is failing to start or crash-looping"));
4112
+ if (f.logHint) lines.push(bullet("", `check the logs: ${f.logHint}`));
4113
+ }
4114
+ lines.push("", `State file: ${f.stateFile}`);
4115
+ return lines;
4116
+ }
4117
+ async function runStatus(inputs, deps) {
4118
+ const manager = detectManager(deps.platform);
4119
+ let service;
4120
+ let logHint;
4121
+ if (manager !== "unsupported") {
4122
+ const unit = unitPath(manager, deps.homeDir);
4123
+ const exists = deps.fileExists(unit);
4124
+ const probe2 = exists ? deps.capture(statusCommand(manager)) : { code: 1, stdout: "" };
4125
+ service = parseServiceStatus(manager, exists, probe2);
4126
+ logHint = manager === "launchd" ? `tail -f ${deps.homeDir}/.dahrk/logs/node.err.log` : "journalctl --user -u dahrk-node.service -f";
4127
+ }
4128
+ const facts = {
4129
+ clientVersion: inputs.clientVersion,
4130
+ hubUrl: inputs.hubUrl,
4131
+ stateFile: stateFile(deps.env),
4132
+ state: readState(stateFile(deps.env)),
4133
+ envToken: Boolean(deps.env.DAHRK_ENROL_TOKEN),
4134
+ runtimes: await deps.detectRuntimes(),
4135
+ ...service ? { service } : {},
4136
+ ...logHint ? { logHint } : {}
4137
+ };
4138
+ for (const line of renderStatus(facts)) deps.out(line);
4139
+ return service?.installed && !service.running ? 1 : 0;
4140
+ }
4141
+
4142
+ // src/main.ts
4143
+ var CLIENT_VERSION = "0.1.8";
4144
+ var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
4145
+ var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
4146
+ var RUNTIMES = ["claude-code", "codex", "pi"];
4147
+ var isRuntime = (r) => RUNTIMES.includes(r);
3971
4148
  function resolveNodeId(env, opts = {}) {
3972
4149
  if (env.DAHRK_NODE_ID) return env.DAHRK_NODE_ID;
3973
4150
  if (opts.ephemeral) return randomUUID3();
3974
- const dir = stateDir(env);
3975
- const file = join9(dir, "node.json");
3976
- const existing = readNodeId(file);
4151
+ const existing = readState(stateFile(env)).nodeId;
3977
4152
  if (existing) return existing;
3978
4153
  const legacy = legacyStateDir(env);
3979
4154
  if (legacy) {
3980
- const legacyId = readNodeId(join9(legacy, "node.json"));
4155
+ const legacyId = readState(join10(legacy, "node.json")).nodeId;
3981
4156
  if (legacyId) return legacyId;
3982
4157
  }
3983
4158
  const nodeId = randomUUID3();
3984
- try {
3985
- mkdirSync7(dir, { recursive: true });
3986
- writeFileSync7(file, `${JSON.stringify({ nodeId }, null, 2)}
3987
- `);
3988
- } catch (e) {
3989
- console.warn(`could not persist node id to ${file}: ${e.message}`);
3990
- }
4159
+ writeState(env, { nodeId });
3991
4160
  return nodeId;
3992
4161
  }
3993
4162
  async function resolveRuntimes(env) {
@@ -4053,6 +4222,8 @@ function envWithFlags(env, flags) {
4053
4222
  async function start(flags) {
4054
4223
  const env = envWithFlags(process.env, flags);
4055
4224
  const nodeId = resolveNodeId(env, { ephemeral: flags.ephemeral });
4225
+ const token = resolveEnrolToken(env, { ephemeral: flags.ephemeral });
4226
+ if (token) env.DAHRK_ENROL_TOKEN = token;
4056
4227
  const runtimes = await resolveRuntimes(env);
4057
4228
  if (runtimes.length === 0) {
4058
4229
  console.warn(
@@ -4060,7 +4231,34 @@ async function start(flags) {
4060
4231
  );
4061
4232
  }
4062
4233
  const resolved = { nodeId, runtimes, clientVersion: CLIENT_VERSION };
4063
- await startEdgeNode(buildEdgeOptions(env, resolved));
4234
+ const persist = token !== void 0 && !flags.ephemeral;
4235
+ await startEdgeNode({
4236
+ ...buildEdgeOptions(env, resolved),
4237
+ ...persist ? {
4238
+ onEnrolled: (welcome) => persistEnrolment(env, { token, name: welcome.name, tenantId: welcome.tenantId })
4239
+ } : {}
4240
+ });
4241
+ }
4242
+ function statusDeps(env) {
4243
+ return {
4244
+ platform: osPlatform4(),
4245
+ homeDir: homedir5(),
4246
+ env,
4247
+ detectRuntimes: () => resolveRuntimes(env),
4248
+ fileExists: (path) => existsSync7(path),
4249
+ capture: (argv) => {
4250
+ const [cmd, ...args] = argv;
4251
+ try {
4252
+ const stdout = execFileSync7(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
4253
+ return { code: 0, stdout };
4254
+ } catch (e) {
4255
+ const status = e.status;
4256
+ return { code: typeof status === "number" ? status : 1, stdout: "" };
4257
+ }
4258
+ },
4259
+ out: (line) => void process.stdout.write(`${line}
4260
+ `)
4261
+ };
4064
4262
  }
4065
4263
  var KNOWN_WORKFLOWS = ["preflight"];
4066
4264
  async function runWorkflow(flags) {
@@ -4072,7 +4270,7 @@ async function runWorkflow(flags) {
4072
4270
  }
4073
4271
  const env = applyEnvAliases(process.env);
4074
4272
  const hubUrl = flags.hubUrl ?? env.DAHRK_HUB_URL;
4075
- const token = flags.token ?? env.DAHRK_ENROL_TOKEN;
4273
+ const token = flags.token ?? resolveEnrolToken(env);
4076
4274
  return runPreflight({
4077
4275
  ...flags.repo ? { repoPath: flags.repo } : {},
4078
4276
  ...hubUrl ? { hubUrl } : {},
@@ -4101,11 +4299,21 @@ async function main() {
4101
4299
  const env = envWithFlags(process.env, parsed.flags);
4102
4300
  process.exitCode = await runDoctor({
4103
4301
  hubUrl: env.DAHRK_HUB_URL,
4104
- token: env.DAHRK_ENROL_TOKEN,
4302
+ // Same resolution as `start`, so doctor checks the token the node would actually present:
4303
+ // the flag/env if given, else the one cached by the last successful enrolment.
4304
+ token: resolveEnrolToken(env),
4105
4305
  clientVersion: CLIENT_VERSION
4106
4306
  });
4107
4307
  break;
4108
4308
  }
4309
+ case "status": {
4310
+ const env = envWithFlags(process.env, parsed.flags);
4311
+ process.exitCode = await runStatus(
4312
+ { clientVersion: CLIENT_VERSION, hubUrl: env.DAHRK_HUB_URL ?? DEFAULT_HUB_URL },
4313
+ statusDeps(env)
4314
+ );
4315
+ break;
4316
+ }
4109
4317
  case "run":
4110
4318
  process.exitCode = await runWorkflow(parsed.flags);
4111
4319
  break;
@@ -4115,7 +4323,7 @@ async function main() {
4115
4323
  break;
4116
4324
  }
4117
4325
  const env = applyEnvAliases(process.env);
4118
- const token = parsed.flags.token ?? env.DAHRK_ENROL_TOKEN;
4326
+ const token = parsed.flags.token ?? resolveEnrolToken(env);
4119
4327
  const name = parsed.flags.name ?? env.DAHRK_NODE_NAME;
4120
4328
  const hubUrl = parsed.flags.hubUrl ?? env.DAHRK_HUB_URL;
4121
4329
  process.exitCode = await runServiceInstall({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dahrk-node",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",