@onklave/agent-cli 0.1.41 → 0.1.43

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/main.js +231 -49
  2. package/package.json +1 -1
package/main.js CHANGED
@@ -127,6 +127,7 @@ var CredentialStore = class {
127
127
 
128
128
  // _apps/@onklave/agent-cli/src/services/auth.service.ts
129
129
  var DEFAULT_PLATFORM_URL = "https://api.onklave.app";
130
+ var DEFAULT_COMMS_URL = "wss://comms.onklave.app";
130
131
  var AuthService = class {
131
132
  constructor() {
132
133
  this.store = new CredentialStore();
@@ -147,6 +148,7 @@ var AuthService = class {
147
148
  ...existing,
148
149
  token,
149
150
  platformUrl: metadata?.platformUrl ?? existing?.platformUrl ?? DEFAULT_PLATFORM_URL,
151
+ commsUrl: metadata?.commsUrl ?? existing?.commsUrl,
150
152
  userId: metadata?.userId ?? existing?.userId,
151
153
  orgId: metadata?.orgId ?? existing?.orgId,
152
154
  expiresAt: metadata?.expiresAt ?? existing?.expiresAt
@@ -206,6 +208,15 @@ var AuthService = class {
206
208
  const creds = await this.getCredentials();
207
209
  return creds?.platformUrl ?? DEFAULT_PLATFORM_URL;
208
210
  }
211
+ /*
212
+ * Get the comms (Socket.IO) URL from credentials or fallback to default.
213
+ * Resolved separately from the platform URL because comms is reached on a
214
+ * dedicated host, not through the gateway.
215
+ */
216
+ async getCommsUrl() {
217
+ const creds = await this.getCredentials();
218
+ return creds?.commsUrl ?? DEFAULT_COMMS_URL;
219
+ }
209
220
  };
210
221
 
211
222
  // _apps/@onklave/agent-cli/src/utils.ts
@@ -257,10 +268,11 @@ async function loginCommand(args) {
257
268
  const authService = new AuthService();
258
269
  const token = flags["token"];
259
270
  const platformUrl = flags["platform-url"];
271
+ const commsUrl = flags["comms-url"];
260
272
  if (typeof token !== "string" || !token) {
261
273
  console.log("Onklave Agent CLI \u2014 Login\n");
262
274
  console.log(
263
- "Usage: onklave login --token <token> [--platform-url <url>]\n"
275
+ "Usage: onklave login --token <token> [--platform-url <url>] [--comms-url <url>]\n"
264
276
  );
265
277
  console.log("To obtain a token:");
266
278
  console.log(
@@ -277,6 +289,9 @@ async function loginCommand(args) {
277
289
  if (typeof platformUrl === "string") {
278
290
  metadata.platformUrl = platformUrl;
279
291
  }
292
+ if (typeof commsUrl === "string") {
293
+ metadata.commsUrl = commsUrl;
294
+ }
280
295
  try {
281
296
  await authService.saveToken(token, metadata);
282
297
  console.log("Successfully authenticated with Onklave platform.");
@@ -284,6 +299,9 @@ async function loginCommand(args) {
284
299
  if (typeof platformUrl === "string") {
285
300
  console.log(`Platform URL: ${platformUrl}`);
286
301
  }
302
+ if (typeof commsUrl === "string") {
303
+ console.log(`Comms URL: ${commsUrl}`);
304
+ }
287
305
  } catch (err) {
288
306
  console.error(`Login failed: ${err.message}`);
289
307
  process.exitCode = 1;
@@ -319,6 +337,7 @@ async function whoamiCommand() {
319
337
  const isValid = await authService.isAuthenticated();
320
338
  console.log("Onklave Agent CLI \u2014 Identity\n");
321
339
  console.log(` Platform URL: ${creds.platformUrl}`);
340
+ console.log(` Comms URL: ${await authService.getCommsUrl()}`);
322
341
  console.log(` User ID: ${creds.userId ?? "(not set)"}`);
323
342
  console.log(` Org ID: ${creds.orgId ?? "(not set)"}`);
324
343
  console.log(` Machine ID: ${creds.machineId ?? "(not registered)"}`);
@@ -548,6 +567,7 @@ var ConfigResolver = class {
548
567
  };
549
568
 
550
569
  // _apps/@onklave/agent-cli/src/services/platform-client.ts
570
+ var GATEWAY_SERVICE_PREFIX = "/agent-orchestration";
551
571
  var PlatformClient = class {
552
572
  constructor(baseUrl, token) {
553
573
  this.baseUrl = baseUrl;
@@ -715,8 +735,8 @@ var PlatformClient = class {
715
735
  /*
716
736
  * Make an authenticated HTTP request to the platform.
717
737
  */
718
- async request(method, path8, body, extraHeaders) {
719
- const url = `${this.baseUrl}${path8}`;
738
+ async request(method, path10, body, extraHeaders) {
739
+ const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${path10}`;
720
740
  const headers = {
721
741
  Authorization: `Bearer ${this.token}`,
722
742
  "Content-Type": "application/json",
@@ -765,13 +785,15 @@ var CommsClient = class {
765
785
  }
766
786
  /*
767
787
  * Connect to the Onklave comms service via Socket.IO.
788
+ * `commsUrl` is the comms service host (e.g. wss://comms.onklave.app) — NOT
789
+ * the gateway/platform URL; comms is reached on its own dedicated ingress.
768
790
  * Pass `machineId` so the server can route inbound runner events
769
791
  * (e.g. `assignment:claim-available`) to this specific runner.
770
792
  */
771
- async connect(platformUrl, token, extra) {
793
+ async connect(commsUrl, token, extra) {
772
794
  return new Promise((resolve3, reject) => {
773
- const commsUrl = platformUrl.replace(/\/+$/, "");
774
- this.socket = io(`${commsUrl}/agent-cli`, {
795
+ const baseUrl = commsUrl.replace(/\/+$/, "");
796
+ this.socket = io(`${baseUrl}/agent-cli`, {
775
797
  auth: {
776
798
  token,
777
799
  machineId: extra?.machineId,
@@ -1387,7 +1409,7 @@ var HeartbeatService = class {
1387
1409
  };
1388
1410
  try {
1389
1411
  const response = await fetch(
1390
- `${this.opts.platformUrl}/api/v1/runner/heartbeat`,
1412
+ `${this.opts.platformUrl}/agent-orchestration/api/v1/runner/heartbeat`,
1391
1413
  {
1392
1414
  method: "POST",
1393
1415
  headers: {
@@ -2037,7 +2059,7 @@ async function runCommand(args) {
2037
2059
  const auditStreamer = new AuditStreamer(commsClient);
2038
2060
  try {
2039
2061
  console.log("Connecting to Onklave comms service...");
2040
- await commsClient.connect(resolvedConfig.platformUrl, creds.token, {
2062
+ await commsClient.connect(await authService.getCommsUrl(), creds.token, {
2041
2063
  machineId: creds.machineId,
2042
2064
  deviceToken: creds.deviceToken,
2043
2065
  orgId: creds.orgId
@@ -2695,7 +2717,7 @@ async function checkWebSocket() {
2695
2717
  }
2696
2718
  const commsClient = new CommsClient();
2697
2719
  try {
2698
- await commsClient.connect(creds.platformUrl, creds.token);
2720
+ await commsClient.connect(await authService.getCommsUrl(), creds.token);
2699
2721
  commsClient.disconnect();
2700
2722
  return {
2701
2723
  name: "WebSocket",
@@ -2987,7 +3009,7 @@ async function logsCommand(args) {
2987
3009
  }
2988
3010
 
2989
3011
  // _apps/@onklave/agent-cli/src/commands/daemon.command.ts
2990
- import * as fs7 from "fs";
3012
+ import * as fs8 from "fs";
2991
3013
 
2992
3014
  // _apps/@onklave/agent-cli/src/services/daemon-comms.service.ts
2993
3015
  var DaemonCommsService = class {
@@ -3011,7 +3033,7 @@ var DaemonCommsService = class {
3011
3033
  let attempt = 0;
3012
3034
  while (!this.stopRequested) {
3013
3035
  try {
3014
- await this.client.connect(this.opts.platformUrl, this.opts.token, {
3036
+ await this.client.connect(this.opts.commsUrl, this.opts.token, {
3015
3037
  machineId: this.opts.machineId,
3016
3038
  deviceToken: this.opts.deviceToken,
3017
3039
  orgId: this.opts.orgId
@@ -3414,8 +3436,8 @@ var PlatformBrokerClient = class {
3414
3436
  params
3415
3437
  );
3416
3438
  }
3417
- async request(method, path8, body) {
3418
- const url = `${this.baseUrl}${path8}`;
3439
+ async request(method, path10, body) {
3440
+ const url = `${this.baseUrl}/agent-orchestration${path10}`;
3419
3441
  const response = await fetch(url, {
3420
3442
  method,
3421
3443
  headers: {
@@ -3947,9 +3969,37 @@ function parseSemverNumeric(v) {
3947
3969
  ];
3948
3970
  }
3949
3971
 
3972
+ // _apps/@onklave/agent-cli/src/services/cli-version.ts
3973
+ import * as fs6 from "node:fs";
3974
+ import * as path7 from "node:path";
3975
+ import { fileURLToPath } from "node:url";
3976
+ function readPackageVersion() {
3977
+ try {
3978
+ const moduleDir = path7.dirname(fileURLToPath(import.meta.url));
3979
+ const candidates = [
3980
+ path7.join(moduleDir, "package.json"),
3981
+ // bundled: dist root
3982
+ path7.join(moduleDir, "..", "package.json"),
3983
+ path7.join(moduleDir, "..", "..", "package.json"),
3984
+ // dev: src/services -> root
3985
+ path7.join(moduleDir, "..", "..", "..", "package.json")
3986
+ ];
3987
+ for (const p of candidates) {
3988
+ if (!fs6.existsSync(p)) continue;
3989
+ const pkg = JSON.parse(fs6.readFileSync(p, "utf8"));
3990
+ if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
3991
+ }
3992
+ } catch {
3993
+ }
3994
+ return null;
3995
+ }
3996
+ function getCurrentVersion() {
3997
+ return readPackageVersion() ?? "0.0.0";
3998
+ }
3999
+
3950
4000
  // _apps/@onklave/agent-cli/src/services/daemon-state.service.ts
3951
- import * as fs6 from "fs";
3952
- import * as path7 from "path";
4001
+ import * as fs7 from "fs";
4002
+ import * as path8 from "path";
3953
4003
  import * as os6 from "os";
3954
4004
  var VALID_TRANSITIONS = {
3955
4005
  installing: ["registered"],
@@ -3961,10 +4011,10 @@ var VALID_TRANSITIONS = {
3961
4011
  stopped: ["starting"]
3962
4012
  };
3963
4013
  function defaultStateFilePath() {
3964
- return path7.join(os6.homedir(), ".config", "onklave", "daemon.state.json");
4014
+ return path8.join(os6.homedir(), ".config", "onklave", "daemon.state.json");
3965
4015
  }
3966
4016
  function defaultPidFilePath() {
3967
- return path7.join(os6.homedir(), ".config", "onklave", "daemon.pid");
4017
+ return path8.join(os6.homedir(), ".config", "onklave", "daemon.pid");
3968
4018
  }
3969
4019
  var DaemonStateError = class extends Error {
3970
4020
  constructor(message) {
@@ -3988,8 +4038,8 @@ var DaemonStateService = class {
3988
4038
  */
3989
4039
  static readPersisted(stateFile = defaultStateFilePath()) {
3990
4040
  try {
3991
- if (!fs6.existsSync(stateFile)) return null;
3992
- const raw = fs6.readFileSync(stateFile, "utf8");
4041
+ if (!fs7.existsSync(stateFile)) return null;
4042
+ const raw = fs7.readFileSync(stateFile, "utf8");
3993
4043
  const parsed = JSON.parse(raw);
3994
4044
  if (!parsed.state || !parsed.enteredAt) return null;
3995
4045
  return parsed;
@@ -4039,8 +4089,8 @@ var DaemonStateService = class {
4039
4089
  }
4040
4090
  }
4041
4091
  persist(reason) {
4042
- const dir = path7.dirname(this.stateFile);
4043
- fs6.mkdirSync(dir, { recursive: true });
4092
+ const dir = path8.dirname(this.stateFile);
4093
+ fs7.mkdirSync(dir, { recursive: true });
4044
4094
  const payload = {
4045
4095
  state: this.current,
4046
4096
  enteredAt: this.enteredAt.toISOString(),
@@ -4050,8 +4100,8 @@ var DaemonStateService = class {
4050
4100
  ...this.latestRuntime ? { runtime: this.latestRuntime } : {}
4051
4101
  };
4052
4102
  const tmp = `${this.stateFile}.tmp`;
4053
- fs6.writeFileSync(tmp, JSON.stringify(payload, null, 2));
4054
- fs6.renameSync(tmp, this.stateFile);
4103
+ fs7.writeFileSync(tmp, JSON.stringify(payload, null, 2));
4104
+ fs7.renameSync(tmp, this.stateFile);
4055
4105
  }
4056
4106
  /**
4057
4107
  * Publish the latest runtime snapshot. Persists to the state file
@@ -4069,7 +4119,7 @@ var DaemonStateService = class {
4069
4119
  */
4070
4120
  clearPersisted() {
4071
4121
  try {
4072
- fs6.unlinkSync(this.stateFile);
4122
+ fs7.unlinkSync(this.stateFile);
4073
4123
  } catch {
4074
4124
  }
4075
4125
  }
@@ -4232,8 +4282,9 @@ async function daemonStart() {
4232
4282
  creds.machineId
4233
4283
  );
4234
4284
  const platformUrl = await authService.getPlatformUrl();
4285
+ const commsUrl = await authService.getCommsUrl();
4235
4286
  const comms = new DaemonCommsService({
4236
- platformUrl,
4287
+ commsUrl,
4237
4288
  token: creds.token,
4238
4289
  machineId: creds.machineId,
4239
4290
  deviceToken: creds.deviceToken,
@@ -4524,7 +4575,7 @@ function transitionToAction(next) {
4524
4575
  }
4525
4576
  function readPid(pidFile) {
4526
4577
  try {
4527
- const raw = fs7.readFileSync(pidFile, "utf8").trim();
4578
+ const raw = fs8.readFileSync(pidFile, "utf8").trim();
4528
4579
  const parsed = Number.parseInt(raw, 10);
4529
4580
  return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
4530
4581
  } catch {
@@ -4533,12 +4584,12 @@ function readPid(pidFile) {
4533
4584
  }
4534
4585
  function writePid(pidFile) {
4535
4586
  const dir = pidFile.replace(/\/[^/]+$/, "");
4536
- fs7.mkdirSync(dir, { recursive: true });
4537
- fs7.writeFileSync(pidFile, String(process.pid), { mode: 384 });
4587
+ fs8.mkdirSync(dir, { recursive: true });
4588
+ fs8.writeFileSync(pidFile, String(process.pid), { mode: 384 });
4538
4589
  }
4539
4590
  function removePid(pidFile) {
4540
4591
  try {
4541
- fs7.unlinkSync(pidFile);
4592
+ fs8.unlinkSync(pidFile);
4542
4593
  } catch {
4543
4594
  }
4544
4595
  }
@@ -4547,25 +4598,6 @@ function isPidAlive(pidFile) {
4547
4598
  if (pid == null) return false;
4548
4599
  return isProcessAlive(pid);
4549
4600
  }
4550
- function readPackageVersion() {
4551
- try {
4552
- const candidates = [
4553
- // dist layout (bin/onklave.js → ../package.json)
4554
- `${__dirname}/../../package.json`,
4555
- // src layout during dev
4556
- `${__dirname}/../../../package.json`
4557
- ];
4558
- for (const p of candidates) {
4559
- if (fs7.existsSync(p)) {
4560
- const pkg = JSON.parse(fs7.readFileSync(p, "utf8"));
4561
- if (pkg.name === "@onklave/agent-cli" && pkg.version)
4562
- return pkg.version;
4563
- }
4564
- }
4565
- } catch {
4566
- }
4567
- return null;
4568
- }
4569
4601
  function isProcessAlive(pid) {
4570
4602
  try {
4571
4603
  process.kill(pid, 0);
@@ -4600,8 +4632,157 @@ var COMMANDS = {
4600
4632
  daemon: daemonCommand
4601
4633
  };
4602
4634
 
4635
+ // _apps/@onklave/agent-cli/src/services/update-notifier.service.ts
4636
+ import * as fs9 from "node:fs";
4637
+ import * as path9 from "node:path";
4638
+ import * as os7 from "node:os";
4639
+ import { createInterface } from "node:readline/promises";
4640
+ import { spawnSync } from "node:child_process";
4641
+ var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
4642
+ var FETCH_DEADLINE_MS = 1500;
4643
+ var CACHE_PATH = path9.join(
4644
+ os7.homedir(),
4645
+ ".config",
4646
+ "onklave",
4647
+ "update-check.json"
4648
+ );
4649
+ function shouldPromptForUpdate(current, latest, dismissedVersion) {
4650
+ if (!latest) return false;
4651
+ if (!isNewerVersion(latest, current)) return false;
4652
+ if (dismissedVersion && dismissedVersion === latest) return false;
4653
+ return true;
4654
+ }
4655
+ async function maybeNotifyUpdate(deps = {}) {
4656
+ const log = deps.log ?? ((m) => console.error(m));
4657
+ try {
4658
+ const isInteractive = deps.isInteractive ?? defaultIsInteractive;
4659
+ if (!isInteractive()) return;
4660
+ const now = deps.now ?? Date.now;
4661
+ const read = deps.readCache ?? readCache;
4662
+ const write = deps.writeCache ?? writeCache;
4663
+ const current = deps.currentVersion ?? "0.0.0";
4664
+ let cache = read();
4665
+ if (!isCacheFresh(cache, now())) {
4666
+ const fetchLatest = deps.fetchLatest ?? (() => defaultRegistryFetcher(PACKAGE_NAME));
4667
+ try {
4668
+ const latestVersion = await withTimeout(
4669
+ fetchLatest(),
4670
+ FETCH_DEADLINE_MS
4671
+ );
4672
+ cache = {
4673
+ ...cache,
4674
+ latestVersion,
4675
+ lastCheckedAt: new Date(now()).toISOString()
4676
+ };
4677
+ write(cache);
4678
+ } catch {
4679
+ write({ ...cache, lastCheckedAt: new Date(now()).toISOString() });
4680
+ return;
4681
+ }
4682
+ }
4683
+ if (!shouldPromptForUpdate(
4684
+ current,
4685
+ cache.latestVersion,
4686
+ cache.dismissedVersion
4687
+ )) {
4688
+ return;
4689
+ }
4690
+ const latest = cache.latestVersion;
4691
+ log(
4692
+ `
4693
+ \u2B06 A new version of the Onklave CLI is available: ${current} \u2192 ${latest}`
4694
+ );
4695
+ const promptYesNo = deps.promptYesNo ?? defaultPromptYesNo;
4696
+ const accepted = await promptYesNo(
4697
+ ` Update now (npm i -g ${PACKAGE_NAME}@latest)? [y/N] `
4698
+ );
4699
+ if (!accepted) {
4700
+ write({ ...cache, dismissedVersion: latest });
4701
+ log(
4702
+ ` Skipped. Run \`npm i -g ${PACKAGE_NAME}@latest\` whenever you're ready.`
4703
+ );
4704
+ return;
4705
+ }
4706
+ const runUpdate = deps.runUpdate ?? defaultRunUpdate;
4707
+ const ok = runUpdate();
4708
+ if (ok) {
4709
+ write({ ...cache, dismissedVersion: latest });
4710
+ log(
4711
+ `
4712
+ \u2705 Updated to ${latest}. Re-run your command to use the new version.`
4713
+ );
4714
+ (deps.exit ?? ((code) => process.exit(code)))(0);
4715
+ return;
4716
+ }
4717
+ log(`
4718
+ \u26A0 Update failed. Try manually: npm i -g ${PACKAGE_NAME}@latest`);
4719
+ } catch {
4720
+ }
4721
+ }
4722
+ function withTimeout(promise, ms) {
4723
+ return new Promise((resolve3, reject) => {
4724
+ const timer = setTimeout(
4725
+ () => reject(new Error("update check timed out")),
4726
+ ms
4727
+ );
4728
+ if (typeof timer.unref === "function") timer.unref();
4729
+ promise.then(
4730
+ (value) => {
4731
+ clearTimeout(timer);
4732
+ resolve3(value);
4733
+ },
4734
+ (err) => {
4735
+ clearTimeout(timer);
4736
+ reject(err);
4737
+ }
4738
+ );
4739
+ });
4740
+ }
4741
+ function defaultIsInteractive() {
4742
+ if (process.env.NO_UPDATE_NOTIFIER) return false;
4743
+ if (process.env.CI) return false;
4744
+ return Boolean(process.stdout.isTTY && process.stdin.isTTY);
4745
+ }
4746
+ function isCacheFresh(cache, nowMs) {
4747
+ if (!cache.lastCheckedAt) return false;
4748
+ const last = new Date(cache.lastCheckedAt).getTime();
4749
+ if (Number.isNaN(last)) return false;
4750
+ return nowMs - last < CHECK_TTL_MS;
4751
+ }
4752
+ function readCache() {
4753
+ try {
4754
+ if (!fs9.existsSync(CACHE_PATH)) return {};
4755
+ return JSON.parse(fs9.readFileSync(CACHE_PATH, "utf8"));
4756
+ } catch {
4757
+ return {};
4758
+ }
4759
+ }
4760
+ function writeCache(cache) {
4761
+ try {
4762
+ fs9.mkdirSync(path9.dirname(CACHE_PATH), { recursive: true, mode: 448 });
4763
+ fs9.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), "utf8");
4764
+ } catch {
4765
+ }
4766
+ }
4767
+ async function defaultPromptYesNo(question) {
4768
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
4769
+ try {
4770
+ const answer = (await rl.question(question)).trim().toLowerCase();
4771
+ return answer === "y" || answer === "yes";
4772
+ } finally {
4773
+ rl.close();
4774
+ }
4775
+ }
4776
+ function defaultRunUpdate() {
4777
+ const result = spawnSync("npm", ["install", "-g", `${PACKAGE_NAME}@latest`], {
4778
+ stdio: "inherit",
4779
+ shell: process.platform === "win32"
4780
+ });
4781
+ return result.status === 0;
4782
+ }
4783
+
4603
4784
  // _apps/@onklave/agent-cli/src/main.ts
4604
- var VERSION = "0.1.0";
4785
+ var VERSION = getCurrentVersion();
4605
4786
  function showHelp() {
4606
4787
  console.log(
4607
4788
  `
@@ -4658,6 +4839,7 @@ async function main() {
4658
4839
  process.exitCode = 1;
4659
4840
  return;
4660
4841
  }
4842
+ await maybeNotifyUpdate({ currentVersion: VERSION });
4661
4843
  const commandArgs = args.slice(1);
4662
4844
  if (commandArgs.includes("--help") || commandArgs.includes("-h")) {
4663
4845
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onklave/agent-cli",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Onklave Agent CLI — local agent runner with cloud orchestration",
5
5
  "bin": {
6
6
  "onklave": "./main.js"