@alphafox/cli 0.1.5 → 0.2.0
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/auth/browser-login.d.ts +30 -0
- package/dist/auth/browser-login.js +193 -0
- package/dist/auth/loopback-callback.d.ts +32 -0
- package/dist/auth/loopback-callback.js +175 -0
- package/dist/auth/open-browser.d.ts +15 -0
- package/dist/auth/open-browser.js +54 -0
- package/dist/auth/refresh.d.ts +27 -2
- package/dist/auth/refresh.js +52 -15
- package/dist/catalog/allowlist.d.ts +20 -4
- package/dist/catalog/allowlist.js +126 -25
- package/dist/catalog/command-tree.d.ts +34 -0
- package/dist/catalog/command-tree.js +117 -0
- package/dist/catalog/compatibility.d.ts +23 -0
- package/dist/catalog/compatibility.js +58 -0
- package/dist/catalog/generated/registry.json +6137 -0
- package/dist/catalog/generated/schemas.json +31036 -0
- package/dist/catalog/operations.d.ts +76 -3
- package/dist/catalog/operations.js +87 -213
- package/dist/commands/run.js +171 -153
- package/dist/config/profiles.js +3 -3
- package/dist/envelope.d.ts +3 -0
- package/dist/envelope.js +43 -3
- package/dist/http/client.js +18 -15
- package/dist/index.d.ts +12 -5
- package/dist/index.js +32 -1
- package/dist/keychain/linux-secret-service.d.ts +11 -0
- package/dist/keychain/linux-secret-service.js +93 -0
- package/dist/keychain/store.d.ts +20 -1
- package/dist/keychain/store.js +94 -6
- package/dist/keychain/windows-credential.d.ts +13 -0
- package/dist/keychain/windows-credential.js +176 -0
- package/dist/safety/confirmation.d.ts +10 -3
- package/dist/safety/confirmation.js +27 -4
- package/dist/version.d.ts +2 -2
- package/dist/version.js +3 -2
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +156 -0
- package/docs/agents/triage-labels.md +18 -0
- package/docs/e2e-staging.md +76 -6
- package/docs/release-supply-chain.md +99 -26
- package/package.json +4 -2
- package/skills/account/SKILL.md +8 -6
- package/skills/admin/SKILL.md +9 -4
- package/skills/alphafox-shared/SKILL.md +23 -14
- package/skills/auth/SKILL.md +21 -6
- package/skills/exchange/SKILL.md +10 -3
- package/skills/market/SKILL.md +9 -4
- package/skills/notification/SKILL.md +8 -3
- package/skills/strategy/SKILL.md +15 -9
- package/skills/trading/SKILL.md +15 -5
package/dist/commands/run.js
CHANGED
|
@@ -1,45 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.parseGlobalFlags = parseGlobalFlags;
|
|
37
4
|
exports.runCli = runCli;
|
|
38
5
|
const node_crypto_1 = require("node:crypto");
|
|
39
6
|
const operations_1 = require("../catalog/operations");
|
|
7
|
+
const command_tree_1 = require("../catalog/command-tree");
|
|
40
8
|
const allowlist_1 = require("../catalog/allowlist");
|
|
41
9
|
const profiles_1 = require("../config/profiles");
|
|
42
10
|
const envelope_1 = require("../envelope");
|
|
11
|
+
const browser_login_1 = require("../auth/browser-login");
|
|
43
12
|
const client_1 = require("../http/client");
|
|
44
13
|
const store_1 = require("../keychain/store");
|
|
45
14
|
const confirmation_1 = require("../safety/confirmation");
|
|
@@ -94,6 +63,14 @@ function parseGlobalFlags(argv) {
|
|
|
94
63
|
return { flags, rest };
|
|
95
64
|
}
|
|
96
65
|
async function runCli(argv, env = process.env) {
|
|
66
|
+
if (argv.some((a) => a === "--token" || a.startsWith("--token="))) {
|
|
67
|
+
(0, envelope_1.writeError)({
|
|
68
|
+
type: "usage",
|
|
69
|
+
subtype: "token_argv_forbidden",
|
|
70
|
+
message: "Refusing --token on argv. Store credentials in the OS keychain via alphafox auth login.",
|
|
71
|
+
hint: "Automation tokens are not supported in v1. Do not copy refresh tokens into CI.",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
97
74
|
const { flags, rest } = parseGlobalFlags(argv);
|
|
98
75
|
const [cmd, ...args] = rest;
|
|
99
76
|
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
@@ -108,13 +85,17 @@ async function runCli(argv, env = process.env) {
|
|
|
108
85
|
"alphafox whoami",
|
|
109
86
|
"alphafox profile list|use <name>",
|
|
110
87
|
"alphafox schema [operationId]",
|
|
88
|
+
"alphafox catalog",
|
|
111
89
|
"alphafox api METHOD PATH [--body JSON]",
|
|
112
90
|
"alphafox <domain> <resource> <action> [flags]",
|
|
113
91
|
],
|
|
114
|
-
}, { format: flags.format });
|
|
92
|
+
}, { format: flags.format, jq: flags.jq });
|
|
115
93
|
return 0;
|
|
116
94
|
}
|
|
117
95
|
try {
|
|
96
|
+
if (cmd !== "version") {
|
|
97
|
+
assertCatalogCompatible();
|
|
98
|
+
}
|
|
118
99
|
switch (cmd) {
|
|
119
100
|
case "version":
|
|
120
101
|
return cmdVersion(flags);
|
|
@@ -154,18 +135,36 @@ async function runCli(argv, env = process.env) {
|
|
|
154
135
|
}, { exitCode: status === 401 || status === 403 ? 77 : 1 });
|
|
155
136
|
}
|
|
156
137
|
}
|
|
138
|
+
function assertCatalogCompatible() {
|
|
139
|
+
const result = (0, operations_1.checkGeneratedCatalogCompatibility)(version_1.CLI_VERSION);
|
|
140
|
+
if (result.ok)
|
|
141
|
+
return;
|
|
142
|
+
(0, envelope_1.writeError)({
|
|
143
|
+
type: "compatibility",
|
|
144
|
+
subtype: result.code,
|
|
145
|
+
message: result.message,
|
|
146
|
+
hint: "Upgrade or pin @alphafox/cli to the catalog minCliVersion/maxCliVersion range.",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
157
149
|
function cmdVersion(flags) {
|
|
150
|
+
const compatibility = (0, operations_1.checkGeneratedCatalogCompatibility)(version_1.CLI_VERSION);
|
|
158
151
|
(0, envelope_1.writeSuccess)({
|
|
159
152
|
name: version_1.CLI_NAME,
|
|
160
153
|
package: version_1.CLI_PACKAGE,
|
|
161
154
|
version: version_1.CLI_VERSION,
|
|
162
155
|
contractVersion: version_1.CLI_CONTRACT_VERSION,
|
|
163
156
|
catalogVersion: operations_1.CATALOG_VERSION,
|
|
157
|
+
registryVersion: operations_1.COMPATIBILITY_RANGE.registryVersion,
|
|
158
|
+
minCliVersion: operations_1.COMPATIBILITY_RANGE.minCliVersion,
|
|
159
|
+
maxCliVersion: operations_1.COMPATIBILITY_RANGE.maxCliVersion,
|
|
160
|
+
openapi: operations_1.COMPATIBILITY_RANGE.openapi,
|
|
161
|
+
contractsSha: operations_1.CATALOG_SOURCE.contractsSha,
|
|
162
|
+
compatibility,
|
|
164
163
|
node: process.version,
|
|
165
164
|
platform: process.platform,
|
|
166
165
|
arch: process.arch,
|
|
167
|
-
}, { format: flags.format });
|
|
168
|
-
return 0;
|
|
166
|
+
}, { format: flags.format, jq: flags.jq });
|
|
167
|
+
return compatibility.ok ? 0 : 1;
|
|
169
168
|
}
|
|
170
169
|
function cmdDoctor(flags, env) {
|
|
171
170
|
const profile = (0, profiles_1.resolveProfile)(flags.profile, env, {
|
|
@@ -174,6 +173,7 @@ function cmdDoctor(flags, env) {
|
|
|
174
173
|
// Avoid macOS `security` stderr noise when no item exists; file keychain is fine for doctor.
|
|
175
174
|
const doctorEnv = { ...env, ALPHAFOX_FORCE_FILE_KEYCHAIN: env.ALPHAFOX_FORCE_FILE_KEYCHAIN ?? "1" };
|
|
176
175
|
const tokens = (0, store_1.loadTokens)(profile.name, doctorEnv);
|
|
176
|
+
const probe = (0, store_1.probeOsKeychain)(env);
|
|
177
177
|
const checks = [
|
|
178
178
|
{
|
|
179
179
|
name: "node",
|
|
@@ -202,6 +202,13 @@ function cmdDoctor(flags, env) {
|
|
|
202
202
|
? `token present (fp=${(0, store_1.tokenFingerprint)(tokens.accessToken)})`
|
|
203
203
|
: "no tokens stored",
|
|
204
204
|
},
|
|
205
|
+
{
|
|
206
|
+
name: "osKeychain",
|
|
207
|
+
ok: true,
|
|
208
|
+
detail: probe.available
|
|
209
|
+
? probe.kind
|
|
210
|
+
: `${probe.kind} unavailable; file fallback (0600) if tokens are saved`,
|
|
211
|
+
},
|
|
205
212
|
{
|
|
206
213
|
name: "configHasNoTokens",
|
|
207
214
|
ok: true,
|
|
@@ -212,9 +219,14 @@ function cmdDoctor(flags, env) {
|
|
|
212
219
|
ok: true,
|
|
213
220
|
detail: "v1 deferred (interactive login only)",
|
|
214
221
|
},
|
|
222
|
+
{
|
|
223
|
+
name: "catalogCompatibility",
|
|
224
|
+
ok: (0, operations_1.checkGeneratedCatalogCompatibility)(version_1.CLI_VERSION).ok,
|
|
225
|
+
detail: `${version_1.CLI_VERSION} in [${operations_1.COMPATIBILITY_RANGE.minCliVersion}, ${operations_1.COMPATIBILITY_RANGE.maxCliVersion}] contract ${operations_1.COMPATIBILITY_RANGE.contractVersion}`,
|
|
226
|
+
},
|
|
215
227
|
];
|
|
216
228
|
const ok = checks.every((c) => c.ok);
|
|
217
|
-
(0, envelope_1.writeSuccess)({ ok, profile: profile.name, checks }, { format: flags.format });
|
|
229
|
+
(0, envelope_1.writeSuccess)({ ok, profile: profile.name, checks }, { format: flags.format, jq: flags.jq });
|
|
218
230
|
return ok ? 0 : 1;
|
|
219
231
|
}
|
|
220
232
|
async function cmdWhoami(flags, env) {
|
|
@@ -236,7 +248,7 @@ async function cmdWhoami(flags, env) {
|
|
|
236
248
|
code: extractErrorCode(res.json),
|
|
237
249
|
}, { requestId: res.requestId, exitCode: res.status === 401 ? 77 : 1 });
|
|
238
250
|
}
|
|
239
|
-
(0, envelope_1.writeSuccess)(res.json, { requestId: res.requestId, format: flags.format });
|
|
251
|
+
(0, envelope_1.writeSuccess)(res.json, { requestId: res.requestId, format: flags.format, jq: flags.jq });
|
|
240
252
|
return 0;
|
|
241
253
|
}
|
|
242
254
|
async function cmdAuth(args, flags, env) {
|
|
@@ -252,7 +264,7 @@ async function cmdAuth(args, flags, env) {
|
|
|
252
264
|
authenticated: false,
|
|
253
265
|
profile: profile.name,
|
|
254
266
|
verified: false,
|
|
255
|
-
}, { format: flags.format });
|
|
267
|
+
}, { format: flags.format, jq: flags.jq });
|
|
256
268
|
return 0;
|
|
257
269
|
}
|
|
258
270
|
let verified = null;
|
|
@@ -278,14 +290,15 @@ async function cmdAuth(args, flags, env) {
|
|
|
278
290
|
expiresAt: tokens.expiresAt,
|
|
279
291
|
verified,
|
|
280
292
|
whoami,
|
|
281
|
-
}, { format: flags.format });
|
|
293
|
+
}, { format: flags.format, jq: flags.jq });
|
|
282
294
|
return 0;
|
|
283
295
|
}
|
|
284
296
|
if (sub === "logout") {
|
|
285
297
|
const tokens = (0, store_1.loadTokens)(profile.name, env);
|
|
298
|
+
let remoteRevoke = "skipped";
|
|
286
299
|
if (tokens?.refreshToken) {
|
|
287
300
|
try {
|
|
288
|
-
await (0, client_1.apiRequest)({
|
|
301
|
+
const res = await (0, client_1.apiRequest)({
|
|
289
302
|
method: "POST",
|
|
290
303
|
path: "/api/auth/oauth/revoke",
|
|
291
304
|
profile,
|
|
@@ -295,14 +308,26 @@ async function cmdAuth(args, flags, env) {
|
|
|
295
308
|
},
|
|
296
309
|
skipAuth: false,
|
|
297
310
|
}, env);
|
|
311
|
+
remoteRevoke =
|
|
312
|
+
res.status >= 200 && res.status < 300 ? "ok" : "failed";
|
|
298
313
|
}
|
|
299
314
|
catch {
|
|
300
|
-
|
|
315
|
+
remoteRevoke = "failed";
|
|
301
316
|
}
|
|
302
317
|
}
|
|
303
318
|
(0, store_1.deleteTokens)(profile.name, env);
|
|
304
|
-
|
|
305
|
-
|
|
319
|
+
const localCleared = true;
|
|
320
|
+
const fullyLoggedOut = remoteRevoke !== "failed";
|
|
321
|
+
// Never claim a clean remote logout when revoke failed while an RT was present.
|
|
322
|
+
(0, envelope_1.writeSuccess)({
|
|
323
|
+
localCleared,
|
|
324
|
+
remoteRevoke,
|
|
325
|
+
fullyLoggedOut,
|
|
326
|
+
// retained for compatibility; false when remote revoke failed
|
|
327
|
+
loggedOut: fullyLoggedOut,
|
|
328
|
+
profile: profile.name,
|
|
329
|
+
}, { format: flags.format, jq: flags.jq });
|
|
330
|
+
return fullyLoggedOut ? 0 : 1;
|
|
306
331
|
}
|
|
307
332
|
if (sub === "login") {
|
|
308
333
|
return await cmdAuthLogin(args.slice(1), flags, env, profile);
|
|
@@ -370,7 +395,7 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
370
395
|
profile: profile.name,
|
|
371
396
|
accessTokenFingerprint: (0, store_1.tokenFingerprint)(tokens.access_token),
|
|
372
397
|
expiresIn: tokens.expires_in,
|
|
373
|
-
}, { format: flags.format, requestId: res.requestId });
|
|
398
|
+
}, { format: flags.format, jq: flags.jq, requestId: res.requestId });
|
|
374
399
|
return 0;
|
|
375
400
|
}
|
|
376
401
|
if (deviceCode) {
|
|
@@ -389,7 +414,7 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
389
414
|
(0, envelope_1.writeSuccess)({
|
|
390
415
|
status: "authorization_pending",
|
|
391
416
|
profile: profile.name,
|
|
392
|
-
}, { format: flags.format, requestId: res.requestId });
|
|
417
|
+
}, { format: flags.format, jq: flags.jq, requestId: res.requestId });
|
|
393
418
|
return 0;
|
|
394
419
|
}
|
|
395
420
|
if (res.status >= 400) {
|
|
@@ -415,7 +440,7 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
415
440
|
profile: profile.name,
|
|
416
441
|
accessTokenFingerprint: (0, store_1.tokenFingerprint)(tokens.access_token),
|
|
417
442
|
expiresIn: tokens.expires_in,
|
|
418
|
-
}, { format: flags.format, requestId: res.requestId });
|
|
443
|
+
}, { format: flags.format, jq: flags.jq, requestId: res.requestId });
|
|
419
444
|
return 0;
|
|
420
445
|
}
|
|
421
446
|
// Start device flow (default headless path)
|
|
@@ -448,7 +473,7 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
448
473
|
expires_in: data.expires_in,
|
|
449
474
|
interval: data.interval,
|
|
450
475
|
next: "Open verification_uri, approve, then: alphafox auth login --device-code <device_code>",
|
|
451
|
-
}, { format: flags.format, requestId: res.requestId });
|
|
476
|
+
}, { format: flags.format, jq: flags.jq, requestId: res.requestId });
|
|
452
477
|
if (noWait) {
|
|
453
478
|
return 0;
|
|
454
479
|
}
|
|
@@ -494,7 +519,7 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
494
519
|
authenticated: true,
|
|
495
520
|
profile: profile.name,
|
|
496
521
|
accessTokenFingerprint: (0, store_1.tokenFingerprint)(tokens.access_token),
|
|
497
|
-
}, { format: flags.format, requestId: poll.requestId });
|
|
522
|
+
}, { format: flags.format, jq: flags.jq, requestId: poll.requestId });
|
|
498
523
|
return 0;
|
|
499
524
|
}
|
|
500
525
|
(0, envelope_1.writeError)({
|
|
@@ -503,36 +528,34 @@ async function cmdAuthLogin(args, flags, env, profile) {
|
|
|
503
528
|
subtype: "expired_token",
|
|
504
529
|
});
|
|
505
530
|
}
|
|
506
|
-
// Browser Authorization Code + PKCE (RFC
|
|
531
|
+
// Browser Authorization Code + PKCE with loopback callback (RFC 8252).
|
|
507
532
|
if (browser || args.includes("--pkce")) {
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
533
|
+
const result = await (0, browser_login_1.runBrowserPkceLogin)({
|
|
534
|
+
profile,
|
|
535
|
+
env,
|
|
536
|
+
timeoutMs: (0, browser_login_1.browserLoginTimeoutMs)(env),
|
|
537
|
+
openBrowser: (0, browser_login_1.resolveOpenBrowser)(env),
|
|
538
|
+
});
|
|
539
|
+
if (result.status !== "authenticated") {
|
|
540
|
+
(0, envelope_1.writeError)({
|
|
541
|
+
type: "auth",
|
|
542
|
+
subtype: result.reason,
|
|
543
|
+
message: result.message,
|
|
544
|
+
details: result.authorizeUrl
|
|
545
|
+
? { authorizeUrl: result.authorizeUrl }
|
|
546
|
+
: undefined,
|
|
547
|
+
hint: result.authorizeUrl
|
|
548
|
+
? "Copy authorizeUrl into a browser, or use Device Flow: alphafox auth login --no-wait"
|
|
549
|
+
: undefined,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
523
552
|
(0, envelope_1.writeSuccess)({
|
|
553
|
+
authenticated: true,
|
|
524
554
|
flow: "authorization_code_pkce",
|
|
525
555
|
profile: profile.name,
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
code_challenge: codeChallenge,
|
|
530
|
-
code_challenge_method: "S256",
|
|
531
|
-
// Required for the follow-up exchange; store securely (not in config).
|
|
532
|
-
code_verifier: codeVerifier,
|
|
533
|
-
next: `After browser redirect: alphafox auth login --code <code> --code-verifier ${codeVerifier} --redirect-uri ${redirectUri}`,
|
|
534
|
-
note: "Sign in at authorizeUrl if prompted. Prefer Device Flow for headless agents.",
|
|
535
|
-
}, { format: flags.format });
|
|
556
|
+
accessTokenFingerprint: result.accessTokenFingerprint,
|
|
557
|
+
expiresIn: result.expiresIn,
|
|
558
|
+
}, { format: flags.format, jq: flags.jq, requestId: result.requestId });
|
|
536
559
|
return 0;
|
|
537
560
|
}
|
|
538
561
|
(0, envelope_1.writeError)({
|
|
@@ -547,7 +570,7 @@ function cmdProfile(args, flags, env) {
|
|
|
547
570
|
(0, envelope_1.writeSuccess)({
|
|
548
571
|
activeProfile: file.activeProfile,
|
|
549
572
|
profiles: ["production", "staging", "local"].map((name) => (0, profiles_1.resolveProfile)(name, env)),
|
|
550
|
-
}, { format: flags.format });
|
|
573
|
+
}, { format: flags.format, jq: flags.jq });
|
|
551
574
|
return 0;
|
|
552
575
|
}
|
|
553
576
|
if (sub === "use") {
|
|
@@ -560,7 +583,7 @@ function cmdProfile(args, flags, env) {
|
|
|
560
583
|
}
|
|
561
584
|
const file = (0, profiles_1.loadConfigFile)(env);
|
|
562
585
|
(0, profiles_1.saveConfigFile)({ ...file, activeProfile: name }, env);
|
|
563
|
-
(0, envelope_1.writeSuccess)({ activeProfile: name }, { format: flags.format });
|
|
586
|
+
(0, envelope_1.writeSuccess)({ activeProfile: name }, { format: flags.format, jq: flags.jq });
|
|
564
587
|
return 0;
|
|
565
588
|
}
|
|
566
589
|
(0, envelope_1.writeError)({ type: "usage", message: "Usage: alphafox profile list|use" });
|
|
@@ -570,12 +593,17 @@ function cmdSchema(args, flags) {
|
|
|
570
593
|
if (!operationId) {
|
|
571
594
|
(0, envelope_1.writeSuccess)({
|
|
572
595
|
contractVersion: operations_1.CATALOG_VERSION,
|
|
596
|
+
registryVersion: operations_1.COMPATIBILITY_RANGE.registryVersion,
|
|
597
|
+
openapi: operations_1.COMPATIBILITY_RANGE.openapi,
|
|
598
|
+
minCliVersion: operations_1.COMPATIBILITY_RANGE.minCliVersion,
|
|
599
|
+
maxCliVersion: operations_1.COMPATIBILITY_RANGE.maxCliVersion,
|
|
573
600
|
operations: operations_1.CATALOG_OPERATIONS.map((o) => o.operationId),
|
|
574
|
-
}, { format: flags.format });
|
|
601
|
+
}, { format: flags.format, jq: flags.jq });
|
|
575
602
|
return 0;
|
|
576
603
|
}
|
|
577
604
|
const op = (0, operations_1.findCatalogOperation)(operationId);
|
|
578
|
-
|
|
605
|
+
const schema = (0, operations_1.getOperationSchemaDocument)(operationId);
|
|
606
|
+
if (!op || !schema) {
|
|
579
607
|
(0, envelope_1.writeError)({
|
|
580
608
|
type: "not_found",
|
|
581
609
|
message: `Unknown operationId: ${operationId}`,
|
|
@@ -583,26 +611,13 @@ function cmdSchema(args, flags) {
|
|
|
583
611
|
});
|
|
584
612
|
}
|
|
585
613
|
(0, envelope_1.writeSuccess)({
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
role: op.role,
|
|
590
|
-
risk: op.risk,
|
|
591
|
-
scopes: op.scopes,
|
|
592
|
-
stream: op.stream ?? false,
|
|
593
|
-
mvp: op.mvp ?? false,
|
|
594
|
-
description: op.description,
|
|
595
|
-
input: {
|
|
596
|
-
type: "object",
|
|
597
|
-
note: "See OpenAPI at GET /api/v1/openapi.json and alphafox-contracts Zod schemas",
|
|
598
|
-
},
|
|
599
|
-
output: { type: "object" },
|
|
600
|
-
errors: ["401", "403", "404", "409", "422", "429", "5xx"],
|
|
601
|
-
}, { format: flags.format });
|
|
614
|
+
...schema,
|
|
615
|
+
examples: (0, command_tree_1.typedCommandExample)(op),
|
|
616
|
+
}, { format: flags.format, jq: flags.jq });
|
|
602
617
|
return 0;
|
|
603
618
|
}
|
|
604
619
|
function cmdCatalog(flags) {
|
|
605
|
-
(0, envelope_1.writeSuccess)((0, operations_1.buildCapabilityManifest)(), { format: flags.format });
|
|
620
|
+
(0, envelope_1.writeSuccess)((0, operations_1.buildCapabilityManifest)(), { format: flags.format, jq: flags.jq });
|
|
606
621
|
return 0;
|
|
607
622
|
}
|
|
608
623
|
async function cmdApi(args, flags, env) {
|
|
@@ -630,19 +645,18 @@ async function cmdApi(args, flags, env) {
|
|
|
630
645
|
const profile = (0, profiles_1.resolveProfile)(flags.profile, env, {
|
|
631
646
|
unsafeCustomEndpoint: flags.unsafeCustomEndpoint,
|
|
632
647
|
});
|
|
633
|
-
// Infer risk from catalog
|
|
634
|
-
const catalogHit = operations_1.
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
648
|
+
// Infer risk from catalog; uncataloged mutations are treated as high-risk.
|
|
649
|
+
const catalogHit = (0, operations_1.findCatalogOperationByRoute)(method, (0, allowlist_1.normalizeApiPath)(path));
|
|
650
|
+
const risk = (0, confirmation_1.inferRawApiRisk)(method, catalogHit?.risk);
|
|
651
|
+
const gate = (0, confirmation_1.assertHighRiskConfirmation)({
|
|
652
|
+
risk,
|
|
653
|
+
yes: flags.yes,
|
|
654
|
+
dryRun: flags.dryRun,
|
|
655
|
+
action: `api ${method} ${path}`,
|
|
656
|
+
});
|
|
657
|
+
if (!gate.allowed && gate.error) {
|
|
658
|
+
process.stderr.write(`${JSON.stringify((0, envelope_1.errorEnvelope)(gate.error))}\n`);
|
|
659
|
+
return 10;
|
|
646
660
|
}
|
|
647
661
|
if (flags.dryRun) {
|
|
648
662
|
(0, envelope_1.writeSuccess)({
|
|
@@ -650,8 +664,8 @@ async function cmdApi(args, flags, env) {
|
|
|
650
664
|
method,
|
|
651
665
|
path,
|
|
652
666
|
profile: profile.name,
|
|
653
|
-
risk
|
|
654
|
-
}, { format: flags.format });
|
|
667
|
+
risk,
|
|
668
|
+
}, { format: flags.format, jq: flags.jq });
|
|
655
669
|
return 0;
|
|
656
670
|
}
|
|
657
671
|
const res = await (0, client_1.apiRequest)({
|
|
@@ -669,44 +683,47 @@ async function cmdApi(args, flags, env) {
|
|
|
669
683
|
code: extractErrorCode(res.json),
|
|
670
684
|
}, { requestId: res.requestId, exitCode: res.status === 401 ? 77 : 1 });
|
|
671
685
|
}
|
|
672
|
-
(0, envelope_1.writeSuccess)(res.json, { requestId: res.requestId, format: flags.format });
|
|
686
|
+
(0, envelope_1.writeSuccess)(res.json, { requestId: res.requestId, format: flags.format, jq: flags.jq });
|
|
673
687
|
return 0;
|
|
674
688
|
}
|
|
675
689
|
async function cmdTyped(domain, args, flags, env) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
690
|
+
const resolved = (0, command_tree_1.resolveTypedCommand)([domain, ...args]);
|
|
691
|
+
if (resolved.kind === "help") {
|
|
692
|
+
(0, envelope_1.writeSuccess)({
|
|
693
|
+
prefix: resolved.prefix,
|
|
694
|
+
operations: resolved.operations.map((op) => ({
|
|
695
|
+
operationId: op.operationId,
|
|
696
|
+
method: op.method,
|
|
697
|
+
path: op.path,
|
|
698
|
+
risk: op.risk,
|
|
699
|
+
scopes: op.scopes,
|
|
700
|
+
stream: Boolean(op.stream),
|
|
701
|
+
file: Boolean(op.file),
|
|
702
|
+
pagination: Boolean(op.pagination),
|
|
703
|
+
examples: (0, command_tree_1.typedCommandExample)(op),
|
|
704
|
+
})),
|
|
705
|
+
}, { format: flags.format, jq: flags.jq });
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
|
708
|
+
if (resolved.kind === "ambiguous") {
|
|
686
709
|
(0, envelope_1.writeError)({
|
|
687
710
|
type: "usage",
|
|
688
|
-
message: `
|
|
711
|
+
message: `Ambiguous command "${resolved.prefix}"`,
|
|
712
|
+
hint: `Matches: ${resolved.candidates.join(", ")}`,
|
|
689
713
|
});
|
|
690
714
|
}
|
|
691
|
-
|
|
692
|
-
const parts = o.operationId.split(".");
|
|
693
|
-
return (parts[0]?.replace(/_/g, "-") === domain.replace(/_/g, "-") ||
|
|
694
|
-
o.operationId.startsWith(domain.replace(/-/g, "_")));
|
|
695
|
-
});
|
|
696
|
-
const op = candidates.find((o) => o.operationId.endsWith(`.${action}`) ||
|
|
697
|
-
o.operationId.includes(`.${resource}.`) &&
|
|
698
|
-
o.operationId.endsWith(action)) ||
|
|
699
|
-
(0, operations_1.findCatalogOperation)(`${domain}.${resource}.${action}`) ||
|
|
700
|
-
(0, operations_1.findCatalogOperation)(`${domain.replace(/-/g, "_")}.${resource.replace(/-/g, "_")}.${action}`);
|
|
701
|
-
if (!op) {
|
|
715
|
+
if (resolved.kind === "missing") {
|
|
702
716
|
(0, envelope_1.writeError)({
|
|
703
717
|
type: "not_found",
|
|
704
|
-
message: `No catalog operation for ${
|
|
718
|
+
message: `No catalog operation for ${resolved.prefix || domain}`,
|
|
705
719
|
status: 404,
|
|
706
720
|
hint: "Run alphafox schema for operationIds",
|
|
707
721
|
});
|
|
708
722
|
}
|
|
709
|
-
|
|
723
|
+
if (resolved.help) {
|
|
724
|
+
return cmdSchema([resolved.operation.operationId], flags);
|
|
725
|
+
}
|
|
726
|
+
return await invokeOperation(resolved.operation.operationId, resolved.flagArgs, flags, env);
|
|
710
727
|
}
|
|
711
728
|
async function invokeOperation(operationId, args, flags, env) {
|
|
712
729
|
const op = (0, operations_1.findCatalogOperation)(operationId);
|
|
@@ -728,11 +745,20 @@ async function invokeOperation(operationId, args, flags, env) {
|
|
|
728
745
|
return 10;
|
|
729
746
|
}
|
|
730
747
|
const params = {};
|
|
748
|
+
const extra = {};
|
|
749
|
+
const pathParamNames = new Set((0, operations_1.getOperationSchemaDocument)(operationId)?.request.pathParamNames ??
|
|
750
|
+
(0, operations_1.extractPathParamNames)(op.path));
|
|
731
751
|
for (let i = 0; i < args.length; i += 1) {
|
|
732
752
|
const a = args[i];
|
|
733
753
|
if (a.startsWith("--") && a !== "--body") {
|
|
734
754
|
const key = a.slice(2);
|
|
735
|
-
|
|
755
|
+
const value = args[++i] ?? "";
|
|
756
|
+
if (pathParamNames.has(key)) {
|
|
757
|
+
params[key] = value;
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
extra[key] = value;
|
|
761
|
+
}
|
|
736
762
|
}
|
|
737
763
|
}
|
|
738
764
|
let body;
|
|
@@ -740,7 +766,12 @@ async function invokeOperation(operationId, args, flags, env) {
|
|
|
740
766
|
if (bodyIdx >= 0) {
|
|
741
767
|
body = JSON.parse(args[bodyIdx + 1] ?? "{}");
|
|
742
768
|
}
|
|
743
|
-
|
|
769
|
+
let path = (0, operations_1.resolveOperationPath)(op.path, params);
|
|
770
|
+
if ((op.method === "GET" || op.method === "HEAD") &&
|
|
771
|
+
Object.keys(extra).length > 0) {
|
|
772
|
+
const query = new URLSearchParams(extra).toString();
|
|
773
|
+
path = `${path}?${query}`;
|
|
774
|
+
}
|
|
744
775
|
const profile = (0, profiles_1.resolveProfile)(flags.profile, env, {
|
|
745
776
|
unsafeCustomEndpoint: flags.unsafeCustomEndpoint,
|
|
746
777
|
});
|
|
@@ -752,7 +783,7 @@ async function invokeOperation(operationId, args, flags, env) {
|
|
|
752
783
|
path,
|
|
753
784
|
risk: op.risk,
|
|
754
785
|
profile: profile.name,
|
|
755
|
-
}, { format: flags.format });
|
|
786
|
+
}, { format: flags.format, jq: flags.jq });
|
|
756
787
|
return 0;
|
|
757
788
|
}
|
|
758
789
|
const res = await (0, client_1.apiRequest)({
|
|
@@ -772,7 +803,7 @@ async function invokeOperation(operationId, args, flags, env) {
|
|
|
772
803
|
}
|
|
773
804
|
(0, envelope_1.writeSuccess)(res.json, {
|
|
774
805
|
requestId: res.requestId,
|
|
775
|
-
format: flags.format,
|
|
806
|
+
format: flags.format, jq: flags.jq,
|
|
776
807
|
meta: { operationId },
|
|
777
808
|
});
|
|
778
809
|
return 0;
|
|
@@ -835,19 +866,6 @@ function extractErrorCode(json) {
|
|
|
835
866
|
}
|
|
836
867
|
return undefined;
|
|
837
868
|
}
|
|
838
|
-
function pathMatches(template, actual) {
|
|
839
|
-
const t = template.split("/").filter(Boolean);
|
|
840
|
-
const a = actual.split("/").filter(Boolean);
|
|
841
|
-
if (t.length !== a.length)
|
|
842
|
-
return false;
|
|
843
|
-
for (let i = 0; i < t.length; i += 1) {
|
|
844
|
-
if (t[i].startsWith("{") && t[i].endsWith("}"))
|
|
845
|
-
continue;
|
|
846
|
-
if (t[i] !== a[i])
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
return true;
|
|
850
|
-
}
|
|
851
869
|
function sleep(ms) {
|
|
852
870
|
return new Promise((r) => setTimeout(r, ms));
|
|
853
871
|
}
|
package/dist/config/profiles.js
CHANGED
|
@@ -16,7 +16,7 @@ const DEFAULTS = {
|
|
|
16
16
|
issuer: "https://alphafox.app/api/auth",
|
|
17
17
|
audience: "https://alphafox.app/api/v1",
|
|
18
18
|
clientId: "alphafox-cli-prod",
|
|
19
|
-
contractVersion: "2026-08-
|
|
19
|
+
contractVersion: "2026-08-13",
|
|
20
20
|
},
|
|
21
21
|
staging: {
|
|
22
22
|
name: "staging",
|
|
@@ -24,7 +24,7 @@ const DEFAULTS = {
|
|
|
24
24
|
issuer: "https://staging.alphafox.app/api/auth",
|
|
25
25
|
audience: "https://staging.alphafox.app/api/v1",
|
|
26
26
|
clientId: "alphafox-cli-staging",
|
|
27
|
-
contractVersion: "2026-08-
|
|
27
|
+
contractVersion: "2026-08-13",
|
|
28
28
|
},
|
|
29
29
|
local: {
|
|
30
30
|
name: "local",
|
|
@@ -33,7 +33,7 @@ const DEFAULTS = {
|
|
|
33
33
|
audience: "http://127.0.0.1:3000/api/v1",
|
|
34
34
|
clientId: "alphafox-cli-local",
|
|
35
35
|
localOrigin: "http://127.0.0.1:3000",
|
|
36
|
-
contractVersion: "2026-08-
|
|
36
|
+
contractVersion: "2026-08-13",
|
|
37
37
|
},
|
|
38
38
|
};
|
|
39
39
|
function defaultConfigDir(env = process.env) {
|
package/dist/envelope.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
1
2
|
export type ExitCode = 0 | 1 | 2 | 3 | 4 | 10 | 64 | 65 | 66 | 69 | 70 | 75 | 77 | 78;
|
|
2
3
|
export interface SuccessEnvelope<T = unknown> {
|
|
3
4
|
readonly ok: true;
|
|
@@ -28,7 +29,9 @@ export declare function writeSuccess(data: unknown, options?: {
|
|
|
28
29
|
readonly meta?: Record<string, unknown>;
|
|
29
30
|
readonly requestId?: string;
|
|
30
31
|
readonly format?: "json" | "jsonl" | "text";
|
|
32
|
+
readonly jq?: string;
|
|
31
33
|
}): void;
|
|
34
|
+
export declare function applyJqFilter(value: unknown, filter: string, spawn?: typeof spawnSync): string;
|
|
32
35
|
export declare function writeError(error: ErrorBody, options?: {
|
|
33
36
|
readonly requestId?: string;
|
|
34
37
|
readonly exitCode?: number;
|