@pmate/cli 0.25.8 → 0.25.9
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/index.d.ts +41 -0
- package/dist/index.js +290 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -452,6 +452,47 @@ type AccountCommandArgs = {
|
|
|
452
452
|
role?: "practitioner" | "mate";
|
|
453
453
|
baseUrl?: string;
|
|
454
454
|
json: boolean;
|
|
455
|
+
} | {
|
|
456
|
+
type: Command.Account;
|
|
457
|
+
action: "access-key-create";
|
|
458
|
+
app: string;
|
|
459
|
+
name: string;
|
|
460
|
+
namespace?: string;
|
|
461
|
+
scopes: string[];
|
|
462
|
+
expiresAt: string;
|
|
463
|
+
out?: string;
|
|
464
|
+
baseUrl?: string;
|
|
465
|
+
json: boolean;
|
|
466
|
+
} | {
|
|
467
|
+
type: Command.Account;
|
|
468
|
+
action: "access-key-list";
|
|
469
|
+
app?: string;
|
|
470
|
+
namespace?: string;
|
|
471
|
+
baseUrl?: string;
|
|
472
|
+
json: boolean;
|
|
473
|
+
} | {
|
|
474
|
+
type: Command.Account;
|
|
475
|
+
action: "access-key-disable";
|
|
476
|
+
id: string;
|
|
477
|
+
baseUrl?: string;
|
|
478
|
+
json: boolean;
|
|
479
|
+
} | {
|
|
480
|
+
type: Command.Account;
|
|
481
|
+
action: "access-key-rotate";
|
|
482
|
+
id: string;
|
|
483
|
+
expiresAt: string;
|
|
484
|
+
gracePeriodSeconds?: number;
|
|
485
|
+
out?: string;
|
|
486
|
+
baseUrl?: string;
|
|
487
|
+
json: boolean;
|
|
488
|
+
} | {
|
|
489
|
+
type: Command.Account;
|
|
490
|
+
action: "access-key-login";
|
|
491
|
+
app: string;
|
|
492
|
+
key?: string;
|
|
493
|
+
keyFile?: string;
|
|
494
|
+
baseUrl?: string;
|
|
495
|
+
json: boolean;
|
|
455
496
|
};
|
|
456
497
|
type OrgCommandArgs = {
|
|
457
498
|
type: Command.Org;
|
package/dist/index.js
CHANGED
|
@@ -22303,6 +22303,88 @@ var AccountCommandHandler = class {
|
|
|
22303
22303
|
this.renderJson(profile, args.json);
|
|
22304
22304
|
return;
|
|
22305
22305
|
}
|
|
22306
|
+
case "access-key-create": {
|
|
22307
|
+
const token = this.readToken();
|
|
22308
|
+
const baseUrl = this.resolveAuthBaseUrl(args.baseUrl);
|
|
22309
|
+
const result = await this.request(
|
|
22310
|
+
baseUrl,
|
|
22311
|
+
token,
|
|
22312
|
+
"POST",
|
|
22313
|
+
"/access-keys",
|
|
22314
|
+
{
|
|
22315
|
+
app: args.app,
|
|
22316
|
+
name: args.name,
|
|
22317
|
+
namespace: args.namespace,
|
|
22318
|
+
scopes: args.scopes,
|
|
22319
|
+
expiresAt: args.expiresAt
|
|
22320
|
+
}
|
|
22321
|
+
);
|
|
22322
|
+
this.persistAccessKeySecret(result, args.out);
|
|
22323
|
+
this.renderAccessKeyCreateResult(result, args.json, args.out);
|
|
22324
|
+
return;
|
|
22325
|
+
}
|
|
22326
|
+
case "access-key-list": {
|
|
22327
|
+
const token = this.readToken();
|
|
22328
|
+
const baseUrl = this.resolveAuthBaseUrl(args.baseUrl);
|
|
22329
|
+
const result = await this.request(
|
|
22330
|
+
baseUrl,
|
|
22331
|
+
token,
|
|
22332
|
+
"GET",
|
|
22333
|
+
`/access-keys?${this.buildAccessKeyListQuery(args)}`
|
|
22334
|
+
);
|
|
22335
|
+
this.renderAccessKeys(result.items, args.json);
|
|
22336
|
+
return;
|
|
22337
|
+
}
|
|
22338
|
+
case "access-key-disable": {
|
|
22339
|
+
const token = this.readToken();
|
|
22340
|
+
const baseUrl = this.resolveAuthBaseUrl(args.baseUrl);
|
|
22341
|
+
const item = await this.request(
|
|
22342
|
+
baseUrl,
|
|
22343
|
+
token,
|
|
22344
|
+
"DELETE",
|
|
22345
|
+
`/access-keys/${encodeURIComponent(args.id)}`
|
|
22346
|
+
);
|
|
22347
|
+
if (!args.json) {
|
|
22348
|
+
ui.success(`Disabled access key ${item.id}`);
|
|
22349
|
+
}
|
|
22350
|
+
this.renderJson(item, args.json);
|
|
22351
|
+
return;
|
|
22352
|
+
}
|
|
22353
|
+
case "access-key-rotate": {
|
|
22354
|
+
const token = this.readToken();
|
|
22355
|
+
const baseUrl = this.resolveAuthBaseUrl(args.baseUrl);
|
|
22356
|
+
const result = await this.request(
|
|
22357
|
+
baseUrl,
|
|
22358
|
+
token,
|
|
22359
|
+
"POST",
|
|
22360
|
+
`/access-keys/${encodeURIComponent(args.id)}/rotate`,
|
|
22361
|
+
{
|
|
22362
|
+
expiresAt: args.expiresAt,
|
|
22363
|
+
gracePeriodSeconds: args.gracePeriodSeconds
|
|
22364
|
+
}
|
|
22365
|
+
);
|
|
22366
|
+
this.persistAccessKeySecret(result, args.out);
|
|
22367
|
+
this.renderAccessKeyCreateResult(result, args.json, args.out);
|
|
22368
|
+
return;
|
|
22369
|
+
}
|
|
22370
|
+
case "access-key-login": {
|
|
22371
|
+
const baseUrl = this.resolveAuthBaseUrl(args.baseUrl);
|
|
22372
|
+
const secret = args.key ?? this.readAccessKeyFile(args.keyFile, args.app).secret;
|
|
22373
|
+
const login = await this.request(
|
|
22374
|
+
baseUrl,
|
|
22375
|
+
secret,
|
|
22376
|
+
"POST",
|
|
22377
|
+
"/access-keys/session",
|
|
22378
|
+
{ app: args.app }
|
|
22379
|
+
);
|
|
22380
|
+
const profileId = await this.resolveLoginProfileId(login, args.app, baseUrl);
|
|
22381
|
+
const saved = saveLoginSession({ ...login, profileId }, args.app);
|
|
22382
|
+
if (!args.json) {
|
|
22383
|
+
ui.success(`Logged in access key ${login.session.identity.accessKeyId ?? "unknown"} to ${saved.app}`);
|
|
22384
|
+
}
|
|
22385
|
+
this.renderJson(login, args.json);
|
|
22386
|
+
return;
|
|
22387
|
+
}
|
|
22306
22388
|
}
|
|
22307
22389
|
}
|
|
22308
22390
|
resolveAuthBaseUrl(baseUrl) {
|
|
@@ -22344,6 +22426,12 @@ var AccountCommandHandler = class {
|
|
|
22344
22426
|
if (args.namespace) params.set("namespace", args.namespace);
|
|
22345
22427
|
return params.toString();
|
|
22346
22428
|
}
|
|
22429
|
+
buildAccessKeyListQuery(args) {
|
|
22430
|
+
const params = new URLSearchParams();
|
|
22431
|
+
if (args.app) params.set("app", args.app);
|
|
22432
|
+
if (args.namespace) params.set("namespace", args.namespace);
|
|
22433
|
+
return params.toString();
|
|
22434
|
+
}
|
|
22347
22435
|
async request(baseUrl, token, method, pathname, body) {
|
|
22348
22436
|
const response = await fetch(`${baseUrl}${pathname}`, {
|
|
22349
22437
|
method,
|
|
@@ -22501,6 +22589,9 @@ var AccountCommandHandler = class {
|
|
|
22501
22589
|
resolveDefaultBotKeyPath(app, name) {
|
|
22502
22590
|
return import_node_path26.default.join(this.homeDir(), ".pmate", "bots", app, `${name}.bot-key.json`);
|
|
22503
22591
|
}
|
|
22592
|
+
resolveDefaultAccessKeyPath(app, name) {
|
|
22593
|
+
return import_node_path26.default.join(this.homeDir(), ".pmate", "access-keys", app, `${name}.access-key.json`);
|
|
22594
|
+
}
|
|
22504
22595
|
homeDir() {
|
|
22505
22596
|
return process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
22506
22597
|
}
|
|
@@ -22537,6 +22628,89 @@ var AccountCommandHandler = class {
|
|
|
22537
22628
|
}
|
|
22538
22629
|
return parsed;
|
|
22539
22630
|
}
|
|
22631
|
+
readAccessKeyFile(filePath, expectedApp) {
|
|
22632
|
+
if (!filePath) {
|
|
22633
|
+
throw new Error("Missing access key file");
|
|
22634
|
+
}
|
|
22635
|
+
const absolutePath = this.resolveFilePath(filePath);
|
|
22636
|
+
if (!(0, import_node_fs22.existsSync)(absolutePath)) {
|
|
22637
|
+
throw new Error(`Access key file not found: ${absolutePath}`);
|
|
22638
|
+
}
|
|
22639
|
+
const parsed = JSON.parse((0, import_node_fs22.readFileSync)(absolutePath, "utf8"));
|
|
22640
|
+
if (parsed.type !== "pmate-access-key" || parsed.version !== 1 || !parsed.id || !parsed.app || !parsed.secret || !parsed.expiresAt) {
|
|
22641
|
+
throw new Error(`Invalid PMate access key file: ${absolutePath}`);
|
|
22642
|
+
}
|
|
22643
|
+
if (expectedApp && parsed.app !== expectedApp) {
|
|
22644
|
+
throw new Error(`Access key app mismatch. expected=${expectedApp}, received=${parsed.app}`);
|
|
22645
|
+
}
|
|
22646
|
+
return parsed;
|
|
22647
|
+
}
|
|
22648
|
+
persistAccessKeySecret(result, out) {
|
|
22649
|
+
const keyFile = {
|
|
22650
|
+
type: "pmate-access-key",
|
|
22651
|
+
version: 1,
|
|
22652
|
+
id: result.key.id,
|
|
22653
|
+
app: result.key.app,
|
|
22654
|
+
name: result.key.name,
|
|
22655
|
+
namespace: result.key.namespace,
|
|
22656
|
+
secret: result.secret,
|
|
22657
|
+
expiresAt: result.key.expiresAt,
|
|
22658
|
+
createdAt: result.key.createdAt
|
|
22659
|
+
};
|
|
22660
|
+
const outPath = out ? this.resolveFilePath(out) : this.resolveDefaultAccessKeyPath(result.key.app, result.key.name);
|
|
22661
|
+
(0, import_node_fs22.mkdirSync)(import_node_path26.default.dirname(outPath), { recursive: true });
|
|
22662
|
+
(0, import_node_fs22.writeFileSync)(outPath, JSON.stringify(keyFile, null, 2), { mode: 384 });
|
|
22663
|
+
(0, import_node_fs22.chmodSync)(outPath, 384);
|
|
22664
|
+
}
|
|
22665
|
+
renderAccessKeyCreateResult(result, asJson, out) {
|
|
22666
|
+
if (asJson) {
|
|
22667
|
+
this.renderJson(result, true);
|
|
22668
|
+
return;
|
|
22669
|
+
}
|
|
22670
|
+
const outPath = out ? this.resolveFilePath(out) : this.resolveDefaultAccessKeyPath(result.key.app, result.key.name);
|
|
22671
|
+
ui.success(`Access key saved to ${outPath}`);
|
|
22672
|
+
ui.info(result.key.id, {
|
|
22673
|
+
app: result.key.app,
|
|
22674
|
+
name: result.key.name,
|
|
22675
|
+
namespace: result.key.namespace,
|
|
22676
|
+
scopes: result.key.scopes,
|
|
22677
|
+
expiresAt: result.key.expiresAt
|
|
22678
|
+
});
|
|
22679
|
+
}
|
|
22680
|
+
renderAccessKeys(items, asJson) {
|
|
22681
|
+
if (asJson) {
|
|
22682
|
+
this.renderJson({ items }, true);
|
|
22683
|
+
return;
|
|
22684
|
+
}
|
|
22685
|
+
if (items.length === 0) {
|
|
22686
|
+
ui.warn("No access keys found.");
|
|
22687
|
+
return;
|
|
22688
|
+
}
|
|
22689
|
+
for (const item of items) {
|
|
22690
|
+
ui.info(item.id, {
|
|
22691
|
+
app: item.app,
|
|
22692
|
+
name: item.name,
|
|
22693
|
+
namespace: item.namespace,
|
|
22694
|
+
scopes: item.scopes,
|
|
22695
|
+
status: item.status,
|
|
22696
|
+
expiresAt: item.expiresAt,
|
|
22697
|
+
lastUsedAt: item.lastUsedAt,
|
|
22698
|
+
rotationExpiresAt: item.rotationExpiresAt
|
|
22699
|
+
});
|
|
22700
|
+
}
|
|
22701
|
+
}
|
|
22702
|
+
async resolveLoginProfileId(login, app, authApiBaseUrl) {
|
|
22703
|
+
try {
|
|
22704
|
+
return await fetchDefaultProfileId({
|
|
22705
|
+
token: login.token,
|
|
22706
|
+
app,
|
|
22707
|
+
accountId: login.session.identity.accountId,
|
|
22708
|
+
authApiBaseUrl
|
|
22709
|
+
});
|
|
22710
|
+
} catch {
|
|
22711
|
+
return login.session.identity.accessKeyId ?? login.session.identity.accountId;
|
|
22712
|
+
}
|
|
22713
|
+
}
|
|
22540
22714
|
signBotChallenge(challenge, privateKey) {
|
|
22541
22715
|
const key = (0, import_node_crypto5.createPrivateKey)({
|
|
22542
22716
|
key: Buffer.from(privateKey, "base64url"),
|
|
@@ -25339,6 +25513,104 @@ function parseAccountArgs(tokens) {
|
|
|
25339
25513
|
}
|
|
25340
25514
|
throw new Error(`Unknown account bot action: ${botAction}`);
|
|
25341
25515
|
}
|
|
25516
|
+
if (action === "access-key") {
|
|
25517
|
+
const keyAction = rest.shift();
|
|
25518
|
+
if (!keyAction) {
|
|
25519
|
+
throw new Error("Missing account access-key action. Use: account access-key create|list|rotate|disable|login");
|
|
25520
|
+
}
|
|
25521
|
+
let app;
|
|
25522
|
+
let id;
|
|
25523
|
+
let name;
|
|
25524
|
+
let namespace2;
|
|
25525
|
+
const scopes = [];
|
|
25526
|
+
let expiresAt;
|
|
25527
|
+
let out;
|
|
25528
|
+
let key;
|
|
25529
|
+
let keyFile;
|
|
25530
|
+
let gracePeriodSeconds;
|
|
25531
|
+
let baseUrl2;
|
|
25532
|
+
let json2 = false;
|
|
25533
|
+
while (rest.length > 0) {
|
|
25534
|
+
const token = rest.shift();
|
|
25535
|
+
switch (token) {
|
|
25536
|
+
case "--app":
|
|
25537
|
+
app = parseNamespacedRef(requireValue8(rest, token), {
|
|
25538
|
+
entityLabel: "app ref"
|
|
25539
|
+
}).fullId;
|
|
25540
|
+
break;
|
|
25541
|
+
case "--name":
|
|
25542
|
+
name = requireValue8(rest, token);
|
|
25543
|
+
break;
|
|
25544
|
+
case "--namespace":
|
|
25545
|
+
namespace2 = requireValue8(rest, token);
|
|
25546
|
+
break;
|
|
25547
|
+
case "--scope":
|
|
25548
|
+
scopes.push(requireValue8(rest, token));
|
|
25549
|
+
break;
|
|
25550
|
+
case "--expires-at":
|
|
25551
|
+
expiresAt = requireValue8(rest, token);
|
|
25552
|
+
break;
|
|
25553
|
+
case "--expires-in":
|
|
25554
|
+
expiresAt = parseExpiresIn(requireValue8(rest, token));
|
|
25555
|
+
break;
|
|
25556
|
+
case "--grace-period":
|
|
25557
|
+
gracePeriodSeconds = parseDurationSeconds(requireValue8(rest, token), token);
|
|
25558
|
+
break;
|
|
25559
|
+
case "--out":
|
|
25560
|
+
out = requireValue8(rest, token);
|
|
25561
|
+
break;
|
|
25562
|
+
case "--key":
|
|
25563
|
+
key = requireValue8(rest, token);
|
|
25564
|
+
break;
|
|
25565
|
+
case "--key-file":
|
|
25566
|
+
keyFile = requireValue8(rest, token);
|
|
25567
|
+
break;
|
|
25568
|
+
case "--base-url":
|
|
25569
|
+
baseUrl2 = requireValue8(rest, token);
|
|
25570
|
+
break;
|
|
25571
|
+
case "--json":
|
|
25572
|
+
json2 = true;
|
|
25573
|
+
break;
|
|
25574
|
+
default:
|
|
25575
|
+
if ((keyAction === "rotate" || keyAction === "disable") && !token.startsWith("-")) {
|
|
25576
|
+
if (id) {
|
|
25577
|
+
throw new Error(`Unexpected account access-key id: ${token}`);
|
|
25578
|
+
}
|
|
25579
|
+
id = token;
|
|
25580
|
+
continue;
|
|
25581
|
+
}
|
|
25582
|
+
throw new Error(`Unknown account access-key option: ${token}`);
|
|
25583
|
+
}
|
|
25584
|
+
}
|
|
25585
|
+
if (keyAction === "create") {
|
|
25586
|
+
if (!app || !name || scopes.length === 0 || !expiresAt) {
|
|
25587
|
+
throw new Error("Usage: account access-key create --app <ns:app> --name <name> --scope <scope>... (--expires-at <iso> | --expires-in <duration>) [--namespace <ns>] [--out <path>] [--base-url <url>] [--json]");
|
|
25588
|
+
}
|
|
25589
|
+
return { type: "account" /* Account */, action: "access-key-create", app, name, namespace: namespace2, scopes, expiresAt, out, baseUrl: baseUrl2, json: json2 };
|
|
25590
|
+
}
|
|
25591
|
+
if (keyAction === "list") {
|
|
25592
|
+
return { type: "account" /* Account */, action: "access-key-list", app, namespace: namespace2, baseUrl: baseUrl2, json: json2 };
|
|
25593
|
+
}
|
|
25594
|
+
if (keyAction === "disable") {
|
|
25595
|
+
if (!id) {
|
|
25596
|
+
throw new Error("Usage: account access-key disable <id> [--base-url <url>] [--json]");
|
|
25597
|
+
}
|
|
25598
|
+
return { type: "account" /* Account */, action: "access-key-disable", id, baseUrl: baseUrl2, json: json2 };
|
|
25599
|
+
}
|
|
25600
|
+
if (keyAction === "rotate") {
|
|
25601
|
+
if (!id || !expiresAt) {
|
|
25602
|
+
throw new Error("Usage: account access-key rotate <id> (--expires-at <iso> | --expires-in <duration>) [--grace-period <duration>] [--out <path>] [--base-url <url>] [--json]");
|
|
25603
|
+
}
|
|
25604
|
+
return { type: "account" /* Account */, action: "access-key-rotate", id, expiresAt, gracePeriodSeconds, out, baseUrl: baseUrl2, json: json2 };
|
|
25605
|
+
}
|
|
25606
|
+
if (keyAction === "login") {
|
|
25607
|
+
if (!app || !key && !keyFile || key && keyFile) {
|
|
25608
|
+
throw new Error("Usage: account access-key login --app <ns:app> (--key <secret> | --key-file <path>) [--base-url <url>] [--json]");
|
|
25609
|
+
}
|
|
25610
|
+
return { type: "account" /* Account */, action: "access-key-login", app, key, keyFile, baseUrl: baseUrl2, json: json2 };
|
|
25611
|
+
}
|
|
25612
|
+
throw new Error(`Unknown account access-key action: ${keyAction}`);
|
|
25613
|
+
}
|
|
25342
25614
|
if (action !== "role") {
|
|
25343
25615
|
throw new Error(`Unknown account action: ${action}`);
|
|
25344
25616
|
}
|
|
@@ -25420,6 +25692,19 @@ function parsePositiveInt(value, optionName) {
|
|
|
25420
25692
|
}
|
|
25421
25693
|
return parsed;
|
|
25422
25694
|
}
|
|
25695
|
+
function parseExpiresIn(value) {
|
|
25696
|
+
return new Date(Date.now() + parseDurationSeconds(value, "--expires-in") * 1e3).toISOString();
|
|
25697
|
+
}
|
|
25698
|
+
function parseDurationSeconds(value, optionName) {
|
|
25699
|
+
const match = /^(\d+)(s|m|h|d)?$/.exec(value.trim());
|
|
25700
|
+
if (!match) {
|
|
25701
|
+
throw new Error(`${optionName} must be a duration like 3600s, 60m, 24h, or 30d`);
|
|
25702
|
+
}
|
|
25703
|
+
const amount = Number(match[1]);
|
|
25704
|
+
const unit = match[2] ?? "s";
|
|
25705
|
+
const multiplier = unit === "d" ? 86400 : unit === "h" ? 3600 : unit === "m" ? 60 : 1;
|
|
25706
|
+
return amount * multiplier;
|
|
25707
|
+
}
|
|
25423
25708
|
|
|
25424
25709
|
// src/orgCommandParser.ts
|
|
25425
25710
|
init_namespacedRef();
|
|
@@ -26173,6 +26458,11 @@ var HELP_TEXT = [
|
|
|
26173
26458
|
` ${import_chalk4.default.gray(" bot list --app <ns:app> [--base-url <url>] [--json]")}`,
|
|
26174
26459
|
` ${import_chalk4.default.gray(" bot login --app <ns:app> --name <name> (--key-file <path> | --private-key <key>) [--base-url <url>] [--json]")}`,
|
|
26175
26460
|
` ${import_chalk4.default.gray(" bot profile create --app <ns:app> --name <name> --nickname <nickname> [--role <role>] [--base-url <url>] [--json]")}`,
|
|
26461
|
+
` ${import_chalk4.default.gray(" access-key create --app <ns:app> --name <name> --scope <scope>... (--expires-at <iso> | --expires-in <duration>) [--namespace <ns>] [--out <path>] [--base-url <url>] [--json]")}`,
|
|
26462
|
+
` ${import_chalk4.default.gray(" access-key list [--app <ns:app>] [--namespace <ns>] [--base-url <url>] [--json]")}`,
|
|
26463
|
+
` ${import_chalk4.default.gray(" access-key rotate <id> (--expires-at <iso> | --expires-in <duration>) [--grace-period <duration>] [--out <path>] [--base-url <url>] [--json]")}`,
|
|
26464
|
+
` ${import_chalk4.default.gray(" access-key disable <id> [--base-url <url>] [--json]")}`,
|
|
26465
|
+
` ${import_chalk4.default.gray(" access-key login --app <ns:app> (--key <secret> | --key-file <path>) [--base-url <url>] [--json]")}`,
|
|
26176
26466
|
` ${import_chalk4.default.gray(" default base URL: https://auth-api-v2.pmate.chat")}`,
|
|
26177
26467
|
` ${import_chalk4.default.cyan("org")} Manage orgs, org members, and org visible apps via apps-api`,
|
|
26178
26468
|
` ${import_chalk4.default.gray(" list [--base-url <url>] [--json]")}`,
|