@rosthq/cli 0.7.162 → 0.7.164

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
@@ -29223,6 +29223,24 @@ var runnerSetupValidationAttemptSchema = external_exports.object({
29223
29223
  (attempt) => attempt.kind === "aicos_acceptance" === (attempt.activation_receipt_id !== null),
29224
29224
  "aicos_acceptance must be attributed to an activation receipt"
29225
29225
  );
29226
+ var RUNNER_SETUP_PROBE_CAPABILITY_VERSION = 1;
29227
+ var runnerSetupValidationMcpCapabilityPayloadSchema = external_exports.object({
29228
+ capability_version: external_exports.literal(RUNNER_SETUP_PROBE_CAPABILITY_VERSION),
29229
+ tenant_id: uuid3,
29230
+ runner_id: uuid3,
29231
+ setup_id: uuid3,
29232
+ job_id: uuid3,
29233
+ runtime: runnerSetupRuntimeSchema,
29234
+ service_key_generation: external_exports.number().int().nonnegative(),
29235
+ nonce: external_exports.string().min(16).max(120),
29236
+ expires_at: external_exports.number().int().positive(),
29237
+ allowed_command_ids: external_exports.tuple([external_exports.literal("runner_setup.status")])
29238
+ }).strict();
29239
+ var runnerSetupValidationMcpConfigSchema = external_exports.object({
29240
+ url: external_exports.string().url(),
29241
+ token: external_exports.string().min(1).max(4096),
29242
+ expires_at: isoDateTime
29243
+ }).strict();
29226
29244
  var runnerSetupValidationClaimRequestSchema = external_exports.object({
29227
29245
  setup_id: uuid3,
29228
29246
  runtime: runnerSetupRuntimeSchema
@@ -29231,7 +29249,8 @@ var runnerSetupValidationClaimResponseSchema = external_exports.object({
29231
29249
  job_id: uuid3,
29232
29250
  challenge: runnerSetupValidationChallengeSchema,
29233
29251
  probe_capability_version: external_exports.number().int().positive(),
29234
- allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20)
29252
+ allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20),
29253
+ mcp: runnerSetupValidationMcpConfigSchema
29235
29254
  }).strict();
29236
29255
  var runnerSetupValidationReportRequestSchema = external_exports.object({
29237
29256
  job_id: uuid3,
@@ -29434,6 +29453,19 @@ var runnerPairingStartOutputSchema = external_exports.object({
29434
29453
  expires_at: isoDateTime2,
29435
29454
  expires_in: external_exports.number().int().nonnegative()
29436
29455
  }).strict();
29456
+ var runnerPairingClaimInputSchema = external_exports.object({
29457
+ user_code: external_exports.string().min(8).max(12),
29458
+ service_key_public: external_exports.string().trim().min(1).max(1024).optional()
29459
+ }).strict();
29460
+ var runnerPairingClaimOutputSchema = external_exports.object({
29461
+ runner_id: uuid5,
29462
+ tenant_id: uuid5,
29463
+ runner_secret: external_exports.string().min(1),
29464
+ name: external_exports.string(),
29465
+ platform: external_exports.string(),
29466
+ service_key_generation: external_exports.number().int().nonnegative().optional(),
29467
+ service_key_bound: external_exports.boolean().optional()
29468
+ }).strict();
29437
29469
  var runnerRevokeInputSchema = external_exports.object({
29438
29470
  runner_id: uuid5
29439
29471
  }).strict();
@@ -69609,7 +69641,7 @@ async function removeWorkspace(ws) {
69609
69641
 
69610
69642
  // src/runner-serve.ts
69611
69643
  import { execFile as execFileCallback5, spawn } from "node:child_process";
69612
- import { createHash as createHash4, randomUUID } from "node:crypto";
69644
+ import { createHash as createHash4, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, randomUUID } from "node:crypto";
69613
69645
  import { constants as fsConstants6 } from "node:fs";
69614
69646
  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
69647
  import { cpus, homedir as homedir7, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
@@ -70369,16 +70401,20 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
70369
70401
  `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
70402
  );
70371
70403
  }
70404
+ const serviceKey = runnerServiceKeyPairForState(priorState);
70372
70405
  const { runnerId, runnerSecret, tenantId: claimedTenantId, name: claimedName } = await claimRunnerPairingCode(
70373
70406
  fetchImpl,
70374
70407
  appUrl2,
70375
- userCode
70408
+ userCode,
70409
+ serviceKey
70376
70410
  );
70377
70411
  const state = {
70378
70412
  runner_id: runnerId,
70379
70413
  runner_secret: runnerSecret,
70380
70414
  ...typeof claimedTenantId === "string" ? { tenant_id: claimedTenantId } : config2.expectedTenantId ? { tenant_id: config2.expectedTenantId } : {},
70381
- name: typeof claimedName === "string" ? claimedName : config2.name
70415
+ name: typeof claimedName === "string" ? claimedName : config2.name,
70416
+ service_key_private: serviceKey.privateKey,
70417
+ service_key_public: serviceKey.publicKey
70382
70418
  };
70383
70419
  await saveState(config2.stateFile, state);
70384
70420
  await reapSupersededRunnerHomes(priorState, runnerId, config2.homeDir);
@@ -70462,6 +70498,8 @@ async function loadState(filePath, expectedTenantId) {
70462
70498
  runner_secret: parsed.runner_secret,
70463
70499
  ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
70464
70500
  ...typeof parsed.name === "string" ? { name: parsed.name } : {},
70501
+ ...typeof parsed.service_key_private === "string" ? { service_key_private: parsed.service_key_private } : {},
70502
+ ...typeof parsed.service_key_public === "string" ? { service_key_public: parsed.service_key_public } : {},
70465
70503
  ...isRuntimeSessionMap(parsed.runtime_sessions) ? { runtime_sessions: parsed.runtime_sessions } : {},
70466
70504
  ...isInFlightWorkOrderMap(parsed.in_flight_work_orders) ? { in_flight_work_orders: parsed.in_flight_work_orders } : {}
70467
70505
  };
@@ -70496,8 +70534,56 @@ async function saveState(filePath, state) {
70496
70534
  throw error51;
70497
70535
  }
70498
70536
  }
70499
- async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode) {
70500
- const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", { user_code: userCode });
70537
+ 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.";
70538
+ var RunnerServiceKeyError = class extends Error {
70539
+ code = "service_key_invalid";
70540
+ constructor() {
70541
+ super(RUNNER_SERVICE_KEY_REPAIR_MESSAGE);
70542
+ this.name = "RunnerServiceKeyError";
70543
+ }
70544
+ };
70545
+ function sshWireField(value) {
70546
+ const bytes = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf8");
70547
+ const length = Buffer.allocUnsafe(4);
70548
+ length.writeUInt32BE(bytes.length, 0);
70549
+ return Buffer.concat([length, bytes]);
70550
+ }
70551
+ function openSshEd25519PublicKey(publicKey) {
70552
+ const der = publicKey.export({ type: "spki", format: "der" });
70553
+ if (!Buffer.isBuffer(der) || der.length < 32) {
70554
+ throw new Error("Unable to encode the runner service key.");
70555
+ }
70556
+ const rawKey = der.subarray(der.length - 32);
70557
+ const wire = Buffer.concat([sshWireField("ssh-ed25519"), sshWireField(rawKey)]);
70558
+ return `ssh-ed25519 ${wire.toString("base64")}`;
70559
+ }
70560
+ function generateRunnerServiceKeyPair() {
70561
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
70562
+ const privatePem = privateKey.export({ type: "pkcs8", format: "pem" });
70563
+ if (typeof privatePem !== "string") {
70564
+ throw new Error("Unable to persist the runner service key.");
70565
+ }
70566
+ return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(publicKey) };
70567
+ }
70568
+ function runnerServiceKeyPairFromPrivate(privatePem) {
70569
+ const privateKey = createPrivateKey(privatePem);
70570
+ return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(createPublicKey2(privateKey)) };
70571
+ }
70572
+ function runnerServiceKeyPairForState(state) {
70573
+ if (state?.service_key_private === void 0) {
70574
+ return generateRunnerServiceKeyPair();
70575
+ }
70576
+ try {
70577
+ return runnerServiceKeyPairFromPrivate(state.service_key_private);
70578
+ } catch {
70579
+ throw new RunnerServiceKeyError();
70580
+ }
70581
+ }
70582
+ async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode, serviceKey) {
70583
+ const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", {
70584
+ user_code: userCode,
70585
+ ...serviceKey ? { service_key_public: serviceKey.publicKey } : {}
70586
+ });
70501
70587
  const runnerSecret = claim.json.runner_secret;
70502
70588
  const runnerId = claim.json.runner_id;
70503
70589
  const tenantId = claim.json.tenant_id;
@@ -70508,7 +70594,10 @@ async function claimRunnerPairingCode(fetchImpl, appUrl2, userCode) {
70508
70594
  runnerId,
70509
70595
  runnerSecret,
70510
70596
  tenantId: typeof tenantId === "string" ? tenantId : null,
70511
- name: typeof claim.json.name === "string" ? claim.json.name : null
70597
+ name: typeof claim.json.name === "string" ? claim.json.name : null,
70598
+ ...serviceKey ? { serviceKeyPrivate: serviceKey.privateKey, serviceKeyPublic: serviceKey.publicKey } : {},
70599
+ ...typeof claim.json.service_key_generation === "number" ? { serviceKeyGeneration: claim.json.service_key_generation } : {},
70600
+ ...typeof claim.json.service_key_bound === "boolean" ? { serviceKeyBound: claim.json.service_key_bound } : {}
70512
70601
  };
70513
70602
  }
70514
70603
  async function startPairing(client, config2) {
@@ -73974,7 +74063,14 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
73974
74063
  }
73975
74064
  if (userCode !== void 0) {
73976
74065
  const priorState = await loadState(sidecar.state_file, expectedTenantId);
73977
- const claimResult = await claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode);
74066
+ let serviceKey;
74067
+ try {
74068
+ serviceKey = runnerServiceKeyPairForState(priorState);
74069
+ } catch (error51) {
74070
+ const message = error51 instanceof RunnerServiceKeyError ? error51.message : RUNNER_SERVICE_KEY_REPAIR_MESSAGE;
74071
+ return { kind: "claim_failed", exitCode: 1, message: redactForLog(message) };
74072
+ }
74073
+ const claimResult = await claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceKey);
73978
74074
  if (!claimResult.ok) {
73979
74075
  return { kind: "claim_failed", exitCode: claimResult.exitCode, message: claimResult.message };
73980
74076
  }
@@ -73996,7 +74092,9 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
73996
74092
  runner_id: claimResult.runnerId,
73997
74093
  runner_secret: claimResult.runnerSecret,
73998
74094
  ...claimResult.tenantId !== null ? { tenant_id: claimResult.tenantId } : {},
73999
- ...claimResult.name !== null ? { name: claimResult.name } : {}
74095
+ ...claimResult.name !== null ? { name: claimResult.name } : {},
74096
+ ...claimResult.serviceKeyPrivate !== void 0 ? { service_key_private: claimResult.serviceKeyPrivate } : {},
74097
+ ...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {}
74000
74098
  };
74001
74099
  await saveState(sidecar.state_file, newState);
74002
74100
  await reapSupersededRunnerHomes(priorState, newState.runner_id, homeDir);
@@ -74021,9 +74119,9 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
74021
74119
  }
74022
74120
  return { kind: "needs_user_code" };
74023
74121
  }
74024
- async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode) {
74122
+ async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceKey) {
74025
74123
  try {
74026
- const claim = await claimRunnerPairingCode(fetchImpl, appUrl2, userCode);
74124
+ const claim = await claimRunnerPairingCode(fetchImpl, appUrl2, userCode, serviceKey);
74027
74125
  return { ok: true, ...claim };
74028
74126
  } catch (error51) {
74029
74127
  const message = error51 instanceof Error ? error51.message : String(error51);