@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/README.md +23 -0
- package/dist/cli.js +638 -385
- package/dist/scopes-map.json +1 -1
- package/dist/skills/create-connector-lib/SKILL.md +1 -1
- package/dist/skills/{datarails-connectors-onboard-connector → drip-implement-connector}/SKILL.md +14 -3
- package/dist/skills/drip-implement-connector/reference/sftp.md +246 -0
- package/dist/skills/plugins.json +1 -1
- package/dist/skills/validate-workato-migration/SKILL.md +3 -3
- package/package.json +2 -2
- package/scripts/postinstall.mjs +6 -12
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.
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
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)} ${
|
|
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
|
-
|
|
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
|
|
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
|
|
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;
|