@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.
- package/dist/index.js +57 -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,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
|
|
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
|
-
fillPercent: Number(data.fillPercent ?? 0)
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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")
|
|
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(
|
|
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
|
-
|
|
353
|
+
if (prev.type === "tool_call" && data.status) {
|
|
354
|
+
prev.toolStatus = String(data.status);
|
|
345
355
|
}
|
|
346
356
|
}
|
|
347
357
|
continue;
|
|
348
358
|
}
|
|
349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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) {
|