@tryarcanist/cli 0.1.177 → 0.1.179
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 +32 -52
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -295,7 +295,6 @@ function validateApiUrl(url) {
|
|
|
295
295
|
if (parsed.username || parsed.password) {
|
|
296
296
|
return "API URL must not include embedded credentials. Use --token or ARCANIST_TOKEN to authenticate.";
|
|
297
297
|
}
|
|
298
|
-
const host = parsed.hostname.replace(/^\[|\]$/g, "");
|
|
299
298
|
if (parsed.protocol !== "https:" && !isLoopbackHost(parsed)) {
|
|
300
299
|
return "API URL must use HTTPS for non-local hosts";
|
|
301
300
|
}
|
|
@@ -1005,6 +1004,7 @@ var AUTOMATION_ERROR_HINTS = {
|
|
|
1005
1004
|
installation_unresolved: "Reconnect or reinstall the GitHub integration for that repository, then retry.",
|
|
1006
1005
|
model_not_available: "That model's backend (Claude/opencode) is limited to Arcanist team businesses; use the codex default or a codex model."
|
|
1007
1006
|
};
|
|
1007
|
+
var MAX_ALL_PAGES = 1e3;
|
|
1008
1008
|
async function createAutomationCommand(repoUrl, promptArg, options, command) {
|
|
1009
1009
|
const repo = parseAutomationRepo(repoUrl);
|
|
1010
1010
|
const prompt = await resolvePromptInput(promptArg, options);
|
|
@@ -1043,7 +1043,12 @@ async function listAutomationsCommand(options, command) {
|
|
|
1043
1043
|
const items = [];
|
|
1044
1044
|
let cursor = options.cursor;
|
|
1045
1045
|
let nextCursor = null;
|
|
1046
|
+
let pageCount = 0;
|
|
1046
1047
|
do {
|
|
1048
|
+
pageCount += 1;
|
|
1049
|
+
if (options.all === true && pageCount > MAX_ALL_PAGES) {
|
|
1050
|
+
throw new CliError("user", `automations list --all exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
|
|
1051
|
+
}
|
|
1047
1052
|
const query = new URLSearchParams();
|
|
1048
1053
|
if (options.limit) query.set("limit", options.limit);
|
|
1049
1054
|
if (cursor) query.set("cursor", cursor);
|
|
@@ -1125,7 +1130,7 @@ function mapAutomationApiError(err) {
|
|
|
1125
1130
|
}
|
|
1126
1131
|
const parsed = parseApiErrorBody2(err.body);
|
|
1127
1132
|
const serverCode = parsed?.error;
|
|
1128
|
-
return new CliError(
|
|
1133
|
+
return new CliError(codeForHttpStatus(err.status), parsed?.message || serverCode || err.message, {
|
|
1129
1134
|
exitCode: err.exitCode,
|
|
1130
1135
|
hint: serverCode ? AUTOMATION_ERROR_HINTS[serverCode] : void 0,
|
|
1131
1136
|
requestId: err.requestId
|
|
@@ -1144,13 +1149,6 @@ function parseApiErrorBody2(body) {
|
|
|
1144
1149
|
return null;
|
|
1145
1150
|
}
|
|
1146
1151
|
}
|
|
1147
|
-
function codeForStatus(status) {
|
|
1148
|
-
if (status === 401 || status === 403) return "auth";
|
|
1149
|
-
if (status === 404) return "not_found";
|
|
1150
|
-
if (status === 409) return "conflict";
|
|
1151
|
-
if (status >= 500) return "server";
|
|
1152
|
-
return "user";
|
|
1153
|
-
}
|
|
1154
1152
|
function printAutomationRule(rule) {
|
|
1155
1153
|
console.log(`ID: ${rule.id}`);
|
|
1156
1154
|
console.log(`Repo: ${rule.repoOwner}/${rule.repoName}`);
|
|
@@ -3305,13 +3303,7 @@ async function loginCommand(options, command) {
|
|
|
3305
3303
|
if (!token.startsWith("arc_")) {
|
|
3306
3304
|
throw new CliError("user", "Invalid token format. Token must start with 'arc_'.");
|
|
3307
3305
|
}
|
|
3308
|
-
|
|
3309
|
-
const urlError = validateApiUrl(runtime.apiUrl);
|
|
3310
|
-
if (urlError) {
|
|
3311
|
-
throw new CliError("user", urlError);
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
const apiUrl = normalizeBaseUrl(resolveLoginApiUrl(runtime.apiUrl));
|
|
3306
|
+
const apiUrl = resolveLoginApiUrl(runtime.apiUrl);
|
|
3315
3307
|
saveConfig({ apiUrl, token });
|
|
3316
3308
|
if (runtime.json) {
|
|
3317
3309
|
writeJson({ ok: true, apiUrl });
|
|
@@ -3536,7 +3528,7 @@ async function qaCommand(prUrl, options, command) {
|
|
|
3536
3528
|
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
3537
3529
|
}
|
|
3538
3530
|
|
|
3539
|
-
// src/
|
|
3531
|
+
// src/git.ts
|
|
3540
3532
|
import { execFileSync } from "child_process";
|
|
3541
3533
|
function git(args) {
|
|
3542
3534
|
try {
|
|
@@ -3550,6 +3542,8 @@ function currentRepo() {
|
|
|
3550
3542
|
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
3551
3543
|
return { owner: parsed.owner, repo: parsed.repo };
|
|
3552
3544
|
}
|
|
3545
|
+
|
|
3546
|
+
// src/commands/repos.ts
|
|
3553
3547
|
function parseRepoArg2(value) {
|
|
3554
3548
|
if (!value) return currentRepo();
|
|
3555
3549
|
const parsed = parseGithubRepoFullName(value);
|
|
@@ -3645,7 +3639,6 @@ function parseRespondConflict(err) {
|
|
|
3645
3639
|
}
|
|
3646
3640
|
|
|
3647
3641
|
// src/commands/sandbox.ts
|
|
3648
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
3649
3642
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3650
3643
|
import { dirname as dirname2 } from "path";
|
|
3651
3644
|
|
|
@@ -4181,27 +4174,14 @@ var DEFAULT_MANIFEST_PATH = SANDBOX_LAYER_MANIFEST_PATH;
|
|
|
4181
4174
|
var DEFAULT_POLL_INTERVAL_MS = 2500;
|
|
4182
4175
|
var MIN_POLL_INTERVAL_MS = 250;
|
|
4183
4176
|
var TERMINAL_BUILD_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled"]);
|
|
4184
|
-
function git2(args) {
|
|
4185
|
-
try {
|
|
4186
|
-
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
4187
|
-
} catch (err) {
|
|
4188
|
-
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
4189
|
-
}
|
|
4190
|
-
}
|
|
4191
|
-
function currentRepo2() {
|
|
4192
|
-
const remote = git2(["remote", "get-url", "origin"]);
|
|
4193
|
-
const parsed = parseGithubRepoFullName(remote);
|
|
4194
|
-
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
4195
|
-
return { owner: parsed.owner, repo: parsed.repo };
|
|
4196
|
-
}
|
|
4197
4177
|
function currentRef() {
|
|
4198
|
-
return
|
|
4178
|
+
return git(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
4199
4179
|
}
|
|
4200
4180
|
function assertCleanAndPushed() {
|
|
4201
|
-
const dirty =
|
|
4181
|
+
const dirty = git(["status", "--porcelain"]);
|
|
4202
4182
|
if (dirty) throw new CliError("user", "Sandbox layer source must be committed before build.");
|
|
4203
|
-
const upstream =
|
|
4204
|
-
const ahead =
|
|
4183
|
+
const upstream = git(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
|
|
4184
|
+
const ahead = git(["rev-list", "--count", `${upstream}..HEAD`]);
|
|
4205
4185
|
if (ahead !== "0")
|
|
4206
4186
|
throw new CliError("user", "Current commit is not pushed; push before building the sandbox layer.");
|
|
4207
4187
|
}
|
|
@@ -4385,7 +4365,7 @@ function formatValidationPayload(input) {
|
|
|
4385
4365
|
}
|
|
4386
4366
|
function nextBuildCommand(manifestPath) {
|
|
4387
4367
|
try {
|
|
4388
|
-
const repo =
|
|
4368
|
+
const repo = currentRepo();
|
|
4389
4369
|
const manifestArg = manifestPath === DEFAULT_MANIFEST_PATH ? "" : ` --manifest ${manifestPath}`;
|
|
4390
4370
|
return `arcanist sandbox build ${repoPath(repo)} --wait --follow${manifestArg}`;
|
|
4391
4371
|
} catch {
|
|
@@ -4475,8 +4455,8 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4475
4455
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4476
4456
|
assertManifestExists(manifestPath);
|
|
4477
4457
|
if (!options.ref) assertCleanAndPushed();
|
|
4478
|
-
const sourceRepo = parseRepoArg3(sourceRepoArg,
|
|
4479
|
-
const targetRepo = options.targetRepo ? parseRepoArg3(options.targetRepo,
|
|
4458
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo);
|
|
4459
|
+
const targetRepo = options.targetRepo ? parseRepoArg3(options.targetRepo, currentRepo) : null;
|
|
4480
4460
|
const businessId = await resolveBusinessId(config, options);
|
|
4481
4461
|
const ref = options.ref ?? currentRef();
|
|
4482
4462
|
let payload;
|
|
@@ -4527,7 +4507,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4527
4507
|
async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
4528
4508
|
const { config } = resolveBusinessContext(command, options);
|
|
4529
4509
|
const businessId = await resolveBusinessId(config, options);
|
|
4530
|
-
const repo = parseRepoArg3(repoArg,
|
|
4510
|
+
const repo = parseRepoArg3(repoArg, currentRepo);
|
|
4531
4511
|
const payload = await apiFetch(
|
|
4532
4512
|
config,
|
|
4533
4513
|
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
|
|
@@ -4539,9 +4519,9 @@ async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
|
4539
4519
|
async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
4540
4520
|
const { config } = resolveBusinessContext(command, options);
|
|
4541
4521
|
const businessId = await resolveBusinessId(config, options);
|
|
4542
|
-
const sourceRepo = parseRepoArg3(sourceRepoArg,
|
|
4522
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo);
|
|
4543
4523
|
const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
|
|
4544
|
-
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArg3(options.targetRepo,
|
|
4524
|
+
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArg3(options.targetRepo, currentRepo)));
|
|
4545
4525
|
if (options.status) params.set("status", options.status);
|
|
4546
4526
|
if (options.limit) {
|
|
4547
4527
|
const limit = Number(options.limit);
|
|
@@ -4574,7 +4554,7 @@ async function sandboxRebuildStaleCommand(options = {}, command) {
|
|
|
4574
4554
|
dryRun: Boolean(options.dryRun)
|
|
4575
4555
|
})
|
|
4576
4556
|
}).catch((error) => {
|
|
4577
|
-
if (error instanceof
|
|
4557
|
+
if (error instanceof ApiError && error.status === 403) {
|
|
4578
4558
|
throw new CliError("auth", "Only Arcanist internal admins can rebuild stale sandbox layers.");
|
|
4579
4559
|
}
|
|
4580
4560
|
throw error;
|
|
@@ -4642,7 +4622,7 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
|
4642
4622
|
async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
|
|
4643
4623
|
const { config } = resolveBusinessContext(command, options);
|
|
4644
4624
|
const businessId = await resolveBusinessId(config, options);
|
|
4645
|
-
const sourceRepo = parseRepoArg3(sourceRepoArg,
|
|
4625
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo);
|
|
4646
4626
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4647
4627
|
const payload = await apiFetch(
|
|
4648
4628
|
config,
|
|
@@ -4659,8 +4639,8 @@ async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command)
|
|
|
4659
4639
|
async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
|
|
4660
4640
|
const { config } = resolveBusinessContext(command, options);
|
|
4661
4641
|
const businessId = await resolveBusinessId(config, options);
|
|
4662
|
-
const targetRepo = parseRepoArg3(targetRepoArg,
|
|
4663
|
-
const sourceRepo = parseRepoArg3(sourceRepoArg,
|
|
4642
|
+
const targetRepo = parseRepoArg3(targetRepoArg, currentRepo);
|
|
4643
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo);
|
|
4664
4644
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4665
4645
|
const payload = await apiFetch(
|
|
4666
4646
|
config,
|
|
@@ -4698,7 +4678,7 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
4698
4678
|
}
|
|
4699
4679
|
const { config } = resolveBusinessContext(command, options);
|
|
4700
4680
|
const businessId = await resolveBusinessId(config, options);
|
|
4701
|
-
const targetRepo = parseRepoArg3(targetRepoArg,
|
|
4681
|
+
const targetRepo = parseRepoArg3(targetRepoArg, currentRepo);
|
|
4702
4682
|
if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
|
|
4703
4683
|
const payload = await apiFetch(
|
|
4704
4684
|
config,
|
|
@@ -4711,7 +4691,7 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
4711
4691
|
}
|
|
4712
4692
|
|
|
4713
4693
|
// src/commands/sessions.ts
|
|
4714
|
-
var
|
|
4694
|
+
var MAX_ALL_PAGES2 = 1e3;
|
|
4715
4695
|
async function listSessionsCommand(options, command) {
|
|
4716
4696
|
const runtime = getRuntimeOptions(command, options);
|
|
4717
4697
|
const config = requireConfig(runtime);
|
|
@@ -4721,8 +4701,8 @@ async function listSessionsCommand(options, command) {
|
|
|
4721
4701
|
let pageCount = 0;
|
|
4722
4702
|
do {
|
|
4723
4703
|
pageCount += 1;
|
|
4724
|
-
if (options.all === true && pageCount >
|
|
4725
|
-
throw new CliError("user", `sessions list --all exceeded ${
|
|
4704
|
+
if (options.all === true && pageCount > MAX_ALL_PAGES2) {
|
|
4705
|
+
throw new CliError("user", `sessions list --all exceeded ${MAX_ALL_PAGES2} pages without reaching the end.`);
|
|
4726
4706
|
}
|
|
4727
4707
|
const query = new URLSearchParams();
|
|
4728
4708
|
if (options.status) query.set("status", options.status);
|
|
@@ -4968,7 +4948,7 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
4968
4948
|
}
|
|
4969
4949
|
|
|
4970
4950
|
// src/commands/tokens.ts
|
|
4971
|
-
var
|
|
4951
|
+
var MAX_ALL_PAGES3 = 1e3;
|
|
4972
4952
|
async function listTokensCommand(options, command) {
|
|
4973
4953
|
const { config } = resolveBusinessContext(command, options);
|
|
4974
4954
|
const data = [];
|
|
@@ -4977,8 +4957,8 @@ async function listTokensCommand(options, command) {
|
|
|
4977
4957
|
let pageCount = 0;
|
|
4978
4958
|
do {
|
|
4979
4959
|
pageCount += 1;
|
|
4980
|
-
if (options.all === true && pageCount >
|
|
4981
|
-
throw new CliError("user", `tokens list --all exceeded ${
|
|
4960
|
+
if (options.all === true && pageCount > MAX_ALL_PAGES3) {
|
|
4961
|
+
throw new CliError("user", `tokens list --all exceeded ${MAX_ALL_PAGES3} pages without reaching the end.`);
|
|
4982
4962
|
}
|
|
4983
4963
|
const query = new URLSearchParams();
|
|
4984
4964
|
if (options.limit) query.set("limit", options.limit);
|