dahrk-node 0.1.15 → 0.1.17

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 +82 -22
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -902,6 +902,10 @@ function mapUsage2(u) {
902
902
 
903
903
  // ../../packages/executor-worktree/src/codex-adapter.ts
904
904
  var COALESCE_MS2 = Number(process.env.DAHRK_COALESCE_MS ?? process.env.SKAKEL_COALESCE_MS ?? 40);
905
+ var CODEX_COST_UNAVAILABLE_NOTE = "codex-adapter: cost reporting unavailable for the Codex runtime (the SDK reports tokens, not price); costUsd left unset, not $0\n";
906
+ function warnCostUnavailable(write = (s) => void process.stderr.write(s)) {
907
+ write(CODEX_COST_UNAVAILABLE_NOTE);
908
+ }
905
909
  function runtimeEnvOptions2(ctx) {
906
910
  if (!ctx.runtimeEnv) return {};
907
911
  const env = {};
@@ -961,6 +965,7 @@ function createCodexRunner() {
961
965
  }
962
966
  captureThreadId(t);
963
967
  if (cancelled) status = "fail";
968
+ warnCostUnavailable();
964
969
  return { status, ...sessionId ? { sessionId } : {} };
965
970
  },
966
971
  async runInteractive(ctx, turns, onTrace) {
@@ -1031,6 +1036,7 @@ function createCodexRunner() {
1031
1036
  summary = "(stage cancelled)";
1032
1037
  }
1033
1038
  captureThreadId(t);
1039
+ warnCostUnavailable();
1034
1040
  return { status, summary, ...sessionId ? { sessionId } : {} };
1035
1041
  },
1036
1042
  async summarise(ctx) {
@@ -1186,6 +1192,10 @@ function createPiRunner(deps = {}) {
1186
1192
  const captureSessionId = (s) => {
1187
1193
  if (s.sessionId) sessionId = s.sessionId;
1188
1194
  };
1195
+ const readSessionCost = (s) => {
1196
+ const cost = s.getSessionStats?.()?.cost;
1197
+ return typeof cost === "number" ? cost : void 0;
1198
+ };
1189
1199
  return {
1190
1200
  runtime: "pi",
1191
1201
  async runBatch(ctx, onTrace) {
@@ -1209,7 +1219,8 @@ function createPiRunner(deps = {}) {
1209
1219
  }
1210
1220
  captureSessionId(s);
1211
1221
  if (cancelled) status = "fail";
1212
- return { status, ...sessionId ? { sessionId } : {} };
1222
+ const costUsd = readSessionCost(s);
1223
+ return { status, ...sessionId ? { sessionId } : {}, ...costUsd !== void 0 ? { costUsd } : {} };
1213
1224
  },
1214
1225
  async runInteractive(ctx, turns, onTrace) {
1215
1226
  const emit = makeEmit("pi", onTrace);
@@ -1304,7 +1315,13 @@ function createPiRunner(deps = {}) {
1304
1315
  }
1305
1316
  unsub();
1306
1317
  captureSessionId(s);
1307
- return { status, summary, ...sessionId ? { sessionId } : {} };
1318
+ const costUsd = readSessionCost(s);
1319
+ return {
1320
+ status,
1321
+ summary,
1322
+ ...sessionId ? { sessionId } : {},
1323
+ ...costUsd !== void 0 ? { costUsd } : {}
1324
+ };
1308
1325
  },
1309
1326
  async summarise(ctx) {
1310
1327
  if (!session) return "(no summary: session not established)";
@@ -1354,7 +1371,9 @@ async function defaultCreatePiSession(ctx) {
1354
1371
  let model;
1355
1372
  if (ctx.config.model) {
1356
1373
  const resolved = resolveCliModel({ cliModel: ctx.config.model, modelRegistry });
1357
- if (!resolved?.error) model = resolved?.model;
1374
+ if (!resolved?.error) {
1375
+ model = pickAuthedModel(resolved?.model, modelRegistry.getAvailable());
1376
+ }
1358
1377
  }
1359
1378
  const stageComplete = defineTool({
1360
1379
  name: PI_STAGE_COMPLETE_TOOL,
@@ -1379,6 +1398,17 @@ var PROVIDER_BY_ENV = {
1379
1398
  GEMINI_API_KEY: "google",
1380
1399
  GOOGLE_API_KEY: "google"
1381
1400
  };
1401
+ function modelFamily(id) {
1402
+ const last = id.split(".").pop() ?? id;
1403
+ return last.replace(/-v\d+:\d+$/, "").toLowerCase();
1404
+ }
1405
+ function pickAuthedModel(resolved, available) {
1406
+ if (!resolved || !available?.length) return resolved;
1407
+ const providers = new Set(available.map((m) => m.provider));
1408
+ if (providers.has(resolved.provider)) return resolved;
1409
+ const family = modelFamily(resolved.id);
1410
+ return available.find((m) => modelFamily(m.id) === family) ?? resolved;
1411
+ }
1382
1412
 
1383
1413
  // ../../packages/executor-worktree/src/git-service.ts
1384
1414
  import { execFileSync } from "child_process";
@@ -6265,6 +6295,7 @@ async function runUpdate(inputs, deps = {}) {
6265
6295
  d.out(hint(`You are on ${current}. See https://www.npmjs.com/package/dahrk-node for releases.`));
6266
6296
  return 1;
6267
6297
  }
6298
+ d.saveResult({ updateCheckedAt: new Date(d.now()).toISOString(), updateLatest: latest });
6268
6299
  if (!isNewer(latest, current)) {
6269
6300
  d.out(verdict("ok", `Already on the latest version (${current}).`));
6270
6301
  return 0;
@@ -6352,6 +6383,10 @@ var defaultDeps4 = () => ({
6352
6383
  binPath: process.argv[1],
6353
6384
  fetchLatest: fetchLatestVersion,
6354
6385
  runUpgrade: spawnUpgrade,
6386
+ // The same cache the daemon's periodic check writes (`updateCheckDeps` in main.ts), so both writers agree
6387
+ // on where "what the registry last said" lives and `status` has one place to read it from.
6388
+ saveResult: (patch) => writeState(process.env, patch),
6389
+ now: () => Date.now(),
6355
6390
  nodeRunning: nodeIsRunning,
6356
6391
  interactive: isInteractive,
6357
6392
  confirm,
@@ -6367,7 +6402,8 @@ var defaultDeps4 = () => ({
6367
6402
  });
6368
6403
 
6369
6404
  // src/update-check.ts
6370
- var DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1e3;
6405
+ var DEFAULT_INTERVAL_MS = 6 * 60 * 60 * 1e3;
6406
+ var STALE_AFTER_INTERVALS = 4;
6371
6407
  var FETCH_TIMEOUT_MS = 1500;
6372
6408
  function checkSuppressed(env) {
6373
6409
  return Boolean(env.DAHRK_NO_UPDATE_CHECK || env.NO_UPDATE_NOTIFIER || env.CI);
@@ -6400,7 +6436,8 @@ async function checkForUpdate(currentVersion, deps) {
6400
6436
  if (checkSuppressed(deps.env)) return void 0;
6401
6437
  const state = deps.readState();
6402
6438
  if (!shouldCheck(deps.now(), state.updateCheckedAt, checkIntervalMs(deps.env), deps.env)) {
6403
- return cachedUpdate(state, currentVersion, deps.binPath);
6439
+ const cached = cachedUpdate(state, currentVersion, deps.binPath);
6440
+ return cached.kind === "available" ? cached : void 0;
6404
6441
  }
6405
6442
  let latest;
6406
6443
  try {
@@ -6412,10 +6449,12 @@ async function checkForUpdate(currentVersion, deps) {
6412
6449
  if (!isNewer(latest, currentVersion)) return void 0;
6413
6450
  return { current: currentVersion, latest, channel: detectChannel(deps.binPath) };
6414
6451
  }
6452
+ var isStale = (checkedAt, now, intervalMs) => now - checkedAt >= intervalMs * STALE_AFTER_INTERVALS;
6415
6453
  function cachedUpdate(state, currentVersion, binPath) {
6416
- const latest = state.updateLatest;
6417
- if (!latest || !isNewer(latest, currentVersion)) return void 0;
6418
- return { current: currentVersion, latest, channel: detectChannel(binPath) };
6454
+ const { updateLatest: latest, updateCheckedAt } = state;
6455
+ const checkedAt = updateCheckedAt ? Date.parse(updateCheckedAt) : NaN;
6456
+ if (!latest || !Number.isFinite(checkedAt)) return { kind: "unknown" };
6457
+ return isNewer(latest, currentVersion) ? { kind: "available", checkedAt, current: currentVersion, latest, channel: detectChannel(binPath) } : { kind: "current", checkedAt };
6419
6458
  }
6420
6459
 
6421
6460
  // src/status.ts
@@ -6454,7 +6493,13 @@ function presenceHints(f) {
6454
6493
  }
6455
6494
  function renderStatus(f) {
6456
6495
  const v = presenceVerdict(f);
6457
- const lines = ["", verdict(v.level, v.text), ""];
6496
+ const lines = ["", verdict(v.level, v.text)];
6497
+ if (f.update?.kind === "available") {
6498
+ lines.push(
6499
+ verdict("warn", `Update available: ${f.clientVersion} ${arrow()} ${f.update.latest}`) + ` ${dim("run `dahrk update`")}`
6500
+ );
6501
+ }
6502
+ lines.push("");
6458
6503
  const { nodeId, name, tenantId, enrolToken } = f.state;
6459
6504
  if (enrolToken) {
6460
6505
  const who = name ? name : "yes";
@@ -6465,12 +6510,7 @@ function renderStatus(f) {
6465
6510
  lines.push(kv("Enrolled", `no ${dim("run `dahrk start --token <token>` once to enrol")}`));
6466
6511
  }
6467
6512
  lines.push(kv("Node id", nodeId ?? dim("not yet minted (first `dahrk start` mints one)")));
6468
- lines.push(
6469
- kv(
6470
- "Client",
6471
- f.update ? `${f.clientVersion} ${dim(`(update available: ${f.update.latest} - run \`dahrk update\`)`)}` : f.clientVersion
6472
- )
6473
- );
6513
+ lines.push(kv("Client", `${f.clientVersion}${currency(f)}`));
6474
6514
  const conn = f.connection ? ` ${dim(`(${f.connection.event} ${ago(f.now - f.connection.at)}${f.connection.detail ? `, ${f.connection.detail}` : ""})`)}` : "";
6475
6515
  lines.push(kv("Hub", `${f.hubUrl}${conn}`));
6476
6516
  const installed = f.runtimes.filter((r) => r.installed);
@@ -6491,6 +6531,19 @@ function renderStatus(f) {
6491
6531
  lines.push("", dim(` State file: ${f.stateFile}`));
6492
6532
  return lines;
6493
6533
  }
6534
+ function currency(f) {
6535
+ const u = f.update;
6536
+ if (!u) return "";
6537
+ if (u.kind === "unknown") {
6538
+ return ` ${dim("update status unknown - run `dahrk update --check`")}`;
6539
+ }
6540
+ const age = ago(f.now - u.checkedAt);
6541
+ if (isStale(u.checkedAt, f.now, f.updateIntervalMs)) {
6542
+ return ` ${dim(`as of ${age} - run \`dahrk update --check\` to refresh`)}`;
6543
+ }
6544
+ if (u.kind === "available") return ` ${dim(`(checked ${age})`)}`;
6545
+ return ` ${symbol("ok")} ${dim(`up to date (checked ${age})`)}`;
6546
+ }
6494
6547
  function jobLine(j, now) {
6495
6548
  const where = j.stageId ? `${j.runId} ${dim("/")} ${j.stageId}` : `${j.runId} ${dim(`(${j.kind})`)}`;
6496
6549
  return `${where} ${dim(humanDuration(now - j.startedAt))}`;
@@ -6530,6 +6583,7 @@ async function gatherFacts(inputs, deps) {
6530
6583
  runtimes: await deps.probeRuntimes(),
6531
6584
  presence,
6532
6585
  jobs,
6586
+ updateIntervalMs: checkIntervalMs(deps.env),
6533
6587
  now: deps.now(),
6534
6588
  ...service ? { service } : {},
6535
6589
  ...connection ? { connection } : {},
@@ -6567,7 +6621,7 @@ async function runStatus(inputs, deps) {
6567
6621
  }
6568
6622
 
6569
6623
  // src/main.ts
6570
- var CLIENT_VERSION = "0.1.15";
6624
+ var CLIENT_VERSION = "0.1.17";
6571
6625
  var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
6572
6626
  var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
6573
6627
  var RUNTIMES2 = ["claude-code", "codex", "pi"];
@@ -6757,6 +6811,9 @@ async function stop(env, force) {
6757
6811
  if (code === 0 || code === STOP_FOREIGN_NODE) setDesired(env, "stopped");
6758
6812
  return code;
6759
6813
  }
6814
+ var updateStateDeps = (env) => ({
6815
+ saveResult: (patch) => writeState(env, patch)
6816
+ });
6760
6817
  function updateCheckDeps(env) {
6761
6818
  return {
6762
6819
  env,
@@ -6774,7 +6831,7 @@ async function offerUpdate(env) {
6774
6831
  out(renderUpdateNotice(available));
6775
6832
  if (!isInteractive()) return;
6776
6833
  if (!await confirm("Update now?")) return;
6777
- const code = await runUpdate({ currentVersion: CLIENT_VERSION, check: false });
6834
+ const code = await runUpdate({ currentVersion: CLIENT_VERSION, check: false }, updateStateDeps(env));
6778
6835
  if (code !== 0) out(hint("Update failed; starting the node on the current version anyway."));
6779
6836
  }
6780
6837
  function scheduleUpdateChecks(env) {
@@ -6924,11 +6981,14 @@ async function main() {
6924
6981
  break;
6925
6982
  }
6926
6983
  case "update":
6927
- process.exitCode = await runUpdate({
6928
- currentVersion: CLIENT_VERSION,
6929
- check: parsed.flags.check,
6930
- verbose: parsed.flags.verbose
6931
- });
6984
+ process.exitCode = await runUpdate(
6985
+ {
6986
+ currentVersion: CLIENT_VERSION,
6987
+ check: parsed.flags.check,
6988
+ verbose: parsed.flags.verbose
6989
+ },
6990
+ updateStateDeps(applyEnvAliases(process.env))
6991
+ );
6932
6992
  break;
6933
6993
  case "start":
6934
6994
  process.exitCode = await start(parsed.flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dahrk-node",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",