@datarails-ai/dr-cli 1.6.139 → 1.6.141

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@datarails-ai/dr-cli",
11
- version: "1.6.139",
11
+ version: "1.6.141",
12
12
  description: "Datarails CLI",
13
13
  type: "module",
14
14
  engines: {
@@ -40,7 +40,7 @@ var package_default = {
40
40
  postinstall: "node scripts/postinstall.mjs"
41
41
  },
42
42
  dependencies: {
43
- "@datarails-ai/dr-appspace-cli": "0.12.14",
43
+ "@datarails-ai/dr-appspace-cli": "0.12.15",
44
44
  "@modelcontextprotocol/sdk": "^1.29.0",
45
45
  "@napi-rs/keyring": "^1.3.0",
46
46
  commander: "^12.1.0",
@@ -69,7 +69,7 @@ var package_default = {
69
69
  };
70
70
 
71
71
  // src/buildInfo.ts
72
- var BUILD_TIME = "2026-10-15T21:07:03.000Z";
72
+ var BUILD_TIME = "2026-10-19T11:02:10.572Z";
73
73
  function formatBuildTime(iso) {
74
74
  if (iso === "") return "";
75
75
  const d = new Date(iso);
@@ -4282,6 +4282,575 @@ function isENOENT(err) {
4282
4282
  return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
4283
4283
  }
4284
4284
 
4285
+ // src/scopes/matcher.ts
4286
+ var SCOPE_RE = /^(\*|[a-z][a-z0-9-]*):(\*|read|write|destructive)$/;
4287
+ var ACTION_RANK = new Map(
4288
+ ["read", "write", "destructive"].map((action, rank) => [action, rank])
4289
+ );
4290
+ function actionSatisfies(granted, wanted) {
4291
+ if (granted === "*" || granted === wanted) return true;
4292
+ const g = ACTION_RANK.get(granted);
4293
+ const w = ACTION_RANK.get(wanted);
4294
+ if (g === void 0 || w === void 0) return false;
4295
+ return g > w;
4296
+ }
4297
+ var WHITESPACE = "\\t\\n\\v\\f\\r \\x1c-\\x1f\\x85\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff";
4298
+ var TRIM_RE = new RegExp(`^[${WHITESPACE}]+|[${WHITESPACE}]+$`, "g");
4299
+ var SPLIT_RE = new RegExp(`[${WHITESPACE}]+`);
4300
+ function trimShared(value) {
4301
+ return value.replace(TRIM_RE, "");
4302
+ }
4303
+ function splitScopeString(value) {
4304
+ return value.split(SPLIT_RE).filter((part) => part !== "");
4305
+ }
4306
+ function parseScope(value) {
4307
+ if (typeof value !== "string") return null;
4308
+ const m = SCOPE_RE.exec(trimShared(value).toLowerCase());
4309
+ return m ? [m[1], m[2]] : null;
4310
+ }
4311
+ function parsed(scopes) {
4312
+ const list28 = scopes ?? [];
4313
+ const entries = typeof list28 === "string" ? splitScopeString(list28) : Array.isArray(list28) ? list28 : [];
4314
+ return entries.map(parseScope).filter((p) => p !== null);
4315
+ }
4316
+ function scopeEntries(scopes) {
4317
+ return parsed(scopes);
4318
+ }
4319
+ function isAgentScoped(scopes) {
4320
+ return parsed(scopes).length > 0;
4321
+ }
4322
+ function isAllowed(scopes, resource, action) {
4323
+ const entries = parsed(scopes);
4324
+ if (entries.length === 0) return true;
4325
+ const res = trimShared(resource).toLowerCase();
4326
+ const act = trimShared(action).toLowerCase();
4327
+ return entries.some(([r, a]) => (r === "*" || r === res) && actionSatisfies(a, act));
4328
+ }
4329
+ function isEntryAllowed(scopes, entry) {
4330
+ return entry.requires.every((r) => isAllowed(scopes, r.resource, r.action));
4331
+ }
4332
+
4333
+ // src/scopes/scopesMap.ts
4334
+ import { readFileSync } from "fs";
4335
+ import * as path3 from "path";
4336
+ import { fileURLToPath } from "url";
4337
+ function isDeniedEntry(entry) {
4338
+ return entry !== void 0 && Object.prototype.hasOwnProperty.call(entry, "denied");
4339
+ }
4340
+ var ACTIONS = /* @__PURE__ */ new Set(["read", "write", "destructive"]);
4341
+ var RESOURCE_RE = /^[a-z][a-z0-9-]*$/;
4342
+ function isValidResourceName(v) {
4343
+ return RESOURCE_RE.test(v);
4344
+ }
4345
+ function isAction(v) {
4346
+ return typeof v === "string" && ACTIONS.has(v);
4347
+ }
4348
+ function isResource(v) {
4349
+ return typeof v === "string" && isValidResourceName(v);
4350
+ }
4351
+ function parseRequirement(v) {
4352
+ if (typeof v !== "object" || v === null) return null;
4353
+ const o = v;
4354
+ if (!isResource(o.resource) || !isAction(o.action)) return null;
4355
+ return { resource: o.resource, action: o.action };
4356
+ }
4357
+ function parseDenial(v) {
4358
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return null;
4359
+ const o = v;
4360
+ if (typeof o.reason !== "string" || o.reason.trim() === "") return null;
4361
+ if (typeof o.until !== "string" || o.until.trim() === "") return null;
4362
+ return { reason: o.reason, until: o.until };
4363
+ }
4364
+ function parseEntry(v) {
4365
+ if (typeof v !== "object" || v === null) return null;
4366
+ const o = v;
4367
+ if (Object.prototype.hasOwnProperty.call(o, "denied")) {
4368
+ if (Object.prototype.hasOwnProperty.call(o, "requires")) return null;
4369
+ const denied = parseDenial(o.denied);
4370
+ if (denied === null) return null;
4371
+ return { denied };
4372
+ }
4373
+ if (!Array.isArray(o.requires)) return null;
4374
+ const requires = [];
4375
+ for (const raw of o.requires) {
4376
+ const req = parseRequirement(raw);
4377
+ if (req === null) return null;
4378
+ requires.push(req);
4379
+ }
4380
+ return { requires };
4381
+ }
4382
+ function emptyTable() {
4383
+ return /* @__PURE__ */ Object.create(null);
4384
+ }
4385
+ function parseStringMap(v) {
4386
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4387
+ const out = emptyTable();
4388
+ for (const [k, val] of Object.entries(v)) {
4389
+ if (typeof val === "string") out[k] = val;
4390
+ }
4391
+ return out;
4392
+ }
4393
+ function parseResources(v) {
4394
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4395
+ const out = emptyTable();
4396
+ for (const [resource, val] of Object.entries(v)) {
4397
+ if (!isResource(resource)) continue;
4398
+ if (typeof val !== "object" || val === null || Array.isArray(val)) continue;
4399
+ const o = val;
4400
+ if (typeof o.product !== "string" || o.product === "") continue;
4401
+ if (typeof o.productName !== "string" || o.productName === "") continue;
4402
+ out[resource] = { product: o.product, productName: o.productName };
4403
+ }
4404
+ return out;
4405
+ }
4406
+ function parsePendingCommands(v, pending) {
4407
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4408
+ const out = emptyTable();
4409
+ for (const [group, val] of Object.entries(v)) {
4410
+ if (pending === void 0 || !Object.prototype.hasOwnProperty.call(pending, group)) continue;
4411
+ if (!Array.isArray(val)) continue;
4412
+ if (!val.every((p) => typeof p === "string" && p.split(" ")[0] === group)) continue;
4413
+ out[group] = val;
4414
+ }
4415
+ return out;
4416
+ }
4417
+ function parseScopesMap(text2) {
4418
+ let raw;
4419
+ try {
4420
+ raw = JSON.parse(text2);
4421
+ } catch {
4422
+ return void 0;
4423
+ }
4424
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
4425
+ const o = raw;
4426
+ if (o.version !== 1 || o.cliVersion !== void 0 && typeof o.cliVersion !== "string" || typeof o.commands !== "object" || o.commands === null || Array.isArray(o.commands))
4427
+ return void 0;
4428
+ const commands2 = emptyTable();
4429
+ for (const [k, v] of Object.entries(o.commands)) {
4430
+ const entry = parseEntry(v);
4431
+ if (entry === null) return void 0;
4432
+ commands2[k] = entry;
4433
+ }
4434
+ const map = { version: 1, commands: commands2 };
4435
+ if (typeof o.cliVersion === "string") map.cliVersion = o.cliVersion;
4436
+ if (typeof o._contract === "string") map._contract = o._contract;
4437
+ const aliases = parseStringMap(o.aliases);
4438
+ if (aliases !== void 0) map.aliases = aliases;
4439
+ const pending = parseStringMap(o.pending);
4440
+ if (pending !== void 0) map.pending = pending;
4441
+ const pendingCommands = parsePendingCommands(o.pendingCommands, pending);
4442
+ if (pendingCommands !== void 0) map.pendingCommands = pendingCommands;
4443
+ const resources = parseResources(o.resources);
4444
+ if (resources !== void 0) map.resources = resources;
4445
+ const deferred = new Set(Object.values(pendingCommands ?? {}).flat());
4446
+ for (const cmdPath of Object.keys(commands2)) {
4447
+ if (deferred.has(cmdPath)) return void 0;
4448
+ if (pending !== void 0 && Object.prototype.hasOwnProperty.call(pending, cmdPath.split(" ")[0]))
4449
+ return void 0;
4450
+ }
4451
+ return map;
4452
+ }
4453
+ function loadScopesMap(here = path3.dirname(fileURLToPath(import.meta.url))) {
4454
+ let text2;
4455
+ try {
4456
+ text2 = readFileSync(path3.join(here, "scopes-map.json"), "utf8");
4457
+ } catch {
4458
+ return void 0;
4459
+ }
4460
+ return parseScopesMap(text2);
4461
+ }
4462
+ function scopesMapKey(cmd) {
4463
+ const names = [];
4464
+ for (let c = cmd; c !== null && c.parent !== null; c = c.parent)
4465
+ names.unshift(c.name());
4466
+ return names.join(" ");
4467
+ }
4468
+ function commandPathOf(argv, root) {
4469
+ const names = [];
4470
+ let cmd = root;
4471
+ for (let i = 0; i < argv.length; i += 1) {
4472
+ const token = argv[i];
4473
+ if (token === "--") break;
4474
+ if (token.startsWith("-") && token !== "-") {
4475
+ if (!token.startsWith("--") && token.length > 2) continue;
4476
+ if (token.includes("=")) continue;
4477
+ if (takesValue(token, cmd)) i += 1;
4478
+ continue;
4479
+ }
4480
+ const child2 = childNamed(cmd, token);
4481
+ if (child2 === void 0) break;
4482
+ names.push(child2.name());
4483
+ cmd = child2;
4484
+ }
4485
+ return names.join(" ");
4486
+ }
4487
+ function childNamed(cmd, token) {
4488
+ return cmd.commands.find((c) => c.name() === token || c.aliases().includes(token));
4489
+ }
4490
+ function takesValue(flag, cmd) {
4491
+ for (let c = cmd; c !== null; c = c.parent) {
4492
+ for (const opt of c.options) {
4493
+ if (opt.short !== flag && opt.long !== flag) continue;
4494
+ return opt.required || opt.optional;
4495
+ }
4496
+ }
4497
+ return declaredWithValueBelow(flag, cmd);
4498
+ }
4499
+ function declaredWithValueBelow(flag, cmd) {
4500
+ for (const child2 of cmd.commands) {
4501
+ for (const opt of child2.options) {
4502
+ if ((opt.short === flag || opt.long === flag) && (opt.required || opt.optional)) return true;
4503
+ }
4504
+ if (declaredWithValueBelow(flag, child2)) return true;
4505
+ }
4506
+ return false;
4507
+ }
4508
+
4509
+ // src/scopes/filter.ts
4510
+ var SCOPE_EXIT_CODE = 3;
4511
+ var ScopeFilterError = class extends Error {
4512
+ constructor(command, message) {
4513
+ super(message);
4514
+ this.command = command;
4515
+ }
4516
+ command;
4517
+ exitCode = SCOPE_EXIT_CODE;
4518
+ };
4519
+ var ScopeHiddenError = class extends ScopeFilterError {
4520
+ constructor(command, required2) {
4521
+ super(
4522
+ command,
4523
+ `'dr ${command}' is not available under this identity's credential scope (needs ${required2.join(", ")}); run 'dr whoami' to see the scopes this identity carries`
4524
+ );
4525
+ this.required = required2;
4526
+ this.name = "ScopeHiddenError";
4527
+ }
4528
+ required;
4529
+ };
4530
+ var ScopeUnavailableError = class extends ScopeFilterError {
4531
+ constructor(command, reason, until) {
4532
+ super(
4533
+ command,
4534
+ `'dr ${command}' is unavailable to any agent-scoped credential (one whose scopes claim carries at least one resource:action entry): ${reason} (this is not a missing scope \u2014 no scope grants it; ${until} lifts the restriction)`
4535
+ );
4536
+ this.reason = reason;
4537
+ this.until = until;
4538
+ this.name = "ScopeUnavailableError";
4539
+ }
4540
+ reason;
4541
+ until;
4542
+ };
4543
+ var ScopeGroupHiddenError = class extends ScopeFilterError {
4544
+ constructor(command, required2, tickets) {
4545
+ super(
4546
+ command,
4547
+ `'dr ${command}' has no subcommand available to this identity.` + (required2.length > 0 ? ` Its subcommands need ${required2.join(", ")} \u2014 run 'dr whoami' to see the scopes this identity carries.` : "") + (tickets.length > 0 ? ` Some are closed to any agent-scoped credential whatever its scopes (${tickets.join(", ")}).` : "")
4548
+ );
4549
+ this.required = required2;
4550
+ this.tickets = tickets;
4551
+ this.name = "ScopeGroupHiddenError";
4552
+ }
4553
+ required;
4554
+ tickets;
4555
+ };
4556
+ function hide(cmd) {
4557
+ cmd._hidden = true;
4558
+ }
4559
+ function refuseHelp(cmd, err) {
4560
+ cmd.outputHelp = () => {
4561
+ throw err;
4562
+ };
4563
+ }
4564
+ function leafAnswer(leaf, scopes, map) {
4565
+ const key = scopesMapKey(leaf);
4566
+ if (!Object.prototype.hasOwnProperty.call(map.commands, key)) return void 0;
4567
+ const entry = map.commands[key];
4568
+ if (entry === void 0) return void 0;
4569
+ if (isDeniedEntry(entry))
4570
+ return {
4571
+ err: new ScopeUnavailableError(key, entry.denied.reason, entry.denied.until),
4572
+ required: [],
4573
+ denial: entry.denied
4574
+ };
4575
+ if (isEntryAllowed(scopes, entry)) return void 0;
4576
+ const required2 = entry.requires.map((r) => `${r.resource}:${r.action}`);
4577
+ return { err: new ScopeHiddenError(key, required2), required: required2, denial: void 0 };
4578
+ }
4579
+ var intercepted = /* @__PURE__ */ new WeakMap();
4580
+ function intercept(cmd, err, armed) {
4581
+ intercepted.set(cmd, err);
4582
+ refuseHelp(cmd, err);
4583
+ cmd.action(() => {
4584
+ throw err;
4585
+ });
4586
+ const parent = cmd.parent;
4587
+ if (parent === null || armed.has(parent)) return;
4588
+ armed.add(parent);
4589
+ parent.hook("preSubcommand", (_parent, sub) => {
4590
+ const denial = intercepted.get(sub);
4591
+ if (denial !== void 0) throw denial;
4592
+ });
4593
+ }
4594
+ function interceptGroup(group, err) {
4595
+ group.allowExcessArguments(true);
4596
+ group.allowUnknownOption(true);
4597
+ group.helpCommand(true);
4598
+ refuseHelp(group, err);
4599
+ group.action(() => {
4600
+ throw err;
4601
+ });
4602
+ }
4603
+ function groupAnswer(group, below) {
4604
+ const key = scopesMapKey(group);
4605
+ const [mark] = below.denials;
4606
+ if (mark !== void 0 && below.denials.length === 1 && below.required.length === 0)
4607
+ return new ScopeUnavailableError(key, mark.reason, mark.until);
4608
+ if (below.required.length === 0 && below.denials.length === 0) return void 0;
4609
+ return new ScopeGroupHiddenError(
4610
+ key,
4611
+ below.required,
4612
+ below.denials.map((d) => d.until).filter((t, i, all) => all.indexOf(t) === i)
4613
+ );
4614
+ }
4615
+ function filterBelow(node, scopes, map, armed) {
4616
+ const here = { count: 0, answer: void 0, required: [], denials: [] };
4617
+ let allAnswered = node.commands.length > 0;
4618
+ for (const child2 of node.commands) {
4619
+ if (child2.commands.length > 0) {
4620
+ const below = filterBelow(child2, scopes, map, armed);
4621
+ here.count += below.count;
4622
+ if (below.answer === void 0) {
4623
+ allAnswered = false;
4624
+ continue;
4625
+ }
4626
+ interceptGroup(child2, below.answer);
4627
+ hide(child2);
4628
+ addRequired(here, below.required);
4629
+ addDenials(here, below.denials);
4630
+ continue;
4631
+ }
4632
+ const answer = leafAnswer(child2, scopes, map);
4633
+ if (answer === void 0) {
4634
+ allAnswered = false;
4635
+ continue;
4636
+ }
4637
+ intercept(child2, answer.err, armed);
4638
+ hide(child2);
4639
+ here.count += 1;
4640
+ addRequired(here, answer.required);
4641
+ if (answer.denial !== void 0) addDenials(here, [answer.denial]);
4642
+ }
4643
+ if (allAnswered) here.answer = groupAnswer(node, here);
4644
+ return here;
4645
+ }
4646
+ function addRequired(into, required2) {
4647
+ for (const r of required2) if (!into.required.includes(r)) into.required.push(r);
4648
+ }
4649
+ function addDenials(into, denials) {
4650
+ for (const d of denials)
4651
+ if (!into.denials.some((x) => x.reason === d.reason && x.until === d.until))
4652
+ into.denials.push(d);
4653
+ }
4654
+ function applyScopeFilter(root, scopes, map) {
4655
+ if (map === void 0 || !isAgentScoped(scopes)) return 0;
4656
+ return filterBelow(root, scopes, map, /* @__PURE__ */ new Set()).count;
4657
+ }
4658
+
4659
+ // src/commands/_tenantPin.ts
4660
+ var SERVER_VAR = "DR_PIN_SERVER";
4661
+ var ACCOUNT_VAR = "DR_PIN_ACCOUNT";
4662
+ var ORG_MODE_VAR = "DR_PIN_ORG_MODE";
4663
+ var TenantPinError = class extends Error {
4664
+ /** "You cannot do this", the same exit code a scope denial uses (spec §8). */
4665
+ exitCode = SCOPE_EXIT_CODE;
4666
+ constructor(message) {
4667
+ super(`tenant pin: ${message}`);
4668
+ this.name = "TenantPinError";
4669
+ }
4670
+ };
4671
+ var ORG_MODE_UNSCOPED_ALLOWED = /* @__PURE__ */ new Set([
4672
+ "login",
4673
+ "whoami",
4674
+ "users me",
4675
+ // Local only: skills, the CLI's own update, the Help Center, MCP registration, and
4676
+ // app-space scaffolding never write to the tenant.
4677
+ "skill list",
4678
+ "skill print",
4679
+ "skill install",
4680
+ "skill init-project",
4681
+ "update",
4682
+ "kb search",
4683
+ "kb get",
4684
+ "kb categories",
4685
+ "kb sections",
4686
+ "kb articles-in-section",
4687
+ "kb recent",
4688
+ "kb attachments",
4689
+ "kb article-images",
4690
+ "mcp register",
4691
+ "mcp unregister",
4692
+ "mcp list",
4693
+ "mcp serve",
4694
+ "app-space scaffold",
4695
+ "app-space contract",
4696
+ "app-space eject"
4697
+ ]);
4698
+ var ORG_MODE_READ_SCOPED_REFUSED = /* @__PURE__ */ new Set([
4699
+ "devices request-logs",
4700
+ "desk smtp test",
4701
+ "dashboards prepare",
4702
+ "storyboards prepare"
4703
+ ]);
4704
+ var LOCAL_COMMANDS_WITH_TARGET_FLAGS = /* @__PURE__ */ new Set([
4705
+ "app-space scaffold"
4706
+ ]);
4707
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+$/;
4708
+ function readTenantPin(env = process.env) {
4709
+ const rawServer = env[SERVER_VAR];
4710
+ const rawAccount = env[ACCOUNT_VAR];
4711
+ const rawOrgMode = env[ORG_MODE_VAR];
4712
+ if (rawServer === void 0 && rawAccount === void 0 && rawOrgMode === void 0) {
4713
+ return void 0;
4714
+ }
4715
+ if (rawServer === void 0 || rawServer.trim() === "") {
4716
+ throw new TenantPinError(`${SERVER_VAR} is required when any DR_PIN_* variable is set`);
4717
+ }
4718
+ let server;
4719
+ try {
4720
+ server = resolve(rawServer).key;
4721
+ } catch {
4722
+ throw new TenantPinError(`${SERVER_VAR} is not a known server key or Datarails URL`);
4723
+ }
4724
+ if (rawOrgMode !== void 0 && rawOrgMode !== "1") {
4725
+ throw new TenantPinError(`${ORG_MODE_VAR} must be 1 when set`);
4726
+ }
4727
+ if (rawOrgMode === "1") {
4728
+ if (rawAccount !== void 0) {
4729
+ throw new TenantPinError(
4730
+ `${ORG_MODE_VAR}=1 pins a server only; ${ACCOUNT_VAR} must not be set with it`
4731
+ );
4732
+ }
4733
+ return { kind: "org", server };
4734
+ }
4735
+ const account = normaliseAccount(rawAccount);
4736
+ if (account === void 0 || !EMAIL_RE.test(account)) {
4737
+ throw new TenantPinError(`${ACCOUNT_VAR} must be the login email (or set ${ORG_MODE_VAR}=1)`);
4738
+ }
4739
+ return { kind: "customer", server, account };
4740
+ }
4741
+ function describePin(pin) {
4742
+ return pin.kind === "customer" ? `${pin.server} as ${pin.account}` : `${pin.server} (org mode)`;
4743
+ }
4744
+ function assertFlagsWithinPin(pin, serverFlag, accountFlag) {
4745
+ assertServerFlagWithinPin(pin, serverFlag);
4746
+ assertAccountFlagWithinPin(pin, accountFlag);
4747
+ }
4748
+ function assertServerFlagWithinPin(pin, serverFlag) {
4749
+ if ((serverFlag ?? "").trim() === "") {
4750
+ throw new TenantPinError(
4751
+ `--server is required while dr is pinned to ${describePin(pin)}; pass --server ${pin.server}`
4752
+ );
4753
+ }
4754
+ const server = resolveServer(serverFlag);
4755
+ if (server !== pin.server) {
4756
+ throw new TenantPinError(
4757
+ `--server ${server} is outside the pin; this session may only reach ${describePin(pin)}`
4758
+ );
4759
+ }
4760
+ }
4761
+ function assertAccountFlagWithinPin(pin, accountFlag) {
4762
+ if (pin.kind !== "customer") return;
4763
+ const account = normaliseAccount(accountFlag);
4764
+ if (account === void 0) {
4765
+ throw new TenantPinError(
4766
+ `--account is required while dr is pinned to ${describePin(pin)}; pass --account ${pin.account}`
4767
+ );
4768
+ }
4769
+ if (account !== pin.account) {
4770
+ throw new TenantPinError(
4771
+ `--account ${account} is outside the pin; this session may only act as ${pin.account}`
4772
+ );
4773
+ }
4774
+ }
4775
+ function assertIdentityWithinPin(pin, serverKey, email) {
4776
+ let canonical = serverKey;
4777
+ try {
4778
+ canonical = resolve(serverKey).key;
4779
+ } catch {
4780
+ }
4781
+ if (canonical !== pin.server) {
4782
+ throw new TenantPinError(
4783
+ `server ${serverKey} is outside the pin; this session may only reach ${describePin(pin)}`
4784
+ );
4785
+ }
4786
+ if (pin.kind === "customer" && email !== pin.account) {
4787
+ throw new TenantPinError(
4788
+ `account ${email} is outside the pin; this session may only act as ${pin.account}`
4789
+ );
4790
+ }
4791
+ }
4792
+ function declaresOption(cmd, flag) {
4793
+ for (let c = cmd; c !== null; c = c.parent) {
4794
+ if (c.options.some((o) => o.long === flag)) return true;
4795
+ }
4796
+ return false;
4797
+ }
4798
+ function assertReadOnly(key, map) {
4799
+ if (map === void 0) {
4800
+ throw new TenantPinError(
4801
+ `org mode is read-only, and without scopes-map.json dr cannot tell whether \`dr ${key}\` writes`
4802
+ );
4803
+ }
4804
+ const entry = Object.prototype.hasOwnProperty.call(map.commands, key) ? map.commands[key] : void 0;
4805
+ const writes = entry === void 0 || isDeniedEntry(entry) || (entry.requires.length === 0 ? !ORG_MODE_UNSCOPED_ALLOWED.has(key) : entry.requires.some((r) => r.action !== "read") || ORG_MODE_READ_SCOPED_REFUSED.has(key));
4806
+ if (writes) {
4807
+ throw new TenantPinError(`org mode is read-only; \`dr ${key}\` is not a read-only command`);
4808
+ }
4809
+ }
4810
+ var defaultTenantPinDeps = {
4811
+ get env() {
4812
+ return process.env;
4813
+ },
4814
+ loadMap: () => loadScopesMap()
4815
+ };
4816
+ function enforceTenantPin(cmd, deps = defaultTenantPinDeps) {
4817
+ const pin = readTenantPin(deps.env);
4818
+ if (pin === void 0) return;
4819
+ const key = scopesMapKey(cmd);
4820
+ if (key === "logout") {
4821
+ throw new TenantPinError(`\`dr logout\` is refused while dr is pinned to ${describePin(pin)}`);
4822
+ }
4823
+ const opts = cmd.optsWithGlobals();
4824
+ if (key === "login-as") {
4825
+ const target = normaliseAccount(cmd.processedArgs[0]);
4826
+ if (pin.kind !== "customer" || target !== pin.account || opts.userId !== void 0) {
4827
+ throw new TenantPinError(
4828
+ `\`dr login-as\` may only target the pinned account, by email, while dr is pinned to ${describePin(pin)}`
4829
+ );
4830
+ }
4831
+ }
4832
+ for (const { flag, value, envVar } of [
4833
+ { flag: "--app-space-host", value: opts.appSpaceHost, envVar: "DR_APP_SPACE_HOST" },
4834
+ { flag: "--connectors-url", value: opts.connectorsUrl, envVar: void 0 }
4835
+ ]) {
4836
+ if (!declaresOption(cmd, flag)) continue;
4837
+ const fromEnv = envVar === void 0 ? "" : deps.env[envVar] ?? "";
4838
+ if ((value ?? "").trim() !== "" || fromEnv.trim() !== "") {
4839
+ throw new TenantPinError(
4840
+ `${flag}${envVar === void 0 ? "" : ` / ${envVar}`} is refused while dr is pinned to ${describePin(pin)}`
4841
+ );
4842
+ }
4843
+ }
4844
+ const required2 = !LOCAL_COMMANDS_WITH_TARGET_FLAGS.has(key);
4845
+ if (declaresOption(cmd, "--server") && (required2 || opts.server !== void 0)) {
4846
+ assertServerFlagWithinPin(pin, opts.server);
4847
+ }
4848
+ if (declaresOption(cmd, "--account") && (required2 || opts.account !== void 0)) {
4849
+ assertAccountFlagWithinPin(pin, opts.account);
4850
+ }
4851
+ if (pin.kind === "org") assertReadOnly(key, deps.loadMap());
4852
+ }
4853
+
4285
4854
  // src/telemetry/context.ts
4286
4855
  var NO_IDENTITY = {
4287
4856
  operator: "",
@@ -4551,10 +5120,13 @@ function batchSetupOptions(timeoutFlag) {
4551
5120
  var BATCH_TIMEOUT_FLAG_HELP = `Minimum whole-command budget in seconds. Default ${DEFAULT_CALL_TIMEOUT_MS / 1e3}s for one item; a batch gets ${DEFAULT_CALL_TIMEOUT_MS / 1e3}s + ${BATCH_PER_TARGET_TIMEOUT_MS / 1e3}s per call (capped at ${BATCH_MAX_TIMEOUT_MS / 1e3}s). This is a floor, so it only ever widens that budget; max ${BATCH_TIMEOUT_MAX_SECONDS}s`;
4552
5121
  async function setupCall(serverFlag, accountFlag, options = {}) {
4553
5122
  setConfirmCmdAccountFlag(normaliseAccount(accountFlag));
5123
+ const pin = readTenantPin();
5124
+ if (pin !== void 0) assertFlagsWithinPin(pin, serverFlag, accountFlag);
4554
5125
  const fileStore = await resolveFileStore(serverFlag);
4555
5126
  const key = fileStore?.key ?? resolveServer(serverFlag);
4556
5127
  const store = options.store ?? fileStore?.store ?? await KeyringStore.defaultWithCleanup();
4557
5128
  const email = await resolveAccount(store, key, accountFlag);
5129
+ if (pin !== void 0) assertIdentityWithinPin(pin, key, email);
4558
5130
  const stored = await store.getSession(key, email);
4559
5131
  if (stored !== void 0) {
4560
5132
  emitApiKeyExpiryWarning(stored, `${key}:${email}`);
@@ -4610,56 +5182,8 @@ async function setupCall(serverFlag, accountFlag, options = {}) {
4610
5182
  };
4611
5183
  }
4612
5184
 
4613
- // src/scopes/matcher.ts
4614
- var SCOPE_RE = /^(\*|[a-z][a-z0-9-]*):(\*|read|write|destructive)$/;
4615
- var ACTION_RANK = new Map(
4616
- ["read", "write", "destructive"].map((action, rank) => [action, rank])
4617
- );
4618
- function actionSatisfies(granted, wanted) {
4619
- if (granted === "*" || granted === wanted) return true;
4620
- const g = ACTION_RANK.get(granted);
4621
- const w = ACTION_RANK.get(wanted);
4622
- if (g === void 0 || w === void 0) return false;
4623
- return g > w;
4624
- }
4625
- var WHITESPACE = "\\t\\n\\v\\f\\r \\x1c-\\x1f\\x85\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff";
4626
- var TRIM_RE = new RegExp(`^[${WHITESPACE}]+|[${WHITESPACE}]+$`, "g");
4627
- var SPLIT_RE = new RegExp(`[${WHITESPACE}]+`);
4628
- function trimShared(value) {
4629
- return value.replace(TRIM_RE, "");
4630
- }
4631
- function splitScopeString(value) {
4632
- return value.split(SPLIT_RE).filter((part) => part !== "");
4633
- }
4634
- function parseScope(value) {
4635
- if (typeof value !== "string") return null;
4636
- const m = SCOPE_RE.exec(trimShared(value).toLowerCase());
4637
- return m ? [m[1], m[2]] : null;
4638
- }
4639
- function parsed(scopes) {
4640
- const list28 = scopes ?? [];
4641
- const entries = typeof list28 === "string" ? splitScopeString(list28) : Array.isArray(list28) ? list28 : [];
4642
- return entries.map(parseScope).filter((p) => p !== null);
4643
- }
4644
- function scopeEntries(scopes) {
4645
- return parsed(scopes);
4646
- }
4647
- function isAgentScoped(scopes) {
4648
- return parsed(scopes).length > 0;
4649
- }
4650
- function isAllowed(scopes, resource, action) {
4651
- const entries = parsed(scopes);
4652
- if (entries.length === 0) return true;
4653
- const res = trimShared(resource).toLowerCase();
4654
- const act = trimShared(action).toLowerCase();
4655
- return entries.some(([r, a]) => (r === "*" || r === res) && actionSatisfies(a, act));
4656
- }
4657
- function isEntryAllowed(scopes, entry) {
4658
- return entry.requires.every((r) => isAllowed(scopes, r.resource, r.action));
4659
- }
4660
-
4661
5185
  // src/scopes/render.ts
4662
- var ACTIONS = ["read", "write", "destructive"];
5186
+ var ACTIONS2 = ["read", "write", "destructive"];
4663
5187
  var OTHER = "Other";
4664
5188
  var ALL_PRODUCTS = "All products";
4665
5189
  var ALL_RESOURCES = "* (all resources)";
@@ -4673,7 +5197,7 @@ function describeScopes(scopes, resources) {
4673
5197
  const byResource = /* @__PURE__ */ new Map();
4674
5198
  for (const [resource, action] of entries) {
4675
5199
  const actions = byResource.get(resource) ?? /* @__PURE__ */ new Set();
4676
- if (action === "*") for (const a of ACTIONS) actions.add(a);
5200
+ if (action === "*") for (const a of ACTIONS2) actions.add(a);
4677
5201
  else actions.add(action);
4678
5202
  byResource.set(resource, actions);
4679
5203
  }
@@ -4703,7 +5227,7 @@ function renderTree(byResource, resources) {
4703
5227
  const width = Math.max(...[...byResource.keys()].map((r) => label(r).length));
4704
5228
  const row = (resource, indent) => {
4705
5229
  const actions = byResource.get(resource);
4706
- return `${indent}${label(resource).padEnd(width)} ${ACTIONS.filter((a) => actions.has(a)).join(", ")}`;
5230
+ return `${indent}${label(resource).padEnd(width)} ${ACTIONS2.filter((a) => actions.has(a)).join(", ")}`;
4707
5231
  };
4708
5232
  const sorted = [...byResource.keys()].sort();
4709
5233
  if (resources === void 0 || Object.keys(resources).length === 0)
@@ -4724,182 +5248,6 @@ function renderTree(byResource, resources) {
4724
5248
  return lines;
4725
5249
  }
4726
5250
 
4727
- // src/scopes/scopesMap.ts
4728
- import { readFileSync } from "fs";
4729
- import * as path3 from "path";
4730
- import { fileURLToPath } from "url";
4731
- function isDeniedEntry(entry) {
4732
- return entry !== void 0 && Object.prototype.hasOwnProperty.call(entry, "denied");
4733
- }
4734
- var ACTIONS2 = /* @__PURE__ */ new Set(["read", "write", "destructive"]);
4735
- var RESOURCE_RE = /^[a-z][a-z0-9-]*$/;
4736
- function isValidResourceName(v) {
4737
- return RESOURCE_RE.test(v);
4738
- }
4739
- function isAction(v) {
4740
- return typeof v === "string" && ACTIONS2.has(v);
4741
- }
4742
- function isResource(v) {
4743
- return typeof v === "string" && isValidResourceName(v);
4744
- }
4745
- function parseRequirement(v) {
4746
- if (typeof v !== "object" || v === null) return null;
4747
- const o = v;
4748
- if (!isResource(o.resource) || !isAction(o.action)) return null;
4749
- return { resource: o.resource, action: o.action };
4750
- }
4751
- function parseDenial(v) {
4752
- if (typeof v !== "object" || v === null || Array.isArray(v)) return null;
4753
- const o = v;
4754
- if (typeof o.reason !== "string" || o.reason.trim() === "") return null;
4755
- if (typeof o.until !== "string" || o.until.trim() === "") return null;
4756
- return { reason: o.reason, until: o.until };
4757
- }
4758
- function parseEntry(v) {
4759
- if (typeof v !== "object" || v === null) return null;
4760
- const o = v;
4761
- if (Object.prototype.hasOwnProperty.call(o, "denied")) {
4762
- if (Object.prototype.hasOwnProperty.call(o, "requires")) return null;
4763
- const denied = parseDenial(o.denied);
4764
- if (denied === null) return null;
4765
- return { denied };
4766
- }
4767
- if (!Array.isArray(o.requires)) return null;
4768
- const requires = [];
4769
- for (const raw of o.requires) {
4770
- const req = parseRequirement(raw);
4771
- if (req === null) return null;
4772
- requires.push(req);
4773
- }
4774
- return { requires };
4775
- }
4776
- function emptyTable() {
4777
- return /* @__PURE__ */ Object.create(null);
4778
- }
4779
- function parseStringMap(v) {
4780
- if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4781
- const out = emptyTable();
4782
- for (const [k, val] of Object.entries(v)) {
4783
- if (typeof val === "string") out[k] = val;
4784
- }
4785
- return out;
4786
- }
4787
- function parseResources(v) {
4788
- if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4789
- const out = emptyTable();
4790
- for (const [resource, val] of Object.entries(v)) {
4791
- if (!isResource(resource)) continue;
4792
- if (typeof val !== "object" || val === null || Array.isArray(val)) continue;
4793
- const o = val;
4794
- if (typeof o.product !== "string" || o.product === "") continue;
4795
- if (typeof o.productName !== "string" || o.productName === "") continue;
4796
- out[resource] = { product: o.product, productName: o.productName };
4797
- }
4798
- return out;
4799
- }
4800
- function parsePendingCommands(v, pending) {
4801
- if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
4802
- const out = emptyTable();
4803
- for (const [group, val] of Object.entries(v)) {
4804
- if (pending === void 0 || !Object.prototype.hasOwnProperty.call(pending, group)) continue;
4805
- if (!Array.isArray(val)) continue;
4806
- if (!val.every((p) => typeof p === "string" && p.split(" ")[0] === group)) continue;
4807
- out[group] = val;
4808
- }
4809
- return out;
4810
- }
4811
- function parseScopesMap(text2) {
4812
- let raw;
4813
- try {
4814
- raw = JSON.parse(text2);
4815
- } catch {
4816
- return void 0;
4817
- }
4818
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
4819
- const o = raw;
4820
- if (o.version !== 1 || o.cliVersion !== void 0 && typeof o.cliVersion !== "string" || typeof o.commands !== "object" || o.commands === null || Array.isArray(o.commands))
4821
- return void 0;
4822
- const commands2 = emptyTable();
4823
- for (const [k, v] of Object.entries(o.commands)) {
4824
- const entry = parseEntry(v);
4825
- if (entry === null) return void 0;
4826
- commands2[k] = entry;
4827
- }
4828
- const map = { version: 1, commands: commands2 };
4829
- if (typeof o.cliVersion === "string") map.cliVersion = o.cliVersion;
4830
- if (typeof o._contract === "string") map._contract = o._contract;
4831
- const aliases = parseStringMap(o.aliases);
4832
- if (aliases !== void 0) map.aliases = aliases;
4833
- const pending = parseStringMap(o.pending);
4834
- if (pending !== void 0) map.pending = pending;
4835
- const pendingCommands = parsePendingCommands(o.pendingCommands, pending);
4836
- if (pendingCommands !== void 0) map.pendingCommands = pendingCommands;
4837
- const resources = parseResources(o.resources);
4838
- if (resources !== void 0) map.resources = resources;
4839
- const deferred = new Set(Object.values(pendingCommands ?? {}).flat());
4840
- for (const cmdPath of Object.keys(commands2)) {
4841
- if (deferred.has(cmdPath)) return void 0;
4842
- if (pending !== void 0 && Object.prototype.hasOwnProperty.call(pending, cmdPath.split(" ")[0]))
4843
- return void 0;
4844
- }
4845
- return map;
4846
- }
4847
- function loadScopesMap(here = path3.dirname(fileURLToPath(import.meta.url))) {
4848
- let text2;
4849
- try {
4850
- text2 = readFileSync(path3.join(here, "scopes-map.json"), "utf8");
4851
- } catch {
4852
- return void 0;
4853
- }
4854
- return parseScopesMap(text2);
4855
- }
4856
- function scopesMapKey(cmd) {
4857
- const names = [];
4858
- for (let c = cmd; c !== null && c.parent !== null; c = c.parent)
4859
- names.unshift(c.name());
4860
- return names.join(" ");
4861
- }
4862
- function commandPathOf(argv, root) {
4863
- const names = [];
4864
- let cmd = root;
4865
- for (let i = 0; i < argv.length; i += 1) {
4866
- const token = argv[i];
4867
- if (token === "--") break;
4868
- if (token.startsWith("-") && token !== "-") {
4869
- if (!token.startsWith("--") && token.length > 2) continue;
4870
- if (token.includes("=")) continue;
4871
- if (takesValue(token, cmd)) i += 1;
4872
- continue;
4873
- }
4874
- const child2 = childNamed(cmd, token);
4875
- if (child2 === void 0) break;
4876
- names.push(child2.name());
4877
- cmd = child2;
4878
- }
4879
- return names.join(" ");
4880
- }
4881
- function childNamed(cmd, token) {
4882
- return cmd.commands.find((c) => c.name() === token || c.aliases().includes(token));
4883
- }
4884
- function takesValue(flag, cmd) {
4885
- for (let c = cmd; c !== null; c = c.parent) {
4886
- for (const opt of c.options) {
4887
- if (opt.short !== flag && opt.long !== flag) continue;
4888
- return opt.required || opt.optional;
4889
- }
4890
- }
4891
- return declaredWithValueBelow(flag, cmd);
4892
- }
4893
- function declaredWithValueBelow(flag, cmd) {
4894
- for (const child2 of cmd.commands) {
4895
- for (const opt of child2.options) {
4896
- if ((opt.short === flag || opt.long === flag) && (opt.required || opt.optional)) return true;
4897
- }
4898
- if (declaredWithValueBelow(flag, child2)) return true;
4899
- }
4900
- return false;
4901
- }
4902
-
4903
5251
  // src/commands/whoami.ts
4904
5252
  function registerWhoamiCommand(root) {
4905
5253
  const cmd = root.command("whoami").description(
@@ -31927,7 +32275,7 @@ function registerErrorEnrichmentsCommand(root) {
31927
32275
 
31928
32276
  // src/commands/skill.ts
31929
32277
  import { createHash as createHash8 } from "crypto";
31930
- import { readFile as readFile3, mkdir as mkdir2, readdir as readdir2, rm as rm2, rmdir, stat, writeFile as writeFile3 } from "fs/promises";
32278
+ import { lstat, readFile as readFile3, mkdir as mkdir2, readdir as readdir2, rm as rm2, rmdir, stat, writeFile as writeFile3 } from "fs/promises";
31931
32279
  import { dirname as dirname6, join as join9, resolve as resolve2 } from "path";
31932
32280
 
31933
32281
  // src/commands/_skillsRoot.ts
@@ -32107,6 +32455,9 @@ function skillDirAbs(targetAbs, dotDir, name) {
32107
32455
  return join9(targetAbs, dotDir, "skills", name);
32108
32456
  }
32109
32457
  var MANIFEST_FILE = ".dr-cli-manifest.json";
32458
+ var RETIRED_SKILLS = {
32459
+ "datarails-connectors-onboard-connector": "drip-implement-connector"
32460
+ };
32110
32461
  var MARKER_BEGIN = "<!-- dr-cli:begin -->";
32111
32462
  var MARKER_END = "<!-- dr-cli:end -->";
32112
32463
  var USERSPACE_ENV = "DR_CLI_USERSPACE_SKILLS";
@@ -32209,7 +32560,16 @@ async function runSkillInstall(cfg) {
32209
32560
  const skills = await getEmbeddedSkills();
32210
32561
  const verbose = cfg.verbose ?? false;
32211
32562
  const probe = await pluginManagedProbe(abs, cfg.env);
32212
- if (probe.managed && !(cfg.force ?? false)) {
32563
+ const pluginSkip = probe.managed && !(cfg.force ?? false);
32564
+ await removeRetiredSkills({
32565
+ targetAbs: abs,
32566
+ skills,
32567
+ // On a skipped install, a replacement whose plugin is absent is not written below.
32568
+ servedByPlugins: pluginSkip ? probe.installedPlugins : null,
32569
+ out: cfg.out,
32570
+ verbose
32571
+ });
32572
+ if (pluginSkip) {
32213
32573
  cfg.out.write(
32214
32574
  `Skipped ${join9(abs, ".claude", "skills")}: this machine gets the dr-cli skills from the dr-cli plugin (${probe.reason}). Pass --force to write them anyway.
32215
32575
  `
@@ -32350,9 +32710,34 @@ async function readManifest(skillDir) {
32350
32710
  return [];
32351
32711
  }
32352
32712
  }
32713
+ async function removeRetiredSkills(cfg) {
32714
+ const { targetAbs, out } = cfg;
32715
+ for (const [old, replacement] of Object.entries(RETIRED_SKILLS)) {
32716
+ for (const dotDir of [".claude", ".gemini"]) {
32717
+ const skillDir = skillDirAbs(targetAbs, dotDir, old);
32718
+ const removed = await removeUserSpaceCopy(skillDir);
32719
+ if (removed === null) continue;
32720
+ const files = `${removed.files} file${removed.files === 1 ? "" : "s"}`;
32721
+ out.write(
32722
+ `Removed retired skill ${old}: ${files} at ${skillDir} (now ${replacement})` + (removed.dirRemoved ? "" : "; directory kept, it holds files the CLI did not write") + "\n"
32723
+ );
32724
+ if (dotDir !== ".claude" || cfg.servedByPlugins === null) continue;
32725
+ const next = cfg.skills.find((s) => s.name === replacement);
32726
+ if (next === void 0 || cfg.servedByPlugins.has(next.plugin)) continue;
32727
+ await installSkillTree({
32728
+ skillDir: skillDirAbs(targetAbs, dotDir, next.name),
32729
+ files: next.files,
32730
+ out,
32731
+ verbose: cfg.verbose,
32732
+ label: `skill ${next.name}`
32733
+ });
32734
+ }
32735
+ }
32736
+ }
32353
32737
  async function removeUserSpaceCopy(skillDir) {
32354
32738
  const manifestPath = join9(skillDir, MANIFEST_FILE);
32355
32739
  try {
32740
+ if ((await lstat(skillDir)).isSymbolicLink()) return null;
32356
32741
  await stat(manifestPath);
32357
32742
  } catch {
32358
32743
  return null;
@@ -32360,7 +32745,9 @@ async function removeUserSpaceCopy(skillDir) {
32360
32745
  let files = 0;
32361
32746
  for (const rel of await readManifest(skillDir)) {
32362
32747
  try {
32363
- const p = join9(skillDir, ...relSegments(rel));
32748
+ const segs = relSegments(rel);
32749
+ if (await crossesLink(skillDir, segs.slice(0, -1))) continue;
32750
+ const p = join9(skillDir, ...segs);
32364
32751
  await rm2(p, { force: true });
32365
32752
  await rmdir(dirname6(p)).catch(() => {
32366
32753
  });
@@ -32375,6 +32762,18 @@ async function removeUserSpaceCopy(skillDir) {
32375
32762
  );
32376
32763
  return { files, dirRemoved };
32377
32764
  }
32765
+ async function crossesLink(root, dirSegs) {
32766
+ let p = root;
32767
+ for (const seg of dirSegs) {
32768
+ p = join9(p, seg);
32769
+ try {
32770
+ if ((await lstat(p)).isSymbolicLink()) return true;
32771
+ } catch {
32772
+ return false;
32773
+ }
32774
+ }
32775
+ return false;
32776
+ }
32378
32777
  async function installSkillTree(opts) {
32379
32778
  const { skillDir, files, out, verbose, label } = opts;
32380
32779
  const prev = await readManifest(skillDir);
@@ -35874,7 +36273,7 @@ function registerDocumentsCommand(root) {
35874
36273
  // src/commands/integrations.ts
35875
36274
  import { Option as Option2 } from "commander";
35876
36275
  import { constants as fsConstants } from "fs";
35877
- import { lstat, open as open2 } from "fs/promises";
36276
+ import { lstat as lstat2, open as open2 } from "fs/promises";
35878
36277
 
35879
36278
  // src/api/integrations.ts
35880
36279
  async function list23(client, opts, reqOptions) {
@@ -43860,7 +44259,7 @@ function parseSavedSearchId(raw) {
43860
44259
  return id;
43861
44260
  }
43862
44261
  async function writeSqlFile(path13, sql) {
43863
- const existing = await lstat(path13).catch((err) => {
44262
+ const existing = await lstat2(path13).catch((err) => {
43864
44263
  if (err.code === "ENOENT") return void 0;
43865
44264
  throw err;
43866
44265
  });
@@ -72013,156 +72412,6 @@ function copySessionCredentials(mountPath = SESSION_AUTH_MOUNT, target = credent
72013
72412
  }
72014
72413
  }
72015
72414
 
72016
- // src/scopes/filter.ts
72017
- var SCOPE_EXIT_CODE = 3;
72018
- var ScopeFilterError = class extends Error {
72019
- constructor(command, message) {
72020
- super(message);
72021
- this.command = command;
72022
- }
72023
- command;
72024
- exitCode = SCOPE_EXIT_CODE;
72025
- };
72026
- var ScopeHiddenError = class extends ScopeFilterError {
72027
- constructor(command, required2) {
72028
- super(
72029
- command,
72030
- `'dr ${command}' is not available under this identity's credential scope (needs ${required2.join(", ")}); run 'dr whoami' to see the scopes this identity carries`
72031
- );
72032
- this.required = required2;
72033
- this.name = "ScopeHiddenError";
72034
- }
72035
- required;
72036
- };
72037
- var ScopeUnavailableError = class extends ScopeFilterError {
72038
- constructor(command, reason, until) {
72039
- super(
72040
- command,
72041
- `'dr ${command}' is unavailable to any agent-scoped credential (one whose scopes claim carries at least one resource:action entry): ${reason} (this is not a missing scope \u2014 no scope grants it; ${until} lifts the restriction)`
72042
- );
72043
- this.reason = reason;
72044
- this.until = until;
72045
- this.name = "ScopeUnavailableError";
72046
- }
72047
- reason;
72048
- until;
72049
- };
72050
- var ScopeGroupHiddenError = class extends ScopeFilterError {
72051
- constructor(command, required2, tickets) {
72052
- super(
72053
- command,
72054
- `'dr ${command}' has no subcommand available to this identity.` + (required2.length > 0 ? ` Its subcommands need ${required2.join(", ")} \u2014 run 'dr whoami' to see the scopes this identity carries.` : "") + (tickets.length > 0 ? ` Some are closed to any agent-scoped credential whatever its scopes (${tickets.join(", ")}).` : "")
72055
- );
72056
- this.required = required2;
72057
- this.tickets = tickets;
72058
- this.name = "ScopeGroupHiddenError";
72059
- }
72060
- required;
72061
- tickets;
72062
- };
72063
- function hide(cmd) {
72064
- cmd._hidden = true;
72065
- }
72066
- function refuseHelp(cmd, err) {
72067
- cmd.outputHelp = () => {
72068
- throw err;
72069
- };
72070
- }
72071
- function leafAnswer(leaf, scopes, map) {
72072
- const key = scopesMapKey(leaf);
72073
- if (!Object.prototype.hasOwnProperty.call(map.commands, key)) return void 0;
72074
- const entry = map.commands[key];
72075
- if (entry === void 0) return void 0;
72076
- if (isDeniedEntry(entry))
72077
- return {
72078
- err: new ScopeUnavailableError(key, entry.denied.reason, entry.denied.until),
72079
- required: [],
72080
- denial: entry.denied
72081
- };
72082
- if (isEntryAllowed(scopes, entry)) return void 0;
72083
- const required2 = entry.requires.map((r) => `${r.resource}:${r.action}`);
72084
- return { err: new ScopeHiddenError(key, required2), required: required2, denial: void 0 };
72085
- }
72086
- var intercepted = /* @__PURE__ */ new WeakMap();
72087
- function intercept(cmd, err, armed) {
72088
- intercepted.set(cmd, err);
72089
- refuseHelp(cmd, err);
72090
- cmd.action(() => {
72091
- throw err;
72092
- });
72093
- const parent = cmd.parent;
72094
- if (parent === null || armed.has(parent)) return;
72095
- armed.add(parent);
72096
- parent.hook("preSubcommand", (_parent, sub) => {
72097
- const denial = intercepted.get(sub);
72098
- if (denial !== void 0) throw denial;
72099
- });
72100
- }
72101
- function interceptGroup(group, err) {
72102
- group.allowExcessArguments(true);
72103
- group.allowUnknownOption(true);
72104
- group.helpCommand(true);
72105
- refuseHelp(group, err);
72106
- group.action(() => {
72107
- throw err;
72108
- });
72109
- }
72110
- function groupAnswer(group, below) {
72111
- const key = scopesMapKey(group);
72112
- const [mark] = below.denials;
72113
- if (mark !== void 0 && below.denials.length === 1 && below.required.length === 0)
72114
- return new ScopeUnavailableError(key, mark.reason, mark.until);
72115
- if (below.required.length === 0 && below.denials.length === 0) return void 0;
72116
- return new ScopeGroupHiddenError(
72117
- key,
72118
- below.required,
72119
- below.denials.map((d) => d.until).filter((t, i, all) => all.indexOf(t) === i)
72120
- );
72121
- }
72122
- function filterBelow(node, scopes, map, armed) {
72123
- const here = { count: 0, answer: void 0, required: [], denials: [] };
72124
- let allAnswered = node.commands.length > 0;
72125
- for (const child2 of node.commands) {
72126
- if (child2.commands.length > 0) {
72127
- const below = filterBelow(child2, scopes, map, armed);
72128
- here.count += below.count;
72129
- if (below.answer === void 0) {
72130
- allAnswered = false;
72131
- continue;
72132
- }
72133
- interceptGroup(child2, below.answer);
72134
- hide(child2);
72135
- addRequired(here, below.required);
72136
- addDenials(here, below.denials);
72137
- continue;
72138
- }
72139
- const answer = leafAnswer(child2, scopes, map);
72140
- if (answer === void 0) {
72141
- allAnswered = false;
72142
- continue;
72143
- }
72144
- intercept(child2, answer.err, armed);
72145
- hide(child2);
72146
- here.count += 1;
72147
- addRequired(here, answer.required);
72148
- if (answer.denial !== void 0) addDenials(here, [answer.denial]);
72149
- }
72150
- if (allAnswered) here.answer = groupAnswer(node, here);
72151
- return here;
72152
- }
72153
- function addRequired(into, required2) {
72154
- for (const r of required2) if (!into.required.includes(r)) into.required.push(r);
72155
- }
72156
- function addDenials(into, denials) {
72157
- for (const d of denials)
72158
- if (!into.denials.some((x) => x.reason === d.reason && x.until === d.until))
72159
- into.denials.push(d);
72160
- }
72161
- function applyScopeFilter(root, scopes, map) {
72162
- if (map === void 0 || !isAgentScoped(scopes)) return 0;
72163
- return filterBelow(root, scopes, map, /* @__PURE__ */ new Set()).count;
72164
- }
72165
-
72166
72415
  // src/scopes/identityScopes.ts
72167
72416
  function peekIdentityFlags(argv) {
72168
72417
  const flags = {};
@@ -72554,6 +72803,9 @@ function buildRootCommand() {
72554
72803
  root.hook("preAction", (_thisCommand, actionCommand) => {
72555
72804
  recordCommandStart(commandPath(actionCommand), Date.now());
72556
72805
  });
72806
+ root.hook("preAction", (_thisCommand, actionCommand) => {
72807
+ enforceTenantPin(actionCommand);
72808
+ });
72557
72809
  registerLoginCommand(root);
72558
72810
  registerLoginAsCommand(root);
72559
72811
  registerWhoamiCommand(root);
@@ -72707,6 +72959,7 @@ function annotateScopeDenial(err, argv, root, deps = defaultScopeDenialDeps) {
72707
72959
  function exitCodeFor(err) {
72708
72960
  if (err instanceof ScopeFilterError) return err.exitCode;
72709
72961
  if (err instanceof ScopeDeniedError) return SCOPE_EXIT_CODE;
72962
+ if (err instanceof TenantPinError) return err.exitCode;
72710
72963
  return 1;
72711
72964
  }
72712
72965
  var UNKNOWN_OPTION_PATTERN = /(unknown option ')(-[\s\S]*)'/g;