@tryarcanist/cli 0.1.2 → 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.
- package/dist/index.js +65 -47
- 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
|
-
|
|
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,24 +222,32 @@ async function stopCommand(sessionId) {
|
|
|
222
222
|
// src/utils/session-output.ts
|
|
223
223
|
function flattenSessionEvents(raw) {
|
|
224
224
|
const merged = [];
|
|
225
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
243
|
+
const entry2 = {
|
|
239
244
|
type: "context_fill_warning",
|
|
240
245
|
id: `cfw-${data.timestamp ?? merged.length}`,
|
|
241
246
|
fillPercent: Number(data.fillPercent ?? 0)
|
|
242
|
-
}
|
|
247
|
+
};
|
|
248
|
+
if (data.contextTokens != null) entry2.contextTokens = data.contextTokens;
|
|
249
|
+
if (data.contextWindow != null) entry2.contextWindow = data.contextWindow;
|
|
250
|
+
merged.push(entry2);
|
|
243
251
|
continue;
|
|
244
252
|
}
|
|
245
253
|
if (event.type === "sandbox_tool_truncated") {
|
|
@@ -255,12 +263,13 @@ function flattenSessionEvents(raw) {
|
|
|
255
263
|
continue;
|
|
256
264
|
}
|
|
257
265
|
if (event.type === "session_error") {
|
|
258
|
-
|
|
266
|
+
const entry2 = {
|
|
259
267
|
type: "session_error",
|
|
260
268
|
id: String(data.id ?? `err-${merged.length}`),
|
|
261
|
-
error: String(data.error ?? "Unknown error")
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
error: String(data.error ?? "Unknown error")
|
|
270
|
+
};
|
|
271
|
+
if (data.code) entry2.code = String(data.code);
|
|
272
|
+
merged.push(entry2);
|
|
264
273
|
continue;
|
|
265
274
|
}
|
|
266
275
|
if (event.type === "raw_opencode") {
|
|
@@ -270,12 +279,12 @@ function flattenSessionEvents(raw) {
|
|
|
270
279
|
if (event.type === "reasoning") {
|
|
271
280
|
const id = String(data.id ?? `reasoning-${merged.length}`);
|
|
272
281
|
const text = String(data.text ?? "");
|
|
273
|
-
const existingIdx =
|
|
282
|
+
const existingIdx = streamableIndexById.get(id);
|
|
274
283
|
if (existingIdx !== void 0) {
|
|
275
284
|
const existing = merged[existingIdx];
|
|
276
285
|
if (existing.type === "reasoning") existing.text += text;
|
|
277
286
|
} else {
|
|
278
|
-
|
|
287
|
+
streamableIndexById.set(id, merged.length);
|
|
279
288
|
merged.push({ type: "reasoning", id, text });
|
|
280
289
|
}
|
|
281
290
|
continue;
|
|
@@ -304,32 +313,35 @@ function flattenSessionEvents(raw) {
|
|
|
304
313
|
if (event.type === "text") {
|
|
305
314
|
const id = String(data.id ?? `text-${merged.length}`);
|
|
306
315
|
const text = String(data.text ?? "");
|
|
307
|
-
const existingIdx =
|
|
316
|
+
const existingIdx = streamableIndexById.get(id);
|
|
308
317
|
if (existingIdx !== void 0) {
|
|
309
318
|
const existing = merged[existingIdx];
|
|
310
319
|
if (existing.type === "text") existing.text += text;
|
|
311
320
|
} else {
|
|
312
|
-
|
|
321
|
+
streamableIndexById.set(id, merged.length);
|
|
313
322
|
merged.push({ type: "text", id, text });
|
|
314
323
|
}
|
|
315
324
|
continue;
|
|
316
325
|
}
|
|
317
326
|
if (event.type === "tool_call") {
|
|
318
327
|
const id = String(data.id ?? `tool-${merged.length}`);
|
|
319
|
-
const
|
|
328
|
+
const entry2 = {
|
|
320
329
|
type: "tool_call",
|
|
321
330
|
id,
|
|
322
331
|
tool: String(data.tool ?? "unknown"),
|
|
323
|
-
summary: String(data.summary ?? "")
|
|
324
|
-
...data.input && typeof data.input === "object" ? { input: data.input } : {}
|
|
332
|
+
summary: String(data.summary ?? "")
|
|
325
333
|
};
|
|
334
|
+
if (data.input && typeof data.input === "object") entry2.input = data.input;
|
|
326
335
|
const existingIdx = toolCallIndexById.get(id);
|
|
327
336
|
if (existingIdx !== void 0) {
|
|
328
337
|
const prev = merged[existingIdx];
|
|
329
|
-
if (prev.type === "tool_call")
|
|
338
|
+
if (prev.type === "tool_call") {
|
|
339
|
+
if (prev.toolStatus) entry2.toolStatus = prev.toolStatus;
|
|
340
|
+
merged[existingIdx] = entry2;
|
|
341
|
+
}
|
|
330
342
|
} else {
|
|
331
343
|
toolCallIndexById.set(id, merged.length);
|
|
332
|
-
merged.push(
|
|
344
|
+
merged.push(entry2);
|
|
333
345
|
}
|
|
334
346
|
continue;
|
|
335
347
|
}
|
|
@@ -338,19 +350,20 @@ function flattenSessionEvents(raw) {
|
|
|
338
350
|
const existingIdx = toolCallIndexById.get(id);
|
|
339
351
|
if (existingIdx !== void 0) {
|
|
340
352
|
const prev = merged[existingIdx];
|
|
341
|
-
if (prev.type === "tool_call") {
|
|
342
|
-
|
|
353
|
+
if (prev.type === "tool_call" && data.status) {
|
|
354
|
+
prev.toolStatus = String(data.status);
|
|
343
355
|
}
|
|
344
356
|
}
|
|
345
357
|
continue;
|
|
346
358
|
}
|
|
347
|
-
|
|
359
|
+
const entry = {
|
|
348
360
|
type: "question",
|
|
349
361
|
id: String(data.id ?? `question-${merged.length}`),
|
|
350
362
|
question: String(data.question ?? ""),
|
|
351
|
-
answer: data.answer == null ? null : String(data.answer)
|
|
352
|
-
|
|
353
|
-
|
|
363
|
+
answer: data.answer == null ? null : String(data.answer)
|
|
364
|
+
};
|
|
365
|
+
if (Array.isArray(data.options)) entry.options = data.options;
|
|
366
|
+
merged.push(entry);
|
|
354
367
|
}
|
|
355
368
|
return merged;
|
|
356
369
|
}
|
|
@@ -396,11 +409,17 @@ ${event.text}
|
|
|
396
409
|
${event.answer ? `**Answer:** ${event.answer}
|
|
397
410
|
` : ""}`;
|
|
398
411
|
case "compaction_start":
|
|
399
|
-
return
|
|
412
|
+
return event.contextTokens ? `*[compacting context at ${Math.round(event.contextTokens / 1e3)}k tokens]*
|
|
413
|
+
` : "*[compacting context]*\n";
|
|
400
414
|
case "compaction_complete":
|
|
401
|
-
|
|
415
|
+
if (event.contextTokensBefore && event.contextTokensAfter && event.contextTokensBefore > 0) {
|
|
416
|
+
const savedPct = Math.round((1 - event.contextTokensAfter / event.contextTokensBefore) * 100);
|
|
417
|
+
return `*[context compacted: ${Math.round(event.contextTokensBefore / 1e3)}k \u2192 ${Math.round(event.contextTokensAfter / 1e3)}k (${savedPct}% saved)]*
|
|
418
|
+
`;
|
|
419
|
+
}
|
|
420
|
+
return "*[context compacted]*\n";
|
|
402
421
|
case "context_fill_warning":
|
|
403
|
-
return `*[context ${event.fillPercent}% full]*
|
|
422
|
+
return `*[context ${Math.round(event.fillPercent * 100)}% full]*
|
|
404
423
|
`;
|
|
405
424
|
case "tool_truncated":
|
|
406
425
|
return `*[${event.tool} output truncated]*
|
|
@@ -481,7 +500,9 @@ function parseSsePayload(payload) {
|
|
|
481
500
|
let currentData = [];
|
|
482
501
|
function flush() {
|
|
483
502
|
if (currentData.length === 0 && currentId === void 0 && currentEvent === "message") return;
|
|
484
|
-
|
|
503
|
+
const msg = { event: currentEvent, data: currentData.join("\n") };
|
|
504
|
+
if (currentId !== void 0) msg.id = currentId;
|
|
505
|
+
messages.push(msg);
|
|
485
506
|
currentEvent = "message";
|
|
486
507
|
currentId = void 0;
|
|
487
508
|
currentData = [];
|
|
@@ -511,11 +532,14 @@ function parseSsePayload(payload) {
|
|
|
511
532
|
for (const message of messages) {
|
|
512
533
|
const data = message.data ? parseJsonObject(message.data) : {};
|
|
513
534
|
if (message.event === "status") {
|
|
514
|
-
|
|
515
|
-
status: typeof data.status === "string" ? data.status : "unknown"
|
|
516
|
-
...typeof data.title === "string" ? { title: data.title } : {},
|
|
517
|
-
...typeof data.spawnDurationMs === "number" || data.spawnDurationMs === null ? { spawnDurationMs: data.spawnDurationMs } : {}
|
|
535
|
+
const entry = {
|
|
536
|
+
status: typeof data.status === "string" ? data.status : "unknown"
|
|
518
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;
|
|
519
543
|
continue;
|
|
520
544
|
}
|
|
521
545
|
events.push({
|
|
@@ -656,13 +680,7 @@ async function fetchPromptLabels(config, sessionId) {
|
|
|
656
680
|
}
|
|
657
681
|
async function watchCommand(sessionId, options) {
|
|
658
682
|
const config = requireConfig();
|
|
659
|
-
|
|
660
|
-
try {
|
|
661
|
-
pollIntervalMs = parsePollInterval(options.pollInterval);
|
|
662
|
-
} catch (err) {
|
|
663
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
664
|
-
process.exit(1);
|
|
665
|
-
}
|
|
683
|
+
const pollIntervalMs = parsePollInterval(options.pollInterval);
|
|
666
684
|
let promptLabels = /* @__PURE__ */ new Map();
|
|
667
685
|
try {
|
|
668
686
|
promptLabels = await fetchPromptLabels(config, sessionId);
|
|
@@ -683,7 +701,7 @@ async function watchCommand(sessionId, options) {
|
|
|
683
701
|
afterSequence: String(afterSequence),
|
|
684
702
|
limit: String(WATCH_REPLAY_PAGE_SIZE)
|
|
685
703
|
});
|
|
686
|
-
const payload = await apiFetchText(config, `/api/sessions/${sessionId}/events?${query
|
|
704
|
+
const payload = await apiFetchText(config, `/api/sessions/${sessionId}/events?${query}`);
|
|
687
705
|
const parsed = parseSsePayload(payload);
|
|
688
706
|
const receivedFullPage = parsed.events.length >= WATCH_REPLAY_PAGE_SIZE;
|
|
689
707
|
if (parsed.status) {
|