@tryarcanist/cli 0.1.194 → 0.1.196

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.
Files changed (2) hide show
  1. package/dist/index.js +80 -67
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17,6 +17,36 @@ function normalizeBaseUrl(url) {
17
17
  return url.replace(/\/+$/, "");
18
18
  }
19
19
 
20
+ // src/utils/api-error.ts
21
+ function parseApiErrorBody(body) {
22
+ try {
23
+ const parsed = JSON.parse(body);
24
+ if (!parsed || typeof parsed !== "object") return null;
25
+ const record = parsed;
26
+ const topLevelMessage = typeof record.message === "string" && record.message ? record.message : void 0;
27
+ if (typeof record.error === "string" && record.error) {
28
+ return {
29
+ serverCode: typeof record.code === "string" && record.code ? record.code : record.error,
30
+ rawError: record.error,
31
+ message: topLevelMessage ?? record.error
32
+ };
33
+ }
34
+ if (record.error && typeof record.error === "object") {
35
+ const error = record.error;
36
+ const message = typeof error.message === "string" && error.message ? error.message : topLevelMessage;
37
+ const serverCode = typeof error.code === "string" && error.code ? error.code : void 0;
38
+ return message || serverCode ? {
39
+ ...message ? { message } : {},
40
+ ...serverCode ? { serverCode } : {},
41
+ ...serverCode ? { rawError: serverCode } : {}
42
+ } : null;
43
+ }
44
+ return topLevelMessage ? { message: topLevelMessage } : null;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
20
50
  // src/errors.ts
21
51
  var EXIT_CODE_INTERRUPTED = 130;
22
52
  var CliError = class extends Error {
@@ -82,33 +112,6 @@ function messageForApiError(status, body, parsed = parseApiErrorBody(body)) {
82
112
  if (parsed?.serverCode) return parsed.serverCode;
83
113
  return body ? `API error ${status}: ${body}` : `API error ${status}`;
84
114
  }
85
- function parseApiErrorBody(body) {
86
- try {
87
- const parsed = JSON.parse(body);
88
- if (!parsed || typeof parsed !== "object") return null;
89
- const error = parsed.error;
90
- if (typeof error === "string" && error.length > 0) {
91
- const message2 = parsed.message;
92
- const code = parsed.code;
93
- return {
94
- serverCode: typeof code === "string" && code ? code : error,
95
- ...typeof message2 === "string" && message2 ? { message: message2 } : { message: error }
96
- };
97
- }
98
- if (error && typeof error === "object") {
99
- const code = error.code;
100
- const message2 = error.message;
101
- return {
102
- ...typeof message2 === "string" && message2 ? { message: message2 } : {},
103
- ...typeof code === "string" && code ? { serverCode: code } : {}
104
- };
105
- }
106
- const message = parsed.message;
107
- return typeof message === "string" && message.length > 0 ? { message } : null;
108
- } catch {
109
- return null;
110
- }
111
- }
112
115
  function hintForHttpStatus(status, resourceNoun) {
113
116
  if (status === 401 || status === 403) return "Run `arcanist auth login` or set `ARCANIST_TOKEN`.";
114
117
  if (status === 404) {
@@ -1062,7 +1065,7 @@ var CODEX_SUBSCRIPTION_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderG
1062
1065
  function parseGithubRepoFullName(value) {
1063
1066
  const shorthand = value.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
1064
1067
  if (shorthand) return { owner: shorthand[1], repo: shorthand[2] };
1065
- const ssh = value.match(/git@[^:]+:([^/]+)\/([^/]+?)(?:\.git)?$/);
1068
+ const ssh = value.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
1066
1069
  if (ssh) return { owner: ssh[1], repo: ssh[2] };
1067
1070
  const https = value.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
1068
1071
  if (https) return { owner: https[1], repo: https[2] };
@@ -1206,27 +1209,14 @@ function mapAutomationApiError(err) {
1206
1209
  if (!(err instanceof ApiError)) {
1207
1210
  return err instanceof CliError ? err : new CliError("server", stringifyError(err));
1208
1211
  }
1209
- const parsed = parseApiErrorBody2(err.body);
1210
- const serverCode = parsed?.error;
1212
+ const parsed = parseApiErrorBody(err.body);
1213
+ const serverCode = parsed?.rawError ?? parsed?.serverCode;
1211
1214
  return new CliError(codeForHttpStatus(err.status), parsed?.message || serverCode || err.message, {
1212
1215
  exitCode: err.exitCode,
1213
1216
  hint: serverCode ? AUTOMATION_ERROR_HINTS[serverCode] : void 0,
1214
1217
  requestId: err.requestId
1215
1218
  });
1216
1219
  }
1217
- function parseApiErrorBody2(body) {
1218
- try {
1219
- const parsed = JSON.parse(body);
1220
- if (!parsed || typeof parsed !== "object") return null;
1221
- const record = parsed;
1222
- return {
1223
- error: typeof record.error === "string" ? record.error : void 0,
1224
- message: typeof record.message === "string" ? record.message : void 0
1225
- };
1226
- } catch {
1227
- return null;
1228
- }
1229
- }
1230
1220
  function printAutomationRule(rule) {
1231
1221
  console.log(`ID: ${rule.id}`);
1232
1222
  console.log(`Repo: ${rule.repoOwner}/${rule.repoName}`);
@@ -1240,6 +1230,7 @@ function formatTime(value) {
1240
1230
 
1241
1231
  // src/commands/codex.ts
1242
1232
  import { spawn } from "child_process";
1233
+ import { rmSync } from "fs";
1243
1234
  import { mkdtemp, readFile, rm } from "fs/promises";
1244
1235
  import { tmpdir } from "os";
1245
1236
  import { join as join2 } from "path";
@@ -1288,8 +1279,19 @@ async function setCodexSubscriptionEnabled(config, enabled) {
1288
1279
  async function codexLoginCommand(options, command) {
1289
1280
  const { config } = resolveBusinessContext(command, options);
1290
1281
  const codexPath = resolveCodexPath(options.codexPath);
1291
- const codexHome = await mkdtemp(join2(tmpdir(), "arcanist-codex-"));
1282
+ let codexHome;
1283
+ const handleSigint = () => {
1284
+ if (codexHome) {
1285
+ try {
1286
+ rmSync(codexHome, { recursive: true, force: true });
1287
+ } catch {
1288
+ }
1289
+ }
1290
+ process.exit(EXIT_CODE_INTERRUPTED);
1291
+ };
1292
+ process.on("SIGINT", handleSigint);
1292
1293
  try {
1294
+ codexHome = await mkdtemp(join2(tmpdir(), "arcanist-codex-"));
1293
1295
  await runCodexDeviceLogin(codexPath, codexHome);
1294
1296
  let authJson;
1295
1297
  try {
@@ -1325,8 +1327,14 @@ async function codexLoginCommand(options, command) {
1325
1327
  }
1326
1328
  });
1327
1329
  } finally {
1328
- await rm(codexHome, { recursive: true, force: true }).catch(() => {
1329
- });
1330
+ try {
1331
+ if (codexHome) {
1332
+ await rm(codexHome, { recursive: true, force: true }).catch(() => {
1333
+ });
1334
+ }
1335
+ } finally {
1336
+ process.off("SIGINT", handleSigint);
1337
+ }
1330
1338
  }
1331
1339
  }
1332
1340
  async function codexUseCommand(state, options, command) {
@@ -1560,6 +1568,11 @@ function normalizeUploadedFileOptions(files) {
1560
1568
  return Array.isArray(files) ? files : [files];
1561
1569
  }
1562
1570
 
1571
+ // src/utils/poll-interval.ts
1572
+ function clampPollInterval(ms, minimum) {
1573
+ return Math.max(ms, minimum);
1574
+ }
1575
+
1563
1576
  // ../../shared/session/no-change-outcome.ts
1564
1577
  function isNoChangesPromptResult(result) {
1565
1578
  return typeof result === "object" && result !== null && "noChanges" in result && result.noChanges === true;
@@ -2985,6 +2998,11 @@ function renderWatchEvent(event, state) {
2985
2998
  }
2986
2999
  }
2987
3000
 
3001
+ // src/utils/session-payload.ts
3002
+ function unwrapSessionState(payload) {
3003
+ return payload.session && typeof payload.session === "object" ? payload.session : payload;
3004
+ }
3005
+
2988
3006
  // src/commands/model-options.ts
2989
3007
  function resolveModelAndBackend(options) {
2990
3008
  let agentRuntimeBackend = CODEX_AGENT_RUNTIME_BACKEND;
@@ -3228,7 +3246,7 @@ async function fetchCreatedPromptStatus(config, sessionId, promptId) {
3228
3246
  return selectCreatedPrompt(promptList.prompts, promptId);
3229
3247
  }
3230
3248
  async function waitForCreatedPromptToSettle(config, sessionId, promptId, pollIntervalMs) {
3231
- const effectivePollIntervalMs = Math.max(pollIntervalMs, MIN_WATCH_POLL_INTERVAL_MS);
3249
+ const effectivePollIntervalMs = clampPollInterval(pollIntervalMs, MIN_WATCH_POLL_INTERVAL_MS);
3232
3250
  while (true) {
3233
3251
  const createdPrompt = await fetchCreatedPromptStatus(config, sessionId, promptId);
3234
3252
  if (createdPrompt && PROMPT_TERMINAL_STATUSES.has(createdPrompt.status)) {
@@ -3270,15 +3288,12 @@ async function waitForCreatedPrompt(sessionId, promptId, sessionUrl, pollInterva
3270
3288
  );
3271
3289
  }
3272
3290
  }
3273
- function unwrapSessionState(payload) {
3274
- return payload.session && typeof payload.session === "object" ? payload.session : payload;
3275
- }
3276
3291
  async function fetchSessionResultFields(config, sessionId) {
3277
3292
  const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
3278
3293
  return extractSessionResultFields(unwrapSessionState(sessionPayload));
3279
3294
  }
3280
3295
  async function fetchSettledSessionResultFields(config, sessionId, pollIntervalMs) {
3281
- const effectivePollIntervalMs = Math.max(pollIntervalMs, MIN_WATCH_POLL_INTERVAL_MS);
3296
+ const effectivePollIntervalMs = clampPollInterval(pollIntervalMs, MIN_WATCH_POLL_INTERVAL_MS);
3282
3297
  let latest = {};
3283
3298
  for (let attempt = 0; attempt < RESULT_FETCH_ATTEMPTS; attempt++) {
3284
3299
  latest = await fetchSessionResultFields(config, sessionId);
@@ -3457,10 +3472,14 @@ function parseEgressAllowlistSourceFile(content, key = EGRESS_ALLOWLIST_SOURCE_P
3457
3472
 
3458
3473
  // src/commands/egress.ts
3459
3474
  function parseRepoArg(value) {
3460
- const trimmed = value.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
3461
- const [owner, repo, extra] = trimmed.split("/");
3462
- if (!owner || !repo || extra) throw new CliError("user", "repo must be owner/name or a GitHub URL");
3463
- return { owner, repo };
3475
+ const parsed = parseGithubRepoFullName(value);
3476
+ if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
3477
+ return {
3478
+ ...parsed,
3479
+ // Preserve the legacy shorthand contract while URLs and SSH remotes are
3480
+ // normalized by the shared parser itself.
3481
+ repo: /^[^/:]+\/[^/]+\.git$/.test(value.trim()) ? parsed.repo.slice(0, -4) : parsed.repo
3482
+ };
3464
3483
  }
3465
3484
  async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
3466
3485
  const { config } = resolveBusinessContext(command, options);
@@ -3585,9 +3604,6 @@ async function loginCommand(options, command) {
3585
3604
  }
3586
3605
 
3587
3606
  // src/commands/message.ts
3588
- function unwrapSessionState2(payload) {
3589
- return payload.session && typeof payload.session === "object" ? payload.session : payload;
3590
- }
3591
3607
  async function messageCommand(sessionId, promptArg, options = {}, command) {
3592
3608
  const runtime = getRuntimeOptions(command, options);
3593
3609
  assertArcanistSessionMutationAllowed("send");
@@ -3619,7 +3635,7 @@ async function messageCommand(sessionId, promptArg, options = {}, command) {
3619
3635
  }
3620
3636
  try {
3621
3637
  const sessionPayload = await apiFetch(config, `/api/sessions/${sessionId}`);
3622
- const resultLine = formatSessionResult(unwrapSessionState2(sessionPayload));
3638
+ const resultLine = formatSessionResult(unwrapSessionState(sessionPayload));
3623
3639
  if (resultLine) console.log(resultLine);
3624
3640
  } catch {
3625
3641
  }
@@ -3812,7 +3828,7 @@ function currentRepo() {
3812
3828
 
3813
3829
  // src/commands/repos.ts
3814
3830
  function parseRepoArg2(value) {
3815
- if (!value) return currentRepo();
3831
+ if (value === void 0) return currentRepo();
3816
3832
  const parsed = parseGithubRepoFullName(value);
3817
3833
  if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
3818
3834
  return { owner: parsed.owner, repo: parsed.repo };
@@ -5161,13 +5177,10 @@ function parseStopBlocked(err) {
5161
5177
 
5162
5178
  // src/commands/test-creds.ts
5163
5179
  import { readFileSync as readFileSync4 } from "fs";
5164
- function parseRepoArg4(repo) {
5165
- const trimmed = repo.trim();
5166
- const slashIndex = trimmed.indexOf("/");
5167
- if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
5168
- throw new CliError("user", `repo must be in the form 'owner/name' (got: '${repo}')`);
5169
- }
5170
- return { owner: trimmed.slice(0, slashIndex), name: trimmed.slice(slashIndex + 1) };
5180
+ function parseRepoArg4(value) {
5181
+ const parsed = parseGithubRepoFullName(value);
5182
+ if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
5183
+ return { owner: parsed.owner, name: parsed.repo };
5171
5184
  }
5172
5185
  function basePath(businessId, repo) {
5173
5186
  return `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.name)}/test-credentials`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.194",
3
+ "version": "0.1.196",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {