@tinycloud/operations 0.5.0-beta.1 → 0.5.0-beta.10

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 (51) hide show
  1. package/coverage.json +11 -1
  2. package/dist/artifacts.cjs +235 -43
  3. package/dist/artifacts.cjs.map +1 -1
  4. package/dist/artifacts.d.cts +1 -1
  5. package/dist/artifacts.d.ts +1 -1
  6. package/dist/artifacts.js +236 -43
  7. package/dist/artifacts.js.map +1 -1
  8. package/dist/cli-runtime.cjs +961 -178
  9. package/dist/cli-runtime.cjs.map +1 -1
  10. package/dist/cli-runtime.d.cts +1 -1
  11. package/dist/cli-runtime.d.ts +1 -1
  12. package/dist/cli-runtime.js +960 -177
  13. package/dist/cli-runtime.js.map +1 -1
  14. package/dist/{contract-DfyrluLB.d.cts → contract-zRklonbR.d.cts} +6 -3
  15. package/dist/{contract-DfyrluLB.d.ts → contract-zRklonbR.d.ts} +6 -3
  16. package/dist/delegation-binding.cjs +242 -43
  17. package/dist/delegation-binding.cjs.map +1 -1
  18. package/dist/delegation-binding.d.cts +15 -3
  19. package/dist/delegation-binding.d.ts +15 -3
  20. package/dist/delegation-binding.js +242 -43
  21. package/dist/delegation-binding.js.map +1 -1
  22. package/dist/index.cjs +989 -179
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +986 -176
  27. package/dist/index.js.map +1 -1
  28. package/dist/profile.cjs +13 -4
  29. package/dist/profile.cjs.map +1 -1
  30. package/dist/profile.d.cts +14 -2
  31. package/dist/profile.d.ts +14 -2
  32. package/dist/profile.js +13 -4
  33. package/dist/profile.js.map +1 -1
  34. package/dist/recovery.cjs +67 -0
  35. package/dist/recovery.cjs.map +1 -0
  36. package/dist/recovery.d.cts +38 -0
  37. package/dist/recovery.d.ts +38 -0
  38. package/dist/recovery.js +39 -0
  39. package/dist/recovery.js.map +1 -0
  40. package/dist/secret-request.cjs +271 -0
  41. package/dist/secret-request.cjs.map +1 -0
  42. package/dist/secret-request.d.cts +119 -0
  43. package/dist/secret-request.d.ts +119 -0
  44. package/dist/secret-request.js +222 -0
  45. package/dist/secret-request.js.map +1 -0
  46. package/dist/state.cjs +232 -40
  47. package/dist/state.cjs.map +1 -1
  48. package/dist/state.js +233 -40
  49. package/dist/state.js.map +1 -1
  50. package/generated/operations.json +1838 -89
  51. package/package.json +15 -5
@@ -300,6 +300,7 @@ var init_errors2 = __esm({
300
300
  "PROFILE_NOT_FOUND",
301
301
  "PROFILE_POSTURE_NOT_ALLOWED",
302
302
  "PROFILE_OWNER_OPT_IN_REQUIRED",
303
+ "PROFILE_MIGRATION_REQUIRED",
303
304
  "AUTH_REQUIRED",
304
305
  "PERMISSION_DENIED",
305
306
  "SESSION_NOT_FOUND",
@@ -4108,10 +4109,10 @@ function evaluateOperationAuthority(granted, required, resolveSpace2) {
4108
4109
  const canonicalRequired = canonicalizeOperationCapabilities(required, resolveSpace2);
4109
4110
  const missing = [];
4110
4111
  for (const requested of canonicalRequired) {
4111
- const covered = canonicalGranted.some(
4112
- (candidate) => candidate.service === requested.service && operationSpacesEqual(candidate.space, requested.space) && isCapabilitySubset([requested], [candidate]).subset
4112
+ const sameScope = canonicalGranted.filter(
4113
+ (candidate) => candidate.service === requested.service && operationSpacesEqual(candidate.space, requested.space)
4113
4114
  );
4114
- if (!covered) missing.push(requested);
4115
+ if (!isCapabilitySubset([requested], sameScope).subset) missing.push(requested);
4115
4116
  }
4116
4117
  return {
4117
4118
  satisfied: missing.length === 0,
@@ -4234,9 +4235,161 @@ var init_authority = __esm({
4234
4235
 
4235
4236
  // src/state.ts
4236
4237
  function processWideContext(key) {
4237
- var _a3;
4238
+ var _a4;
4238
4239
  const registry = globalThis;
4239
- return registry[_a3 = Symbol.for(key)] ?? (registry[_a3] = new import_node_async_hooks.AsyncLocalStorage());
4240
+ return registry[_a4 = Symbol.for(key)] ?? (registry[_a4] = new import_node_async_hooks.AsyncLocalStorage());
4241
+ }
4242
+ function processOrigin() {
4243
+ if (cachedProcessOrigin !== void 0) return cachedProcessOrigin;
4244
+ if (process.platform !== "linux") return cachedProcessOrigin = null;
4245
+ try {
4246
+ const bootId = (0, import_node_fs.readFileSync)("/proc/sys/kernel/random/boot_id", "utf8").trim();
4247
+ const pidNamespace = (0, import_node_fs.readlinkSync)("/proc/self/ns/pid");
4248
+ const processStat = (0, import_node_fs.readFileSync)("/proc/self/stat", "utf8");
4249
+ const closingParen = processStat.lastIndexOf(") ");
4250
+ const processStartTime = closingParen < 0 ? void 0 : processStat.slice(closingParen + 2).trim().split(/\s+/)[19];
4251
+ if (!/^[0-9a-f-]{36}$/.test(bootId) || !/^pid:\[[0-9]+\]$/.test(pidNamespace) || !processStartTime || !/^[0-9]+$/.test(processStartTime)) {
4252
+ return cachedProcessOrigin = null;
4253
+ }
4254
+ return cachedProcessOrigin = { bootId, pidNamespace, processStartTime };
4255
+ } catch {
4256
+ return cachedProcessOrigin = null;
4257
+ }
4258
+ }
4259
+ function processOwner(liveness) {
4260
+ return { instanceId: processInstanceId, ...processOrigin(), ...liveness ? { liveness: liveness.record } : {} };
4261
+ }
4262
+ function socketDirectory(name) {
4263
+ const match = LIVENESS_SOCKET.exec(name);
4264
+ return match ? `${match[1]}-${match[2]}` : null;
4265
+ }
4266
+ async function pinDirectory(directory) {
4267
+ const handle = await (0, import_promises.open)(directory, "r");
4268
+ try {
4269
+ const { dev, ino } = await handle.stat({ bigint: true });
4270
+ return {
4271
+ identity: `${dev.toString(16)}-${ino.toString(16)}`,
4272
+ path: (name) => `/proc/self/fd/${handle.fd}/${name}`,
4273
+ close: () => handle.close()
4274
+ };
4275
+ } catch (error) {
4276
+ await handle.close();
4277
+ throw error;
4278
+ }
4279
+ }
4280
+ async function bindLiveness(directory) {
4281
+ if (!processOrigin()) return null;
4282
+ let pinned;
4283
+ try {
4284
+ pinned = await pinDirectory(directory);
4285
+ } catch {
4286
+ return null;
4287
+ }
4288
+ const socket = `.live-${pinned.identity}-${(0, import_node_crypto.randomUUID)()}`;
4289
+ const server = (0, import_node_net.createServer)((connection) => connection.destroy());
4290
+ let bound = false;
4291
+ try {
4292
+ await new Promise((resolve2, reject) => {
4293
+ server.once("error", reject);
4294
+ server.listen({ path: pinned.path(`${socket}.new`), exclusive: true }, () => {
4295
+ server.off("error", reject);
4296
+ resolve2();
4297
+ });
4298
+ });
4299
+ bound = true;
4300
+ server.on("error", () => void 0);
4301
+ server.unref();
4302
+ await (0, import_promises.rename)(pinned.path(`${socket}.new`), pinned.path(socket));
4303
+ const stats = await (0, import_promises.lstat)(pinned.path(socket), { bigint: true });
4304
+ if (!stats.isSocket()) throw new Error(`${socket} is not a socket`);
4305
+ await waitForTestBarrier(TEST_LOCK_LIVENESS_BARRIER_DIR, (0, import_node_path.basename)(directory));
4306
+ return { record: { socket, dev: String(stats.dev), ino: String(stats.ino) }, path: (0, import_node_path.join)(directory, socket), server };
4307
+ } catch {
4308
+ if (bound) {
4309
+ server.close();
4310
+ for (const name of [`${socket}.new`, socket]) await (0, import_promises.unlink)(pinned.path(name)).catch(() => void 0);
4311
+ }
4312
+ return null;
4313
+ } finally {
4314
+ await pinned.close();
4315
+ }
4316
+ }
4317
+ async function ensureLiveness(acquisition) {
4318
+ if (acquisition.held === void 0) acquisition.held = await bindLiveness(acquisition.directory);
4319
+ return acquisition.held;
4320
+ }
4321
+ async function closeLiveness(held) {
4322
+ held.server.close();
4323
+ await (0, import_promises.unlink)(held.path).catch(() => void 0);
4324
+ }
4325
+ function parseLiveness(value) {
4326
+ if (typeof value !== "object" || value === null) return null;
4327
+ const { socket, dev, ino } = value;
4328
+ const name = typeof socket === "string" ? LIVENESS_SOCKET.exec(socket) : null;
4329
+ return name !== null && name[3] === void 0 && typeof dev === "string" && /^[0-9]+$/.test(dev) && typeof ino === "string" && /^[0-9]+$/.test(ino) ? { socket: name[0], dev, ino } : null;
4330
+ }
4331
+ async function controlIn(pinned, acquisition) {
4332
+ const own = await ensureLiveness(acquisition);
4333
+ if (!own || socketDirectory(own.record.socket) !== pinned.identity) return null;
4334
+ const stats = await (0, import_promises.lstat)(pinned.path(own.record.socket), { bigint: true }).catch(() => null);
4335
+ return stats?.isSocket() && String(stats.dev) === own.record.dev && String(stats.ino) === own.record.ino ? own : null;
4336
+ }
4337
+ async function tryConnect(path) {
4338
+ return new Promise((resolve2) => {
4339
+ const probe = (0, import_node_net.connect)({ path });
4340
+ const settle = (outcome) => {
4341
+ clearTimeout(timer);
4342
+ probe.destroy();
4343
+ resolve2(outcome);
4344
+ };
4345
+ const timer = setTimeout(() => settle("other"), LIVENESS_PROBE_TIMEOUT_MS);
4346
+ probe.once("connect", () => settle("connected"));
4347
+ probe.once("error", (error) => settle(isErrno(error, "ECONNREFUSED") ? "refused" : "other"));
4348
+ });
4349
+ }
4350
+ async function kernelRefuses(pinned, name, control) {
4351
+ if (process.versions.bun !== void 0) return false;
4352
+ return await tryConnect(pinned.path(name)) === "refused" && await tryConnect(pinned.path(control.record.socket)) === "connected";
4353
+ }
4354
+ async function probeLiveness(liveness, acquisition) {
4355
+ let pinned;
4356
+ try {
4357
+ pinned = await pinDirectory(acquisition.directory);
4358
+ } catch {
4359
+ return "unknown";
4360
+ }
4361
+ try {
4362
+ if (socketDirectory(liveness.socket) !== pinned.identity) return "unknown";
4363
+ const control = await controlIn(pinned, acquisition);
4364
+ if (!control) return "unknown";
4365
+ let stats;
4366
+ try {
4367
+ stats = await (0, import_promises.lstat)(pinned.path(liveness.socket), { bigint: true });
4368
+ } catch (error) {
4369
+ return isErrno(error, "ENOENT") ? "gone" : "unknown";
4370
+ }
4371
+ if (!stats.isSocket() || String(stats.dev) !== liveness.dev || String(stats.ino) !== liveness.ino) return "unknown";
4372
+ return await kernelRefuses(pinned, liveness.socket, control) ? "refused" : "unknown";
4373
+ } finally {
4374
+ await pinned.close();
4375
+ }
4376
+ }
4377
+ async function sweepLiveness(acquisition) {
4378
+ if (process.versions.bun !== void 0 || !acquisition.held) return;
4379
+ const pinned = await pinDirectory(acquisition.directory);
4380
+ try {
4381
+ const control = await controlIn(pinned, acquisition);
4382
+ if (!control) return;
4383
+ for (const name of await (0, import_promises.readdir)(pinned.path("."))) {
4384
+ if (name === control.record.socket || socketDirectory(name) !== pinned.identity) continue;
4385
+ const stats = await (0, import_promises.lstat)(pinned.path(name)).catch(() => null);
4386
+ if (stats?.isSocket() && await kernelRefuses(pinned, name, control)) {
4387
+ await (0, import_promises.unlink)(pinned.path(name)).catch(() => void 0);
4388
+ }
4389
+ }
4390
+ } finally {
4391
+ await pinned.close();
4392
+ }
4240
4393
  }
4241
4394
  function tinycloudHomePath() {
4242
4395
  const home = invocationStateRoot.getStore() ?? process.env.TC_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os.homedir)();
@@ -4413,7 +4566,18 @@ async function acquireProfileLock(profile, options) {
4413
4566
  if (path !== directory || !isErrno(error, "ENOENT")) throw error;
4414
4567
  });
4415
4568
  }
4416
- const { turn, firstWaitedTurn } = await acquireTurn(profile, turnDirectory, deadline);
4569
+ const liveness = { directory: turnDirectory, held: void 0 };
4570
+ const closeAcquisitionLiveness = async () => {
4571
+ if (liveness.held) await closeLiveness(liveness.held);
4572
+ };
4573
+ let taken;
4574
+ try {
4575
+ taken = await acquireTurn(profile, turnDirectory, deadline, liveness);
4576
+ } catch (error) {
4577
+ await closeAcquisitionLiveness();
4578
+ throw error;
4579
+ }
4580
+ const { turn, firstWaitedTurn } = taken;
4417
4581
  try {
4418
4582
  if (options.requireProfile && !await exists(profileConfigPath(profile))) {
4419
4583
  await (0, import_promises.rmdir)(directory).catch(() => void 0);
@@ -4423,7 +4587,7 @@ async function acquireProfileLock(profile, options) {
4423
4587
  const deletedWhileWaiting = typeof deletion?.slot === "number" && deletion.slot >= firstWaitedTurn;
4424
4588
  while (true) {
4425
4589
  const token = (0, import_node_crypto.randomUUID)();
4426
- if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true)) {
4590
+ if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true, liveness.held ?? null)) {
4427
4591
  return {
4428
4592
  deletedWhileWaiting,
4429
4593
  turnSlot: turn.slot,
@@ -4431,17 +4595,19 @@ async function acquireProfileLock(profile, options) {
4431
4595
  try {
4432
4596
  await releaseProfileLock(profile, lockPath, token);
4433
4597
  } finally {
4434
- await releaseTurn(profile, turn);
4598
+ await releaseTurn(profile, turn, liveness);
4599
+ await closeAcquisitionLiveness();
4435
4600
  }
4436
4601
  }
4437
4602
  };
4438
4603
  }
4439
4604
  await signalTestLockContention(profile);
4440
- if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;
4605
+ if (await recoverStaleLock(profile, lockPath, liveness, staleAfterMs)) continue;
4441
4606
  await waitOrTimeOut(profile, deadline);
4442
4607
  }
4443
4608
  } catch (error) {
4444
- await releaseTurn(profile, turn);
4609
+ await releaseTurn(profile, turn, liveness);
4610
+ await closeAcquisitionLiveness();
4445
4611
  throw error;
4446
4612
  }
4447
4613
  }
@@ -4454,7 +4620,7 @@ async function waitOrTimeOut(profile, deadline, detail) {
4454
4620
  checkDeadline(profile, deadline, detail);
4455
4621
  await sleep(Math.min(deadline.retryMs, deadline.timeoutMs - (Date.now() - deadline.startedAt)));
4456
4622
  }
4457
- async function acquireTurn(profile, directory, deadline) {
4623
+ async function acquireTurn(profile, directory, deadline, liveness) {
4458
4624
  let firstWaitedTurn;
4459
4625
  let problem;
4460
4626
  const damaged = (what) => `The turn lock ${directory} ${what}, so no process can take it. If no tc or MCP process is using profile "${profile}", remove that directory.`;
@@ -4479,13 +4645,13 @@ async function acquireTurn(profile, directory, deadline) {
4479
4645
  }
4480
4646
  firstWaitedTurn ?? (firstWaitedTurn = latest.done ? latest.slot + 1 : latest.slot);
4481
4647
  if (latest.held && latest.done) {
4482
- const turn = await takeTurn(profile, directory, latest);
4648
+ const turn = await takeTurn(profile, directory, latest, await ensureLiveness(liveness));
4483
4649
  if (turn !== null) return { turn, firstWaitedTurn };
4484
4650
  await waitOrTimeOut(profile, deadline);
4485
4651
  } else if (latest.done) {
4486
4652
  problem = damaged(`ends with turn ${latest.slot}, which was never granted`);
4487
4653
  await waitOrTimeOut(profile, deadline, problem);
4488
- } else if (latest.pid === process.pid ? relinquishedTurns.has(latest.token) : !isProcessAlive(latest.pid)) {
4654
+ } else if (latest.pid === process.pid && relinquishedTurns.has(latest.token) || !await isRecordedProcessAlive(latest, liveness)) {
4489
4655
  await waitForTestBarrier(TEST_LOCK_TURN_SETTLE_BARRIER_DIR, profile);
4490
4656
  await settleAbandonedTurn(directory, latest);
4491
4657
  relinquishedTurns.delete(latest.token);
@@ -4530,6 +4696,7 @@ async function readTurn(directory, slot) {
4530
4696
  const done = await exists((0, import_node_path.join)(path, `${owner.token}.done`));
4531
4697
  const held = await exists((0, import_node_path.join)(path, `${owner.token}.held`));
4532
4698
  return {
4699
+ ...owner,
4533
4700
  slot,
4534
4701
  token: owner.token,
4535
4702
  pid: owner.pid,
@@ -4543,11 +4710,11 @@ async function isFinishedTurn(directory, slot, token) {
4543
4710
  if (turn === "damaged") throw new Error(`Turn ${slot} of the turn lock ${directory} is unreadable.`);
4544
4711
  return turn !== null && turn.token === token && turn.held && turn.done;
4545
4712
  }
4546
- async function takeTurn(profile, directory, latest) {
4713
+ async function takeTurn(profile, directory, latest, liveness) {
4547
4714
  const token = (0, import_node_crypto.randomUUID)();
4548
4715
  const slot = latest.slot + 1;
4549
4716
  const path = (0, import_node_path.join)(directory, String(slot));
4550
- const owner = { pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token, after: latest.token };
4717
+ const owner = { pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token, after: latest.token, ...processOwner(liveness) };
4551
4718
  let published = false;
4552
4719
  try {
4553
4720
  published = await publishDirectory(
@@ -4601,7 +4768,7 @@ async function markTurn(path, token, kind) {
4601
4768
  }
4602
4769
  async function createTurnDirectory(directory) {
4603
4770
  const token = (0, import_node_crypto.randomUUID)();
4604
- const owner = { pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token, after: null };
4771
+ const owner = { pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token, after: null, ...processOwner(null) };
4605
4772
  await publishDirectory((0, import_node_path.join)((0, import_node_path.dirname)(directory), `.stage-${(0, import_node_path.basename)(directory)}-${process.pid}-${token}`), directory, {
4606
4773
  "0/owner.json": `${JSON.stringify(owner, null, 2)}
4607
4774
  `,
@@ -4625,31 +4792,39 @@ async function publishDirectory(stage, target, files, beforeRename) {
4625
4792
  await (0, import_promises.rm)(stage, { recursive: true, force: true }).catch(() => void 0);
4626
4793
  }
4627
4794
  }
4628
- async function releaseTurn(profile, turn) {
4795
+ async function releaseTurn(profile, turn, liveness) {
4629
4796
  try {
4630
4797
  await markTurn((0, import_node_path.join)(turn.directory, String(turn.slot)), turn.token, "done");
4631
4798
  } catch {
4632
4799
  settleOwnTurn(turn.directory, turn.slot, turn.token);
4633
4800
  return;
4634
4801
  }
4635
- await collectTurns(profile, turn).catch(() => void 0);
4802
+ await collectTurns(profile, turn, liveness).catch(() => void 0);
4636
4803
  }
4637
- function isAbandonedTurnEntry(name) {
4804
+ async function isAbandonedTurnEntry(directory, name, liveness) {
4638
4805
  if (name.startsWith(".trash-")) return true;
4639
4806
  const writer = /^\.(?:stage|deleted\.json)[-.]([0-9]+)[-.]/.exec(name)?.[1];
4640
- return writer !== void 0 && !isProcessAlive(Number(writer));
4807
+ if (writer === void 0) return false;
4808
+ if (Number(writer) !== process.pid) return !await isRecordedProcessAlive({ pid: Number(writer) }, liveness);
4809
+ return name.startsWith(".stage-") && await isStagedOwnerGone((0, import_node_path.join)(directory, name, "owner.json"), liveness);
4641
4810
  }
4642
- function isAbandonedTurnDirectoryStage(name) {
4811
+ async function isAbandonedTurnDirectoryStage(directory, name, liveness) {
4643
4812
  const writer = /^\.stage-.+-([0-9]+)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.exec(name)?.[1];
4644
- return writer !== void 0 && !isProcessAlive(Number(writer));
4813
+ if (writer === void 0) return false;
4814
+ if (Number(writer) !== process.pid) return !await isRecordedProcessAlive({ pid: Number(writer) }, liveness);
4815
+ return isStagedOwnerGone((0, import_node_path.join)(directory, name, "0", "owner.json"), liveness);
4645
4816
  }
4646
- async function collectTurns(profile, turn) {
4817
+ async function isStagedOwnerGone(recordPath, liveness) {
4818
+ const owner = await readJson(recordPath).catch(() => null);
4819
+ return typeof owner === "object" && owner !== null && owner.pid === process.pid && !await isRecordedProcessAlive(owner, liveness);
4820
+ }
4821
+ async function collectTurns(profile, turn, liveness) {
4647
4822
  for (const [directory, abandoned] of [
4648
4823
  [turn.directory, isAbandonedTurnEntry],
4649
4824
  [(0, import_node_path.dirname)(turn.directory), isAbandonedTurnDirectoryStage]
4650
4825
  ]) {
4651
- for (const name of (await (0, import_promises.readdir)(directory)).filter(abandoned)) {
4652
- await (0, import_promises.rm)((0, import_node_path.join)(directory, name), { recursive: true, force: true }).catch(() => void 0);
4826
+ for (const name of await (0, import_promises.readdir)(directory)) {
4827
+ if (await abandoned(directory, name, liveness)) await (0, import_promises.rm)((0, import_node_path.join)(directory, name), { recursive: true, force: true }).catch(() => void 0);
4653
4828
  }
4654
4829
  }
4655
4830
  const older = (await (0, import_promises.readdir)(turn.directory)).filter((name) => TURN.test(name)).map(Number).filter((slot) => slot < turn.slot).sort((left, right) => left - right);
@@ -4667,8 +4842,9 @@ async function collectTurns(profile, turn) {
4667
4842
  await (0, import_promises.rm)(trash, { recursive: true, force: true }).catch(() => void 0);
4668
4843
  }
4669
4844
  }
4845
+ await sweepLiveness(liveness);
4670
4846
  }
4671
- async function publishProfileLock(profile, lockPath, token, requireProfile) {
4847
+ async function publishProfileLock(profile, lockPath, token, requireProfile, liveness) {
4672
4848
  try {
4673
4849
  await (0, import_promises.mkdir)(lockPath, { mode: PRIVATE_DIR_MODE });
4674
4850
  } catch (error) {
@@ -4681,7 +4857,7 @@ async function publishProfileLock(profile, lockPath, token, requireProfile) {
4681
4857
  await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);
4682
4858
  const ownerPath = (0, import_node_path.join)(lockPath, "owner.json");
4683
4859
  const staged = (0, import_node_path.join)((0, import_node_path.dirname)(lockPath), `.lock-owner-${token}.tmp`);
4684
- const owner = `${JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token }, null, 2)}
4860
+ const owner = `${JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), token, ...processOwner(liveness) }, null, 2)}
4685
4861
  `;
4686
4862
  try {
4687
4863
  await (0, import_promises.writeFile)(staged, owner, { encoding: "utf8", mode: PRIVATE_FILE_MODE, flag: "wx" });
@@ -4725,7 +4901,7 @@ async function signalTestLockContention(profile) {
4725
4901
  await (0, import_promises.writeFile)(signalPath, `${profile}
4726
4902
  `, { encoding: "utf8", flag: "wx" }).catch(() => void 0);
4727
4903
  }
4728
- async function recoverOwnerlessLock(profile, lockPath, staleAfterMs) {
4904
+ async function recoverOwnerlessLock(profile, lockPath, liveness, staleAfterMs) {
4729
4905
  let aged;
4730
4906
  try {
4731
4907
  const { mtimeMs } = await (0, import_promises.stat)(lockPath);
@@ -4742,7 +4918,7 @@ async function recoverOwnerlessLock(profile, lockPath, staleAfterMs) {
4742
4918
  if (!onlyOrphanedClaims && (!aged || recoveryFenced(checkedAt, staleAfterMs))) return false;
4743
4919
  for (const name of entries.filter((entry) => RECOVERY_CLAIM.test(entry))) {
4744
4920
  const claimPath = (0, import_node_path.join)(lockPath, name);
4745
- if (await hasLiveRecoveryOwner(claimPath)) {
4921
+ if (await hasLiveRecoveryOwner(claimPath, liveness)) {
4746
4922
  if (!await exists((0, import_node_path.join)(lockPath, "owner.json"))) await (0, import_promises.rename)(claimPath, (0, import_node_path.join)(lockPath, "owner.json"));
4747
4923
  return false;
4748
4924
  }
@@ -4758,7 +4934,7 @@ async function recoverOwnerlessLock(profile, lockPath, staleAfterMs) {
4758
4934
  await removeAgedOwnerFiles(lockPath, staleAfterMs);
4759
4935
  return true;
4760
4936
  }
4761
- async function hasLiveRecoveryOwner(claimPath) {
4937
+ async function hasLiveRecoveryOwner(claimPath, liveness) {
4762
4938
  let contents;
4763
4939
  try {
4764
4940
  contents = await (0, import_promises.readFile)(claimPath, "utf8");
@@ -4772,8 +4948,8 @@ async function hasLiveRecoveryOwner(claimPath) {
4772
4948
  } catch {
4773
4949
  return false;
4774
4950
  }
4775
- const pid = typeof owner === "object" && owner !== null && "pid" in owner ? owner.pid : null;
4776
- return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && isProcessAlive(pid);
4951
+ const record = typeof owner === "object" && owner !== null && "pid" in owner ? owner : null;
4952
+ return typeof record?.pid === "number" && Number.isInteger(record.pid) && record.pid > 0 && await isRecordedProcessAlive(record, liveness);
4777
4953
  }
4778
4954
  function recoveryFenced(since, staleAfterMs) {
4779
4955
  return performance.now() - since >= staleAfterMs / 2;
@@ -4787,11 +4963,11 @@ async function removeAgedOwnerFiles(lockPath, staleAfterMs) {
4787
4963
  if (aged) await (0, import_promises.rm)(path, { force: true }).catch(() => void 0);
4788
4964
  }
4789
4965
  }
4790
- async function recoverStaleLock(profile, lockPath, staleAfterMs) {
4966
+ async function recoverStaleLock(profile, lockPath, liveness, staleAfterMs) {
4791
4967
  const ownerPath = (0, import_node_path.join)(lockPath, "owner.json");
4792
4968
  const observed = await (0, import_promises.readFile)(ownerPath, "utf8").catch(() => null);
4793
- if (observed === null) return recoverOwnerlessLock(profile, lockPath, staleAfterMs);
4794
- if (!await isAbandonedOwner(observed, ownerPath, staleAfterMs)) return false;
4969
+ if (observed === null) return recoverOwnerlessLock(profile, lockPath, liveness, staleAfterMs);
4970
+ if (!await isAbandonedOwner(observed, ownerPath, liveness, staleAfterMs)) return false;
4795
4971
  await waitForTestBarrier(TEST_LOCK_RECOVERY_BARRIER_DIR, profile);
4796
4972
  const claimPath = (0, import_node_path.join)(lockPath, `.recover-${(0, import_node_crypto.randomUUID)()}.json`);
4797
4973
  let moved = false;
@@ -4827,7 +5003,7 @@ async function recoverStaleLock(profile, lockPath, staleAfterMs) {
4827
5003
  await removeAgedOwnerFiles(lockPath, staleAfterMs);
4828
5004
  return true;
4829
5005
  }
4830
- async function isAbandonedOwner(observed, ownerPath, staleAfterMs) {
5006
+ async function isAbandonedOwner(observed, ownerPath, liveness, staleAfterMs) {
4831
5007
  let owner;
4832
5008
  try {
4833
5009
  owner = JSON.parse(observed);
@@ -4835,7 +5011,7 @@ async function isAbandonedOwner(observed, ownerPath, staleAfterMs) {
4835
5011
  owner = null;
4836
5012
  }
4837
5013
  if (typeof owner === "object" && owner !== null && "pid" in owner) {
4838
- return isStaleOwner(owner, staleAfterMs);
5014
+ return isStaleOwner(owner, liveness, staleAfterMs);
4839
5015
  }
4840
5016
  const modifiedAt = await (0, import_promises.stat)(ownerPath).then(({ mtimeMs }) => mtimeMs, () => Number.NaN);
4841
5017
  return Date.now() - modifiedAt >= staleAfterMs;
@@ -4857,14 +5033,14 @@ async function waitForTestBarrier(environmentName, profile) {
4857
5033
  }
4858
5034
  }
4859
5035
  }
4860
- function isStaleOwner(owner, staleAfterMs) {
5036
+ async function isStaleOwner(owner, liveness, staleAfterMs) {
4861
5037
  const now = Date.now();
4862
5038
  const createdAt = typeof owner?.createdAt === "string" ? Date.parse(owner.createdAt) : Number.NaN;
4863
5039
  if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;
4864
5040
  if (typeof owner?.pid !== "number" || !Number.isInteger(owner.pid) || owner.pid <= 0) {
4865
5041
  return false;
4866
5042
  }
4867
- return !isProcessAlive(owner.pid);
5043
+ return !await isRecordedProcessAlive(owner, liveness);
4868
5044
  }
4869
5045
  async function exists(path) {
4870
5046
  try {
@@ -4875,13 +5051,22 @@ async function exists(path) {
4875
5051
  throw error;
4876
5052
  }
4877
5053
  }
4878
- function isProcessAlive(pid) {
5054
+ async function isRecordedProcessAlive(owner, acquisition) {
5055
+ const pid = owner.pid;
5056
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return true;
4879
5057
  try {
4880
5058
  process.kill(pid, 0);
4881
- return true;
4882
5059
  } catch (error) {
4883
5060
  return !isErrno(error, "ESRCH");
4884
5061
  }
5062
+ if (pid !== process.pid || typeof owner.instanceId !== "string" || !/^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/.test(owner.instanceId) || owner.instanceId === processInstanceId) return true;
5063
+ const origin = processOrigin();
5064
+ if (!origin || owner.bootId !== origin.bootId || typeof owner.pidNamespace !== "string" || !/^pid:\[[0-9]+\]$/.test(owner.pidNamespace)) return true;
5065
+ if (owner.pidNamespace === origin.pidNamespace) {
5066
+ return typeof owner.processStartTime !== "string" || !/^[0-9]+$/.test(owner.processStartTime) || owner.processStartTime === origin.processStartTime;
5067
+ }
5068
+ const liveness = parseLiveness(owner.liveness);
5069
+ return liveness === null || await probeLiveness(liveness, acquisition) === "unknown";
4885
5070
  }
4886
5071
  function validateProfileName(profile) {
4887
5072
  if (!profile || profile === "." || profile === ".." || profile.includes("/") || profile.includes("\\") || profile.includes("\0")) {
@@ -4895,13 +5080,15 @@ function isErrno(error, code) {
4895
5080
  function sleep(milliseconds) {
4896
5081
  return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
4897
5082
  }
4898
- var import_node_crypto, import_node_async_hooks, import_promises, import_node_os, import_node_path, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, PRIVATE_FILE_MODE, PRIVATE_DIR_MODE, TEST_LOCK_RECOVERY_BARRIER_DIR, TEST_LOCK_OWNERLESS_BARRIER_DIR, TEST_LOCK_PUBLISH_BARRIER_DIR, TEST_LOCK_RELEASE_BARRIER_DIR, TEST_LOCK_CLAIM_BARRIER_DIR, TEST_LOCK_CLAIMED_BARRIER_DIR, TEST_LOCK_VERIFIED_BARRIER_DIR, TEST_LOCK_MOVED_BARRIER_DIR, TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, TEST_LOCK_TURN_SETTLE_BARRIER_DIR, TEST_LOCK_TURN_COLLECT_BARRIER_DIR, invocationStateRoot, heldProfileLocks, ProfileLockTimeoutError, ProfileDeletedError, TURN, _a2, relinquishedTurns, MARKER_RETRY_DELAYS_MS, SETTLE_RETRY_MS, SETTLE_RETRIES, NO_HARD_LINK_ERRORS, ABANDONED_CLAIM, RECOVERY_CLAIM, STAGED_OWNER;
5083
+ var import_node_crypto, import_node_async_hooks, import_node_fs, import_promises, import_node_net, import_node_os, import_node_path, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, PRIVATE_FILE_MODE, PRIVATE_DIR_MODE, TEST_LOCK_RECOVERY_BARRIER_DIR, TEST_LOCK_OWNERLESS_BARRIER_DIR, TEST_LOCK_PUBLISH_BARRIER_DIR, TEST_LOCK_RELEASE_BARRIER_DIR, TEST_LOCK_CLAIM_BARRIER_DIR, TEST_LOCK_CLAIMED_BARRIER_DIR, TEST_LOCK_VERIFIED_BARRIER_DIR, TEST_LOCK_MOVED_BARRIER_DIR, TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, TEST_LOCK_TURN_SETTLE_BARRIER_DIR, TEST_LOCK_TURN_COLLECT_BARRIER_DIR, TEST_LOCK_LIVENESS_BARRIER_DIR, invocationStateRoot, heldProfileLocks, _a2, processInstanceId, cachedProcessOrigin, LIVENESS_SOCKET, LIVENESS_PROBE_TIMEOUT_MS, ProfileLockTimeoutError, ProfileDeletedError, TURN, _a3, relinquishedTurns, MARKER_RETRY_DELAYS_MS, SETTLE_RETRY_MS, SETTLE_RETRIES, NO_HARD_LINK_ERRORS, ABANDONED_CLAIM, RECOVERY_CLAIM, STAGED_OWNER;
4899
5084
  var init_state = __esm({
4900
5085
  "src/state.ts"() {
4901
5086
  "use strict";
4902
5087
  import_node_crypto = require("crypto");
4903
5088
  import_node_async_hooks = require("async_hooks");
5089
+ import_node_fs = require("fs");
4904
5090
  import_promises = require("fs/promises");
5091
+ import_node_net = require("net");
4905
5092
  import_node_os = require("os");
4906
5093
  import_node_path = require("path");
4907
5094
  DEFAULT_PROFILE = "default";
@@ -4922,8 +5109,12 @@ var init_state = __esm({
4922
5109
  TEST_LOCK_TURN_PUBLISH_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_TURN_PUBLISH_BARRIER_DIR";
4923
5110
  TEST_LOCK_TURN_SETTLE_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_TURN_SETTLE_BARRIER_DIR";
4924
5111
  TEST_LOCK_TURN_COLLECT_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_TURN_COLLECT_BARRIER_DIR";
5112
+ TEST_LOCK_LIVENESS_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_LIVENESS_BARRIER_DIR";
4925
5113
  invocationStateRoot = processWideContext("tinycloud.operations.invocationStateRoot.v1");
4926
5114
  heldProfileLocks = processWideContext("tinycloud.operations.heldProfileLocks.v2");
5115
+ processInstanceId = globalThis[_a2 = /* @__PURE__ */ Symbol.for("tinycloud.operations.profileLockInstance.v1")] ?? (globalThis[_a2] = (0, import_node_crypto.randomUUID)());
5116
+ LIVENESS_SOCKET = /^\.live-([0-9a-f]+)-([0-9a-f]+)-[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}(\.new)?$/;
5117
+ LIVENESS_PROBE_TIMEOUT_MS = 1e3;
4927
5118
  ProfileLockTimeoutError = class extends Error {
4928
5119
  /** `detail` names the cause when it is not simply another process holding the lock. */
4929
5120
  constructor(profile, timeoutMs, detail) {
@@ -4940,7 +5131,7 @@ var init_state = __esm({
4940
5131
  }
4941
5132
  };
4942
5133
  TURN = /^(?:0|[1-9][0-9]*)$/;
4943
- relinquishedTurns = globalThis[_a2 = /* @__PURE__ */ Symbol.for("tinycloud.operations.relinquishedProfileTurns.v1")] ?? (globalThis[_a2] = /* @__PURE__ */ new Set());
5134
+ relinquishedTurns = globalThis[_a3 = /* @__PURE__ */ Symbol.for("tinycloud.operations.relinquishedProfileTurns.v1")] ?? (globalThis[_a3] = /* @__PURE__ */ new Set());
4944
5135
  MARKER_RETRY_DELAYS_MS = [10, 50, 250];
4945
5136
  SETTLE_RETRY_MS = 1e3;
4946
5137
  SETTLE_RETRIES = 300;
@@ -5232,6 +5423,45 @@ var init_artifacts = __esm({
5232
5423
  }
5233
5424
  });
5234
5425
 
5426
+ // src/recovery.ts
5427
+ function signInRecovery(profile, posture) {
5428
+ if (!SAFE_PROFILE_TOKEN.test(profile)) return { action: "sign_in" };
5429
+ const template = posture === "local-owner-key" ? SIGN_IN_TEMPLATES.local : posture === "delegate-session" ? SIGN_IN_TEMPLATES.scoped : SIGN_IN_TEMPLATES.openkey;
5430
+ return { action: "sign_in", command: template(profile) };
5431
+ }
5432
+ function migrationRecovery(profile) {
5433
+ return SAFE_PROFILE_TOKEN.test(profile) ? { action: "complete_migration", command: migrationCommand(profile), operationId: MIGRATION_OPERATION_ID } : { action: "complete_migration", operationId: MIGRATION_OPERATION_ID };
5434
+ }
5435
+ function isSafeRecovery(value, action) {
5436
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
5437
+ const record = value;
5438
+ if (record.action !== action) return false;
5439
+ const keys = Object.keys(record);
5440
+ const allowed = action === "sign_in" ? ["action", "command"] : ["action", "command", "operationId"];
5441
+ if (!keys.every((key) => allowed.includes(key))) return false;
5442
+ if (action === "complete_migration" && record.operationId !== MIGRATION_OPERATION_ID) return false;
5443
+ if (!("command" in record)) return true;
5444
+ const command = record.command;
5445
+ if (typeof command !== "string") return false;
5446
+ const profile = /^tc --profile (\S+) /.exec(command)?.[1];
5447
+ if (profile === void 0 || !SAFE_PROFILE_TOKEN.test(profile)) return false;
5448
+ return action === "sign_in" ? Object.values(SIGN_IN_TEMPLATES).some((template) => template(profile) === command) : migrationCommand(profile) === command;
5449
+ }
5450
+ var SAFE_PROFILE_TOKEN, MIGRATION_OPERATION_ID, SIGN_IN_TEMPLATES, migrationCommand;
5451
+ var init_recovery = __esm({
5452
+ "src/recovery.ts"() {
5453
+ "use strict";
5454
+ SAFE_PROFILE_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
5455
+ MIGRATION_OPERATION_ID = "tinycloud.auth.capabilities";
5456
+ SIGN_IN_TEMPLATES = {
5457
+ local: (profile) => `tc --profile ${profile} auth login --method local`,
5458
+ openkey: (profile) => `tc --profile ${profile} auth login --method openkey`,
5459
+ scoped: (profile) => `tc --profile ${profile} auth login --method openkey --paste --manifest <manifest.json>`
5460
+ };
5461
+ migrationCommand = (profile) => `tc --profile ${profile} auth caps`;
5462
+ }
5463
+ });
5464
+
5235
5465
  // src/secrets.ts
5236
5466
  function normalizeSecretsGetInput(input) {
5237
5467
  const reference = resolveSecretReference(input);
@@ -5318,15 +5548,6 @@ function canonicalizeSpaceUri(space) {
5318
5548
  }
5319
5549
  return space;
5320
5550
  }
5321
- function buildSecretSetupUrl(reference) {
5322
- const parameters = [
5323
- ["secret", reference.name],
5324
- ...reference.scope === void 0 ? [] : [["scope", reference.scope]],
5325
- ["space", reference.space]
5326
- ];
5327
- const query = parameters.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
5328
- return `${SECRET_MANAGER_BASE_URL}/app?${query}`;
5329
- }
5330
5551
  function canonicalizeSecretScope(scope) {
5331
5552
  if (scope === void 0 || scope === "") return void 0;
5332
5553
  const trimmed = scope.trim();
@@ -5354,14 +5575,13 @@ function invalidSecretInput() {
5354
5575
  operationError("INPUT_INVALID", "The secret reference is invalid.")
5355
5576
  );
5356
5577
  }
5357
- var nodeSdk, DEFAULT_SECRETS_SPACE, SECRET_MANAGER_BASE_URL, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
5578
+ var nodeSdk, DEFAULT_SECRETS_SPACE, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
5358
5579
  var init_secrets = __esm({
5359
5580
  "src/secrets.ts"() {
5360
5581
  "use strict";
5361
5582
  nodeSdk = __toESM(require("@tinycloud/node-sdk"), 1);
5362
5583
  init_errors2();
5363
5584
  DEFAULT_SECRETS_SPACE = "secrets";
5364
- SECRET_MANAGER_BASE_URL = "https://secrets.tinycloud.xyz";
5365
5585
  SECRET_NAME_RE2 = /^[A-Z][A-Z0-9_]*$/;
5366
5586
  RESERVED_SECRET_SCOPES = /* @__PURE__ */ new Set(["default", "global"]);
5367
5587
  }
@@ -5846,6 +6066,33 @@ var init_auth = __esm({
5846
6066
  }
5847
6067
  });
5848
6068
 
6069
+ // src/kv-failure.ts
6070
+ function kvRefusal(error) {
6071
+ const verdict = (0, import_sdk_services2.authorizationVerdictOf)(error);
6072
+ if (verdict === "unauthenticated") {
6073
+ return (0, import_sdk_services2.validatedCapabilityOf)(error) === void 0 ? "auth_required" : "permission_denied";
6074
+ }
6075
+ return verdict === "forbidden" ? "permission_denied" : void 0;
6076
+ }
6077
+ function authorizationFailure(error, action) {
6078
+ switch (kvRefusal(error)) {
6079
+ case "auth_required":
6080
+ return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
6081
+ case "permission_denied":
6082
+ return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
6083
+ case void 0:
6084
+ return void 0;
6085
+ }
6086
+ }
6087
+ var import_sdk_services2;
6088
+ var init_kv_failure = __esm({
6089
+ "src/kv-failure.ts"() {
6090
+ "use strict";
6091
+ import_sdk_services2 = require("@tinycloud/sdk-services");
6092
+ init_errors2();
6093
+ }
6094
+ });
6095
+
5849
6096
  // src/operations/exploration.ts
5850
6097
  async function planAccountRegistryRead(context, path) {
5851
6098
  return [{
@@ -6213,19 +6460,6 @@ function caughtKvFailure(error, action) {
6213
6460
  if (error instanceof OperationInvocationError) return { status: "error", error: error.operationError };
6214
6461
  return authorizationFailure(error, action) ?? nodeFailure(action);
6215
6462
  }
6216
- function authorizationFailure(error, action) {
6217
- const verdict = (0, import_sdk_services2.authorizationVerdictOf)(error);
6218
- if (verdict === "unauthenticated") {
6219
- if ((0, import_sdk_services2.validatedCapabilityOf)(error) !== void 0) {
6220
- return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
6221
- }
6222
- return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
6223
- }
6224
- if (verdict === "forbidden") {
6225
- return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
6226
- }
6227
- return void 0;
6228
- }
6229
6463
  function accountRegistryOutput(output) {
6230
6464
  if (Buffer.byteLength(JSON.stringify(output), "utf8") > MAX_ACCOUNT_REGISTRY_BYTES) {
6231
6465
  return {
@@ -6274,11 +6508,11 @@ function nodeFailure(action) {
6274
6508
  )
6275
6509
  };
6276
6510
  }
6277
- var import_sdk_services2, import_zod4, MAX_KV_VALUE_BYTES, DEFAULT_KV_LIST_LIMIT, MAX_ACCOUNT_REGISTRY_RECORDS, MAX_ACCOUNT_REGISTRY_BYTES, EmptyInputSchema2, SpaceSchema, PrefixSchema, KeySchema, LimitSchema, StrongEtagSchema, ContentTypeSchema, JsonNumberSchema, JsonValueSchema, KvMetadataSchema, AccountSpaceSchema, AccountSpacesOutputSchema, AccountApplicationSchema, AccountApplicationsOutputSchema, KvListInputSchema, KvListOutputSchema, KvGetInputSchema, KvGetOutputSchema, KvPutContentSchema, KvPutCommonSchema, KvPutInputSchema, KvPutOutputSchema, KvHeadInputSchema, KvHeadOutputSchema, KvDeleteInputSchema, KvDeleteOutputSchema, AUTHENTICATED_POSTURES, EXPLORATION_EXPOSURE, ACCOUNT_SPACES_SENSITIVITY, ACCOUNT_APPLICATIONS_SENSITIVITY, KV_LIST_SENSITIVITY, KV_GET_SENSITIVITY, KV_PUT_SENSITIVITY, KV_KEY_SENSITIVITY, explorationOperationDefinitions;
6511
+ var import_zod4, MAX_KV_VALUE_BYTES, DEFAULT_KV_LIST_LIMIT, MAX_ACCOUNT_REGISTRY_RECORDS, MAX_ACCOUNT_REGISTRY_BYTES, EmptyInputSchema2, SpaceSchema, PrefixSchema, KeySchema, LimitSchema, StrongEtagSchema, ContentTypeSchema, JsonNumberSchema, JsonValueSchema, KvMetadataSchema, AccountSpaceSchema, AccountSpacesOutputSchema, AccountApplicationSchema, AccountApplicationsOutputSchema, KvListInputSchema, KvListOutputSchema, KvGetInputSchema, KvGetOutputSchema, KvPutContentSchema, KvPutCommonSchema, KvPutInputSchema, KvPutOutputSchema, KvHeadInputSchema, KvHeadOutputSchema, KvDeleteInputSchema, KvDeleteOutputSchema, AUTHENTICATED_POSTURES, EXPLORATION_EXPOSURE, ACCOUNT_SPACES_SENSITIVITY, ACCOUNT_APPLICATIONS_SENSITIVITY, KV_LIST_SENSITIVITY, KV_GET_SENSITIVITY, KV_PUT_SENSITIVITY, KV_KEY_SENSITIVITY, explorationOperationDefinitions;
6278
6512
  var init_exploration = __esm({
6279
6513
  "src/operations/exploration.ts"() {
6280
6514
  "use strict";
6281
- import_sdk_services2 = require("@tinycloud/sdk-services");
6515
+ init_kv_failure();
6282
6516
  import_zod4 = require("zod");
6283
6517
  init_errors2();
6284
6518
  init_secrets();
@@ -6580,6 +6814,191 @@ var init_exploration = __esm({
6580
6814
  }
6581
6815
  });
6582
6816
 
6817
+ // src/secret-request.ts
6818
+ function canonicalSecretRequestOwner(did) {
6819
+ const match = typeof did === "string" ? OWNER_RE.exec(did) : null;
6820
+ if (match === null || !Number.isSafeInteger(Number(match[1]))) throw invalidOwner();
6821
+ const address = match[2];
6822
+ let checksummed;
6823
+ try {
6824
+ checksummed = nodeSdk3.canonicalizeAddress(address);
6825
+ } catch {
6826
+ throw invalidOwner();
6827
+ }
6828
+ if (/[A-F]/.test(address.slice(2)) && address !== checksummed) throw invalidOwner();
6829
+ return `did:pkh:eip155:${match[1]}:${checksummed}`;
6830
+ }
6831
+ function buildSecretRequestUrl(input) {
6832
+ const owner = canonicalSecretRequestOwner(input.owner);
6833
+ const names = validSecretNames(input.names);
6834
+ if (input.scope !== void 0) assertSecretRequestScope(input.scope);
6835
+ const requester = input.requester;
6836
+ if (requester !== void 0 && (typeof requester !== "string" || requester.length === 0 || requester.length > MAX_REQUESTER_LENGTH || CONTROL_OR_FORMAT.test(requester))) {
6837
+ throw new SecretRequestInputError("INVALID_REQUESTER", "The requester label must be 1-64 characters with no control or format characters.");
6838
+ }
6839
+ if (input.returnTo !== void 0 && !isAllowedReturnTo(input.returnTo)) {
6840
+ throw new SecretRequestInputError("INVALID_RETURN_TO", "The return address is not an allowed Secret Manager callback.");
6841
+ }
6842
+ if (input.state !== void 0 && !STATE_RE.test(input.state)) {
6843
+ throw new SecretRequestInputError("INVALID_STATE", "The state must be 1-128 characters of A-Z, a-z, 0-9, '.', '_', '~' or '-'.");
6844
+ }
6845
+ const parameters = [
6846
+ ["owner", owner],
6847
+ ...names.map((name) => ["name", name]),
6848
+ ...input.scope === void 0 ? [] : [["scope", input.scope]],
6849
+ ...input.requester === void 0 ? [] : [["requester", input.requester]],
6850
+ ...input.returnTo === void 0 ? [] : [["return_to", input.returnTo]],
6851
+ ...input.state === void 0 ? [] : [["state", input.state]],
6852
+ ["v", "1"]
6853
+ ];
6854
+ const url = `${SECRET_MANAGER_ORIGIN}/request?${parameters.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}`;
6855
+ if (url.length > SECRET_REQUEST_MAX_URL_LENGTH) {
6856
+ throw new SecretRequestInputError("REQUEST_LINK_TOO_LONG", `The request link is longer than ${SECRET_REQUEST_MAX_URL_LENGTH} characters.`);
6857
+ }
6858
+ return { url, owner, names, ...input.scope === void 0 ? {} : { scope: input.scope } };
6859
+ }
6860
+ function secretSetupFor(link2) {
6861
+ return {
6862
+ url: link2.url,
6863
+ names: [...link2.names],
6864
+ ...link2.scope === void 0 ? {} : { scope: link2.scope },
6865
+ owner: link2.owner,
6866
+ action: "add_secret"
6867
+ };
6868
+ }
6869
+ function resolveSecretsOwner(profile, requestedOwner) {
6870
+ const resolved = profile.local ? ownerOrUndefined(profile.principalDid) : ownerOrUndefined(profile.ownerDid) ?? (profile.posture === "delegate-session" ? ownerOfSpace(profile.space) : void 0) ?? ownerOrUndefined(profile.principalDid);
6871
+ const requested = requestedOwner === void 0 ? void 0 : ownerOrUndefined(requestedOwner);
6872
+ if (resolved !== void 0) {
6873
+ if (requestedOwner !== void 0 && requested !== resolved) {
6874
+ return {
6875
+ ok: false,
6876
+ code: "OWNER_MISMATCH",
6877
+ message: "The requested owner is not the owner of this identity's secrets."
6878
+ };
6879
+ }
6880
+ return { ok: true, owner: resolved, local: profile.local };
6881
+ }
6882
+ if (requested !== void 0 && !profile.local) return { ok: true, owner: requested, local: false };
6883
+ return {
6884
+ ok: false,
6885
+ code: "OWNER_DID_UNKNOWN",
6886
+ message: "The owner of this identity's secrets is unknown. Pass the owner's did:pkh with --owner."
6887
+ };
6888
+ }
6889
+ function missingSecretRecovery(input) {
6890
+ if (input.local) {
6891
+ return { kind: "put_locally", hint: `Store it with: ${putCommands(input.names, input.scope)}` };
6892
+ }
6893
+ if (input.owner !== void 0) {
6894
+ try {
6895
+ return {
6896
+ kind: "add_secret",
6897
+ setup: secretSetupFor(buildSecretRequestUrl({
6898
+ owner: input.owner,
6899
+ names: input.names,
6900
+ ...input.scope === void 0 ? {} : { scope: input.scope }
6901
+ }))
6902
+ };
6903
+ } catch {
6904
+ }
6905
+ }
6906
+ return {
6907
+ kind: "not_requestable",
6908
+ hint: `No Secret Manager request link can express this secret. The owner can store it with: ${putCommands(input.names, input.scope)}`
6909
+ };
6910
+ }
6911
+ function isValidSecretRequestScope(scope) {
6912
+ return scope.length <= MAX_SCOPE_LENGTH && SCOPE_RE.test(scope) && !RESERVED_SCOPES.includes(scope);
6913
+ }
6914
+ function isValidSecretName(name) {
6915
+ return name.length <= MAX_NAME_LENGTH && NAME_RE.test(name);
6916
+ }
6917
+ function validSecretNames(names) {
6918
+ for (const name of names) assertSecretName(name);
6919
+ const unique = dedupe(names);
6920
+ if (unique.length === 0) {
6921
+ throw new SecretRequestInputError("INVALID_SECRET_NAME", "At least one secret name is required.");
6922
+ }
6923
+ if (unique.length > SECRET_REQUEST_MAX_NAMES) {
6924
+ throw new SecretRequestInputError("TOO_MANY_SECRET_NAMES", `At most ${SECRET_REQUEST_MAX_NAMES} secret names can be requested at once.`);
6925
+ }
6926
+ return unique;
6927
+ }
6928
+ function assertSecretName(name) {
6929
+ if (typeof name !== "string" || !isValidSecretName(name)) {
6930
+ throw new SecretRequestInputError("INVALID_SECRET_NAME", "Secret names are 1-64 characters of A-Z, 0-9 and '_', starting with a letter.");
6931
+ }
6932
+ }
6933
+ function assertSecretRequestScope(scope) {
6934
+ if (typeof scope !== "string" || !isValidSecretRequestScope(scope)) {
6935
+ throw new SecretRequestInputError("INVALID_SECRET_SCOPE", "The scope must be a canonical scope of at most 64 characters, not default, global or scoped.");
6936
+ }
6937
+ }
6938
+ function isAllowedReturnTo(returnTo) {
6939
+ if (typeof returnTo !== "string" || returnTo.length > MAX_RETURN_TO_LENGTH) return false;
6940
+ let url;
6941
+ try {
6942
+ url = new URL(returnTo);
6943
+ } catch {
6944
+ return false;
6945
+ }
6946
+ if (url.username !== "" || url.password !== "") return false;
6947
+ return RETURN_TO_ORIGINS.includes(url.origin) || RETURN_TO_PREVIEW.test(url.origin);
6948
+ }
6949
+ function ownerOrUndefined(did) {
6950
+ if (did === void 0) return void 0;
6951
+ try {
6952
+ return canonicalSecretRequestOwner(did);
6953
+ } catch {
6954
+ return void 0;
6955
+ }
6956
+ }
6957
+ function ownerOfSpace(space) {
6958
+ const match = space === void 0 ? null : PKH_SPACE_RE.exec(space);
6959
+ if (match === null) return void 0;
6960
+ return ownerOrUndefined(`did:pkh:eip155:${match[1]}:${match[2].toLowerCase()}`);
6961
+ }
6962
+ function putCommands(names, scope) {
6963
+ return dedupe(names).map((name) => `tc secrets put ${name}${scope === void 0 ? "" : ` --scope ${scope}`} --stdin`).join("; ");
6964
+ }
6965
+ function dedupe(names) {
6966
+ return [...new Set(names)];
6967
+ }
6968
+ function invalidOwner() {
6969
+ return new SecretRequestInputError("INVALID_OWNER", "The owner must be a did:pkh:eip155 account with a valid EIP-55 address.");
6970
+ }
6971
+ var nodeSdk3, SECRET_MANAGER_ORIGIN, SECRET_REQUEST_MAX_NAMES, SECRET_REQUEST_MAX_URL_LENGTH, OWNER_RE, NAME_RE, MAX_NAME_LENGTH, SCOPE_RE, MAX_SCOPE_LENGTH, RESERVED_SCOPES, MAX_REQUESTER_LENGTH, CONTROL_OR_FORMAT, STATE_RE, MAX_RETURN_TO_LENGTH, RETURN_TO_ORIGINS, RETURN_TO_PREVIEW, PKH_SPACE_RE, SecretRequestInputError;
6972
+ var init_secret_request = __esm({
6973
+ "src/secret-request.ts"() {
6974
+ "use strict";
6975
+ nodeSdk3 = __toESM(require("@tinycloud/node-sdk"), 1);
6976
+ SECRET_MANAGER_ORIGIN = "https://secrets.tinycloud.xyz";
6977
+ SECRET_REQUEST_MAX_NAMES = 10;
6978
+ SECRET_REQUEST_MAX_URL_LENGTH = 8192;
6979
+ OWNER_RE = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/;
6980
+ NAME_RE = /^[A-Z][A-Z0-9_]*$/;
6981
+ MAX_NAME_LENGTH = 64;
6982
+ SCOPE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
6983
+ MAX_SCOPE_LENGTH = 64;
6984
+ RESERVED_SCOPES = ["default", "global", "scoped"];
6985
+ MAX_REQUESTER_LENGTH = 64;
6986
+ CONTROL_OR_FORMAT = /[\p{Cc}\p{Cf}]/u;
6987
+ STATE_RE = /^[A-Za-z0-9._~-]{1,128}$/;
6988
+ MAX_RETURN_TO_LENGTH = 2048;
6989
+ RETURN_TO_ORIGINS = ["https://feed.tinycloud.xyz"];
6990
+ RETURN_TO_PREVIEW = /^https:\/\/[a-z0-9-]+\.tinyfeed\.pages\.dev$/;
6991
+ PKH_SPACE_RE = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40}):[^:]+$/;
6992
+ SecretRequestInputError = class extends Error {
6993
+ constructor(code, message) {
6994
+ super(message);
6995
+ this.name = "SecretRequestInputError";
6996
+ this.code = code;
6997
+ }
6998
+ };
6999
+ }
7000
+ });
7001
+
6583
7002
  // src/operations/secrets-get.ts
6584
7003
  async function planSecretGet(context, input) {
6585
7004
  const reference = resolveSecretReferenceForOperation(
@@ -6646,27 +7065,14 @@ async function executeSecretGet(context, input) {
6646
7065
  )
6647
7066
  };
6648
7067
  }
6649
- return classifySecretRead(reference, result);
7068
+ return classifySecretRead(reference, result, context.runtime?.localOwner === true);
6650
7069
  }
6651
- function classifySecretRead(reference, result) {
7070
+ function classifySecretRead(reference, result, localOwner) {
6652
7071
  switch (result.status) {
6653
7072
  case "ok":
6654
7073
  return { status: "ok", output: { value: result.value } };
6655
7074
  case "not_found":
6656
- return {
6657
- status: "setup_required",
6658
- setup: {
6659
- kind: "secret_manager",
6660
- secret: {
6661
- name: reference.name,
6662
- ...reference.scope === void 0 ? {} : { scope: reference.scope },
6663
- space: reference.space
6664
- },
6665
- url: buildSecretSetupUrl(reference),
6666
- message: "Enter this secret in Secret Manager, then retry the operation."
6667
- },
6668
- requiresCallerInput: true
6669
- };
7075
+ return missingSecretOutcome(reference, localOwner);
6670
7076
  case "permission_required":
6671
7077
  return {
6672
7078
  status: "authority_required",
@@ -6711,6 +7117,47 @@ function classifySecretRead(reference, result) {
6711
7117
  error: operationError("SECRET_READ_FAILED", "The secret read failed.")
6712
7118
  };
6713
7119
  }
7120
+ function missingSecretOutcome(reference, localOwner) {
7121
+ const owner = ownerOfSpace(reference.space);
7122
+ const recovery = missingSecretRecovery({
7123
+ owner,
7124
+ local: localOwner,
7125
+ names: [reference.name],
7126
+ ...reference.scope === void 0 ? {} : { scope: reference.scope }
7127
+ });
7128
+ if (recovery.kind === "add_secret") {
7129
+ const { setup } = recovery;
7130
+ return {
7131
+ status: "setup_required",
7132
+ setup: {
7133
+ kind: "secret_manager",
7134
+ action: setup.action,
7135
+ url: setup.url,
7136
+ names: setup.names,
7137
+ ...setup.scope === void 0 ? {} : { scope: setup.scope },
7138
+ owner: setup.owner,
7139
+ secret: {
7140
+ name: reference.name,
7141
+ ...reference.scope === void 0 ? {} : { scope: reference.scope },
7142
+ space: reference.space
7143
+ },
7144
+ message: "Ask the owner to add this secret in Secret Manager, then retry the operation."
7145
+ },
7146
+ requiresCallerInput: true
7147
+ };
7148
+ }
7149
+ return {
7150
+ status: "error",
7151
+ error: operationError("KV_NOT_FOUND", recovery.hint, {
7152
+ details: {
7153
+ names: [reference.name],
7154
+ ...reference.scope === void 0 ? {} : { scope: reference.scope },
7155
+ ...owner === void 0 ? {} : { owner },
7156
+ recoveryKind: recovery.kind
7157
+ }
7158
+ })
7159
+ };
7160
+ }
6714
7161
  function resolveEncryptionNetworkId(node, space) {
6715
7162
  const candidate = node;
6716
7163
  let scoped;
@@ -6738,6 +7185,7 @@ var init_secrets_get = __esm({
6738
7185
  "use strict";
6739
7186
  import_zod5 = require("zod");
6740
7187
  init_errors2();
7188
+ init_secret_request();
6741
7189
  init_secrets();
6742
7190
  SecretsGetInputSchema = import_zod5.z.object({
6743
7191
  name: import_zod5.z.string(),
@@ -6789,6 +7237,194 @@ var init_secrets_get = __esm({
6789
7237
  }
6790
7238
  });
6791
7239
 
7240
+ // src/operations/secrets-status.ts
7241
+ async function executeSecretsStatus(context, input) {
7242
+ const runtime = context.runtime;
7243
+ const node = runtime?.node;
7244
+ if (runtime === void 0 || typeof node?.kvForSpace !== "function") {
7245
+ return {
7246
+ status: "error",
7247
+ error: operationError("NODE_ERROR", "The TinyCloud node could not check the secrets.", { retryable: true })
7248
+ };
7249
+ }
7250
+ const local = runtime.localOwner === true;
7251
+ const resolution = resolveSecretsOwner({
7252
+ local,
7253
+ posture: context.summary.posture,
7254
+ ...context.summary.ownerDid === void 0 ? {} : { ownerDid: context.summary.ownerDid },
7255
+ ...context.summary.principalDid === void 0 ? {} : { principalDid: context.summary.principalDid },
7256
+ ...context.summary.space === void 0 ? {} : { space: context.summary.space }
7257
+ }, input.owner);
7258
+ if (!resolution.ok) {
7259
+ return {
7260
+ status: "error",
7261
+ error: operationError("INPUT_INVALID", resolution.message, { details: { reason: resolution.code } })
7262
+ };
7263
+ }
7264
+ const owner = resolution.owner;
7265
+ const skipped2 = runtime.skippedGrantReasons ?? [];
7266
+ const pkh = nodeSdk4.parsePkhDid(owner);
7267
+ const kv = node.kvForSpace(nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets"));
7268
+ const outcomes = await Promise.all(input.names.map(async (name) => {
7269
+ const path = input.scope === void 0 ? `vault/secrets/${name}` : `vault/secrets/scoped/${input.scope}/${name}`;
7270
+ try {
7271
+ const result = await kv.get(path, { raw: true, prefix: "", timeout: PROBE_TIMEOUT_MS });
7272
+ if (result.ok) return "present";
7273
+ const error = result.error;
7274
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "KV_NOT_FOUND") {
7275
+ return "pending";
7276
+ }
7277
+ const refusal = kvRefusal(error);
7278
+ if (refusal === "auth_required") return "auth_required";
7279
+ if (refusal === "permission_denied") return "denied";
7280
+ return "unreachable";
7281
+ } catch {
7282
+ return "unreachable";
7283
+ }
7284
+ }));
7285
+ const namesWith = (outcome) => input.names.filter((_, index) => outcomes[index] === outcome);
7286
+ const present = namesWith("present");
7287
+ const pending = namesWith("pending");
7288
+ const denied = namesWith("denied");
7289
+ if (present.length + pending.length < input.names.length) {
7290
+ if (outcomes.includes("auth_required") || skipped2.includes("activation_unauthenticated")) {
7291
+ return {
7292
+ status: "error",
7293
+ error: operationError(
7294
+ "AUTH_REQUIRED",
7295
+ "The node did not accept this profile's stored session. Sign in again, then retry.",
7296
+ { retryable: false, details: { recovery: signInRecovery(context.summary.profile, context.summary.posture) } }
7297
+ )
7298
+ };
7299
+ }
7300
+ if (outcomes.includes("unreachable") || skipped2.includes("activation_unavailable")) {
7301
+ return {
7302
+ status: "error",
7303
+ error: operationError("NODE_UNREACHABLE", "The TinyCloud node could not be reached.", { retryable: true })
7304
+ };
7305
+ }
7306
+ }
7307
+ const recovery = pending.length === 0 ? void 0 : missingSecretRecovery({
7308
+ owner,
7309
+ local,
7310
+ names: pending,
7311
+ ...input.scope === void 0 ? {} : { scope: input.scope }
7312
+ });
7313
+ return {
7314
+ status: "ok",
7315
+ output: {
7316
+ status: denied.length > 0 ? "consent_required" : pending.length > 0 ? "pending" : "ready",
7317
+ owner,
7318
+ names: [...input.names],
7319
+ ...input.scope === void 0 ? {} : { scope: input.scope },
7320
+ present,
7321
+ pending,
7322
+ denied,
7323
+ ...recovery === void 0 ? {} : recovery.kind === "add_secret" ? { setup: recovery.setup } : { hint: recovery.hint }
7324
+ }
7325
+ };
7326
+ }
7327
+ var import_zod6, nodeSdk4, PROBE_TIMEOUT_MS, SECRETS_STATUS_DEADLINE_MS, SecretsStatusInputSchema, SecretSetupSchema, SecretsStatusOutputSchema, SECRETS_STATUS_POSTURES, SECRETS_STATUS_EXPOSURE, SECRETS_STATUS_SENSITIVITY, secretsStatusOperationDefinition, secretsStatusOperationDefinitions;
7328
+ var init_secrets_status = __esm({
7329
+ "src/operations/secrets-status.ts"() {
7330
+ "use strict";
7331
+ import_zod6 = require("zod");
7332
+ nodeSdk4 = __toESM(require("@tinycloud/node-sdk"), 1);
7333
+ init_errors2();
7334
+ init_kv_failure();
7335
+ init_recovery();
7336
+ init_secret_request();
7337
+ init_secrets();
7338
+ PROBE_TIMEOUT_MS = 15e3;
7339
+ SECRETS_STATUS_DEADLINE_MS = 18e3;
7340
+ SecretsStatusInputSchema = import_zod6.z.object({
7341
+ names: import_zod6.z.array(import_zod6.z.string()),
7342
+ scope: import_zod6.z.string().optional(),
7343
+ owner: import_zod6.z.string().optional()
7344
+ }).strict().superRefine((input, context) => {
7345
+ try {
7346
+ validSecretNames(input.names);
7347
+ } catch {
7348
+ context.addIssue({ code: import_zod6.z.ZodIssueCode.custom, path: ["names"], message: "Invalid secret names." });
7349
+ }
7350
+ try {
7351
+ const scope = canonicalizeSecretScope(input.scope);
7352
+ if (scope !== void 0 && !isValidSecretRequestScope(scope)) throw new Error("invalid scope");
7353
+ } catch {
7354
+ context.addIssue({ code: import_zod6.z.ZodIssueCode.custom, path: ["scope"], message: "Invalid secret scope." });
7355
+ }
7356
+ if (input.owner !== void 0) {
7357
+ try {
7358
+ canonicalSecretRequestOwner(input.owner);
7359
+ } catch {
7360
+ context.addIssue({ code: import_zod6.z.ZodIssueCode.custom, path: ["owner"], message: "Invalid owner." });
7361
+ }
7362
+ }
7363
+ }).transform((input) => {
7364
+ const scope = canonicalizeSecretScope(input.scope);
7365
+ return {
7366
+ names: validSecretNames(input.names),
7367
+ ...scope === void 0 ? {} : { scope },
7368
+ ...input.owner === void 0 ? {} : { owner: canonicalSecretRequestOwner(input.owner) }
7369
+ };
7370
+ });
7371
+ SecretSetupSchema = import_zod6.z.object({
7372
+ url: import_zod6.z.string().url(),
7373
+ names: import_zod6.z.array(import_zod6.z.string()).min(1),
7374
+ scope: import_zod6.z.string().optional(),
7375
+ owner: import_zod6.z.string(),
7376
+ action: import_zod6.z.literal("add_secret")
7377
+ }).strict();
7378
+ SecretsStatusOutputSchema = import_zod6.z.object({
7379
+ status: import_zod6.z.enum(["ready", "pending", "consent_required"]),
7380
+ owner: import_zod6.z.string(),
7381
+ names: import_zod6.z.array(import_zod6.z.string()).min(1),
7382
+ scope: import_zod6.z.string().optional(),
7383
+ present: import_zod6.z.array(import_zod6.z.string()),
7384
+ pending: import_zod6.z.array(import_zod6.z.string()),
7385
+ denied: import_zod6.z.array(import_zod6.z.string()),
7386
+ setup: SecretSetupSchema.optional(),
7387
+ hint: import_zod6.z.string().optional()
7388
+ }).strict();
7389
+ SECRETS_STATUS_POSTURES = [
7390
+ "owner-openkey",
7391
+ "delegate-session",
7392
+ "local-owner-key"
7393
+ ];
7394
+ SECRETS_STATUS_EXPOSURE = {
7395
+ cli: { status: "required" },
7396
+ mcp: { status: "required" },
7397
+ skill: { status: "required" },
7398
+ docs: { status: "required" }
7399
+ };
7400
+ SECRETS_STATUS_SENSITIVITY = {
7401
+ input: [],
7402
+ output: []
7403
+ };
7404
+ secretsStatusOperationDefinition = {
7405
+ id: "tinycloud.secrets.status",
7406
+ version: 1,
7407
+ title: "Check whether TinyCloud secrets exist",
7408
+ description: "Report, without reading any value, which named secrets exist in the owner's secrets space: ready, pending (with a Secret Manager request link), or consent_required for names this identity may not read. Each name needs tinycloud.kv/get; this operation never requests it. It never signs in: it uses only the profile's stored session, and writes no profile state.",
7409
+ input: SecretsStatusInputSchema,
7410
+ output: SecretsStatusOutputSchema,
7411
+ effects: ["read"],
7412
+ runtime: "restored",
7413
+ postures: SECRETS_STATUS_POSTURES,
7414
+ exposure: SECRETS_STATUS_EXPOSURE,
7415
+ sensitivity: SECRETS_STATUS_SENSITIVITY,
7416
+ deadlineMs: SECRETS_STATUS_DEADLINE_MS,
7417
+ // Never evaluated against the session: a missing grant is reported as
7418
+ // `consent_required`, never saved as an authority request.
7419
+ authority: async () => [],
7420
+ execute: executeSecretsStatus
7421
+ };
7422
+ secretsStatusOperationDefinitions = [
7423
+ secretsStatusOperationDefinition
7424
+ ];
7425
+ }
7426
+ });
7427
+
6792
7428
  // src/operations/sql.ts
6793
7429
  async function planSqlRead(context, input) {
6794
7430
  const space = resolveSqlSpace(context, input.space);
@@ -7101,15 +7737,15 @@ function isByte(value) {
7101
7737
  function isRecord3(value) {
7102
7738
  return typeof value === "object" && value !== null && !Array.isArray(value);
7103
7739
  }
7104
- var import_node_sql_parser, import_zod6, DEFAULT_MAX_ROWS, MAX_MAX_ROWS, DEFAULT_MAX_BYTES, MAX_MAX_BYTES, SCHEMA_MAX_ROWS, SCHEMA_MAX_BYTES, MAX_PARAMETERS, MAX_SQL_LENGTH, SQL_WRITE_AUTHORITY_NOTICE, SCHEMA_QUERY, sqlParser, SpaceSchema2, DatabaseSchema, SafeNumberSchema, Base64Schema, SqlInputValueSchema, SqlParamsSchema, RequiredSqlParamsSchema, SqlOutputValueSchema, SqlTargetInputSchema, SqlQueryInputSchema, SqlExecuteInputSchema, SqlLimitsOutputSchema, SqlSchemaInspectOutputSchema, SqlQueryOutputSchema, SqlExecuteOutputSchema, AUTHENTICATED_POSTURES2, SQL_EXPOSURE, SQL_SCHEMA_SENSITIVITY, SQL_QUERY_SENSITIVITY, SQL_EXECUTE_SENSITIVITY, sqlOperationDefinitions;
7740
+ var import_node_sql_parser, import_zod7, DEFAULT_MAX_ROWS, MAX_MAX_ROWS, DEFAULT_MAX_BYTES, MAX_MAX_BYTES, SCHEMA_MAX_ROWS, SCHEMA_MAX_BYTES, MAX_PARAMETERS, MAX_SQL_LENGTH, SQL_WRITE_AUTHORITY_NOTICE, SCHEMA_QUERY, sqlParser, SpaceSchema2, DatabaseSchema, SafeNumberSchema, Base64Schema, SqlInputValueSchema, SqlParamsSchema, RequiredSqlParamsSchema, SqlOutputValueSchema, SqlTargetInputSchema, SqlQueryInputSchema, SqlExecuteInputSchema, SqlLimitsOutputSchema, SqlSchemaInspectOutputSchema, SqlQueryOutputSchema, SqlExecuteOutputSchema, AUTHENTICATED_POSTURES2, SQL_EXPOSURE, SQL_SCHEMA_SENSITIVITY, SQL_QUERY_SENSITIVITY, SQL_EXECUTE_SENSITIVITY, sqlOperationDefinitions;
7105
7741
  var init_sql = __esm({
7106
7742
  "src/operations/sql.ts"() {
7107
7743
  "use strict";
7108
7744
  import_node_sql_parser = __toESM(require("node-sql-parser"), 1);
7109
- import_zod6 = require("zod");
7745
+ import_zod7 = require("zod");
7110
7746
  init_errors2();
7111
7747
  init_secrets();
7112
- init_exploration();
7748
+ init_kv_failure();
7113
7749
  DEFAULT_MAX_ROWS = 100;
7114
7750
  MAX_MAX_ROWS = 1e3;
7115
7751
  DEFAULT_MAX_BYTES = 1024 * 1024;
@@ -7125,29 +7761,29 @@ WHERE type IN ('table', 'view', 'index', 'trigger')
7125
7761
  AND name NOT LIKE 'sqlite_%'
7126
7762
  ORDER BY type, name`;
7127
7763
  sqlParser = new import_node_sql_parser.default.Parser();
7128
- SpaceSchema2 = import_zod6.z.string().min(1).refine(
7764
+ SpaceSchema2 = import_zod7.z.string().min(1).refine(
7129
7765
  (space) => (/^[A-Za-z0-9_-]+$/.test(space) || space.startsWith("tinycloud:")) && !space.includes("*"),
7130
7766
  "Invalid TinyCloud space."
7131
7767
  );
7132
- DatabaseSchema = import_zod6.z.string().min(1).max(256).refine(
7768
+ DatabaseSchema = import_zod7.z.string().min(1).max(256).refine(
7133
7769
  (database) => database !== "/" && !database.endsWith("/") && !database.includes("*") && !/[\u0000-\u001f\u007f]/.test(database),
7134
7770
  "Invalid SQLite database name."
7135
7771
  );
7136
- SafeNumberSchema = import_zod6.z.number().finite().refine(
7772
+ SafeNumberSchema = import_zod7.z.number().finite().refine(
7137
7773
  (value) => !Number.isInteger(value) || Number.isSafeInteger(value),
7138
7774
  "SQLite integer parameters must be JavaScript safe integers."
7139
7775
  );
7140
- Base64Schema = import_zod6.z.string().max(Math.ceil(MAX_MAX_BYTES / 3) * 4).refine(
7776
+ Base64Schema = import_zod7.z.string().max(Math.ceil(MAX_MAX_BYTES / 3) * 4).refine(
7141
7777
  isCanonicalBase642,
7142
7778
  "Invalid base64 BLOB value."
7143
7779
  );
7144
- SqlInputValueSchema = import_zod6.z.union([
7145
- import_zod6.z.null(),
7780
+ SqlInputValueSchema = import_zod7.z.union([
7781
+ import_zod7.z.null(),
7146
7782
  SafeNumberSchema,
7147
- import_zod6.z.string().max(MAX_MAX_BYTES),
7148
- import_zod6.z.object({ type: import_zod6.z.literal("blob"), base64: Base64Schema }).strict()
7783
+ import_zod7.z.string().max(MAX_MAX_BYTES),
7784
+ import_zod7.z.object({ type: import_zod7.z.literal("blob"), base64: Base64Schema }).strict()
7149
7785
  ]);
7150
- SqlParamsSchema = import_zod6.z.array(SqlInputValueSchema).max(MAX_PARAMETERS).superRefine(
7786
+ SqlParamsSchema = import_zod7.z.array(SqlInputValueSchema).max(MAX_PARAMETERS).superRefine(
7151
7787
  (params, context) => {
7152
7788
  const totalBytes = params.reduce(
7153
7789
  (total, value) => total + sqlInputValueBytes(value),
@@ -7155,13 +7791,13 @@ ORDER BY type, name`;
7155
7791
  );
7156
7792
  if (totalBytes > MAX_MAX_BYTES) {
7157
7793
  context.addIssue({
7158
- code: import_zod6.z.ZodIssueCode.custom,
7794
+ code: import_zod7.z.ZodIssueCode.custom,
7159
7795
  message: "SQLite query parameters exceed the 4 MiB input limit."
7160
7796
  });
7161
7797
  }
7162
7798
  }
7163
7799
  );
7164
- RequiredSqlParamsSchema = import_zod6.z.array(SqlInputValueSchema).min(1).max(MAX_PARAMETERS).superRefine(
7800
+ RequiredSqlParamsSchema = import_zod7.z.array(SqlInputValueSchema).min(1).max(MAX_PARAMETERS).superRefine(
7165
7801
  (params, context) => {
7166
7802
  const totalBytes = params.reduce(
7167
7803
  (total, value) => total + sqlInputValueBytes(value),
@@ -7169,88 +7805,88 @@ ORDER BY type, name`;
7169
7805
  );
7170
7806
  if (totalBytes > MAX_MAX_BYTES) {
7171
7807
  context.addIssue({
7172
- code: import_zod6.z.ZodIssueCode.custom,
7808
+ code: import_zod7.z.ZodIssueCode.custom,
7173
7809
  message: "SQLite query parameters exceed the 4 MiB input limit."
7174
7810
  });
7175
7811
  }
7176
7812
  }
7177
7813
  );
7178
- SqlOutputValueSchema = import_zod6.z.union([
7179
- import_zod6.z.null(),
7814
+ SqlOutputValueSchema = import_zod7.z.union([
7815
+ import_zod7.z.null(),
7180
7816
  SafeNumberSchema,
7181
- import_zod6.z.string(),
7182
- import_zod6.z.object({
7183
- type: import_zod6.z.literal("blob"),
7184
- base64: import_zod6.z.string(),
7185
- byteLength: import_zod6.z.number().int().nonnegative()
7817
+ import_zod7.z.string(),
7818
+ import_zod7.z.object({
7819
+ type: import_zod7.z.literal("blob"),
7820
+ base64: import_zod7.z.string(),
7821
+ byteLength: import_zod7.z.number().int().nonnegative()
7186
7822
  }).strict()
7187
7823
  ]);
7188
- SqlTargetInputSchema = import_zod6.z.object({
7824
+ SqlTargetInputSchema = import_zod7.z.object({
7189
7825
  space: SpaceSchema2,
7190
7826
  database: DatabaseSchema
7191
7827
  }).strict();
7192
- SqlQueryInputSchema = import_zod6.z.object({
7828
+ SqlQueryInputSchema = import_zod7.z.object({
7193
7829
  space: SpaceSchema2,
7194
7830
  database: DatabaseSchema,
7195
- sql: import_zod6.z.string().min(1).max(MAX_SQL_LENGTH).superRefine((sql, context) => {
7831
+ sql: import_zod7.z.string().min(1).max(MAX_SQL_LENGTH).superRefine((sql, context) => {
7196
7832
  if (!isSingleReadQuery(sql)) {
7197
7833
  context.addIssue({
7198
- code: import_zod6.z.ZodIssueCode.custom,
7834
+ code: import_zod7.z.ZodIssueCode.custom,
7199
7835
  message: "SQL must parse as exactly one SQLite SELECT statement."
7200
7836
  });
7201
7837
  }
7202
7838
  }),
7203
7839
  params: SqlParamsSchema.optional(),
7204
- maxRows: import_zod6.z.number().int().positive().max(MAX_MAX_ROWS).optional(),
7205
- maxBytes: import_zod6.z.number().int().positive().max(MAX_MAX_BYTES).optional()
7840
+ maxRows: import_zod7.z.number().int().positive().max(MAX_MAX_ROWS).optional(),
7841
+ maxBytes: import_zod7.z.number().int().positive().max(MAX_MAX_BYTES).optional()
7206
7842
  }).strict();
7207
- SqlExecuteInputSchema = import_zod6.z.object({
7843
+ SqlExecuteInputSchema = import_zod7.z.object({
7208
7844
  space: SpaceSchema2,
7209
7845
  database: DatabaseSchema,
7210
- sql: import_zod6.z.string().min(1).max(MAX_SQL_LENGTH),
7846
+ sql: import_zod7.z.string().min(1).max(MAX_SQL_LENGTH),
7211
7847
  params: RequiredSqlParamsSchema,
7212
- acknowledgeDatabaseWideAuthority: import_zod6.z.literal(true).describe(SQL_WRITE_AUTHORITY_NOTICE)
7848
+ acknowledgeDatabaseWideAuthority: import_zod7.z.literal(true).describe(SQL_WRITE_AUTHORITY_NOTICE)
7213
7849
  }).strict().superRefine((input, context) => {
7214
7850
  if (parseSingleDmlStatement(input.sql, input.params.length) === void 0) {
7215
7851
  context.addIssue({
7216
- code: import_zod6.z.ZodIssueCode.custom,
7852
+ code: import_zod7.z.ZodIssueCode.custom,
7217
7853
  path: ["sql"],
7218
7854
  message: "SQL must parse as exactly one parameterized INSERT, UPDATE, or DELETE with one value per positional placeholder."
7219
7855
  });
7220
7856
  }
7221
7857
  });
7222
- SqlLimitsOutputSchema = import_zod6.z.object({
7223
- maxRows: import_zod6.z.number().int().positive(),
7224
- maxBytes: import_zod6.z.number().int().positive(),
7225
- enforcement: import_zod6.z.literal("node-requested-client-verified")
7858
+ SqlLimitsOutputSchema = import_zod7.z.object({
7859
+ maxRows: import_zod7.z.number().int().positive(),
7860
+ maxBytes: import_zod7.z.number().int().positive(),
7861
+ enforcement: import_zod7.z.literal("node-requested-client-verified")
7226
7862
  }).strict();
7227
- SqlSchemaInspectOutputSchema = import_zod6.z.object({
7228
- space: import_zod6.z.string().min(1),
7229
- database: import_zod6.z.string().min(1),
7230
- objects: import_zod6.z.array(import_zod6.z.object({
7231
- type: import_zod6.z.enum(["table", "view", "index", "trigger"]),
7232
- name: import_zod6.z.string().min(1),
7233
- tableName: import_zod6.z.string().min(1),
7234
- sql: import_zod6.z.string().optional()
7863
+ SqlSchemaInspectOutputSchema = import_zod7.z.object({
7864
+ space: import_zod7.z.string().min(1),
7865
+ database: import_zod7.z.string().min(1),
7866
+ objects: import_zod7.z.array(import_zod7.z.object({
7867
+ type: import_zod7.z.enum(["table", "view", "index", "trigger"]),
7868
+ name: import_zod7.z.string().min(1),
7869
+ tableName: import_zod7.z.string().min(1),
7870
+ sql: import_zod7.z.string().optional()
7235
7871
  }).strict()),
7236
- count: import_zod6.z.number().int().nonnegative(),
7872
+ count: import_zod7.z.number().int().nonnegative(),
7237
7873
  limits: SqlLimitsOutputSchema
7238
7874
  }).strict();
7239
- SqlQueryOutputSchema = import_zod6.z.object({
7240
- space: import_zod6.z.string().min(1),
7241
- database: import_zod6.z.string().min(1),
7242
- columns: import_zod6.z.array(import_zod6.z.string()),
7243
- rows: import_zod6.z.array(import_zod6.z.array(SqlOutputValueSchema)),
7244
- rowCount: import_zod6.z.number().int().nonnegative(),
7875
+ SqlQueryOutputSchema = import_zod7.z.object({
7876
+ space: import_zod7.z.string().min(1),
7877
+ database: import_zod7.z.string().min(1),
7878
+ columns: import_zod7.z.array(import_zod7.z.string()),
7879
+ rows: import_zod7.z.array(import_zod7.z.array(SqlOutputValueSchema)),
7880
+ rowCount: import_zod7.z.number().int().nonnegative(),
7245
7881
  limits: SqlLimitsOutputSchema
7246
7882
  }).strict();
7247
- SqlExecuteOutputSchema = import_zod6.z.object({
7248
- space: import_zod6.z.string().min(1),
7249
- database: import_zod6.z.string().min(1),
7250
- statementType: import_zod6.z.enum(["insert", "update", "delete"]),
7251
- changes: import_zod6.z.number().int().nonnegative(),
7252
- lastInsertRowId: import_zod6.z.number().int().nullable(),
7253
- authorityNotice: import_zod6.z.literal(SQL_WRITE_AUTHORITY_NOTICE)
7883
+ SqlExecuteOutputSchema = import_zod7.z.object({
7884
+ space: import_zod7.z.string().min(1),
7885
+ database: import_zod7.z.string().min(1),
7886
+ statementType: import_zod7.z.enum(["insert", "update", "delete"]),
7887
+ changes: import_zod7.z.number().int().nonnegative(),
7888
+ lastInsertRowId: import_zod7.z.number().int().nullable(),
7889
+ authorityNotice: import_zod7.z.literal(SQL_WRITE_AUTHORITY_NOTICE)
7254
7890
  }).strict();
7255
7891
  AUTHENTICATED_POSTURES2 = [
7256
7892
  "owner-openkey",
@@ -7350,6 +7986,8 @@ var init_contract = __esm({
7350
7986
  "resources_mismatch",
7351
7987
  "unsupported",
7352
7988
  "activation_rejected",
7989
+ "activation_unavailable",
7990
+ "activation_unauthenticated",
7353
7991
  "invalid"
7354
7992
  ];
7355
7993
  DELEGATION_CID_PATTERN = "^bafkr4i[a-z2-7]{52}$";
@@ -7452,6 +8090,9 @@ function skipped(reason) {
7452
8090
  }
7453
8091
  async function replayProfileDelegations(profile, node, options) {
7454
8092
  await prepareStoredDelegationReplay(profile, node, { host: options.host, migrate: options.migrate });
8093
+ return replayStoredDelegationRecords(profile, node, options);
8094
+ }
8095
+ async function replayStoredDelegationRecords(profile, node, options) {
7455
8096
  const installed = [];
7456
8097
  const warnings = [];
7457
8098
  const seen = /* @__PURE__ */ new Set();
@@ -7492,7 +8133,7 @@ function storedDelegationForAnotherKey(entry, node) {
7492
8133
  const audience = verifiedCompactAudience(node, delegation);
7493
8134
  if (audience === void 0) return false;
7494
8135
  try {
7495
- return nodeSdk3.principalDidEquals(delegation.delegateDID, audience) && !nodeSdk3.principalDidEquals(audience, node.sessionDid);
8136
+ return nodeSdk5.principalDidEquals(delegation.delegateDID, audience) && !nodeSdk5.principalDidEquals(audience, node.sessionDid);
7496
8137
  } catch {
7497
8138
  return false;
7498
8139
  }
@@ -7526,7 +8167,7 @@ async function assess(node, entry, options, mode) {
7526
8167
  let outsideRequest = false;
7527
8168
  let effectivePermissions;
7528
8169
  try {
7529
- const installed = await nodeSdk3.activateValidatedRuntimeDelegation(node, toActivate, {
8170
+ const installed = await nodeSdk5.activateValidatedRuntimeDelegation(node, toActivate, {
7530
8171
  host: options.host,
7531
8172
  authorize: (effective) => {
7532
8173
  verifyCompactProof(toActivate);
@@ -7558,7 +8199,7 @@ function verifyCompactProof(delegation) {
7558
8199
  try {
7559
8200
  parseSignedCompactUcanAttenuation(withoutBearer(authorization), delegation.cid);
7560
8201
  } catch {
7561
- throw new nodeSdk3.RuntimeDelegationRejectedError("proof_invalid", "The compact delegation proof is invalid.");
8202
+ throw new nodeSdk5.RuntimeDelegationRejectedError("proof_invalid", "The compact delegation proof is invalid.");
7562
8203
  }
7563
8204
  }
7564
8205
  function rejectionReason(error) {
@@ -7575,7 +8216,7 @@ async function bindingMigrationRecorded(profile) {
7575
8216
  async function signedCapabilities(node, delegation, host) {
7576
8217
  let signed;
7577
8218
  try {
7578
- await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, {
8219
+ await nodeSdk5.activateValidatedRuntimeDelegation(node, delegation, {
7579
8220
  // The transport host is not signed authority; check it at replay instead.
7580
8221
  host: delegation.host ?? host,
7581
8222
  authorize: (effective) => {
@@ -7610,13 +8251,13 @@ function recordKey(entry) {
7610
8251
  function isRecord4(value) {
7611
8252
  return typeof value === "object" && value !== null && !Array.isArray(value);
7612
8253
  }
7613
- var import_node_path2, nodeSdk3, REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS, NOT_REPLAYED, DELEGATION_CID;
8254
+ var import_node_path2, nodeSdk5, REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS, NOT_REPLAYED, DELEGATION_CID;
7614
8255
  var init_delegation_binding = __esm({
7615
8256
  "src/delegation-binding.ts"() {
7616
8257
  "use strict";
7617
8258
  import_node_path2 = require("path");
7618
8259
  init_operations_bundle();
7619
- nodeSdk3 = __toESM(require("@tinycloud/node-sdk"), 1);
8260
+ nodeSdk5 = __toESM(require("@tinycloud/node-sdk"), 1);
7620
8261
  init_artifacts();
7621
8262
  init_authority();
7622
8263
  init_contract();
@@ -7757,35 +8398,35 @@ function profileNotFound(profile) {
7757
8398
  function isRecord5(value) {
7758
8399
  return typeof value === "object" && value !== null && !Array.isArray(value);
7759
8400
  }
7760
- var import_zod7, StatusInputSchema, StatusOutputSchema, STATUS_POSTURES, STATUS_EXPOSURE, STATUS_SENSITIVITY, statusOperationDefinitions;
8401
+ var import_zod8, StatusInputSchema, StatusOutputSchema, STATUS_POSTURES, STATUS_EXPOSURE, STATUS_SENSITIVITY, statusOperationDefinitions;
7761
8402
  var init_status = __esm({
7762
8403
  "src/operations/status.ts"() {
7763
8404
  "use strict";
7764
- import_zod7 = require("zod");
8405
+ import_zod8 = require("zod");
7765
8406
  init_delegation_binding();
7766
8407
  init_errors2();
7767
8408
  init_state();
7768
- StatusInputSchema = import_zod7.z.object({}).strict();
7769
- StatusOutputSchema = import_zod7.z.object({
7770
- profile: import_zod7.z.string().min(1),
7771
- host: import_zod7.z.string().min(1),
7772
- posture: import_zod7.z.enum([
8409
+ StatusInputSchema = import_zod8.z.object({}).strict();
8410
+ StatusOutputSchema = import_zod8.z.object({
8411
+ profile: import_zod8.z.string().min(1),
8412
+ host: import_zod8.z.string().min(1),
8413
+ posture: import_zod8.z.enum([
7773
8414
  "owner-openkey",
7774
8415
  "delegate-session",
7775
8416
  "local-owner-key",
7776
8417
  "unauthenticated"
7777
8418
  ]),
7778
- operatorType: import_zod7.z.enum(["human", "agent"]).optional(),
7779
- principalDid: import_zod7.z.string().min(1).optional(),
7780
- sessionDid: import_zod7.z.string().min(1).optional(),
7781
- ownerDid: import_zod7.z.string().min(1).optional(),
7782
- space: import_zod7.z.string().min(1).optional(),
7783
- session: import_zod7.z.object({
7784
- present: import_zod7.z.boolean(),
7785
- expired: import_zod7.z.boolean().nullable(),
7786
- expiresAt: import_zod7.z.string().datetime({ offset: true }).nullable()
8419
+ operatorType: import_zod8.z.enum(["human", "agent"]).optional(),
8420
+ principalDid: import_zod8.z.string().min(1).optional(),
8421
+ sessionDid: import_zod8.z.string().min(1).optional(),
8422
+ ownerDid: import_zod8.z.string().min(1).optional(),
8423
+ space: import_zod8.z.string().min(1).optional(),
8424
+ session: import_zod8.z.object({
8425
+ present: import_zod8.z.boolean(),
8426
+ expired: import_zod8.z.boolean().nullable(),
8427
+ expiresAt: import_zod8.z.string().datetime({ offset: true }).nullable()
7787
8428
  }).strict(),
7788
- liveAdditionalDelegationCount: import_zod7.z.number().int().nonnegative().describe(
8429
+ liveAdditionalDelegationCount: import_zod8.z.number().int().nonnegative().describe(
7789
8430
  "Stored, unexpired compact-UCAN or signed-login delegations with a valid request binding. Each still requires local proof validation and node activation before it grants authority."
7790
8431
  )
7791
8432
  }).strict();
@@ -7829,7 +8470,8 @@ function registeredOperationDefinitions() {
7829
8470
  ...authOperationDefinitions,
7830
8471
  ...explorationOperationDefinitions,
7831
8472
  ...sqlOperationDefinitions,
7832
- ...secretsGetOperationDefinitions
8473
+ ...secretsGetOperationDefinitions,
8474
+ ...secretsStatusOperationDefinitions
7833
8475
  ]);
7834
8476
  return operationDefinitions;
7835
8477
  }
@@ -7861,6 +8503,7 @@ var init_registry = __esm({
7861
8503
  init_auth();
7862
8504
  init_exploration();
7863
8505
  init_secrets_get();
8506
+ init_secrets_status();
7864
8507
  init_sql();
7865
8508
  init_status();
7866
8509
  }
@@ -7913,6 +8556,9 @@ function resolvePosture(profile) {
7913
8556
  if (profile.authMethod === "local") return "local-owner-key";
7914
8557
  return "owner-openkey";
7915
8558
  }
8559
+ function isLocalOwnerProfile(profile) {
8560
+ return resolvePosture(profile) === "local-owner-key" || profile.authMethod === "local" || typeof profile.privateKey === "string";
8561
+ }
7916
8562
  function profileNotFound2(profile) {
7917
8563
  return {
7918
8564
  ok: false,
@@ -7997,11 +8643,14 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
7997
8643
  if (requirement === "inspection") {
7998
8644
  return { ok: true, context: { summary } };
7999
8645
  }
8646
+ if (requirement === "restored") {
8647
+ return createRestoredRuntime(target, resolved);
8648
+ }
8000
8649
  const profileName = summary.profile;
8001
8650
  try {
8002
8651
  const {
8003
8652
  TinyCloudNode: TinyCloudNodeConstructor
8004
- } = nodeSdk4;
8653
+ } = nodeSdk6;
8005
8654
  const explicitPrivateKeyOverride = typeof target.privateKey === "string";
8006
8655
  const [session, key] = explicitPrivateKeyOverride ? [null, null] : await Promise.all([
8007
8656
  readSession(profileName),
@@ -8079,7 +8728,9 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
8079
8728
  const runtime = {
8080
8729
  node,
8081
8730
  granted: canonicalizeCapabilities(livePermissions),
8082
- ...warnings.length === 0 ? {} : { warnings }
8731
+ ...warnings.length === 0 ? {} : { warnings },
8732
+ localOwner: explicitPrivateKeyOverride || isLocalOwnerProfile(profile),
8733
+ skippedGrantReasons: warnings.map((warning) => warning.reason)
8083
8734
  };
8084
8735
  return {
8085
8736
  ok: true,
@@ -8103,11 +8754,112 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
8103
8754
  ));
8104
8755
  }
8105
8756
  }
8757
+ async function createRestoredRuntime(target, resolved) {
8758
+ const summary = resolved.context;
8759
+ const profileName = summary.profile;
8760
+ const profile = resolved.profile;
8761
+ if (typeof target.privateKey === "string") {
8762
+ return failed(summary, operationError(
8763
+ "AUTH_REQUIRED",
8764
+ "This operation never signs in; an explicit private key has no stored session.",
8765
+ { details: { reason: "explicit_key" } }
8766
+ ));
8767
+ }
8768
+ try {
8769
+ const actualPosture = resolvePosture(profile);
8770
+ const selectedPosture = profile.authMethod === "local" ? "local-owner-key" : summary.posture;
8771
+ if (actualPosture !== summary.posture || selectedPosture !== summary.posture) {
8772
+ return failed(
8773
+ { ...summary, posture: actualPosture !== summary.posture ? actualPosture : selectedPosture },
8774
+ operationError(
8775
+ "PROFILE_POSTURE_NOT_ALLOWED",
8776
+ "The selected profile changed posture during runtime resolution."
8777
+ )
8778
+ );
8779
+ }
8780
+ const [session, key] = await Promise.all([
8781
+ readSession(profileName),
8782
+ readJson(`${profilePath(profileName)}/key.json`)
8783
+ ]);
8784
+ const activeSession = session === null ? void 0 : normalizeSession(session, key);
8785
+ if (activeSession === void 0) {
8786
+ return failed(summary, operationError(
8787
+ "SESSION_NOT_FOUND",
8788
+ "The selected profile does not have an active session. This operation never signs in.",
8789
+ { details: { recovery: signInRecovery(profileName, summary.posture) } }
8790
+ ));
8791
+ }
8792
+ if (!await bindingMigrationRecorded(profileName)) {
8793
+ const records = await readAdditionalDelegations(profileName);
8794
+ if (records.some(
8795
+ (entry) => storedDelegationKind(entry) === "compact" && !("authorityRequest" in entry)
8796
+ )) {
8797
+ return failed(summary, operationError(
8798
+ "PROFILE_MIGRATION_REQUIRED",
8799
+ `Profile "${profileName}" has a one-time stored-grant upgrade to finish. Run one authenticated command on it (tc --profile <profile> auth caps), then retry.`,
8800
+ { retryable: false, details: { recovery: migrationRecovery(profileName) } }
8801
+ ));
8802
+ }
8803
+ }
8804
+ const { TinyCloudNode: TinyCloudNodeConstructor } = nodeSdk6;
8805
+ const node = new TinyCloudNodeConstructor({
8806
+ host: summary.host,
8807
+ ...typeof profile.privateKey === "string" ? { privateKey: profile.privateKey } : {}
8808
+ });
8809
+ try {
8810
+ await node.restoreSession(activeSession);
8811
+ } catch (error) {
8812
+ if (isRecord7(error) && error.code === "AUTH_EXPIRED") {
8813
+ return failed(summary, operationError(
8814
+ "AUTH_REQUIRED",
8815
+ "The stored session has expired or is no longer valid. Sign in again.",
8816
+ { retryable: false, details: { recovery: signInRecovery(profileName, summary.posture) } }
8817
+ ));
8818
+ }
8819
+ throw error;
8820
+ }
8821
+ const activeSessionDid = normalizeDid(node.sessionDid);
8822
+ const livePrincipalDid = normalizeDid(node.did);
8823
+ const livePermissions = [...node.getVerifiedSessionCapabilities()];
8824
+ const replay = await replayStoredDelegationRecords(
8825
+ profileName,
8826
+ node,
8827
+ { host: summary.host, resolveSpace: operationSpaceResolver(node, summary.space) }
8828
+ );
8829
+ for (const installed of replay.installed) {
8830
+ livePermissions.push(...installed.effectivePermissions);
8831
+ }
8832
+ const runtime = {
8833
+ node,
8834
+ granted: canonicalizeCapabilities(livePermissions),
8835
+ ...replay.warnings.length === 0 ? {} : { warnings: replay.warnings },
8836
+ localOwner: isLocalOwnerProfile(profile),
8837
+ skippedGrantReasons: replay.warnings.map((warning) => warning.reason)
8838
+ };
8839
+ return {
8840
+ ok: true,
8841
+ context: {
8842
+ summary: {
8843
+ ...summary,
8844
+ ...livePrincipalDid === void 0 ? {} : { principalDid: livePrincipalDid },
8845
+ ...activeSessionDid === void 0 ? {} : { sessionDid: activeSessionDid }
8846
+ },
8847
+ runtime
8848
+ }
8849
+ };
8850
+ } catch {
8851
+ return failed(summary, operationError(
8852
+ "NODE_ERROR",
8853
+ "The selected profile runtime could not be initialized.",
8854
+ { retryable: true }
8855
+ ));
8856
+ }
8857
+ }
8106
8858
  function spaceForAuthenticatedPrincipal(principal2) {
8107
8859
  if (principal2 === void 0) return void 0;
8108
8860
  try {
8109
- const pkh = nodeSdk4.parsePkhDid(principal2);
8110
- return pkh === null ? void 0 : nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
8861
+ const pkh = nodeSdk6.parsePkhDid(principal2);
8862
+ return pkh === null ? void 0 : nodeSdk6.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
8111
8863
  } catch {
8112
8864
  return void 0;
8113
8865
  }
@@ -8151,14 +8903,15 @@ function unresolvedSummary(target) {
8151
8903
  posture: "unauthenticated"
8152
8904
  };
8153
8905
  }
8154
- var nodeSdk4;
8906
+ var nodeSdk6;
8155
8907
  var init_runtime = __esm({
8156
8908
  "src/runtime.ts"() {
8157
8909
  "use strict";
8158
- nodeSdk4 = __toESM(require("@tinycloud/node-sdk"), 1);
8910
+ nodeSdk6 = __toESM(require("@tinycloud/node-sdk"), 1);
8159
8911
  init_authority();
8160
8912
  init_errors2();
8161
8913
  init_profile();
8914
+ init_recovery();
8162
8915
  init_secrets();
8163
8916
  init_state();
8164
8917
  init_delegation_binding();
@@ -8181,6 +8934,9 @@ init_errors2();
8181
8934
  init_authority();
8182
8935
  init_artifacts();
8183
8936
 
8937
+ // src/redaction.ts
8938
+ init_recovery();
8939
+
8184
8940
  // src/safe-values.ts
8185
8941
  function safeOriginHost(value) {
8186
8942
  if (typeof value !== "string" || value.length === 0) return void 0;
@@ -8257,7 +9013,25 @@ function redactOperationError(_definition, error) {
8257
9013
  }
8258
9014
  var SAFE_ERROR_DETAIL_POLICIES = {
8259
9015
  INPUT_INVALID: {
8260
- issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0
9016
+ issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0,
9017
+ reason: (value) => value === "OWNER_MISMATCH" || value === "OWNER_DID_UNKNOWN" ? value : void 0
9018
+ },
9019
+ AUTH_REQUIRED: {
9020
+ recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0,
9021
+ reason: (value) => value === "explicit_key" ? value : void 0
9022
+ },
9023
+ SESSION_NOT_FOUND: {
9024
+ recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0
9025
+ },
9026
+ PROFILE_MIGRATION_REQUIRED: {
9027
+ recovery: (value) => isSafeRecovery(value, "complete_migration") ? { ...value } : void 0
9028
+ },
9029
+ // `secrets.get` not found for an identity no request link serves.
9030
+ KV_NOT_FOUND: {
9031
+ recoveryKind: (value) => value === "put_locally" || value === "not_requestable" ? value : void 0,
9032
+ names: (value) => Array.isArray(value) && value.length >= 1 && value.length <= 10 && value.every((name) => typeof name === "string" && name.length <= 256 && /^[A-Z][A-Z0-9_]*$/.test(name)) ? [...value] : void 0,
9033
+ scope: (value) => typeof value === "string" && value.length <= 256 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(value) ? value : void 0,
9034
+ owner: (value) => typeof value === "string" && /^did:pkh:eip155:[1-9][0-9]*:0x[0-9a-fA-F]{40}$/.test(value) ? value : void 0
8261
9035
  },
8262
9036
  OPERATION_VERSION_UNSUPPORTED: {
8263
9037
  supportedVersions: (value) => Array.isArray(value) && value.every((item) => Number.isInteger(item)) ? value : void 0
@@ -8541,7 +9315,7 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
8541
9315
  )
8542
9316
  ) };
8543
9317
  }
8544
- if (target.privateKey === void 0 && definition.runtime === "authenticated" && isOwnerPosture(inspectionResolution.context.summary.posture) && target.allowOwnerProfile !== true) {
9318
+ if (target.privateKey === void 0 && requiresRuntime(definition.runtime) && isOwnerPosture(inspectionResolution.context.summary.posture) && target.allowOwnerProfile !== true) {
8545
9319
  return { status: "error", result: errorResult(
8546
9320
  operation,
8547
9321
  inspectionResolution.context.summary,
@@ -8551,15 +9325,15 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
8551
9325
  )
8552
9326
  ) };
8553
9327
  }
8554
- const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target, "authenticated");
9328
+ const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target, definition.runtime);
8555
9329
  if (!runtimeResolution.ok) {
8556
9330
  return { status: "error", result: errorResult(
8557
9331
  operation,
8558
9332
  runtimeResolution.context,
8559
- runtimeResolution.error
9333
+ redactOperationError(definition, runtimeResolution.error)
8560
9334
  ) };
8561
9335
  }
8562
- if (definition.runtime === "authenticated") {
9336
+ if (requiresRuntime(definition.runtime)) {
8563
9337
  const actualPosture = runtimeResolution.context.summary.posture;
8564
9338
  if (!definition.postures.includes(actualPosture)) {
8565
9339
  return { status: "error", result: errorResult(
@@ -9045,6 +9819,15 @@ function internalOperationErrorResult(prepared) {
9045
9819
  function isOwnerPosture(posture) {
9046
9820
  return posture === "owner-openkey" || posture === "local-owner-key";
9047
9821
  }
9822
+ function requiresRuntime(requirement) {
9823
+ switch (requirement) {
9824
+ case "authenticated":
9825
+ case "restored":
9826
+ return true;
9827
+ case "inspection":
9828
+ return false;
9829
+ }
9830
+ }
9048
9831
  function zodIssueDetails(error) {
9049
9832
  return {
9050
9833
  // The canonical error channel carries only schema-owned issue codes.