@tryarcanist/cli 0.1.160 → 0.1.162
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.js +223 -273
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -138,6 +138,16 @@ async function apiFetchText(config, path, init) {
|
|
|
138
138
|
const res = await apiRequest(config, path, init);
|
|
139
139
|
return res.text();
|
|
140
140
|
}
|
|
141
|
+
async function resolveBusinessId(config, options) {
|
|
142
|
+
if (options.business && options.business.trim()) return options.business.trim();
|
|
143
|
+
const whoami = await apiFetch(config, "/api/auth/whoami");
|
|
144
|
+
if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
|
|
145
|
+
throw new CliError("user", "--business is required when the authenticated token has no business context.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/runtime.ts
|
|
149
|
+
import { randomUUID } from "crypto";
|
|
150
|
+
import { createInterface } from "readline/promises";
|
|
141
151
|
|
|
142
152
|
// src/config.ts
|
|
143
153
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -289,8 +299,6 @@ function isLoopbackHost(parsed) {
|
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
// src/runtime.ts
|
|
292
|
-
import { randomUUID } from "crypto";
|
|
293
|
-
import { createInterface } from "readline/promises";
|
|
294
302
|
var ASCII_FIRST_PRINTABLE = 32;
|
|
295
303
|
var ASCII_DELETE = 127;
|
|
296
304
|
var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9:;<=>?]*[ -/]*[@-~]/g;
|
|
@@ -306,6 +314,17 @@ function writeJson(value) {
|
|
|
306
314
|
process.stdout.write(`${JSON.stringify(value)}
|
|
307
315
|
`);
|
|
308
316
|
}
|
|
317
|
+
function emit(command, options, payload, printer) {
|
|
318
|
+
if (isJson(command, options)) {
|
|
319
|
+
writeJson(payload);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
printer(payload);
|
|
323
|
+
}
|
|
324
|
+
function resolveBusinessContext(command, options = {}) {
|
|
325
|
+
const runtime = getRuntimeOptions(command, options);
|
|
326
|
+
return { runtime, config: requireConfig(runtime) };
|
|
327
|
+
}
|
|
309
328
|
async function readStdin() {
|
|
310
329
|
const chunks = [];
|
|
311
330
|
for await (const chunk of process.stdin) {
|
|
@@ -468,15 +487,12 @@ function isControlCharacter(char) {
|
|
|
468
487
|
|
|
469
488
|
// src/commands/auth.ts
|
|
470
489
|
async function whoamiCommand(options, command) {
|
|
471
|
-
const
|
|
472
|
-
const config = requireConfig(runtime);
|
|
490
|
+
const { config } = resolveBusinessContext(command, options);
|
|
473
491
|
const payload = await apiFetch(config, "/api/auth/whoami");
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
console.log(`User: ${String(payload.email ?? payload.userId ?? "unknown")}`);
|
|
479
|
-
if (payload.tokenScope) console.log(`Token scope: ${String(payload.tokenScope)}`);
|
|
492
|
+
emit(command, options, payload, (whoami) => {
|
|
493
|
+
console.log(`User: ${String(whoami.email ?? whoami.userId ?? "unknown")}`);
|
|
494
|
+
if (whoami.tokenScope) console.log(`Token scope: ${String(whoami.tokenScope)}`);
|
|
495
|
+
});
|
|
480
496
|
}
|
|
481
497
|
|
|
482
498
|
// ../../shared/github/repo-url.ts
|
|
@@ -991,9 +1007,6 @@ function buildModelProviderGroups(models) {
|
|
|
991
1007
|
var SESSION_START_MODEL_ID_SET_ANY_BACKEND = new Set(
|
|
992
1008
|
AGENT_RUNTIME_BACKENDS.flatMap((backend) => [...SESSION_START_MODEL_IDS_BY_BACKEND[backend]])
|
|
993
1009
|
);
|
|
994
|
-
var SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
|
|
995
|
-
MODEL_REGISTRY.filter((model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id))
|
|
996
|
-
);
|
|
997
1010
|
var PUBLIC_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
|
|
998
1011
|
MODEL_REGISTRY.filter(
|
|
999
1012
|
(model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && model.visibility !== "internal_probe"
|
|
@@ -1444,7 +1457,8 @@ function createStreamCoalescerState() {
|
|
|
1444
1457
|
return {
|
|
1445
1458
|
streamableIndexById: /* @__PURE__ */ new Map(),
|
|
1446
1459
|
segmentOrdinalByStreamId: /* @__PURE__ */ new Map(),
|
|
1447
|
-
concatByStreamId: /* @__PURE__ */ new Map()
|
|
1460
|
+
concatByStreamId: /* @__PURE__ */ new Map(),
|
|
1461
|
+
interstitialScanEndByStreamId: /* @__PURE__ */ new Map()
|
|
1448
1462
|
};
|
|
1449
1463
|
}
|
|
1450
1464
|
function streamableKey(type, streamId) {
|
|
@@ -1462,6 +1476,27 @@ function resolveTextValue(data) {
|
|
|
1462
1476
|
const content = data?.content;
|
|
1463
1477
|
return typeof content === "string" ? content : "";
|
|
1464
1478
|
}
|
|
1479
|
+
function isNonContentInterstitial(event) {
|
|
1480
|
+
switch (event.type) {
|
|
1481
|
+
case "agent_progress":
|
|
1482
|
+
case "retry_status":
|
|
1483
|
+
case "memory_usage":
|
|
1484
|
+
case "memory_recall_usage":
|
|
1485
|
+
case "agent_timeline":
|
|
1486
|
+
return true;
|
|
1487
|
+
default:
|
|
1488
|
+
return false;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
function canAppendAcrossNonContentInterstitials(events, state, key, existingIdx) {
|
|
1492
|
+
if (existingIdx === events.length - 1) return true;
|
|
1493
|
+
const scanStart = Math.max(existingIdx + 1, state.interstitialScanEndByStreamId.get(key) ?? existingIdx + 1);
|
|
1494
|
+
for (let i = scanStart; i < events.length; i++) {
|
|
1495
|
+
if (!isNonContentInterstitial(events[i])) return false;
|
|
1496
|
+
}
|
|
1497
|
+
state.interstitialScanEndByStreamId.set(key, events.length);
|
|
1498
|
+
return true;
|
|
1499
|
+
}
|
|
1465
1500
|
function resolvePromptId(data) {
|
|
1466
1501
|
return typeof data?.promptId === "string" ? data.promptId : void 0;
|
|
1467
1502
|
}
|
|
@@ -1469,22 +1504,24 @@ function coalesceStreamDelta(events, state, type, streamId, text, promptId) {
|
|
|
1469
1504
|
const key = streamableKey(type, streamId);
|
|
1470
1505
|
const existingText = state.concatByStreamId.get(key) ?? "";
|
|
1471
1506
|
const existingIdx = state.streamableIndexById.get(key);
|
|
1472
|
-
|
|
1507
|
+
const appendableText = shouldAppendTextDelta(existingText, text);
|
|
1508
|
+
if (existingIdx !== void 0) {
|
|
1473
1509
|
const existing = events[existingIdx];
|
|
1474
|
-
if (existing.type === type &&
|
|
1510
|
+
if (existing.type === type && appendableText && canAppendAcrossNonContentInterstitials(events, state, key, existingIdx)) {
|
|
1475
1511
|
existing.text += text;
|
|
1476
1512
|
state.concatByStreamId.set(key, existingText + text);
|
|
1477
1513
|
if (!existing.promptId && promptId) existing.promptId = promptId;
|
|
1478
1514
|
return true;
|
|
1479
1515
|
}
|
|
1480
|
-
return false;
|
|
1516
|
+
if (existingIdx === events.length - 1 || !appendableText) return false;
|
|
1481
1517
|
}
|
|
1482
|
-
if (existingText && !
|
|
1518
|
+
if (existingText && !appendableText) return false;
|
|
1483
1519
|
const previousOrdinal = state.segmentOrdinalByStreamId.get(key);
|
|
1484
1520
|
const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
|
|
1485
1521
|
state.segmentOrdinalByStreamId.set(key, segmentOrdinal);
|
|
1486
1522
|
state.streamableIndexById.set(key, events.length);
|
|
1487
1523
|
state.concatByStreamId.set(key, existingText + text);
|
|
1524
|
+
state.interstitialScanEndByStreamId.set(key, events.length + 1);
|
|
1488
1525
|
if (type === "text") {
|
|
1489
1526
|
events.push({
|
|
1490
1527
|
type: "text",
|
|
@@ -1517,6 +1554,7 @@ function mergeToolCall(previous, incoming) {
|
|
|
1517
1554
|
...incoming.inputEstimatedTokens === void 0 && previous.inputEstimatedTokens !== void 0 ? { inputEstimatedTokens: previous.inputEstimatedTokens } : {},
|
|
1518
1555
|
...incoming.outputEstimatedTokens === void 0 && previous.outputEstimatedTokens !== void 0 ? { outputEstimatedTokens: previous.outputEstimatedTokens } : {},
|
|
1519
1556
|
...incoming.outputChars === void 0 && previous.outputChars !== void 0 ? { outputChars: previous.outputChars } : {},
|
|
1557
|
+
...incoming.failure === void 0 && previous.failure !== void 0 ? { failure: previous.failure } : {},
|
|
1520
1558
|
...incoming.duplicateCount === void 0 && previous.duplicateCount !== void 0 ? { duplicateCount: previous.duplicateCount } : {}
|
|
1521
1559
|
};
|
|
1522
1560
|
}
|
|
@@ -1530,6 +1568,7 @@ function applyToolCallUpdate(previous, data) {
|
|
|
1530
1568
|
...toolStatus ? { toolStatus } : {},
|
|
1531
1569
|
...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
|
|
1532
1570
|
...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
|
|
1571
|
+
...isRecord(data?.failure) ? { failure: data.failure } : {},
|
|
1533
1572
|
...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {}
|
|
1534
1573
|
};
|
|
1535
1574
|
}
|
|
@@ -1800,7 +1839,8 @@ function projectToolCall(data, state) {
|
|
|
1800
1839
|
...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
|
|
1801
1840
|
...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
|
|
1802
1841
|
...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
|
|
1803
|
-
...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
|
|
1842
|
+
...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
|
|
1843
|
+
...isRecord(data?.failure) ? { failure: data.failure } : {}
|
|
1804
1844
|
};
|
|
1805
1845
|
state.merged[existingIdx] = mergeToolCall(previous, nextEntry);
|
|
1806
1846
|
}
|
|
@@ -1817,7 +1857,8 @@ function projectToolCall(data, state) {
|
|
|
1817
1857
|
...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
|
|
1818
1858
|
...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
|
|
1819
1859
|
...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
|
|
1820
|
-
...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
|
|
1860
|
+
...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
|
|
1861
|
+
...isRecord(data?.failure) ? { failure: data.failure } : {}
|
|
1821
1862
|
};
|
|
1822
1863
|
state.toolCallIndexById.set(id, state.merged.length);
|
|
1823
1864
|
state.merged.push(nextEntry);
|
|
@@ -2972,105 +3013,88 @@ function parseRepoArg(value) {
|
|
|
2972
3013
|
if (!owner || !repo || extra) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
2973
3014
|
return { owner, repo };
|
|
2974
3015
|
}
|
|
2975
|
-
function encodePathSegment(value) {
|
|
2976
|
-
return encodeURIComponent(value);
|
|
2977
|
-
}
|
|
2978
|
-
async function resolveBusinessId(config, options) {
|
|
2979
|
-
if (options.business && options.business.trim()) return options.business.trim();
|
|
2980
|
-
const whoami = await apiFetch(config, "/api/auth/whoami");
|
|
2981
|
-
if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
|
|
2982
|
-
throw new CliError("user", "--business is required when the authenticated token has no business context.");
|
|
2983
|
-
}
|
|
2984
3016
|
async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
|
|
2985
|
-
const
|
|
2986
|
-
const config = requireConfig(runtime);
|
|
3017
|
+
const { config } = resolveBusinessContext(command, options);
|
|
2987
3018
|
const businessId = await resolveBusinessId(config, options);
|
|
2988
3019
|
const source = parseRepoArg(sourceRepoArg);
|
|
2989
3020
|
const payload = await apiFetch(
|
|
2990
3021
|
config,
|
|
2991
|
-
`/api/businesses/${
|
|
3022
|
+
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`,
|
|
2992
3023
|
{
|
|
2993
3024
|
method: "PUT",
|
|
2994
3025
|
body: JSON.stringify({ sourceRepoOwner: source.owner, sourceRepoName: source.repo })
|
|
2995
3026
|
}
|
|
2996
3027
|
);
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3028
|
+
emit(
|
|
3029
|
+
command,
|
|
3030
|
+
options,
|
|
3031
|
+
payload,
|
|
3032
|
+
() => console.log(`Set egress allowlist source to ${source.owner}/${source.repo}:${EGRESS_ALLOWLIST_SOURCE_PATH}.`)
|
|
3033
|
+
);
|
|
3002
3034
|
}
|
|
3003
3035
|
async function egressSourceGetCommand(options = {}, command) {
|
|
3004
|
-
const
|
|
3005
|
-
const config = requireConfig(runtime);
|
|
3036
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3006
3037
|
const businessId = await resolveBusinessId(config, options);
|
|
3007
3038
|
const payload = await apiFetch(
|
|
3008
3039
|
config,
|
|
3009
|
-
`/api/businesses/${
|
|
3040
|
+
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`
|
|
3010
3041
|
);
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
console.log(
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
);
|
|
3022
|
-
console.log(`${payload.domains.length} ${payload.domains.length === 1 ? "domain" : "domains"}.`);
|
|
3042
|
+
emit(command, options, payload, (sourcePayload) => {
|
|
3043
|
+
if (!sourcePayload.source) {
|
|
3044
|
+
console.log("No egress allowlist source configured.");
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
console.log(
|
|
3048
|
+
`${sourcePayload.source.sourceRepoOwner}/${sourcePayload.source.sourceRepoName}:${sourcePayload.path} (${sourcePayload.defaultBranch ?? "default branch"})`
|
|
3049
|
+
);
|
|
3050
|
+
console.log(`${sourcePayload.domains.length} ${sourcePayload.domains.length === 1 ? "domain" : "domains"}.`);
|
|
3051
|
+
});
|
|
3023
3052
|
}
|
|
3024
3053
|
async function egressAddCommand(domains, options = {}, command) {
|
|
3025
|
-
const
|
|
3026
|
-
const config = requireConfig(runtime);
|
|
3054
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3027
3055
|
const businessId = await resolveBusinessId(config, options);
|
|
3028
3056
|
const normalized = normalizeEgressDomains(domains, "domains");
|
|
3029
3057
|
const payload = await apiFetch(
|
|
3030
3058
|
config,
|
|
3031
|
-
`/api/businesses/${
|
|
3059
|
+
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/pull-request`,
|
|
3032
3060
|
{
|
|
3033
3061
|
method: "POST",
|
|
3034
3062
|
body: JSON.stringify({ domains: normalized, ...options.reason ? { reason: options.reason } : {} })
|
|
3035
3063
|
}
|
|
3036
3064
|
);
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
console.log(
|
|
3043
|
-
|
|
3044
|
-
}
|
|
3045
|
-
console.log(`Egress allowlist PR ${payload.status}: ${payload.prUrl}`);
|
|
3046
|
-
console.log(`Added: ${payload.addedDomains.join(", ")}`);
|
|
3065
|
+
emit(command, options, payload, (addPayload) => {
|
|
3066
|
+
if (addPayload.status === "unchanged") {
|
|
3067
|
+
console.log("All requested domains are already present in the source file.");
|
|
3068
|
+
return;
|
|
3069
|
+
}
|
|
3070
|
+
console.log(`Egress allowlist PR ${addPayload.status}: ${addPayload.prUrl}`);
|
|
3071
|
+
console.log(`Added: ${addPayload.addedDomains.join(", ")}`);
|
|
3072
|
+
});
|
|
3047
3073
|
}
|
|
3048
3074
|
async function egressSyncCommand(options = {}, command) {
|
|
3049
|
-
const
|
|
3050
|
-
const config = requireConfig(runtime);
|
|
3075
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3051
3076
|
const businessId = await resolveBusinessId(config, options);
|
|
3052
3077
|
const payload = await apiFetch(
|
|
3053
3078
|
config,
|
|
3054
|
-
`/api/businesses/${
|
|
3079
|
+
`/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/sync`,
|
|
3055
3080
|
{ method: "POST" }
|
|
3056
3081
|
);
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3082
|
+
emit(
|
|
3083
|
+
command,
|
|
3084
|
+
options,
|
|
3085
|
+
payload,
|
|
3086
|
+
(syncPayload) => console.log(
|
|
3087
|
+
`Applied ${syncPayload.egressAllowlist.length} egress allowlist domains from ${syncPayload.source.sourceRepoOwner}/${syncPayload.source.sourceRepoName}:${syncPayload.path}.`
|
|
3088
|
+
)
|
|
3063
3089
|
);
|
|
3064
3090
|
}
|
|
3065
3091
|
async function egressValidateCommand(path = EGRESS_ALLOWLIST_SOURCE_PATH, options = {}, command) {
|
|
3066
3092
|
const content = readFileSync2(path, "utf8");
|
|
3067
3093
|
const domains = parseEgressAllowlistSourceFile(content, path);
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
}
|
|
3072
|
-
console.log(`Valid egress allowlist: ${path}`);
|
|
3073
|
-
console.log(`${domains.length} ${domains.length === 1 ? "domain" : "domains"}.`);
|
|
3094
|
+
emit(command, options, { ok: true, path, domains }, (payload) => {
|
|
3095
|
+
console.log(`Valid egress allowlist: ${payload.path}`);
|
|
3096
|
+
console.log(`${payload.domains.length} ${payload.domains.length === 1 ? "domain" : "domains"}.`);
|
|
3097
|
+
});
|
|
3074
3098
|
}
|
|
3075
3099
|
|
|
3076
3100
|
// src/commands/login.ts
|
|
@@ -3714,15 +3738,6 @@ function parseRepoArg2(value, fallback) {
|
|
|
3714
3738
|
function repoPath(repo) {
|
|
3715
3739
|
return `${repo.owner}/${repo.repo}`;
|
|
3716
3740
|
}
|
|
3717
|
-
function encodePathSegment2(value) {
|
|
3718
|
-
return encodeURIComponent(value);
|
|
3719
|
-
}
|
|
3720
|
-
async function resolveBusinessId2(config, options) {
|
|
3721
|
-
if (options.business && options.business.trim()) return options.business.trim();
|
|
3722
|
-
const whoami = await apiFetch(config, "/api/auth/whoami");
|
|
3723
|
-
if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
|
|
3724
|
-
throw new CliError("user", "--business is required when the authenticated token has no business context.");
|
|
3725
|
-
}
|
|
3726
3741
|
function parsePollInterval2(value) {
|
|
3727
3742
|
if (!value) return DEFAULT_POLL_INTERVAL_MS;
|
|
3728
3743
|
const parsed = Number(value);
|
|
@@ -3731,9 +3746,6 @@ function parsePollInterval2(value) {
|
|
|
3731
3746
|
}
|
|
3732
3747
|
return parsed;
|
|
3733
3748
|
}
|
|
3734
|
-
function sleep2(ms) {
|
|
3735
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3736
|
-
}
|
|
3737
3749
|
function printBuildRequest(buildRequest) {
|
|
3738
3750
|
console.log(
|
|
3739
3751
|
[
|
|
@@ -3856,7 +3868,7 @@ function buildLogsPath(businessId, buildId, options = {}) {
|
|
|
3856
3868
|
if (options.afterSequence != null) params.set("afterSequence", String(options.afterSequence));
|
|
3857
3869
|
if (options.limit != null) params.set("limit", String(options.limit));
|
|
3858
3870
|
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
3859
|
-
return `/api/businesses/${
|
|
3871
|
+
return `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests/${encodeURIComponent(
|
|
3860
3872
|
buildId
|
|
3861
3873
|
)}/logs${suffix}`;
|
|
3862
3874
|
}
|
|
@@ -3873,12 +3885,12 @@ async function waitForBuild(config, businessId, buildId, options) {
|
|
|
3873
3885
|
}
|
|
3874
3886
|
const status = await apiFetch(
|
|
3875
3887
|
config,
|
|
3876
|
-
`/api/businesses/${
|
|
3888
|
+
`/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests/${encodeURIComponent(buildId)}`
|
|
3877
3889
|
);
|
|
3878
3890
|
if (TERMINAL_BUILD_STATUSES.has(status.buildRequest.status)) {
|
|
3879
3891
|
return status.buildRequest;
|
|
3880
3892
|
}
|
|
3881
|
-
await
|
|
3893
|
+
await sleep(options.pollIntervalMs);
|
|
3882
3894
|
}
|
|
3883
3895
|
}
|
|
3884
3896
|
function formatValidationPayload(input) {
|
|
@@ -3953,11 +3965,7 @@ async function sandboxInitCommand(options = {}, command) {
|
|
|
3953
3965
|
"utf8"
|
|
3954
3966
|
);
|
|
3955
3967
|
const payload = { ok: true, manifestPath: DEFAULT_MANIFEST_PATH, layerPath };
|
|
3956
|
-
|
|
3957
|
-
writeJson(payload);
|
|
3958
|
-
return;
|
|
3959
|
-
}
|
|
3960
|
-
console.log(`Created ${DEFAULT_MANIFEST_PATH} and ${layerPath}`);
|
|
3968
|
+
emit(command, runtime, payload, () => console.log(`Created ${DEFAULT_MANIFEST_PATH} and ${layerPath}`));
|
|
3961
3969
|
}
|
|
3962
3970
|
async function sandboxValidateCommand(options = {}, command) {
|
|
3963
3971
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
@@ -3970,34 +3978,31 @@ async function sandboxValidateCommand(options = {}, command) {
|
|
|
3970
3978
|
const layerText = readFileSync3(layerPath, "utf8");
|
|
3971
3979
|
const parsed = await parseSandboxLayerSource({ manifestPath, manifestText, layerPath, layerText });
|
|
3972
3980
|
const payload = formatValidationPayload({ manifestPath, layerPath, parsed });
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
console.log(`Smoke commands: ${payload.smokeCommandCount}`);
|
|
3983
|
-
console.log(`Next: ${nextBuildCommand(manifestPath)}`);
|
|
3981
|
+
emit(command, options, payload, (validationPayload) => {
|
|
3982
|
+
console.log("Valid sandbox template.");
|
|
3983
|
+
console.log(`Manifest: ${validationPayload.manifestPath}`);
|
|
3984
|
+
console.log(`Layer: ${validationPayload.layerPath}`);
|
|
3985
|
+
console.log(`Normalized source hash: ${validationPayload.normalizedSourceHash}`);
|
|
3986
|
+
console.log(`Instructions: ${validationPayload.instructionCount}`);
|
|
3987
|
+
console.log(`Smoke commands: ${validationPayload.smokeCommandCount}`);
|
|
3988
|
+
console.log(`Next: ${nextBuildCommand(manifestPath)}`);
|
|
3989
|
+
});
|
|
3984
3990
|
} catch (err) {
|
|
3985
3991
|
handleValidationError(err, options, command);
|
|
3986
3992
|
}
|
|
3987
3993
|
}
|
|
3988
3994
|
async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
3989
|
-
const
|
|
3990
|
-
const config = requireConfig(runtime);
|
|
3995
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3991
3996
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
3992
3997
|
assertManifestExists(manifestPath);
|
|
3993
3998
|
if (!options.ref) assertCleanAndPushed();
|
|
3994
3999
|
const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
|
|
3995
4000
|
const targetRepo = options.targetRepo ? parseRepoArg2(options.targetRepo, currentRepo) : null;
|
|
3996
|
-
const businessId = await
|
|
4001
|
+
const businessId = await resolveBusinessId(config, options);
|
|
3997
4002
|
const ref = options.ref ?? currentRef();
|
|
3998
4003
|
const payload = await apiFetch(
|
|
3999
4004
|
config,
|
|
4000
|
-
`/api/businesses/${
|
|
4005
|
+
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(sourceRepo.owner)}/${encodeURIComponent(
|
|
4001
4006
|
sourceRepo.repo
|
|
4002
4007
|
)}/sandbox-layer/build-requests`,
|
|
4003
4008
|
{
|
|
@@ -4009,8 +4014,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4009
4014
|
})
|
|
4010
4015
|
}
|
|
4011
4016
|
);
|
|
4012
|
-
|
|
4013
|
-
else printBuildRequest(payload.buildRequest);
|
|
4017
|
+
emit(command, options, payload, (buildPayload) => printBuildRequest(buildPayload.buildRequest));
|
|
4014
4018
|
if (!options.wait && !options.follow) return;
|
|
4015
4019
|
const json = isJson(command, options);
|
|
4016
4020
|
const finalBuild = await waitForBuild(config, businessId, payload.buildRequest.id, {
|
|
@@ -4025,26 +4029,20 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4025
4029
|
}
|
|
4026
4030
|
}
|
|
4027
4031
|
async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
4028
|
-
const
|
|
4029
|
-
const
|
|
4030
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4032
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4033
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4031
4034
|
const repo = parseRepoArg2(repoArg, currentRepo);
|
|
4032
4035
|
const payload = await apiFetch(
|
|
4033
4036
|
config,
|
|
4034
|
-
`/api/businesses/${
|
|
4037
|
+
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
|
|
4035
4038
|
repo.repo
|
|
4036
4039
|
)}/sandbox-layer/resolution`
|
|
4037
4040
|
);
|
|
4038
|
-
|
|
4039
|
-
writeJson(payload);
|
|
4040
|
-
return;
|
|
4041
|
-
}
|
|
4042
|
-
printSandboxStatus(payload);
|
|
4041
|
+
emit(command, options, payload, printSandboxStatus);
|
|
4043
4042
|
}
|
|
4044
4043
|
async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
4045
|
-
const
|
|
4046
|
-
const
|
|
4047
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4044
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4045
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4048
4046
|
const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
|
|
4049
4047
|
const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
|
|
4050
4048
|
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArg2(options.targetRepo, currentRepo)));
|
|
@@ -4058,24 +4056,19 @@ async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
|
4058
4056
|
}
|
|
4059
4057
|
const payload = await apiFetch(
|
|
4060
4058
|
config,
|
|
4061
|
-
`/api/businesses/${
|
|
4059
|
+
`/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests?${params.toString()}`
|
|
4062
4060
|
);
|
|
4063
|
-
|
|
4064
|
-
writeJson(payload);
|
|
4065
|
-
return;
|
|
4066
|
-
}
|
|
4067
|
-
printBuildHistory(payload.builds);
|
|
4061
|
+
emit(command, options, payload, (historyPayload) => printBuildHistory(historyPayload.builds));
|
|
4068
4062
|
}
|
|
4069
4063
|
async function sandboxRebuildStaleCommand(options = {}, command) {
|
|
4070
|
-
const
|
|
4071
|
-
const config = requireConfig(runtime);
|
|
4064
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4072
4065
|
if (!options.dryRun && !options.yes) {
|
|
4073
4066
|
throw new CliError("user", "Use --dry-run to preview or --yes to start a rebuild campaign.");
|
|
4074
4067
|
}
|
|
4075
4068
|
if (options.all && options.business) {
|
|
4076
4069
|
throw new CliError("user", "Use either --all or --business, not both.");
|
|
4077
4070
|
}
|
|
4078
|
-
const businessId = options.all ? null : await
|
|
4071
|
+
const businessId = options.all ? null : await resolveBusinessId(config, options);
|
|
4079
4072
|
let payload = await apiFetch(config, "/api/admin/sandbox-layer/rebuild-campaigns", {
|
|
4080
4073
|
method: "POST",
|
|
4081
4074
|
body: JSON.stringify({
|
|
@@ -4093,20 +4086,16 @@ async function sandboxRebuildStaleCommand(options = {}, command) {
|
|
|
4093
4086
|
if (options.follow && !options.dryRun) {
|
|
4094
4087
|
payload = await followRebuildCampaign(config, payload.campaign.id);
|
|
4095
4088
|
}
|
|
4096
|
-
|
|
4097
|
-
writeJson(payload);
|
|
4098
|
-
return;
|
|
4099
|
-
}
|
|
4100
|
-
printRebuildCampaign(payload);
|
|
4089
|
+
emit(command, options, payload, printRebuildCampaign);
|
|
4101
4090
|
}
|
|
4102
4091
|
async function followRebuildCampaign(config, campaignId) {
|
|
4103
4092
|
for (; ; ) {
|
|
4104
4093
|
const payload = await apiFetch(
|
|
4105
4094
|
config,
|
|
4106
|
-
`/api/admin/sandbox-layer/rebuild-campaigns/${
|
|
4095
|
+
`/api/admin/sandbox-layer/rebuild-campaigns/${encodeURIComponent(campaignId)}`
|
|
4107
4096
|
);
|
|
4108
4097
|
if (isCampaignTerminal(payload.campaign.status)) return payload;
|
|
4109
|
-
await
|
|
4098
|
+
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
4110
4099
|
}
|
|
4111
4100
|
}
|
|
4112
4101
|
function isCampaignTerminal(status) {
|
|
@@ -4130,9 +4119,8 @@ function printRebuildCampaign(payload) {
|
|
|
4130
4119
|
}
|
|
4131
4120
|
}
|
|
4132
4121
|
async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
4133
|
-
const
|
|
4134
|
-
const
|
|
4135
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4122
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4123
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4136
4124
|
let afterSequence = options.afterSequence == null ? void 0 : Number(options.afterSequence);
|
|
4137
4125
|
if (afterSequence != null && (!Number.isInteger(afterSequence) || afterSequence < 0)) {
|
|
4138
4126
|
throw new CliError("user", "--after-sequence must be a non-negative integer.");
|
|
@@ -4150,88 +4138,72 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
|
4150
4138
|
else printLogs(payload.logs);
|
|
4151
4139
|
if (!options.follow) return;
|
|
4152
4140
|
if (payload.logs.length > 0) afterSequence = payload.logs[payload.logs.length - 1].sequence;
|
|
4153
|
-
await
|
|
4141
|
+
await sleep(parsePollInterval2(options.pollInterval));
|
|
4154
4142
|
}
|
|
4155
4143
|
}
|
|
4156
4144
|
async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
|
|
4157
|
-
const
|
|
4158
|
-
const
|
|
4159
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4145
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4146
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4160
4147
|
const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
|
|
4161
4148
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4162
4149
|
const payload = await apiFetch(
|
|
4163
4150
|
config,
|
|
4164
|
-
`/api/businesses/${
|
|
4151
|
+
`/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/default-source`,
|
|
4165
4152
|
{ method: "PUT", body: JSON.stringify(sourceBody(sourceRepo, manifestPath)) }
|
|
4166
4153
|
);
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
`Build missing target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --business ${businessId}`
|
|
4174
|
-
);
|
|
4154
|
+
emit(command, options, payload, () => {
|
|
4155
|
+
console.log(`Set default sandbox layer source to ${repoPath(sourceRepo)}.`);
|
|
4156
|
+
console.log(
|
|
4157
|
+
`Build missing target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --business ${businessId}`
|
|
4158
|
+
);
|
|
4159
|
+
});
|
|
4175
4160
|
}
|
|
4176
4161
|
async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
|
|
4177
|
-
const
|
|
4178
|
-
const
|
|
4179
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4162
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4163
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4180
4164
|
const targetRepo = parseRepoArg2(targetRepoArg, currentRepo);
|
|
4181
4165
|
const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
|
|
4182
4166
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4183
4167
|
const payload = await apiFetch(
|
|
4184
4168
|
config,
|
|
4185
|
-
`/api/businesses/${
|
|
4169
|
+
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(targetRepo.owner)}/${encodeURIComponent(
|
|
4186
4170
|
targetRepo.repo
|
|
4187
4171
|
)}/sandbox-layer/assignment`,
|
|
4188
4172
|
{ method: "PUT", body: JSON.stringify(sourceBody(sourceRepo, manifestPath)) }
|
|
4189
4173
|
);
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
)} --business ${businessId}`
|
|
4199
|
-
);
|
|
4174
|
+
emit(command, options, payload, () => {
|
|
4175
|
+
console.log(`Assigned ${repoPath(sourceRepo)} as sandbox layer source for ${repoPath(targetRepo)}.`);
|
|
4176
|
+
console.log(
|
|
4177
|
+
`Build target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --target-repo ${repoPath(
|
|
4178
|
+
targetRepo
|
|
4179
|
+
)} --business ${businessId}`
|
|
4180
|
+
);
|
|
4181
|
+
});
|
|
4200
4182
|
}
|
|
4201
4183
|
async function sandboxUnassignDefaultCommand(options = {}, command) {
|
|
4202
|
-
const
|
|
4203
|
-
const
|
|
4204
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4184
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4185
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4205
4186
|
if (!options.yes) await confirmOrThrow("Clear the business default sandbox layer source?");
|
|
4206
4187
|
const payload = await apiFetch(
|
|
4207
4188
|
config,
|
|
4208
|
-
`/api/businesses/${
|
|
4189
|
+
`/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/default-source`,
|
|
4209
4190
|
{ method: "DELETE" }
|
|
4210
4191
|
);
|
|
4211
|
-
|
|
4212
|
-
writeJson(payload);
|
|
4213
|
-
return;
|
|
4214
|
-
}
|
|
4215
|
-
console.log("Cleared default sandbox layer source.");
|
|
4192
|
+
emit(command, options, payload, () => console.log("Cleared default sandbox layer source."));
|
|
4216
4193
|
}
|
|
4217
4194
|
async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command) {
|
|
4218
|
-
const
|
|
4219
|
-
const
|
|
4220
|
-
const businessId = await resolveBusinessId2(config, options);
|
|
4195
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4196
|
+
const businessId = await resolveBusinessId(config, options);
|
|
4221
4197
|
const targetRepo = parseRepoArg2(targetRepoArg, currentRepo);
|
|
4222
4198
|
if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
|
|
4223
4199
|
const payload = await apiFetch(
|
|
4224
4200
|
config,
|
|
4225
|
-
`/api/businesses/${
|
|
4201
|
+
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(targetRepo.owner)}/${encodeURIComponent(
|
|
4226
4202
|
targetRepo.repo
|
|
4227
4203
|
)}/sandbox-layer/assignment`,
|
|
4228
4204
|
{ method: "DELETE" }
|
|
4229
4205
|
);
|
|
4230
|
-
|
|
4231
|
-
writeJson(payload);
|
|
4232
|
-
return;
|
|
4233
|
-
}
|
|
4234
|
-
console.log(`Cleared sandbox layer assignment for ${repoPath(targetRepo)}.`);
|
|
4206
|
+
emit(command, options, payload, () => console.log(`Cleared sandbox layer assignment for ${repoPath(targetRepo)}.`));
|
|
4235
4207
|
}
|
|
4236
4208
|
|
|
4237
4209
|
// src/commands/sessions.ts
|
|
@@ -4369,8 +4341,7 @@ async function usageCommand(sessionId, options, command) {
|
|
|
4369
4341
|
|
|
4370
4342
|
// src/commands/stop.ts
|
|
4371
4343
|
async function stopCommand(sessionId, options = {}, command) {
|
|
4372
|
-
const
|
|
4373
|
-
const config = requireConfig(runtime);
|
|
4344
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4374
4345
|
let status;
|
|
4375
4346
|
try {
|
|
4376
4347
|
const response = await apiFetch(config, `/api/sessions/${sessionId}/stop`, {
|
|
@@ -4382,17 +4353,15 @@ async function stopCommand(sessionId, options = {}, command) {
|
|
|
4382
4353
|
if (!parsed) throw err;
|
|
4383
4354
|
status = parsed.reason;
|
|
4384
4355
|
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
}
|
|
4394
|
-
console.log(`Session ${sessionId} cannot be stopped from phase=${status}.`);
|
|
4395
|
-
}
|
|
4356
|
+
emit(command, options, { sessionId, status }, (payload) => {
|
|
4357
|
+
if (payload.status === "already_stopped") {
|
|
4358
|
+
console.log(`Session ${payload.sessionId} is already stopped.`);
|
|
4359
|
+
} else if (payload.status === "stopping" || payload.status === "stopped") {
|
|
4360
|
+
console.log(`Stop requested for session ${payload.sessionId}.`);
|
|
4361
|
+
} else {
|
|
4362
|
+
console.log(`Session ${payload.sessionId} cannot be stopped from phase=${payload.status}.`);
|
|
4363
|
+
}
|
|
4364
|
+
});
|
|
4396
4365
|
}
|
|
4397
4366
|
function parseStopBlocked(err) {
|
|
4398
4367
|
if (!(err instanceof ApiError) || err.status !== 409) return null;
|
|
@@ -4428,26 +4397,22 @@ function readValue(options) {
|
|
|
4428
4397
|
return raw.replace(/\r?\n$/, "");
|
|
4429
4398
|
}
|
|
4430
4399
|
async function listTestCredentialsCommand(repo, options, command) {
|
|
4431
|
-
const
|
|
4432
|
-
const config = requireConfig(runtime);
|
|
4400
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4433
4401
|
const repoArg = parseRepoArg3(repo);
|
|
4434
4402
|
const payload = await apiFetch(config, basePath(options.business, repoArg));
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
console.log(`${cred.name} ${ts} ${cred.rotatedByUserId ?? "-"}`);
|
|
4446
|
-
}
|
|
4403
|
+
emit(command, options, payload, (listPayload) => {
|
|
4404
|
+
if (listPayload.credentials.length === 0) {
|
|
4405
|
+
console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.name}.`);
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
for (const cred of listPayload.credentials) {
|
|
4409
|
+
const ts = new Date(cred.updatedAt).toISOString();
|
|
4410
|
+
console.log(`${cred.name} ${ts} ${cred.rotatedByUserId ?? "-"}`);
|
|
4411
|
+
}
|
|
4412
|
+
});
|
|
4447
4413
|
}
|
|
4448
4414
|
async function setTestCredentialCommand(repo, name, options, command) {
|
|
4449
|
-
const
|
|
4450
|
-
const config = requireConfig(runtime);
|
|
4415
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4451
4416
|
const repoArg = parseRepoArg3(repo);
|
|
4452
4417
|
const value = readValue(options);
|
|
4453
4418
|
const payload = await apiFetch(
|
|
@@ -4455,11 +4420,12 @@ async function setTestCredentialCommand(repo, name, options, command) {
|
|
|
4455
4420
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
4456
4421
|
{ method: "PUT", body: JSON.stringify({ value }) }
|
|
4457
4422
|
);
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4423
|
+
emit(
|
|
4424
|
+
command,
|
|
4425
|
+
options,
|
|
4426
|
+
payload,
|
|
4427
|
+
() => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`)
|
|
4428
|
+
);
|
|
4463
4429
|
}
|
|
4464
4430
|
async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
4465
4431
|
if (isJson(command, options) && options.yes !== true) {
|
|
@@ -4469,25 +4435,24 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
4469
4435
|
const repoArg2 = parseRepoArg3(repo);
|
|
4470
4436
|
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.name}?`);
|
|
4471
4437
|
}
|
|
4472
|
-
const
|
|
4473
|
-
const config = requireConfig(runtime);
|
|
4438
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4474
4439
|
const repoArg = parseRepoArg3(repo);
|
|
4475
4440
|
const payload = await apiFetch(
|
|
4476
4441
|
config,
|
|
4477
4442
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
4478
4443
|
{ method: "DELETE" }
|
|
4479
4444
|
);
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4445
|
+
emit(
|
|
4446
|
+
command,
|
|
4447
|
+
options,
|
|
4448
|
+
payload,
|
|
4449
|
+
() => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`)
|
|
4450
|
+
);
|
|
4485
4451
|
}
|
|
4486
4452
|
|
|
4487
4453
|
// src/commands/tokens.ts
|
|
4488
4454
|
async function listTokensCommand(options, command) {
|
|
4489
|
-
const
|
|
4490
|
-
const config = requireConfig(runtime);
|
|
4455
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4491
4456
|
const query = new URLSearchParams();
|
|
4492
4457
|
if (options.limit) query.set("limit", options.limit);
|
|
4493
4458
|
if (options.cursor) query.set("cursor", options.cursor);
|
|
@@ -4495,24 +4460,21 @@ async function listTokensCommand(options, command) {
|
|
|
4495
4460
|
config,
|
|
4496
4461
|
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
4497
4462
|
);
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
}
|
|
4511
|
-
if (payload.nextCursor) console.log(`Next cursor: ${payload.nextCursor}`);
|
|
4463
|
+
emit(command, options, payload, (listPayload) => {
|
|
4464
|
+
if (listPayload.data.length === 0) {
|
|
4465
|
+
console.log("No CLI tokens found.");
|
|
4466
|
+
return;
|
|
4467
|
+
}
|
|
4468
|
+
for (const token of listPayload.data) {
|
|
4469
|
+
console.log(
|
|
4470
|
+
`${String(token.id)} ${String(token.scope)} ${String(token.tokenPrefix)} ${String(token.revokedAt ? "revoked" : "active")}`
|
|
4471
|
+
);
|
|
4472
|
+
}
|
|
4473
|
+
if (listPayload.nextCursor) console.log(`Next cursor: ${listPayload.nextCursor}`);
|
|
4474
|
+
});
|
|
4512
4475
|
}
|
|
4513
4476
|
async function createTokenCommand(options, command) {
|
|
4514
|
-
const
|
|
4515
|
-
const config = requireConfig(runtime);
|
|
4477
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4516
4478
|
const expiresInDays = options.expiresInDays === void 0 ? void 0 : Number(options.expiresInDays);
|
|
4517
4479
|
if (expiresInDays !== void 0 && (!Number.isInteger(expiresInDays) || expiresInDays <= 0)) {
|
|
4518
4480
|
throw new CliError("user", "expiresInDays must be a positive integer.");
|
|
@@ -4524,13 +4486,11 @@ async function createTokenCommand(options, command) {
|
|
|
4524
4486
|
...expiresInDays !== void 0 ? { expiresInDays } : {}
|
|
4525
4487
|
})
|
|
4526
4488
|
});
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
console.log(`ID: ${String(payload.id)}`);
|
|
4533
|
-
console.log(`Scope: ${String(payload.scope)}`);
|
|
4489
|
+
emit(command, options, payload, (tokenPayload) => {
|
|
4490
|
+
console.log(`Token: ${String(tokenPayload.token)}`);
|
|
4491
|
+
console.log(`ID: ${String(tokenPayload.id)}`);
|
|
4492
|
+
console.log(`Scope: ${String(tokenPayload.scope)}`);
|
|
4493
|
+
});
|
|
4534
4494
|
}
|
|
4535
4495
|
async function revokeTokenCommand(tokenId, options, command) {
|
|
4536
4496
|
if (isJson(command, options) && options.yes !== true) {
|
|
@@ -4539,28 +4499,18 @@ async function revokeTokenCommand(tokenId, options, command) {
|
|
|
4539
4499
|
if (options.yes !== true) {
|
|
4540
4500
|
await confirmOrThrow(`Revoke CLI token ${tokenId}?`);
|
|
4541
4501
|
}
|
|
4542
|
-
const
|
|
4543
|
-
const config = requireConfig(runtime);
|
|
4502
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4544
4503
|
const payload = await apiFetch(config, `/api/cli-tokens/${tokenId}/revoke`, {
|
|
4545
4504
|
method: "POST"
|
|
4546
4505
|
});
|
|
4547
|
-
|
|
4548
|
-
writeJson(payload);
|
|
4549
|
-
return;
|
|
4550
|
-
}
|
|
4551
|
-
console.log(`Revoked token ${tokenId}.`);
|
|
4506
|
+
emit(command, options, payload, () => console.log(`Revoked token ${tokenId}.`));
|
|
4552
4507
|
}
|
|
4553
4508
|
|
|
4554
4509
|
// src/commands/transcript.ts
|
|
4555
4510
|
async function transcriptCommand(sessionId, options, command) {
|
|
4556
|
-
const
|
|
4557
|
-
const config = requireConfig(runtime);
|
|
4511
|
+
const { config } = resolveBusinessContext(command, options);
|
|
4558
4512
|
const exportData = await apiFetch(config, `/api/sessions/${sessionId}/export`);
|
|
4559
|
-
|
|
4560
|
-
writeJson(exportData);
|
|
4561
|
-
return;
|
|
4562
|
-
}
|
|
4563
|
-
console.log(renderSessionTranscript(exportData));
|
|
4513
|
+
emit(command, options, exportData, (payload) => console.log(renderSessionTranscript(payload)));
|
|
4564
4514
|
}
|
|
4565
4515
|
|
|
4566
4516
|
// src/index.ts
|