@tinycloud/cli 1.3.5-beta.7 → 1.3.5-beta.8

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.
@@ -899,10 +899,14 @@ import { TinyCloudNode, DelegatedAccess, NodeWasmBindings, activateValidatedRunt
899
899
  // ../courier/dist/profile.js
900
900
  import fs from "fs";
901
901
  import path from "path";
902
- import os from "os";
903
902
  import { randomBytes } from "crypto";
904
903
  function profileDir() {
905
- return process.env.COURIER_HOME || path.join(os.homedir(), ".courier");
904
+ if (process.env.COURIER_HOME)
905
+ return process.env.COURIER_HOME;
906
+ const home = process.env.HOME;
907
+ if (!home)
908
+ throw new CourierError("home-unset", "HOME is not set. Set HOME or COURIER_HOME to choose where Courier stores its state.");
909
+ return path.join(home, ".courier");
906
910
  }
907
911
  function profilePath(name2, home = profileDir()) {
908
912
  return path.join(home, name2);
@@ -2258,7 +2262,7 @@ import path3 from "path";
2258
2262
 
2259
2263
  // ../courier/dist/guard/paths.js
2260
2264
  import fs2 from "fs";
2261
- import os2 from "os";
2265
+ import os from "os";
2262
2266
  import path2 from "path";
2263
2267
  var CREDENTIAL_SUBSTRINGS = [
2264
2268
  ".config/gh",
@@ -2407,7 +2411,7 @@ function installRoots(entry) {
2407
2411
  function protectedSet(home, entry, extra = {}) {
2408
2412
  return {
2409
2413
  roots: [path2.resolve(home), ...installRoots(entry)],
2410
- homes: [.../* @__PURE__ */ new Set([os2.homedir(), ...extra.agentHome ? [extra.agentHome] : []])]
2414
+ homes: [.../* @__PURE__ */ new Set([os.homedir(), ...extra.agentHome ? [extra.agentHome] : []])]
2411
2415
  };
2412
2416
  }
2413
2417
  function isProtectedRead(file, cwd, set) {
@@ -4943,7 +4947,7 @@ function analyzeScript(text, parent, inexact) {
4943
4947
 
4944
4948
  // ../courier/dist/guard/install.js
4945
4949
  import fs4 from "fs";
4946
- import os3 from "os";
4950
+ import os2 from "os";
4947
4951
  import path6 from "path";
4948
4952
  import { createHash } from "crypto";
4949
4953
  var CREDENTIAL_FILES = [".config/gh/**", ".npmrc", ".netrc", ".ssh/**", ".aws/**", ".config/gcloud/**", ".docker/config.json", ".config/wrangler/**", ".config/.wrangler/**", ".git-credentials"];
@@ -4985,7 +4989,7 @@ var GATED_DENY = [
4985
4989
  ].map((prefix) => `Bash(${prefix}:*)`);
4986
4990
  var PRIVILEGE_DENY = ["sudo", "su", "doas", "pkexec", "tmux", "screen", "crontab", "systemctl", "ssh", "gh auth", "git credential", "secret-tool"].map((prefix) => `Bash(${prefix}:*)`);
4987
4991
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
4988
- function hookCommand(argv, home, realHome = os3.homedir()) {
4992
+ function hookCommand(argv, home, realHome = os2.homedir()) {
4989
4993
  return `COURIER_HOME=${shq(home)} COURIER_REAL_HOME=${shq(realHome)} ${argv.map(shq).join(" ")} || exit 2`;
4990
4994
  }
4991
4995
  var guardDir = (home) => path6.join(home, "guard");
@@ -5008,7 +5012,7 @@ function guardSettings(options) {
5008
5012
  for (const f of ["key.json", "grant.json"])
5009
5013
  deny2.push(`Read(${rulePath(path6.join(home, f))})`);
5010
5014
  for (const rel of CREDENTIAL_FILES)
5011
- deny2.push(`Read(${rulePath(path6.join(os3.homedir(), rel))})`);
5015
+ deny2.push(`Read(${rulePath(path6.join(os2.homedir(), rel))})`);
5012
5016
  for (const prog of programs)
5013
5017
  for (const sub2 of ["pair", "revoke", "init", "pause", "resume", "watch"])
5014
5018
  deny2.push(`Bash(${prog} ${sub2}:*)`);
@@ -5188,7 +5192,7 @@ function courierEnvOverrides(base, home, extra = []) {
5188
5192
  // ../courier/dist/wake.js
5189
5193
  import { spawn } from "child_process";
5190
5194
  import fs7 from "fs";
5191
- import os4 from "os";
5195
+ import os3 from "os";
5192
5196
  import path9 from "path";
5193
5197
 
5194
5198
  // ../courier/dist/guard/cleanhome.js
@@ -5567,7 +5571,7 @@ function createWaker(config, deps) {
5567
5571
  if (cwd === courier || cwd.startsWith(`${courier}${path9.sep}`))
5568
5572
  throw new Error("agent cwd must be outside the Courier tree");
5569
5573
  prepareCleanHome({
5570
- realHome: config.realHome ?? env.HOME ?? os4.homedir(),
5574
+ realHome: config.realHome ?? env.HOME ?? os3.homedir(),
5571
5575
  cleanHome: agentHome(config.home),
5572
5576
  claude: config.adapter !== "exec"
5573
5577
  });
@@ -5659,7 +5663,7 @@ ${UNGUARDED_NOTICE}.`
5659
5663
  }
5660
5664
  const existing = agents.find((a) => typeof a.id === "string" && typeof a.provider === "string" && isClaudeProvider(a.provider) && typeof a.cwd === "string" && sameDir(a.cwd, config.cwd))?.id;
5661
5665
  if (existing) {
5662
- const dir = fs7.mkdtempSync(path9.join(os4.tmpdir(), "courier-wake-"));
5666
+ const dir = fs7.mkdtempSync(path9.join(os3.tmpdir(), "courier-wake-"));
5663
5667
  const file = path9.join(dir, "prompt.txt");
5664
5668
  try {
5665
5669
  fs7.writeFileSync(file, prompt(request), { mode: 384 });
@@ -5803,7 +5807,7 @@ function createNotifier(config, deps) {
5803
5807
 
5804
5808
  // ../courier/dist/commands.js
5805
5809
  import fs9 from "fs";
5806
- import os5 from "os";
5810
+ import os4 from "os";
5807
5811
  import path11 from "path";
5808
5812
  import { NodeWasmBindings as NodeWasmBindings2, activateValidatedRuntimeDelegation as activateValidatedRuntimeDelegation2, deserializeDelegation as deserializeDelegation2, serializeDelegation as serializeDelegation2 } from "@tinycloud/node-sdk";
5809
5813
 
@@ -6273,7 +6277,7 @@ async function cmdInit(args) {
6273
6277
  version: 3,
6274
6278
  host,
6275
6279
  openkeyHost,
6276
- label: labelFlag ?? `courier \xB7 ${os5.hostname()}`,
6280
+ label: labelFlag ?? `courier \xB7 ${os4.hostname()}`,
6277
6281
  ...handleFlag ? { selfHandle: handleFlag } : {},
6278
6282
  courierDid: bareDid(key.did),
6279
6283
  status: "awaiting-grant",
@@ -6847,7 +6851,7 @@ ${DEFAULT_ORDERS}`);
6847
6851
 
6848
6852
  // ../courier/dist/watch-cmd.js
6849
6853
  import fs13 from "fs";
6850
- import os8 from "os";
6854
+ import os7 from "os";
6851
6855
  import path16 from "path";
6852
6856
  import { randomBytes as randomBytes2 } from "crypto";
6853
6857
  import { spawnSync as spawnSync4 } from "child_process";
@@ -8276,19 +8280,19 @@ var Watcher = class {
8276
8280
 
8277
8281
  // ../courier/dist/guard/command.js
8278
8282
  import fs11 from "fs";
8279
- import os7 from "os";
8283
+ import os6 from "os";
8280
8284
  import path14 from "path";
8281
8285
  import { spawnSync as spawnSync3 } from "child_process";
8282
8286
 
8283
8287
  // ../courier/dist/broker-config.js
8284
8288
  import fs10 from "fs";
8285
- import os6 from "os";
8289
+ import os5 from "os";
8286
8290
  import path12 from "path";
8287
8291
  var TEST_TOKEN = `ghp_${"d".repeat(36)}`;
8288
8292
  var ID = /^[a-f0-9]{24}$/;
8289
8293
  var PINNED = ["host", "openkeyHost", "spaceId", "ownerDid", "courierDid"];
8290
8294
  function brokerRoot(testOwnerRoot) {
8291
- const owner = os6.userInfo().homedir;
8295
+ const owner = os5.userInfo().homedir;
8292
8296
  if (testOwnerRoot !== void 0) {
8293
8297
  const root = realpathBest(testOwnerRoot);
8294
8298
  const scratch = realpathBest(path12.join(owner, "scratch"));
@@ -8297,7 +8301,7 @@ function brokerRoot(testOwnerRoot) {
8297
8301
  }
8298
8302
  if (fs10.existsSync(root)) {
8299
8303
  const st = fs10.statSync(root);
8300
- if (st.uid !== os6.userInfo().uid || st.mode & 63)
8304
+ if (st.uid !== os5.userInfo().uid || st.mode & 63)
8301
8305
  throw new CourierError("usage", "--test-owner-root must be owner-private (0700)");
8302
8306
  }
8303
8307
  const broker = realpathBest(path12.join(root, ".config/courier-broker"));
@@ -8353,7 +8357,7 @@ function loadBrokerConfig(id, testOwnerRoot) {
8353
8357
  try {
8354
8358
  for (const f of [path12.dirname(file), file]) {
8355
8359
  const st2 = fs10.lstatSync(f);
8356
- if (st2.isSymbolicLink() || st2.uid !== os6.userInfo().uid || st2.mode & 63)
8360
+ if (st2.isSymbolicLink() || st2.uid !== os5.userInfo().uid || st2.mode & 63)
8357
8361
  throw Error("not owner-private");
8358
8362
  }
8359
8363
  const config = JSON.parse(fs10.readFileSync(file, "utf8"));
@@ -8506,8 +8510,8 @@ function realEnv(cwd, home = profileDir(), entry = process.argv[1] ?? "") {
8506
8510
  binaries: installed,
8507
8511
  resolveProgram: (word, dir) => binaryName(word, dir, searchPath, installed),
8508
8512
  literals: [...installLiterals(home, entry), brokerRoot()],
8509
- protectedRoots: [home, brokerRoot(), path14.join(os7.homedir(), ".courier")],
8510
- realHome: process.env.COURIER_REAL_HOME || os7.userInfo().homedir,
8513
+ protectedRoots: [home, brokerRoot(), path14.join(os6.homedir(), ".courier")],
8514
+ realHome: process.env.COURIER_REAL_HOME || os6.userInfo().homedir,
8511
8515
  courierEntries: installed.filter((b) => b.path === realpathBest(entry) && ["courier", "tc"].includes(b.name)).map((b) => b.path),
8512
8516
  readFile(file) {
8513
8517
  try {
@@ -8578,10 +8582,10 @@ async function cmdGuard(args) {
8578
8582
  const input = JSON.parse(readStdin());
8579
8583
  const home = profileDir();
8580
8584
  const entry = process.argv[1] ?? "";
8581
- const realHome = process.env.COURIER_REAL_HOME || os7.homedir();
8585
+ const realHome = process.env.COURIER_REAL_HOME || os6.homedir();
8582
8586
  const deps = {
8583
8587
  env: realEnv(process.cwd(), home, entry),
8584
- protectedSet: { ...protectedSet(home, entry, { agentHome: os7.homedir() }), roots: [...protectedSet(home, entry).roots, brokerRoot(), path14.join(os7.homedir(), ".courier")], homes: [.../* @__PURE__ */ new Set([realHome, os7.homedir()])], realHome }
8588
+ protectedSet: { ...protectedSet(home, entry, { agentHome: os6.homedir() }), roots: [...protectedSet(home, entry).roots, brokerRoot(), path14.join(os6.homedir(), ".courier")], homes: [.../* @__PURE__ */ new Set([realHome, os6.homedir()])], realHome }
8585
8589
  };
8586
8590
  const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("timed out")), DEADLINE_MS).unref());
8587
8591
  const decision = await Promise.race([decide(input, deps), timeout]);
@@ -8950,7 +8954,7 @@ async function installService(args, config) {
8950
8954
  logFile: path16.join(profileDir(), `watch${name2 ? `-${name2}` : ""}.log`),
8951
8955
  name: name2
8952
8956
  };
8953
- const plan = launchd ? launchdPlan(spec, os8.homedir(), process.getuid?.() ?? 0) : systemdPlan(spec, os8.homedir());
8957
+ const plan = launchd ? launchdPlan(spec, os7.homedir(), process.getuid?.() ?? 0) : systemdPlan(spec, os7.homedir());
8954
8958
  if (dry) {
8955
8959
  process.stdout.write(renderPlan(plan));
8956
8960
  return;
@@ -10752,7 +10756,7 @@ ${targetHelp()}`);
10752
10756
  // ../courier/dist/broker.js
10753
10757
  import { execFile } from "child_process";
10754
10758
  import fs18 from "fs";
10755
- import os9 from "os";
10759
+ import os8 from "os";
10756
10760
  import path20 from "path";
10757
10761
  var MAX_OUTPUT = 4e3;
10758
10762
  var EXEC_TIMEOUT_MS = 12e4;
@@ -10761,7 +10765,7 @@ var validToken = (token2) => TOKEN.test(token2);
10761
10765
  async function loadBrokerCredential(config, exec = execFileRunner) {
10762
10766
  if (config.testToken === TEST_TOKEN)
10763
10767
  return { ghBin: config.ghBin, token: TEST_TOKEN };
10764
- const ownerHome = os9.userInfo().homedir;
10768
+ const ownerHome = os8.userInfo().homedir;
10765
10769
  const result = await exec(config.ghBin, ["auth", "token", "--hostname", "github.com"], {
10766
10770
  cwd: ownerHome,
10767
10771
  timeoutMs: 1e4,
@@ -10982,8 +10986,11 @@ import { credentialRequirementDigest } from "@tinycloud/sdk-core";
10982
10986
  import fs19 from "fs";
10983
10987
  import path21 from "path";
10984
10988
  import { createHash as createHash4, randomBytes as randomBytes5 } from "crypto";
10985
- import { createStoredCredentialRecord, credentialMatchesRequirement } from "@tinycloud/sdk-core";
10989
+ import { checkSecureDir, ensureSecureDir as ensureSecureDir2 } from "@tinycloud/node-sdk/credential-pending-store";
10990
+ import { verifyStoredEmailProof } from "@tinycloud/node-sdk/credentials";
10991
+ import { createStoredCredentialRecord, EMAIL_CREDENTIAL_MAX_AGE_SECONDS } from "@tinycloud/sdk-core";
10986
10992
  var DIR_NAME = "credentials";
10993
+ var EXPIRY_MARGIN_MS = 6e4;
10987
10994
  function credentialsDir(home) {
10988
10995
  return path21.join(home, DIR_NAME);
10989
10996
  }
@@ -11015,25 +11022,6 @@ function assertOwnedByCurrentUser(st, label2) {
11015
11022
  throw new CourierError("credential-permissions", `${label2} is not owned by the current user.`);
11016
11023
  }
11017
11024
  }
11018
- function ensureSecureDir2(home) {
11019
- const dir = credentialsDir(home);
11020
- mkdirOwnerOnly(dir);
11021
- const st = fs19.lstatSync(dir);
11022
- if (!st.isDirectory())
11023
- throw new CourierError("credential-permissions", `${dir} must be a real directory, not a symlink or file.`);
11024
- assertOwnedByCurrentUser(st, dir);
11025
- fs19.chmodSync(dir, 448);
11026
- }
11027
- function checkSecureDir(home) {
11028
- const dir = credentialsDir(home);
11029
- const st = lstatOrUndefined2(dir);
11030
- if (!st)
11031
- return false;
11032
- if (!st.isDirectory())
11033
- throw new CourierError("credential-permissions", `${dir} must be a real directory, not a symlink or file.`);
11034
- assertOwnerOnly(st, dir, "0700");
11035
- return true;
11036
- }
11037
11025
  function writeOwnerOnly(home, file, obj) {
11038
11026
  ensureSecureDir2(home);
11039
11027
  const existing = lstatOrUndefined2(file);
@@ -11094,8 +11082,10 @@ function removeFile(file) {
11094
11082
  }
11095
11083
  var StoredCredentialSource = class {
11096
11084
  home;
11097
- constructor(home = profileDir()) {
11085
+ verify;
11086
+ constructor(home = profileDir(), verify = verifyStoredEmailProof) {
11098
11087
  this.home = home;
11088
+ this.verify = verify;
11099
11089
  }
11100
11090
  async find(input) {
11101
11091
  const file = credentialFile(this.home, input.holderDid, input.requirementDigest);
@@ -11103,26 +11093,48 @@ var StoredCredentialSource = class {
11103
11093
  if (!record)
11104
11094
  return void 0;
11105
11095
  const now = input.now ?? /* @__PURE__ */ new Date();
11106
- if (!credentialMatchesRequirement(input.requirement, record, input.holderDid, now)) {
11096
+ const authenticatedAt = Math.max(Date.parse(record.issuedAt), Date.parse(record.notBefore));
11097
+ try {
11098
+ if (!Number.isFinite(authenticatedAt) || !Number.isFinite(Date.parse(record.expiresAt)))
11099
+ throw new Error("stored record has no usable dates");
11100
+ await this.verify({ record, requirement: input.requirement, holderDid: input.holderDid, now: new Date(authenticatedAt) });
11101
+ } catch {
11102
+ throw new CourierError("credential-record-invalid", "Stored credential failed offline verification and was not returned. Run `credential acquire` again.");
11103
+ }
11104
+ const expired = Date.parse(record.expiresAt) - EXPIRY_MARGIN_MS <= now.getTime();
11105
+ const stale = now.getTime() - Date.parse(record.issuedAt) > EMAIL_CREDENTIAL_MAX_AGE_SECONDS * 1e3;
11106
+ if (expired || stale) {
11107
11107
  removeFile(file);
11108
11108
  return void 0;
11109
11109
  }
11110
11110
  return record;
11111
11111
  }
11112
11112
  async store(input) {
11113
- const record = await createStoredCredentialRecord({
11114
- verified: input.verified,
11113
+ const now = input.now ?? /* @__PURE__ */ new Date();
11114
+ const base = await createStoredCredentialRecord({
11115
+ verified: input.result.verified,
11115
11116
  ownerDid: input.holderDid,
11116
11117
  requirement: input.requirement,
11117
11118
  requirementDigest: input.requirementDigest,
11118
- storedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString()
11119
+ storedAt: now.toISOString()
11119
11120
  });
11121
+ const record = {
11122
+ ...base,
11123
+ subjectDid: input.result.envelope.subjectDid,
11124
+ descriptor: input.result.descriptor
11125
+ };
11126
+ try {
11127
+ await this.verify({ record, requirement: input.requirement, holderDid: input.holderDid, now });
11128
+ } catch {
11129
+ throw new CourierError("credential-record-invalid", "Issued credential failed offline verification and was not stored. Request a new code.");
11130
+ }
11120
11131
  writeOwnerOnly(this.home, credentialFile(this.home, input.holderDid, input.requirementDigest), record);
11121
11132
  return record;
11122
11133
  }
11123
11134
  };
11124
11135
 
11125
11136
  // ../courier/dist/credential.js
11137
+ var CODE_WINDOW_MS = 5 * 6e4;
11126
11138
  var acquireUsage = () => `Usage: ${cmd("credential acquire --email <addr> [--code-stdin]")}`;
11127
11139
  var showUsage = () => `Usage: ${cmd("credential show --email <addr>")}`;
11128
11140
  var credentialUsage = () => `${acquireUsage()}
@@ -11188,7 +11200,9 @@ async function acquire(args, deps) {
11188
11200
  if (older && older.requestId !== pending2.requestId)
11189
11201
  await store.delete(older.requestId);
11190
11202
  });
11191
- deps.out(`Verification code sent to ${address}. Run \`${cmd(`credential acquire --email ${address} --code-stdin`)}\` and enter the code within 15 minutes.
11203
+ const codeExpiresAt = new Date(Math.min(Date.parse(pending2.expiresAt), Date.parse(pending2.createdAt) + CODE_WINDOW_MS)).toISOString();
11204
+ deps.out(JSON.stringify({ ok: true, status: "code-sent", address, codeExpiresAt }, null, 2) + "\n");
11205
+ deps.err(`Verification code sent to ${address}. Run \`${cmd(`credential acquire --email ${address} --code-stdin`)}\` and enter the code within 5 minutes (before ${codeExpiresAt}).
11192
11206
  `);
11193
11207
  return;
11194
11208
  }
@@ -11216,15 +11230,19 @@ async function acquire(args, deps) {
11216
11230
  }
11217
11231
  const requirement = emailProofRequirement(address);
11218
11232
  const requirementDigest = await credentialRequirementDigest(requirement);
11219
- await new StoredCredentialSource(deps.home).store({
11220
- verified: result.verified,
11221
- requirement,
11222
- requirementDigest,
11223
- holderDid: holder.did,
11224
- now: deps.now()
11225
- });
11226
- await withStoreErrors(() => store.delete(pending.requestId));
11227
- deps.out(`Email credential stored for ${address}. It is valid for up to 1 hour from issuance.
11233
+ try {
11234
+ await new StoredCredentialSource(deps.home, deps.verifyStored).store({
11235
+ result,
11236
+ requirement,
11237
+ requirementDigest,
11238
+ holderDid: holder.did,
11239
+ now: deps.now()
11240
+ });
11241
+ } finally {
11242
+ await withStoreErrors(() => store.delete(pending.requestId));
11243
+ }
11244
+ deps.out(JSON.stringify({ ok: true, status: "acquired", address, issuedAt: result.issuedAt, expiresAt: result.expiresAt }, null, 2) + "\n");
11245
+ deps.err(`Email credential stored for ${address}. Valid until ${result.expiresAt}.
11228
11246
  `);
11229
11247
  }
11230
11248
  async function show(args, deps) {
@@ -11232,7 +11250,7 @@ async function show(args, deps) {
11232
11250
  const holder = holderFor(deps.loadKey(deps.home));
11233
11251
  const requirement = emailProofRequirement(address);
11234
11252
  const requirementDigest = await credentialRequirementDigest(requirement);
11235
- const record = await new StoredCredentialSource(deps.home).find({
11253
+ const record = await new StoredCredentialSource(deps.home, deps.verifyStored).find({
11236
11254
  requirement,
11237
11255
  requirementDigest,
11238
11256
  holderDid: holder.did,
@@ -11241,14 +11259,7 @@ async function show(args, deps) {
11241
11259
  if (!record) {
11242
11260
  throw new CourierError("credential.show.NOT_FOUND", `No valid stored email credential for ${address}. Run \`${cmd(`credential acquire --email ${address}`)}\`.`);
11243
11261
  }
11244
- deps.out([
11245
- `email: ${record.claims.email}`,
11246
- `credentialId: ${record.credentialId}`,
11247
- `issuedAt: ${record.issuedAt}`,
11248
- `expiresAt: ${record.expiresAt}`,
11249
- `status: ${record.status.method}`,
11250
- ""
11251
- ].join("\n"));
11262
+ deps.out(JSON.stringify({ ok: true, address, credentialId: record.credentialId, issuedAt: record.issuedAt, expiresAt: record.expiresAt, status: record.status.method }, null, 2) + "\n");
11252
11263
  }
11253
11264
  async function cmdCredential(args, deps = {}) {
11254
11265
  const resolved = {
@@ -11258,9 +11269,13 @@ async function cmdCredential(args, deps = {}) {
11258
11269
  loadKey: deps.loadKey ?? ((home) => loadKey(home)),
11259
11270
  requestCode: deps.requestCode ?? requestEmailProofCode,
11260
11271
  completeProof: deps.completeProof ?? completeEmailProof,
11272
+ verifyStored: deps.verifyStored,
11261
11273
  readStdinLine: deps.readStdinLine ?? readStdinFirstLine,
11262
11274
  out: deps.out ?? ((text) => {
11263
11275
  process.stdout.write(text);
11276
+ }),
11277
+ err: deps.err ?? ((text) => {
11278
+ process.stderr.write(text);
11264
11279
  })
11265
11280
  };
11266
11281
  switch (args.positional[0]) {
@@ -11273,6 +11288,15 @@ async function cmdCredential(args, deps = {}) {
11273
11288
  }
11274
11289
  }
11275
11290
 
11291
+ // ../courier/dist/version.js
11292
+ import { readFileSync } from "fs";
11293
+ function courierVersion() {
11294
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
11295
+ if (typeof pkg.version !== "string" || pkg.version === "")
11296
+ throw new Error("courier package.json has no version");
11297
+ return pkg.version;
11298
+ }
11299
+
11276
11300
  // ../courier/dist/main.js
11277
11301
  function usage() {
11278
11302
  const name2 = programName();
@@ -11391,6 +11415,11 @@ api.openkey.so for openkey.so, otherwise the OpenKey host).
11391
11415
  async function run(argv) {
11392
11416
  const args = parseArgs(argv);
11393
11417
  const command = args.positional[0];
11418
+ if (!command && args.has("version")) {
11419
+ process.stdout.write(`${courierVersion()}
11420
+ `);
11421
+ return;
11422
+ }
11394
11423
  if (command === "request" && args.has("help")) {
11395
11424
  process.stdout.write(requestHelp());
11396
11425
  return;