switchroom 0.7.10 → 0.7.11

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.
@@ -24035,8 +24035,8 @@ var {
24035
24035
  } = import__.default;
24036
24036
 
24037
24037
  // src/build-info.ts
24038
- var VERSION = "0.7.10";
24039
- var COMMIT_SHA = "3e362edf";
24038
+ var VERSION = "0.7.11";
24039
+ var COMMIT_SHA = "69ee15a9";
24040
24040
 
24041
24041
  // src/cli/deprecated.ts
24042
24042
  init_source();
@@ -24330,6 +24330,16 @@ var GetRequestSchema = exports_external.object({
24330
24330
  filename: exports_external.string().optional(),
24331
24331
  token: exports_external.string().optional()
24332
24332
  });
24333
+ var PutRequestSchema = exports_external.object({
24334
+ v: exports_external.literal(1),
24335
+ op: exports_external.literal("put"),
24336
+ key: exports_external.string().min(1),
24337
+ entry: exports_external.union([
24338
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
24339
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
24340
+ ]),
24341
+ token: exports_external.string().optional()
24342
+ });
24333
24343
  var ListRequestSchema = exports_external.object({
24334
24344
  v: exports_external.literal(1),
24335
24345
  op: exports_external.literal("list"),
@@ -24414,6 +24424,7 @@ var ApprovalRecordRequestSchema = exports_external.object({
24414
24424
  });
24415
24425
  var RequestSchema = exports_external.discriminatedUnion("op", [
24416
24426
  GetRequestSchema,
24427
+ PutRequestSchema,
24417
24428
  ListRequestSchema,
24418
24429
  StatusRequestSchema,
24419
24430
  LockRequestSchema,
@@ -24466,6 +24477,11 @@ var OkLockResponseSchema = exports_external.object({
24466
24477
  ok: exports_external.literal(true),
24467
24478
  locked: exports_external.literal(true)
24468
24479
  });
24480
+ var OkPutResponseSchema = exports_external.object({
24481
+ ok: exports_external.literal(true),
24482
+ put: exports_external.literal(true),
24483
+ key: exports_external.string()
24484
+ });
24469
24485
  var OkMintGrantResponseSchema = exports_external.object({
24470
24486
  ok: exports_external.literal(true),
24471
24487
  token: exports_external.string(),
@@ -24551,6 +24567,7 @@ var ResponseSchema = exports_external.union([
24551
24567
  OkKeysResponseSchema,
24552
24568
  OkStatusResponseSchema,
24553
24569
  OkLockResponseSchema,
24570
+ OkPutResponseSchema,
24554
24571
  OkMintGrantResponseSchema,
24555
24572
  OkListGrantsResponseSchema,
24556
24573
  OkRevokeGrantResponseSchema,
@@ -24711,6 +24728,23 @@ async function getViaBrokerStructured(key, opts) {
24711
24728
  }
24712
24729
  return { kind: "unreachable", msg: "unexpected broker response shape" };
24713
24730
  }
24731
+ async function putViaBroker(key, entry, opts) {
24732
+ const result = await rpc({ v: 1, op: "put", key, entry }, opts);
24733
+ if (result.kind === "unreachable") {
24734
+ return { kind: "unreachable", msg: result.msg };
24735
+ }
24736
+ const resp = result.resp;
24737
+ if (resp.ok && "put" in resp) {
24738
+ return { kind: "ok" };
24739
+ }
24740
+ if (!resp.ok) {
24741
+ if (resp.code === "UNKNOWN_KEY") {
24742
+ return { kind: "not_found", code: resp.code, msg: resp.msg };
24743
+ }
24744
+ return { kind: "denied", code: resp.code, msg: resp.msg };
24745
+ }
24746
+ return { kind: "unreachable", msg: "unexpected broker response shape" };
24747
+ }
24714
24748
  async function statusViaBroker(opts) {
24715
24749
  const result = await rpc({ v: 1, op: "status" }, opts);
24716
24750
  if (result.kind === "unreachable")
@@ -41373,6 +41407,7 @@ var PID_FILE_DEFAULT = "~/.switchroom/vault-broker.pid";
41373
41407
  class VaultBroker {
41374
41408
  testOpts;
41375
41409
  secrets = null;
41410
+ passphrase = null;
41376
41411
  config = null;
41377
41412
  startedAt = Date.now();
41378
41413
  server = null;
@@ -41444,6 +41479,7 @@ class VaultBroker {
41444
41479
  unlockFromPassphrase(passphrase) {
41445
41480
  const secrets = openVault(passphrase, this.vaultPath);
41446
41481
  this.secrets = secrets;
41482
+ this.passphrase = passphrase;
41447
41483
  }
41448
41484
  lock() {
41449
41485
  if (this.secrets !== null) {
@@ -41456,6 +41492,7 @@ class VaultBroker {
41456
41492
  }
41457
41493
  this.secrets = null;
41458
41494
  }
41495
+ this.passphrase = null;
41459
41496
  }
41460
41497
  stop() {
41461
41498
  this.lock();
@@ -41869,6 +41906,90 @@ class VaultBroker {
41869
41906
  socket.write(encodeResponse(entryResponse(entry)));
41870
41907
  return;
41871
41908
  }
41909
+ if (req.op === "put") {
41910
+ if (this.secrets === null || this.passphrase === null) {
41911
+ socket.write(encodeResponse(errorResponse("LOCKED", "Vault is locked")));
41912
+ return;
41913
+ }
41914
+ if (agentName === null) {
41915
+ socket.write(encodeResponse(errorResponse("DENIED", "put requires path-as-identity (token-based grants are read-only)")));
41916
+ return;
41917
+ }
41918
+ if (this.config === null) {
41919
+ socket.write(encodeResponse(errorResponse("INTERNAL", "Broker config not loaded")));
41920
+ return;
41921
+ }
41922
+ const aclResult = checkAclByAgent(this.config, agentName, req.key);
41923
+ if (!aclResult.allow) {
41924
+ this.auditLogger.write({
41925
+ ts: new Date().toISOString(),
41926
+ op: "put",
41927
+ key: req.key,
41928
+ caller: auditCaller,
41929
+ pid: auditPid,
41930
+ cgroup: auditCgroup,
41931
+ result: `denied:${aclResult.reason}`
41932
+ });
41933
+ socket.write(encodeResponse(errorResponse("DENIED", aclResult.reason)));
41934
+ return;
41935
+ }
41936
+ const existing = this.secrets[req.key];
41937
+ if (existing === undefined) {
41938
+ this.auditLogger.write({
41939
+ ts: new Date().toISOString(),
41940
+ op: "put",
41941
+ key: req.key,
41942
+ caller: auditCaller,
41943
+ pid: auditPid,
41944
+ cgroup: auditCgroup,
41945
+ result: "denied:unknown_key"
41946
+ });
41947
+ socket.write(encodeResponse(errorResponse("UNKNOWN_KEY", `Key not found: ${req.key} (broker put cannot introduce new keys; ask operator to set it once via 'switchroom vault set' from the host)`)));
41948
+ return;
41949
+ }
41950
+ if (existing.kind !== req.entry.kind) {
41951
+ this.auditLogger.write({
41952
+ ts: new Date().toISOString(),
41953
+ op: "put",
41954
+ key: req.key,
41955
+ caller: auditCaller,
41956
+ pid: auditPid,
41957
+ cgroup: auditCgroup,
41958
+ result: `denied:kind_mismatch ${existing.kind}\u2192${req.entry.kind}`
41959
+ });
41960
+ socket.write(encodeResponse(errorResponse("BAD_REQUEST", `kind mismatch: existing entry is '${existing.kind}', new entry is '${req.entry.kind}'`)));
41961
+ return;
41962
+ }
41963
+ const previousEntry = existing;
41964
+ this.secrets[req.key] = req.entry;
41965
+ try {
41966
+ saveVault(this.passphrase, this.vaultPath, this.secrets);
41967
+ } catch (err) {
41968
+ this.secrets[req.key] = previousEntry;
41969
+ this.auditLogger.write({
41970
+ ts: new Date().toISOString(),
41971
+ op: "put",
41972
+ key: req.key,
41973
+ caller: auditCaller,
41974
+ pid: auditPid,
41975
+ cgroup: auditCgroup,
41976
+ result: `error:${err?.message ?? "save failed"}`
41977
+ });
41978
+ socket.write(encodeResponse(errorResponse("INTERNAL", `Failed to persist: ${err?.message ?? "unknown"}`)));
41979
+ return;
41980
+ }
41981
+ this.auditLogger.write({
41982
+ ts: new Date().toISOString(),
41983
+ op: "put",
41984
+ key: req.key,
41985
+ caller: auditCaller,
41986
+ pid: auditPid,
41987
+ cgroup: auditCgroup,
41988
+ result: "allowed"
41989
+ });
41990
+ socket.write(encodeResponse({ ok: true, put: true, key: req.key }));
41991
+ return;
41992
+ }
41872
41993
  if (req.op === "approval_request" || req.op === "approval_lookup" || req.op === "approval_consume" || req.op === "approval_revoke" || req.op === "approval_list" || req.op === "approval_record") {
41873
41994
  await this._handleApprovalOp(socket, req);
41874
41995
  return;
@@ -43355,9 +43476,41 @@ function registerVaultCommand(program3) {
43355
43476
  }
43356
43477
  formatHint = opts.format;
43357
43478
  }
43358
- if (!process.stdin.isTTY && !process.env.SWITCHROOM_VAULT_PASSPHRASE && !opts.file) {
43359
- console.error(source_default.red("Error: piping a value to `vault set` requires SWITCHROOM_VAULT_PASSPHRASE to be set"));
43360
- process.exit(1);
43479
+ if (!process.stdin.isTTY && !process.env.SWITCHROOM_VAULT_PASSPHRASE && !opts.file && opts.allow === undefined && opts.deny === undefined) {
43480
+ const value2 = await readStdinToEnd();
43481
+ if (!value2) {
43482
+ console.error(source_default.red("Error: Value cannot be empty"));
43483
+ process.exit(1);
43484
+ }
43485
+ if (formatHint) {
43486
+ const validationError = validateFormatHint(value2, formatHint);
43487
+ if (validationError) {
43488
+ console.error(source_default.red(`Error: format validation failed for --format ${formatHint}: ${validationError}`));
43489
+ process.exit(1);
43490
+ }
43491
+ }
43492
+ let brokerSocket;
43493
+ try {
43494
+ const config = loadConfig(parentOpts.config);
43495
+ brokerSocket = resolveBrokerSocketPath({
43496
+ vaultBrokerSocket: config.vault?.broker?.socket ? resolvePath(config.vault.broker.socket) : undefined
43497
+ });
43498
+ } catch {
43499
+ brokerSocket = resolveBrokerSocketPath();
43500
+ }
43501
+ const result = await putViaBroker(key, { kind: "string", value: value2 }, { socket: brokerSocket });
43502
+ if (result.kind === "ok") {
43503
+ return;
43504
+ }
43505
+ if (result.kind === "unreachable") {
43506
+ console.error(source_default.red("Error: piping a value to `vault set` requires SWITCHROOM_VAULT_PASSPHRASE " + "to be set OR a reachable vault-broker (broker " + (result.msg ?? "unreachable") + ")"));
43507
+ process.exit(1);
43508
+ }
43509
+ console.error(source_default.red(`VAULT-BROKER-DENIED [${result.code}]: ${result.msg}`));
43510
+ if (result.kind === "not_found") {
43511
+ console.error(source_default.yellow(`Hint: broker put cannot introduce new keys. Run ` + `'switchroom vault set ${key}' once from the host (with the ` + `vault passphrase) to create the entry; agents can rotate it ` + `from then on.`));
43512
+ }
43513
+ process.exit(2);
43361
43514
  }
43362
43515
  const passphrase = await getPassphrase();
43363
43516
  let value;
@@ -11399,6 +11399,16 @@ var GetRequestSchema = exports_external.object({
11399
11399
  filename: exports_external.string().optional(),
11400
11400
  token: exports_external.string().optional()
11401
11401
  });
11402
+ var PutRequestSchema = exports_external.object({
11403
+ v: exports_external.literal(1),
11404
+ op: exports_external.literal("put"),
11405
+ key: exports_external.string().min(1),
11406
+ entry: exports_external.union([
11407
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
11408
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
11409
+ ]),
11410
+ token: exports_external.string().optional()
11411
+ });
11402
11412
  var ListRequestSchema = exports_external.object({
11403
11413
  v: exports_external.literal(1),
11404
11414
  op: exports_external.literal("list"),
@@ -11483,6 +11493,7 @@ var ApprovalRecordRequestSchema = exports_external.object({
11483
11493
  });
11484
11494
  var RequestSchema = exports_external.discriminatedUnion("op", [
11485
11495
  GetRequestSchema,
11496
+ PutRequestSchema,
11486
11497
  ListRequestSchema,
11487
11498
  StatusRequestSchema,
11488
11499
  LockRequestSchema,
@@ -11535,6 +11546,11 @@ var OkLockResponseSchema = exports_external.object({
11535
11546
  ok: exports_external.literal(true),
11536
11547
  locked: exports_external.literal(true)
11537
11548
  });
11549
+ var OkPutResponseSchema = exports_external.object({
11550
+ ok: exports_external.literal(true),
11551
+ put: exports_external.literal(true),
11552
+ key: exports_external.string()
11553
+ });
11538
11554
  var OkMintGrantResponseSchema = exports_external.object({
11539
11555
  ok: exports_external.literal(true),
11540
11556
  token: exports_external.string(),
@@ -11620,6 +11636,7 @@ var ResponseSchema = exports_external.union([
11620
11636
  OkKeysResponseSchema,
11621
11637
  OkStatusResponseSchema,
11622
11638
  OkLockResponseSchema,
11639
+ OkPutResponseSchema,
11623
11640
  OkMintGrantResponseSchema,
11624
11641
  OkListGrantsResponseSchema,
11625
11642
  OkRevokeGrantResponseSchema,
@@ -11258,7 +11258,7 @@ var init_schema = __esm(() => {
11258
11258
 
11259
11259
  // src/config/paths.ts
11260
11260
  import { existsSync as existsSync2 } from "node:fs";
11261
- import { resolve } from "node:path";
11261
+ import { resolve as resolve2 } from "node:path";
11262
11262
  function home() {
11263
11263
  return process.env.HOME ?? "/root";
11264
11264
  }
@@ -11266,18 +11266,18 @@ function resolveDualPath(pathStr) {
11266
11266
  const h = home();
11267
11267
  if (pathStr.startsWith("~/")) {
11268
11268
  const rest = pathStr.slice(2);
11269
- const absolute = resolve(h, rest);
11269
+ const absolute = resolve2(h, rest);
11270
11270
  if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
11271
11271
  const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
11272
11272
  if (!existsSync2(absolute)) {
11273
- const legacy = resolve(h, LEGACY_STATE_DIR, frag);
11273
+ const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
11274
11274
  if (existsSync2(legacy))
11275
11275
  return legacy;
11276
11276
  }
11277
11277
  }
11278
11278
  return absolute;
11279
11279
  }
11280
- return resolve(pathStr);
11280
+ return resolve2(pathStr);
11281
11281
  }
11282
11282
  var DEFAULT_STATE_DIR = ".switchroom", LEGACY_STATE_DIR = ".clerk";
11283
11283
  var init_paths = () => {};
@@ -11293,7 +11293,7 @@ __export(exports_loader, {
11293
11293
  });
11294
11294
  import { readFileSync as readFileSync2, existsSync as existsSync3 } from "node:fs";
11295
11295
  import { homedir } from "node:os";
11296
- import { resolve as resolve2 } from "node:path";
11296
+ import { resolve as resolve3 } from "node:path";
11297
11297
  function formatZodErrors(error) {
11298
11298
  return error.errors.map((e) => {
11299
11299
  const path = e.path.join(".");
@@ -11303,21 +11303,21 @@ function formatZodErrors(error) {
11303
11303
  function findConfigFile(startDir) {
11304
11304
  const envPath = process.env.SWITCHROOM_CONFIG;
11305
11305
  const home2 = homedir();
11306
- const userDir = resolve2(home2, ".switchroom");
11306
+ const userDir = resolve3(home2, ".switchroom");
11307
11307
  const searchPaths = [
11308
- envPath ? resolve2(envPath) : null,
11309
- startDir ? resolve2(startDir, "switchroom.yaml") : null,
11310
- startDir ? resolve2(startDir, "switchroom.yml") : null,
11311
- startDir ? resolve2(startDir, "clerk.yaml") : null,
11312
- startDir ? resolve2(startDir, "clerk.yml") : null,
11313
- resolve2(process.cwd(), "switchroom.yaml"),
11314
- resolve2(process.cwd(), "switchroom.yml"),
11315
- resolve2(process.cwd(), "clerk.yaml"),
11316
- resolve2(process.cwd(), "clerk.yml"),
11317
- resolve2(userDir, "switchroom.yaml"),
11318
- resolve2(userDir, "switchroom.yml"),
11319
- resolve2(userDir, "clerk.yaml"),
11320
- resolve2(userDir, "clerk.yml")
11308
+ envPath ? resolve3(envPath) : null,
11309
+ startDir ? resolve3(startDir, "switchroom.yaml") : null,
11310
+ startDir ? resolve3(startDir, "switchroom.yml") : null,
11311
+ startDir ? resolve3(startDir, "clerk.yaml") : null,
11312
+ startDir ? resolve3(startDir, "clerk.yml") : null,
11313
+ resolve3(process.cwd(), "switchroom.yaml"),
11314
+ resolve3(process.cwd(), "switchroom.yml"),
11315
+ resolve3(process.cwd(), "clerk.yaml"),
11316
+ resolve3(process.cwd(), "clerk.yml"),
11317
+ resolve3(userDir, "switchroom.yaml"),
11318
+ resolve3(userDir, "switchroom.yml"),
11319
+ resolve3(userDir, "clerk.yaml"),
11320
+ resolve3(userDir, "clerk.yml")
11321
11321
  ].filter(Boolean);
11322
11322
  for (const path of searchPaths) {
11323
11323
  if (existsSync3(path)) {
@@ -11676,7 +11676,7 @@ function allocateAgentUid(name) {
11676
11676
  }
11677
11677
 
11678
11678
  // src/vault/broker/server.ts
11679
- import { dirname as dirname2, resolve as resolve3 } from "node:path";
11679
+ import { dirname as dirname3, resolve as resolve4 } from "node:path";
11680
11680
  import * as os3 from "node:os";
11681
11681
  import * as path3 from "node:path";
11682
11682
 
@@ -11690,10 +11690,26 @@ import {
11690
11690
  mkdirSync,
11691
11691
  unlinkSync
11692
11692
  } from "node:fs";
11693
+ import { dirname, basename, resolve } from "node:path";
11693
11694
  var SCRYPT_N = 32768;
11694
11695
  var SCRYPT_R = 8;
11695
11696
  var SCRYPT_P = 1;
11696
11697
  var SCRYPT_MAXMEM = 128 * 1024 * 1024;
11698
+ function atomicWriteFileSync(path, data, mode) {
11699
+ const dir = dirname(resolve(path));
11700
+ const tmp = resolve(dir, `.${basename(path)}.${process.pid}.${Date.now()}.tmp`);
11701
+ try {
11702
+ writeFileSync(tmp, data, { encoding: "utf8", mode });
11703
+ renameSync(tmp, path);
11704
+ } catch (err) {
11705
+ try {
11706
+ if (existsSync(tmp))
11707
+ unlinkSync(tmp);
11708
+ } catch {}
11709
+ throw err;
11710
+ }
11711
+ }
11712
+
11697
11713
  class VaultError extends Error {
11698
11714
  constructor(message) {
11699
11715
  super(message);
@@ -11709,6 +11725,17 @@ function deriveKey(passphrase, salt, params = { N: SCRYPT_N, r: SCRYPT_R, p: SCR
11709
11725
  maxmem: SCRYPT_MAXMEM
11710
11726
  });
11711
11727
  }
11728
+ function encrypt(key, plaintext) {
11729
+ const iv = randomBytes(12);
11730
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
11731
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
11732
+ const tag = cipher.getAuthTag();
11733
+ return {
11734
+ iv: iv.toString("hex"),
11735
+ data: encrypted.toString("hex"),
11736
+ tag: tag.toString("hex")
11737
+ };
11738
+ }
11712
11739
  function decrypt(key, iv, data, tag) {
11713
11740
  const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"));
11714
11741
  decipher.setAuthTag(Buffer.from(tag, "hex"));
@@ -11758,6 +11785,26 @@ function openVault(passphrase, vaultPath) {
11758
11785
  }
11759
11786
  return normalizeSecrets(vaultData.secrets ?? {});
11760
11787
  }
11788
+ function saveVault(passphrase, vaultPath, secrets) {
11789
+ if (!existsSync(vaultPath)) {
11790
+ throw new VaultError(`Vault file not found: ${vaultPath}`);
11791
+ }
11792
+ let vaultFile;
11793
+ try {
11794
+ vaultFile = JSON.parse(readFileSync(vaultPath, "utf8"));
11795
+ } catch {
11796
+ throw new VaultError(`Failed to read vault file: ${vaultPath}`);
11797
+ }
11798
+ const salt = Buffer.from(vaultFile.salt, "hex");
11799
+ const key = deriveKey(passphrase, salt, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
11800
+ vaultFile.kdf = { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P };
11801
+ const vaultData = { secrets };
11802
+ const { iv, data, tag } = encrypt(key, JSON.stringify(vaultData));
11803
+ vaultFile.iv = iv;
11804
+ vaultFile.data = data;
11805
+ vaultFile.tag = tag;
11806
+ atomicWriteFileSync(vaultPath, JSON.stringify(vaultFile, null, 2), 384);
11807
+ }
11761
11808
 
11762
11809
  // src/vault/broker/server.ts
11763
11810
  init_loader();
@@ -12243,6 +12290,16 @@ var GetRequestSchema = exports_external.object({
12243
12290
  filename: exports_external.string().optional(),
12244
12291
  token: exports_external.string().optional()
12245
12292
  });
12293
+ var PutRequestSchema = exports_external.object({
12294
+ v: exports_external.literal(1),
12295
+ op: exports_external.literal("put"),
12296
+ key: exports_external.string().min(1),
12297
+ entry: exports_external.union([
12298
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
12299
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
12300
+ ]),
12301
+ token: exports_external.string().optional()
12302
+ });
12246
12303
  var ListRequestSchema = exports_external.object({
12247
12304
  v: exports_external.literal(1),
12248
12305
  op: exports_external.literal("list"),
@@ -12327,6 +12384,7 @@ var ApprovalRecordRequestSchema = exports_external.object({
12327
12384
  });
12328
12385
  var RequestSchema = exports_external.discriminatedUnion("op", [
12329
12386
  GetRequestSchema,
12387
+ PutRequestSchema,
12330
12388
  ListRequestSchema,
12331
12389
  StatusRequestSchema,
12332
12390
  LockRequestSchema,
@@ -12379,6 +12437,11 @@ var OkLockResponseSchema = exports_external.object({
12379
12437
  ok: exports_external.literal(true),
12380
12438
  locked: exports_external.literal(true)
12381
12439
  });
12440
+ var OkPutResponseSchema = exports_external.object({
12441
+ ok: exports_external.literal(true),
12442
+ put: exports_external.literal(true),
12443
+ key: exports_external.string()
12444
+ });
12382
12445
  var OkMintGrantResponseSchema = exports_external.object({
12383
12446
  ok: exports_external.literal(true),
12384
12447
  token: exports_external.string(),
@@ -12464,6 +12527,7 @@ var ResponseSchema = exports_external.union([
12464
12527
  OkKeysResponseSchema,
12465
12528
  OkStatusResponseSchema,
12466
12529
  OkLockResponseSchema,
12530
+ OkPutResponseSchema,
12467
12531
  OkMintGrantResponseSchema,
12468
12532
  OkListGrantsResponseSchema,
12469
12533
  OkRevokeGrantResponseSchema,
@@ -12613,13 +12677,13 @@ function genSalt(rounds, seed_length, callback) {
12613
12677
  throw Error("Illegal callback: " + typeof callback);
12614
12678
  _async(callback);
12615
12679
  } else
12616
- return new Promise(function(resolve3, reject) {
12680
+ return new Promise(function(resolve4, reject) {
12617
12681
  _async(function(err, res) {
12618
12682
  if (err) {
12619
12683
  reject(err);
12620
12684
  return;
12621
12685
  }
12622
- resolve3(res);
12686
+ resolve4(res);
12623
12687
  });
12624
12688
  });
12625
12689
  }
@@ -12639,13 +12703,13 @@ function hash(password, salt, callback, progressCallback) {
12639
12703
  throw Error("Illegal callback: " + typeof callback);
12640
12704
  _async(callback);
12641
12705
  } else
12642
- return new Promise(function(resolve3, reject) {
12706
+ return new Promise(function(resolve4, reject) {
12643
12707
  _async(function(err, res) {
12644
12708
  if (err) {
12645
12709
  reject(err);
12646
12710
  return;
12647
12711
  }
12648
- resolve3(res);
12712
+ resolve4(res);
12649
12713
  });
12650
12714
  });
12651
12715
  }
@@ -12678,13 +12742,13 @@ function compare(password, hashValue, callback, progressCallback) {
12678
12742
  throw Error("Illegal callback: " + typeof callback);
12679
12743
  _async(callback);
12680
12744
  } else
12681
- return new Promise(function(resolve3, reject) {
12745
+ return new Promise(function(resolve4, reject) {
12682
12746
  _async(function(err, res) {
12683
12747
  if (err) {
12684
12748
  reject(err);
12685
12749
  return;
12686
12750
  }
12687
- resolve3(res);
12751
+ resolve4(res);
12688
12752
  });
12689
12753
  });
12690
12754
  }
@@ -14709,6 +14773,7 @@ var PID_FILE_DEFAULT = "~/.switchroom/vault-broker.pid";
14709
14773
  class VaultBroker {
14710
14774
  testOpts;
14711
14775
  secrets = null;
14776
+ passphrase = null;
14712
14777
  config = null;
14713
14778
  startedAt = Date.now();
14714
14779
  server = null;
@@ -14736,7 +14801,7 @@ class VaultBroker {
14736
14801
  if (process.platform !== "linux" && process.env.SWITCHROOM_BROKER_ALLOW_NON_LINUX !== "1") {
14737
14802
  throw new Error(`vault-broker is Linux-only (running on ${process.platform}). ` + `The broker's ACL relies on cgroup-based systemd unit identification, ` + `which is not available on this platform. ` + `Use 'switchroom vault get --no-broker' for direct vault access. ` + `If you need to run the broker for development on this platform, ` + `set SWITCHROOM_BROKER_ALLOW_NON_LINUX=1 — but understand that the ` + `broker will accept any same-user caller without per-cron ACL enforcement.`);
14738
14803
  }
14739
- this.socketPath = resolve3(socketPath);
14804
+ this.socketPath = resolve4(socketPath);
14740
14805
  this.unlockSocketPath = this.socketPath.replace(/\.sock$/, ".unlock.sock");
14741
14806
  this.startedAt = Date.now();
14742
14807
  if (this.testOpts._testConfig) {
@@ -14746,7 +14811,7 @@ class VaultBroker {
14746
14811
  this.config = loadConfig2(configPath);
14747
14812
  }
14748
14813
  if (vaultPath) {
14749
- this.vaultPath = resolve3(vaultPath);
14814
+ this.vaultPath = resolve4(vaultPath);
14750
14815
  } else {
14751
14816
  this.vaultPath = resolvePath(this.config.vault?.path ?? "~/.switchroom/vault.enc");
14752
14817
  }
@@ -14754,7 +14819,7 @@ class VaultBroker {
14754
14819
  this.secrets = { ...this.testOpts._testSecrets };
14755
14820
  }
14756
14821
  process.umask(63);
14757
- const parentDir = dirname2(this.socketPath);
14822
+ const parentDir = dirname3(this.socketPath);
14758
14823
  mkdirSync4(parentDir, { recursive: true, mode: 448 });
14759
14824
  try {
14760
14825
  chmodSync3(parentDir, 448);
@@ -14780,6 +14845,7 @@ class VaultBroker {
14780
14845
  unlockFromPassphrase(passphrase) {
14781
14846
  const secrets = openVault(passphrase, this.vaultPath);
14782
14847
  this.secrets = secrets;
14848
+ this.passphrase = passphrase;
14783
14849
  }
14784
14850
  lock() {
14785
14851
  if (this.secrets !== null) {
@@ -14792,6 +14858,7 @@ class VaultBroker {
14792
14858
  }
14793
14859
  this.secrets = null;
14794
14860
  }
14861
+ this.passphrase = null;
14795
14862
  }
14796
14863
  stop() {
14797
14864
  this.lock();
@@ -14838,7 +14905,7 @@ class VaultBroker {
14838
14905
  return this.secrets;
14839
14906
  }
14840
14907
  bindAgentSocket(socketPath) {
14841
- const abs = resolve3(socketPath);
14908
+ const abs = resolve4(socketPath);
14842
14909
  const agentName = socketPathToAgent(abs);
14843
14910
  if (agentName === null) {
14844
14911
  return Promise.reject(new Error(`bindAgentSocket: socket path '${abs}' does not match the canonical ` + `/run/switchroom/broker/<agent>.sock shape — refusing to bind without ` + `a verifiable agent identity`));
@@ -14890,7 +14957,7 @@ class VaultBroker {
14890
14957
  });
14891
14958
  }
14892
14959
  _bindDataSocket() {
14893
- return new Promise((resolve4, reject) => {
14960
+ return new Promise((resolve5, reject) => {
14894
14961
  const server = net.createServer((socket) => {
14895
14962
  this._handleDataConnection(socket);
14896
14963
  });
@@ -14902,12 +14969,12 @@ class VaultBroker {
14902
14969
  chmodSync3(this.socketPath, 384);
14903
14970
  } catch {}
14904
14971
  this.server = server;
14905
- resolve4();
14972
+ resolve5();
14906
14973
  });
14907
14974
  });
14908
14975
  }
14909
14976
  _bindUnlockSocket() {
14910
- return new Promise((resolve4, reject) => {
14977
+ return new Promise((resolve5, reject) => {
14911
14978
  const server = net.createServer((socket) => {
14912
14979
  this._handleUnlockConnection(socket);
14913
14980
  });
@@ -14919,7 +14986,7 @@ class VaultBroker {
14919
14986
  chmodSync3(this.unlockSocketPath, 384);
14920
14987
  } catch {}
14921
14988
  this.unlockServer = server;
14922
- resolve4();
14989
+ resolve5();
14923
14990
  });
14924
14991
  });
14925
14992
  }
@@ -15205,6 +15272,90 @@ class VaultBroker {
15205
15272
  socket.write(encodeResponse(entryResponse(entry)));
15206
15273
  return;
15207
15274
  }
15275
+ if (req.op === "put") {
15276
+ if (this.secrets === null || this.passphrase === null) {
15277
+ socket.write(encodeResponse(errorResponse("LOCKED", "Vault is locked")));
15278
+ return;
15279
+ }
15280
+ if (agentName === null) {
15281
+ socket.write(encodeResponse(errorResponse("DENIED", "put requires path-as-identity (token-based grants are read-only)")));
15282
+ return;
15283
+ }
15284
+ if (this.config === null) {
15285
+ socket.write(encodeResponse(errorResponse("INTERNAL", "Broker config not loaded")));
15286
+ return;
15287
+ }
15288
+ const aclResult = checkAclByAgent(this.config, agentName, req.key);
15289
+ if (!aclResult.allow) {
15290
+ this.auditLogger.write({
15291
+ ts: new Date().toISOString(),
15292
+ op: "put",
15293
+ key: req.key,
15294
+ caller: auditCaller,
15295
+ pid: auditPid,
15296
+ cgroup: auditCgroup,
15297
+ result: `denied:${aclResult.reason}`
15298
+ });
15299
+ socket.write(encodeResponse(errorResponse("DENIED", aclResult.reason)));
15300
+ return;
15301
+ }
15302
+ const existing = this.secrets[req.key];
15303
+ if (existing === undefined) {
15304
+ this.auditLogger.write({
15305
+ ts: new Date().toISOString(),
15306
+ op: "put",
15307
+ key: req.key,
15308
+ caller: auditCaller,
15309
+ pid: auditPid,
15310
+ cgroup: auditCgroup,
15311
+ result: "denied:unknown_key"
15312
+ });
15313
+ socket.write(encodeResponse(errorResponse("UNKNOWN_KEY", `Key not found: ${req.key} (broker put cannot introduce new keys; ask operator to set it once via 'switchroom vault set' from the host)`)));
15314
+ return;
15315
+ }
15316
+ if (existing.kind !== req.entry.kind) {
15317
+ this.auditLogger.write({
15318
+ ts: new Date().toISOString(),
15319
+ op: "put",
15320
+ key: req.key,
15321
+ caller: auditCaller,
15322
+ pid: auditPid,
15323
+ cgroup: auditCgroup,
15324
+ result: `denied:kind_mismatch ${existing.kind}→${req.entry.kind}`
15325
+ });
15326
+ socket.write(encodeResponse(errorResponse("BAD_REQUEST", `kind mismatch: existing entry is '${existing.kind}', new entry is '${req.entry.kind}'`)));
15327
+ return;
15328
+ }
15329
+ const previousEntry = existing;
15330
+ this.secrets[req.key] = req.entry;
15331
+ try {
15332
+ saveVault(this.passphrase, this.vaultPath, this.secrets);
15333
+ } catch (err) {
15334
+ this.secrets[req.key] = previousEntry;
15335
+ this.auditLogger.write({
15336
+ ts: new Date().toISOString(),
15337
+ op: "put",
15338
+ key: req.key,
15339
+ caller: auditCaller,
15340
+ pid: auditPid,
15341
+ cgroup: auditCgroup,
15342
+ result: `error:${err?.message ?? "save failed"}`
15343
+ });
15344
+ socket.write(encodeResponse(errorResponse("INTERNAL", `Failed to persist: ${err?.message ?? "unknown"}`)));
15345
+ return;
15346
+ }
15347
+ this.auditLogger.write({
15348
+ ts: new Date().toISOString(),
15349
+ op: "put",
15350
+ key: req.key,
15351
+ caller: auditCaller,
15352
+ pid: auditPid,
15353
+ cgroup: auditCgroup,
15354
+ result: "allowed"
15355
+ });
15356
+ socket.write(encodeResponse({ ok: true, put: true, key: req.key }));
15357
+ return;
15358
+ }
15208
15359
  if (req.op === "approval_request" || req.op === "approval_lookup" || req.op === "approval_consume" || req.op === "approval_revoke" || req.op === "approval_list" || req.op === "approval_record") {
15209
15360
  await this._handleApprovalOp(socket, req);
15210
15361
  return;
@@ -15672,11 +15823,11 @@ async function main() {
15672
15823
  if (e.name.startsWith("."))
15673
15824
  continue;
15674
15825
  if ((e.isFile() || e.isSocket()) && e.name.endsWith(".sock")) {
15675
- flat.push(resolve3(perAgentDir, e.name));
15826
+ flat.push(resolve4(perAgentDir, e.name));
15676
15827
  continue;
15677
15828
  }
15678
15829
  if (e.isDirectory()) {
15679
- const candidate = resolve3(perAgentDir, e.name, "sock");
15830
+ const candidate = resolve4(perAgentDir, e.name, "sock");
15680
15831
  if (socketPathToAgent(candidate) !== null) {
15681
15832
  subdirs.push(candidate);
15682
15833
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.7.10",
3
+ "version": "0.7.11",
4
4
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23553,7 +23553,7 @@ function decodeResponse(line) {
23553
23553
  const obj = JSON.parse(line);
23554
23554
  return ResponseSchema.parse(obj);
23555
23555
  }
23556
- var MAX_FRAME_BYTES, GetRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
23556
+ var MAX_FRAME_BYTES, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
23557
23557
  var init_protocol = __esm(() => {
23558
23558
  init_zod();
23559
23559
  MAX_FRAME_BYTES = 64 * 1024;
@@ -23564,6 +23564,16 @@ var init_protocol = __esm(() => {
23564
23564
  filename: exports_external.string().optional(),
23565
23565
  token: exports_external.string().optional()
23566
23566
  });
23567
+ PutRequestSchema = exports_external.object({
23568
+ v: exports_external.literal(1),
23569
+ op: exports_external.literal("put"),
23570
+ key: exports_external.string().min(1),
23571
+ entry: exports_external.union([
23572
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
23573
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
23574
+ ]),
23575
+ token: exports_external.string().optional()
23576
+ });
23567
23577
  ListRequestSchema = exports_external.object({
23568
23578
  v: exports_external.literal(1),
23569
23579
  op: exports_external.literal("list"),
@@ -23648,6 +23658,7 @@ var init_protocol = __esm(() => {
23648
23658
  });
23649
23659
  RequestSchema = exports_external.discriminatedUnion("op", [
23650
23660
  GetRequestSchema,
23661
+ PutRequestSchema,
23651
23662
  ListRequestSchema,
23652
23663
  StatusRequestSchema,
23653
23664
  LockRequestSchema,
@@ -23700,6 +23711,11 @@ var init_protocol = __esm(() => {
23700
23711
  ok: exports_external.literal(true),
23701
23712
  locked: exports_external.literal(true)
23702
23713
  });
23714
+ OkPutResponseSchema = exports_external.object({
23715
+ ok: exports_external.literal(true),
23716
+ put: exports_external.literal(true),
23717
+ key: exports_external.string()
23718
+ });
23703
23719
  OkMintGrantResponseSchema = exports_external.object({
23704
23720
  ok: exports_external.literal(true),
23705
23721
  token: exports_external.string(),
@@ -23785,6 +23801,7 @@ var init_protocol = __esm(() => {
23785
23801
  OkKeysResponseSchema,
23786
23802
  OkStatusResponseSchema,
23787
23803
  OkLockResponseSchema,
23804
+ OkPutResponseSchema,
23788
23805
  OkMintGrantResponseSchema,
23789
23806
  OkListGrantsResponseSchema,
23790
23807
  OkRevokeGrantResponseSchema,
@@ -26791,7 +26791,7 @@ function decodeResponse(line) {
26791
26791
  const obj = JSON.parse(line);
26792
26792
  return ResponseSchema.parse(obj);
26793
26793
  }
26794
- var MAX_FRAME_BYTES, GetRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
26794
+ var MAX_FRAME_BYTES, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
26795
26795
  var init_protocol = __esm(() => {
26796
26796
  init_zod();
26797
26797
  MAX_FRAME_BYTES = 64 * 1024;
@@ -26802,6 +26802,16 @@ var init_protocol = __esm(() => {
26802
26802
  filename: exports_external.string().optional(),
26803
26803
  token: exports_external.string().optional()
26804
26804
  });
26805
+ PutRequestSchema = exports_external.object({
26806
+ v: exports_external.literal(1),
26807
+ op: exports_external.literal("put"),
26808
+ key: exports_external.string().min(1),
26809
+ entry: exports_external.union([
26810
+ exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
26811
+ exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
26812
+ ]),
26813
+ token: exports_external.string().optional()
26814
+ });
26805
26815
  ListRequestSchema = exports_external.object({
26806
26816
  v: exports_external.literal(1),
26807
26817
  op: exports_external.literal("list"),
@@ -26886,6 +26896,7 @@ var init_protocol = __esm(() => {
26886
26896
  });
26887
26897
  RequestSchema = exports_external.discriminatedUnion("op", [
26888
26898
  GetRequestSchema,
26899
+ PutRequestSchema,
26889
26900
  ListRequestSchema,
26890
26901
  StatusRequestSchema,
26891
26902
  LockRequestSchema,
@@ -26938,6 +26949,11 @@ var init_protocol = __esm(() => {
26938
26949
  ok: exports_external.literal(true),
26939
26950
  locked: exports_external.literal(true)
26940
26951
  });
26952
+ OkPutResponseSchema = exports_external.object({
26953
+ ok: exports_external.literal(true),
26954
+ put: exports_external.literal(true),
26955
+ key: exports_external.string()
26956
+ });
26941
26957
  OkMintGrantResponseSchema = exports_external.object({
26942
26958
  ok: exports_external.literal(true),
26943
26959
  token: exports_external.string(),
@@ -27023,6 +27039,7 @@ var init_protocol = __esm(() => {
27023
27039
  OkKeysResponseSchema,
27024
27040
  OkStatusResponseSchema,
27025
27041
  OkLockResponseSchema,
27042
+ OkPutResponseSchema,
27026
27043
  OkMintGrantResponseSchema,
27027
27044
  OkListGrantsResponseSchema,
27028
27045
  OkRevokeGrantResponseSchema,
@@ -40750,10 +40767,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
40750
40767
  }
40751
40768
 
40752
40769
  // ../src/build-info.ts
40753
- var VERSION = "0.7.10";
40754
- var COMMIT_SHA = "3e362edf";
40755
- var COMMIT_DATE = "2026-05-10T22:18:36+10:00";
40756
- var LATEST_PR = 950;
40770
+ var VERSION = "0.7.11";
40771
+ var COMMIT_SHA = "69ee15a9";
40772
+ var COMMIT_DATE = "2026-05-10T23:42:16+10:00";
40773
+ var LATEST_PR = 953;
40757
40774
  var COMMITS_AHEAD_OF_TAG = 0;
40758
40775
 
40759
40776
  // gateway/unhandled-rejection-policy.ts