@tryarcanist/cli 0.1.3 → 0.1.4

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 +57 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -73,13 +73,13 @@ function validateApiUrl(url) {
73
73
  }
74
74
 
75
75
  // src/commands/create.ts
76
+ var REPO_URL_PATTERNS = [
77
+ /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/,
78
+ /^git@[^:]+:[^/]+\/[^/]+?(?:\.git)?$/,
79
+ /^https?:\/\/github\.com\/[^/]+\/[^/]+?(?:\.git)?\/?$/
80
+ ];
76
81
  function validateRepoUrl(url) {
77
- const shorthand = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(url);
78
- if (shorthand) return null;
79
- const ssh = /^git@[^:]+:[^/]+\/[^/]+?(?:\.git)?$/.test(url);
80
- if (ssh) return null;
81
- const https = /^https?:\/\/github\.com\/[^/]+\/[^/]+?(?:\.git)?\/?$/.test(url);
82
- if (https) return null;
82
+ if (REPO_URL_PATTERNS.some((pattern) => pattern.test(url))) return null;
83
83
  return `Invalid repo URL: "${url}". Expected a GitHub URL (https://github.com/owner/repo) or owner/repo shorthand.`;
84
84
  }
85
85
  async function createCommand(repoUrl, prompt, options) {
@@ -139,7 +139,6 @@ async function loginCommand(options) {
139
139
  console.error("Error: Invalid token format. Token must start with 'arc_'.");
140
140
  process.exit(1);
141
141
  }
142
- const apiUrl = options.apiUrl ?? loadConfig()?.apiUrl ?? "https://app.tryarcanist.com";
143
142
  if (options.apiUrl) {
144
143
  const urlError = validateApiUrl(options.apiUrl);
145
144
  if (urlError) {
@@ -147,6 +146,7 @@ async function loginCommand(options) {
147
146
  process.exit(1);
148
147
  }
149
148
  }
149
+ const apiUrl = options.apiUrl ?? loadConfig()?.apiUrl ?? "https://app.tryarcanist.com";
150
150
  saveConfig({ apiUrl, token });
151
151
  console.log(`Logged in. API: ${apiUrl}`);
152
152
  try {
@@ -222,26 +222,32 @@ async function stopCommand(sessionId) {
222
222
  // src/utils/session-output.ts
223
223
  function flattenSessionEvents(raw) {
224
224
  const merged = [];
225
- const textIndexById = /* @__PURE__ */ new Map();
225
+ const streamableIndexById = /* @__PURE__ */ new Map();
226
226
  const toolCallIndexById = /* @__PURE__ */ new Map();
227
227
  for (const event of raw) {
228
228
  const data = event.data ?? {};
229
229
  if (event.type === "sandbox_compaction_start") {
230
- merged.push({ type: "compaction_start", id: `cs-${data.timestamp ?? merged.length}`, ...data.contextTokens != null ? { contextTokens: data.contextTokens } : {} });
230
+ const entry2 = { type: "compaction_start", id: `cs-${data.timestamp ?? merged.length}` };
231
+ if (data.contextTokens != null) entry2.contextTokens = data.contextTokens;
232
+ merged.push(entry2);
231
233
  continue;
232
234
  }
233
235
  if (event.type === "sandbox_compaction_complete") {
234
- merged.push({ type: "compaction_complete", id: `cc-${data.timestamp ?? merged.length}`, ...data.contextTokensBefore != null ? { contextTokensBefore: data.contextTokensBefore } : {}, ...data.contextTokensAfter != null ? { contextTokensAfter: data.contextTokensAfter } : {} });
236
+ const entry2 = { type: "compaction_complete", id: `cc-${data.timestamp ?? merged.length}` };
237
+ if (data.contextTokensBefore != null) entry2.contextTokensBefore = data.contextTokensBefore;
238
+ if (data.contextTokensAfter != null) entry2.contextTokensAfter = data.contextTokensAfter;
239
+ merged.push(entry2);
235
240
  continue;
236
241
  }
237
242
  if (event.type === "sandbox_context_fill_warning") {
238
- merged.push({
243
+ const entry2 = {
239
244
  type: "context_fill_warning",
240
245
  id: `cfw-${data.timestamp ?? merged.length}`,
241
- fillPercent: Number(data.fillPercent ?? 0),
242
- ...data.contextTokens != null ? { contextTokens: data.contextTokens } : {},
243
- ...data.contextWindow != null ? { contextWindow: data.contextWindow } : {}
244
- });
246
+ fillPercent: Number(data.fillPercent ?? 0)
247
+ };
248
+ if (data.contextTokens != null) entry2.contextTokens = data.contextTokens;
249
+ if (data.contextWindow != null) entry2.contextWindow = data.contextWindow;
250
+ merged.push(entry2);
245
251
  continue;
246
252
  }
247
253
  if (event.type === "sandbox_tool_truncated") {
@@ -257,12 +263,13 @@ function flattenSessionEvents(raw) {
257
263
  continue;
258
264
  }
259
265
  if (event.type === "session_error") {
260
- merged.push({
266
+ const entry2 = {
261
267
  type: "session_error",
262
268
  id: String(data.id ?? `err-${merged.length}`),
263
- error: String(data.error ?? "Unknown error"),
264
- ...data.code ? { code: String(data.code) } : {}
265
- });
269
+ error: String(data.error ?? "Unknown error")
270
+ };
271
+ if (data.code) entry2.code = String(data.code);
272
+ merged.push(entry2);
266
273
  continue;
267
274
  }
268
275
  if (event.type === "raw_opencode") {
@@ -272,12 +279,12 @@ function flattenSessionEvents(raw) {
272
279
  if (event.type === "reasoning") {
273
280
  const id = String(data.id ?? `reasoning-${merged.length}`);
274
281
  const text = String(data.text ?? "");
275
- const existingIdx = textIndexById.get(id);
282
+ const existingIdx = streamableIndexById.get(id);
276
283
  if (existingIdx !== void 0) {
277
284
  const existing = merged[existingIdx];
278
285
  if (existing.type === "reasoning") existing.text += text;
279
286
  } else {
280
- textIndexById.set(id, merged.length);
287
+ streamableIndexById.set(id, merged.length);
281
288
  merged.push({ type: "reasoning", id, text });
282
289
  }
283
290
  continue;
@@ -306,32 +313,35 @@ function flattenSessionEvents(raw) {
306
313
  if (event.type === "text") {
307
314
  const id = String(data.id ?? `text-${merged.length}`);
308
315
  const text = String(data.text ?? "");
309
- const existingIdx = textIndexById.get(id);
316
+ const existingIdx = streamableIndexById.get(id);
310
317
  if (existingIdx !== void 0) {
311
318
  const existing = merged[existingIdx];
312
319
  if (existing.type === "text") existing.text += text;
313
320
  } else {
314
- textIndexById.set(id, merged.length);
321
+ streamableIndexById.set(id, merged.length);
315
322
  merged.push({ type: "text", id, text });
316
323
  }
317
324
  continue;
318
325
  }
319
326
  if (event.type === "tool_call") {
320
327
  const id = String(data.id ?? `tool-${merged.length}`);
321
- const nextEvent = {
328
+ const entry2 = {
322
329
  type: "tool_call",
323
330
  id,
324
331
  tool: String(data.tool ?? "unknown"),
325
- summary: String(data.summary ?? ""),
326
- ...data.input && typeof data.input === "object" ? { input: data.input } : {}
332
+ summary: String(data.summary ?? "")
327
333
  };
334
+ if (data.input && typeof data.input === "object") entry2.input = data.input;
328
335
  const existingIdx = toolCallIndexById.get(id);
329
336
  if (existingIdx !== void 0) {
330
337
  const prev = merged[existingIdx];
331
- if (prev.type === "tool_call") merged[existingIdx] = { ...nextEvent, ...prev.toolStatus ? { toolStatus: prev.toolStatus } : {} };
338
+ if (prev.type === "tool_call") {
339
+ if (prev.toolStatus) entry2.toolStatus = prev.toolStatus;
340
+ merged[existingIdx] = entry2;
341
+ }
332
342
  } else {
333
343
  toolCallIndexById.set(id, merged.length);
334
- merged.push(nextEvent);
344
+ merged.push(entry2);
335
345
  }
336
346
  continue;
337
347
  }
@@ -340,19 +350,20 @@ function flattenSessionEvents(raw) {
340
350
  const existingIdx = toolCallIndexById.get(id);
341
351
  if (existingIdx !== void 0) {
342
352
  const prev = merged[existingIdx];
343
- if (prev.type === "tool_call") {
344
- merged[existingIdx] = { ...prev, ...data.status ? { toolStatus: String(data.status) } : {} };
353
+ if (prev.type === "tool_call" && data.status) {
354
+ prev.toolStatus = String(data.status);
345
355
  }
346
356
  }
347
357
  continue;
348
358
  }
349
- merged.push({
359
+ const entry = {
350
360
  type: "question",
351
361
  id: String(data.id ?? `question-${merged.length}`),
352
362
  question: String(data.question ?? ""),
353
- answer: data.answer == null ? null : String(data.answer),
354
- ...Array.isArray(data.options) ? { options: data.options } : {}
355
- });
363
+ answer: data.answer == null ? null : String(data.answer)
364
+ };
365
+ if (Array.isArray(data.options)) entry.options = data.options;
366
+ merged.push(entry);
356
367
  }
357
368
  return merged;
358
369
  }
@@ -489,7 +500,9 @@ function parseSsePayload(payload) {
489
500
  let currentData = [];
490
501
  function flush() {
491
502
  if (currentData.length === 0 && currentId === void 0 && currentEvent === "message") return;
492
- messages.push({ event: currentEvent, ...currentId !== void 0 ? { id: currentId } : {}, data: currentData.join("\n") });
503
+ const msg = { event: currentEvent, data: currentData.join("\n") };
504
+ if (currentId !== void 0) msg.id = currentId;
505
+ messages.push(msg);
493
506
  currentEvent = "message";
494
507
  currentId = void 0;
495
508
  currentData = [];
@@ -519,11 +532,14 @@ function parseSsePayload(payload) {
519
532
  for (const message of messages) {
520
533
  const data = message.data ? parseJsonObject(message.data) : {};
521
534
  if (message.event === "status") {
522
- status = {
523
- status: typeof data.status === "string" ? data.status : "unknown",
524
- ...typeof data.title === "string" ? { title: data.title } : {},
525
- ...typeof data.spawnDurationMs === "number" || data.spawnDurationMs === null ? { spawnDurationMs: data.spawnDurationMs } : {}
535
+ const entry = {
536
+ status: typeof data.status === "string" ? data.status : "unknown"
526
537
  };
538
+ if (typeof data.title === "string") entry.title = data.title;
539
+ if (typeof data.spawnDurationMs === "number" || data.spawnDurationMs === null) {
540
+ entry.spawnDurationMs = data.spawnDurationMs;
541
+ }
542
+ status = entry;
527
543
  continue;
528
544
  }
529
545
  events.push({
@@ -664,13 +680,7 @@ async function fetchPromptLabels(config, sessionId) {
664
680
  }
665
681
  async function watchCommand(sessionId, options) {
666
682
  const config = requireConfig();
667
- let pollIntervalMs;
668
- try {
669
- pollIntervalMs = parsePollInterval(options.pollInterval);
670
- } catch (err) {
671
- console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
672
- process.exit(1);
673
- }
683
+ const pollIntervalMs = parsePollInterval(options.pollInterval);
674
684
  let promptLabels = /* @__PURE__ */ new Map();
675
685
  try {
676
686
  promptLabels = await fetchPromptLabels(config, sessionId);
@@ -691,7 +701,7 @@ async function watchCommand(sessionId, options) {
691
701
  afterSequence: String(afterSequence),
692
702
  limit: String(WATCH_REPLAY_PAGE_SIZE)
693
703
  });
694
- const payload = await apiFetchText(config, `/api/sessions/${sessionId}/events?${query.toString()}`);
704
+ const payload = await apiFetchText(config, `/api/sessions/${sessionId}/events?${query}`);
695
705
  const parsed = parseSsePayload(payload);
696
706
  const receivedFullPage = parsed.events.length >= WATCH_REPLAY_PAGE_SIZE;
697
707
  if (parsed.status) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {