@rosthq/cli 0.7.162 → 0.7.163

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/index.js CHANGED
@@ -29434,6 +29434,19 @@ var runnerPairingStartOutputSchema = external_exports.object({
29434
29434
  expires_at: isoDateTime2,
29435
29435
  expires_in: external_exports.number().int().nonnegative()
29436
29436
  }).strict();
29437
+ var runnerPairingClaimInputSchema = external_exports.object({
29438
+ user_code: external_exports.string().min(8).max(12),
29439
+ service_key_public: external_exports.string().trim().min(1).max(1024).optional()
29440
+ }).strict();
29441
+ var runnerPairingClaimOutputSchema = external_exports.object({
29442
+ runner_id: uuid5,
29443
+ tenant_id: uuid5,
29444
+ runner_secret: external_exports.string().min(1),
29445
+ name: external_exports.string(),
29446
+ platform: external_exports.string(),
29447
+ service_key_generation: external_exports.number().int().nonnegative().optional(),
29448
+ service_key_bound: external_exports.boolean().optional()
29449
+ }).strict();
29437
29450
  var runnerRevokeInputSchema = external_exports.object({
29438
29451
  runner_id: uuid5
29439
29452
  }).strict();
@@ -69609,7 +69622,7 @@ async function removeWorkspace(ws) {
69609
69622
 
69610
69623
  // src/runner-serve.ts
69611
69624
  import { execFile as execFileCallback5, spawn } from "node:child_process";
69612
- import { createHash as createHash4, randomUUID } from "node:crypto";
69625
+ import { createHash as createHash4, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, randomUUID } from "node:crypto";
69613
69626
  import { constants as fsConstants6 } from "node:fs";
69614
69627
  import { access as access3, chmod as chmod2, lstat as lstat2, mkdir as mkdir8, open as open2, readFile as readFile11, rename as rename2, rm as rm8, statfs, writeFile as writeFile8 } from "node:fs/promises";
69615
69628
  import { cpus, homedir as homedir7, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
@@ -70369,16 +70382,20 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
70369
70382
  `No paired runner state at ${config2.stateFile}. Provide --user-code <code> from Settings > Runners (Add a runner), or run \`${cliBrand.binName} login --device\` once to pair this runner.`
70370
70383
  );
70371
70384
  }
70385
+ const serviceKey = runnerServiceKeyPairForState(priorState);
70372
70386
  const { runnerId, runnerSecret, tenantId: claimedTenantId, name: claimedName } = await claimRunnerPairingCode(
70373
70387
  fetchImpl,
70374
70388
  appUrl2,
70375
- userCode
70389
+ userCode,
70390
+ serviceKey
70376
70391
  );
70377
70392
  const state = {
70378
70393
  runner_id: runnerId,
70379
70394
  runner_secret: runnerSecret,
70380
70395
  ...typeof claimedTenantId === "string" ? { tenant_id: claimedTenantId } : config2.expectedTenantId ? { tenant_id: config2.expectedTenantId } : {},
70381
- name: typeof claimedName === "string" ? claimedName : config2.name
70396
+ name: typeof claimedName === "string" ? claimedName : config2.name,
70397
+ service_key_private: serviceKey.privateKey,
70398
+ service_key_public: serviceKey.publicKey
70382
70399
  };
70383
70400
  await saveState(config2.stateFile, state);
70384
70401
  await reapSupersededRunnerHomes(priorState, runnerId, config2.homeDir);
@@ -70462,6 +70479,8 @@ async function loadState(filePath, expectedTenantId) {
70462
70479
  runner_secret: parsed.runner_secret,
70463
70480
  ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
70464
70481
  ...typeof parsed.name === "string" ? { name: parsed.name } : {},
70482
+ ...typeof parsed.service_key_private === "string" ? { service_key_private: parsed.service_key_private } : {},
70483
+ ...typeof parsed.service_key_public === "string" ? { service_key_public: parsed.service_key_public } : {},
70465
70484
  ...isRuntimeSessionMap(parsed.runtime_sessions) ? { runtime_sessions: parsed.runtime_sessions } : {},
70466
70485
  ...isInFlightWorkOrderMap(parsed.in_flight_work_orders) ? { in_flight_work_orders: parsed.in_flight_work_orders } : {}
70467
70486
  };
@@ -70496,8 +70515,56 @@ async function saveState(filePath, state) {
70496
70515
  throw error51;
70497
70516
  }
70498
70517
  }
70499
- async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode) {
70500
- const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", { user_code: userCode });
70518
+ var RUNNER_SERVICE_KEY_REPAIR_MESSAGE = "The persisted runner service key is invalid. Repair the local service-key state, then re-pair the runner or use explicit service-key rotation.";
70519
+ var RunnerServiceKeyError = class extends Error {
70520
+ code = "service_key_invalid";
70521
+ constructor() {
70522
+ super(RUNNER_SERVICE_KEY_REPAIR_MESSAGE);
70523
+ this.name = "RunnerServiceKeyError";
70524
+ }
70525
+ };
70526
+ function sshWireField(value) {
70527
+ const bytes = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf8");
70528
+ const length = Buffer.allocUnsafe(4);
70529
+ length.writeUInt32BE(bytes.length, 0);
70530
+ return Buffer.concat([length, bytes]);
70531
+ }
70532
+ function openSshEd25519PublicKey(publicKey) {
70533
+ const der = publicKey.export({ type: "spki", format: "der" });
70534
+ if (!Buffer.isBuffer(der) || der.length < 32) {
70535
+ throw new Error("Unable to encode the runner service key.");
70536
+ }
70537
+ const rawKey = der.subarray(der.length - 32);
70538
+ const wire = Buffer.concat([sshWireField("ssh-ed25519"), sshWireField(rawKey)]);
70539
+ return `ssh-ed25519 ${wire.toString("base64")}`;
70540
+ }
70541
+ function generateRunnerServiceKeyPair() {
70542
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
70543
+ const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
70544
+ if (typeof privatePem !== "string") {
70545
+ throw new Error("Unable to persist the runner service key.");
70546
+ }
70547
+ return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(publicKey) };
70548
+ }
70549
+ function runnerServiceKeyPairFromPrivate(privatePem) {
70550
+ const privateKey = createPrivateKey(privatePem);
70551
+ return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(createPublicKey2(privateKey)) };
70552
+ }
70553
+ function runnerServiceKeyPairForState(state) {
70554
+ if (state?.service_key_private === void 0) {
70555
+ return generateRunnerServiceKeyPair();
70556
+ }
70557
+ try {
70558
+ return runnerServiceKeyPairFromPrivate(state.service_key_private);
70559
+ } catch {
70560
+ throw new RunnerServiceKeyError();
70561
+ }
70562
+ }
70563
+ async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode, serviceKey) {
70564
+ const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", {
70565
+ user_code: userCode,
70566
+ ...serviceKey ? { service_key_public: serviceKey.publicKey } : {}
70567
+ });
70501
70568
  const runnerSecret = claim.json.runner_secret;
70502
70569
  const runnerId = claim.json.runner_id;
70503
70570
  const tenantId = claim.json.tenant_id;
@@ -70508,7 +70575,10 @@ async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode) {
70508
70575
  runnerId,
70509
70576
  runnerSecret,
70510
70577
  tenantId: typeof tenantId === "string" ? tenantId : null,
70511
- name: typeof claim.json.name === "string" ? claim.json.name : null
70578
+ name: typeof claim.json.name === "string" ? claim.json.name : null,
70579
+ ...serviceKey ? { serviceKeyPrivate: serviceKey.privateKey, serviceKeyPublic: serviceKey.publicKey } : {},
70580
+ ...typeof claim.json.service_key_generation === "number" ? { serviceKeyGeneration: claim.json.service_key_generation } : {},
70581
+ ...typeof claim.json.service_key_bound === "boolean" ? { serviceKeyBound: claim.json.service_key_bound } : {}
70512
70582
  };
70513
70583
  }
70514
70584
  async function startPairing(client, config2) {
@@ -73974,7 +74044,14 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
73974
74044
  }
73975
74045
  if (userCode !== void 0) {
73976
74046
  const priorState = await loadState(sidecar.state_file, expectedTenantId);
73977
- const claimResult = await claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode);
74047
+ let serviceKey;
74048
+ try {
74049
+ serviceKey = runnerServiceKeyPairForState(priorState);
74050
+ } catch (error51) {
74051
+ const message = error51 instanceof RunnerServiceKeyError ? error51.message : RUNNER_SERVICE_KEY_REPAIR_MESSAGE;
74052
+ return { kind: "claim_failed", exitCode: 1, message: redactForLog(message) };
74053
+ }
74054
+ const claimResult = await claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceKey);
73978
74055
  if (!claimResult.ok) {
73979
74056
  return { kind: "claim_failed", exitCode: claimResult.exitCode, message: claimResult.message };
73980
74057
  }
@@ -73996,7 +74073,9 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
73996
74073
  runner_id: claimResult.runnerId,
73997
74074
  runner_secret: claimResult.runnerSecret,
73998
74075
  ...claimResult.tenantId !== null ? { tenant_id: claimResult.tenantId } : {},
73999
- ...claimResult.name !== null ? { name: claimResult.name } : {}
74076
+ ...claimResult.name !== null ? { name: claimResult.name } : {},
74077
+ ...claimResult.serviceKeyPrivate !== void 0 ? { service_key_private: claimResult.serviceKeyPrivate } : {},
74078
+ ...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {}
74000
74079
  };
74001
74080
  await saveState(sidecar.state_file, newState);
74002
74081
  await reapSupersededRunnerHomes(priorState, newState.runner_id, homeDir);
@@ -74021,9 +74100,9 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
74021
74100
  }
74022
74101
  return { kind: "needs_user_code" };
74023
74102
  }
74024
- async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode) {
74103
+ async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceKey) {
74025
74104
  try {
74026
- const claim = await claimRunnerPairingCode(fetchImpl, appUrl2, userCode);
74105
+ const claim = await claimRunnerPairingCode(fetchImpl, appUrl2, userCode, serviceKey);
74027
74106
  return { ok: true, ...claim };
74028
74107
  } catch (error51) {
74029
74108
  const message = error51 instanceof Error ? error51.message : String(error51);