@comty.io/core 0.1.6 → 0.1.7
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/README.md +13 -5
- package/comty-sdk.js +562 -75
- package/package.json +2 -2
- package/src/browser.js +204 -51
- package/src/capture.js +10 -8
- package/src/claude.mjs +2 -3
- package/src/codex.mjs +2 -4
- package/src/dev.mjs +3 -2
- package/src/inspect-markup.js +283 -0
- package/src/measurements.js +3 -3
- package/src/style.js +62 -13
- package/src/vite.mjs +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comty.io/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Comty visual comments, inspector and local agent tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"check:build": "node scripts/build.mjs --check",
|
|
35
35
|
"test": "node --experimental-websocket --test tests/measurements.test.mjs tests/anchors.test.mjs tests/package.test.mjs tests/packed.test.mjs tests/companion.test.mjs tests/codex.test.mjs tests/claude.test.mjs tests/cursor.test.mjs tests/acp.test.mjs",
|
|
36
36
|
"test:capture": "node --test tests/capture.browser.test.mjs",
|
|
37
|
-
"test:inspector": "node --test tests/inspector.browser.test.mjs",
|
|
37
|
+
"test:inspector": "node --test tests/inspector.browser.test.mjs tests/inspect-markup.browser.test.mjs tests/idle-network.browser.test.mjs",
|
|
38
38
|
"test:placement": "node --test tests/placement.browser.test.mjs",
|
|
39
39
|
"test:anchors": "node --test tests/anchors.test.mjs tests/anchor-reload.browser.test.mjs",
|
|
40
40
|
"test:vite": "node --test tests/vite.test.mjs",
|
package/src/browser.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createInspectMarkup } from "./inspect-markup.js";
|
|
1
2
|
import { getComtyStyle } from "./style.js";
|
|
2
3
|
import { createAnchorMatcher } from "./anchors.js";
|
|
3
4
|
import { createScreenshotCapture, createColorMagnifier, captureContext } from "./capture.js";
|
|
@@ -56,12 +57,14 @@ export function initComty(options = {}) {
|
|
|
56
57
|
const deliveringAgentReplies = new Set();
|
|
57
58
|
let localApprovals = [];
|
|
58
59
|
const seenAgentApprovals = new Set();
|
|
59
|
-
|
|
60
|
+
// Only requests made in this document may interrupt it with agent notifications.
|
|
61
|
+
const requestedAgentTasks = new Set();
|
|
60
62
|
let agentPanelOpen = false;
|
|
61
63
|
let agentPanelSignature = "";
|
|
62
64
|
let agentPanelTransition = 0;
|
|
63
65
|
let agentConnectionError = "";
|
|
64
66
|
let agentActionError = "";
|
|
67
|
+
let clearAgentToast = () => {};
|
|
65
68
|
const agentPendingActions = new Set();
|
|
66
69
|
const session = {
|
|
67
70
|
mode: "off",
|
|
@@ -194,6 +197,9 @@ export function initComty(options = {}) {
|
|
|
194
197
|
const agentBtn = isLocalSite
|
|
195
198
|
? toolButton("Agent task · localhost only", '<path d="M8 7 3 12l5 5m8-10 5 5-5 5M14 4l-4 16"/>', () => {
|
|
196
199
|
if (session.agent) {
|
|
200
|
+
const failedTask = [...localTasks].reverse().find(task => task.status === 'in_review' && !task.thread_comment_id);
|
|
201
|
+
const failedPin = failedTask && placedPins.get(String(failedTask.id));
|
|
202
|
+
if (failedPin && !failedPin.hasAttribute('data-comty-anchor-hidden')) { failedPin.click(); return; }
|
|
197
203
|
if (localTasks.some(task => task.status === 'in_review' || (!task.thread_comment_id && task.status === 'done')) || localApprovals.length) {
|
|
198
204
|
agentPanelOpen = true;
|
|
199
205
|
renderAgentPanel();
|
|
@@ -201,6 +207,10 @@ export function initComty(options = {}) {
|
|
|
201
207
|
} else startAgentSession();
|
|
202
208
|
})
|
|
203
209
|
: null;
|
|
210
|
+
if (agentBtn) {
|
|
211
|
+
agentBtn.title = "Agent · Shift A · localhost only";
|
|
212
|
+
agentBtn.setAttribute("aria-keyshortcuts", "Shift+A");
|
|
213
|
+
}
|
|
204
214
|
const divider = document.createElement("span");
|
|
205
215
|
divider.className = "comty-tool-divider";
|
|
206
216
|
toolbar.appendChild(divider);
|
|
@@ -240,6 +250,7 @@ export function initComty(options = {}) {
|
|
|
240
250
|
root,
|
|
241
251
|
isComtyElement,
|
|
242
252
|
isElementTreeHidden,
|
|
253
|
+
isInteractionBlocked: () => Boolean(markup.mode),
|
|
243
254
|
onExit() { rulersEnabled = false; syncInspectorTools(); },
|
|
244
255
|
});
|
|
245
256
|
const gridLayer = document.createElement("div");
|
|
@@ -266,6 +277,7 @@ export function initComty(options = {}) {
|
|
|
266
277
|
syncInspectorTools();
|
|
267
278
|
});
|
|
268
279
|
const rulersBtn = toolButton("Rulers · R", '<path d="M3 3h18v6H9v12H3Z"/><path d="M7 3v3m4-3v3m4-3v3M3 11h3m-3 4h3m-3 4h3"/>', () => {
|
|
280
|
+
markup.setMode(null);
|
|
269
281
|
closePropertyPanel();
|
|
270
282
|
if (motion.enabled) setMotionEnabled(false);
|
|
271
283
|
rulersEnabled = !rulersEnabled;
|
|
@@ -276,6 +288,7 @@ export function initComty(options = {}) {
|
|
|
276
288
|
syncInspectorTools();
|
|
277
289
|
});
|
|
278
290
|
const gridBtn = toolButton("Layout guides · G", '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 10h18M10 3v18"/>', () => {
|
|
291
|
+
markup.setMode(null);
|
|
279
292
|
closePropertyPanel();
|
|
280
293
|
if (motion.enabled) setMotionEnabled(false);
|
|
281
294
|
gridPanelOpen = !gridPanelOpen;
|
|
@@ -293,7 +306,37 @@ export function initComty(options = {}) {
|
|
|
293
306
|
}
|
|
294
307
|
const motionBtn = toolButton("Motion · M", '<circle cx="12" cy="14" r="8"/><path d="M9 2h6m-3 4V2m6 5 2-2m-8 5v4l3 2"/>', () => setMotionEnabled(!motion.enabled));
|
|
295
308
|
motionBtn.setAttribute("aria-expanded", "false");
|
|
296
|
-
|
|
309
|
+
const annotateBtn = toolButton("Annotate · D", '<path d="m16 3 5 5L8 21H3v-5ZM14 5l5 5"/>', () => toggleMarkup("annotate"));
|
|
310
|
+
const screenshotBtn = toolButton("Capture area · S", '<path d="M8 3H5a2 2 0 0 0-2 2v3m13-5h3a2 2 0 0 1 2 2v3M3 16v3a2 2 0 0 0 2 2h3m13-5v3a2 2 0 0 1-2 2h-3"/><rect x="7" y="8" width="10" height="8" rx="1"/><circle cx="12" cy="12" r="2"/>', () => toggleMarkup("capture"));
|
|
311
|
+
const markup = createInspectMarkup({
|
|
312
|
+
root, icon,
|
|
313
|
+
onError: description => showNotice({ title: "Screenshot unavailable", description }),
|
|
314
|
+
onChange() {
|
|
315
|
+
annotateBtn.setAttribute("aria-pressed", String(markup.mode === "annotate"));
|
|
316
|
+
annotateBtn.setAttribute("aria-expanded", String(markup.mode === "annotate"));
|
|
317
|
+
screenshotBtn.setAttribute("aria-pressed", String(markup.mode === "capture"));
|
|
318
|
+
screenshotBtn.setAttribute("aria-busy", String(markup.busy));
|
|
319
|
+
positionToolbar();
|
|
320
|
+
},
|
|
321
|
+
capture: (region, layer) => captureViewportScreenshot({ region, annotation: () => ({
|
|
322
|
+
elements: [overlayTools, root.querySelector('.comty-distance-layer'), layer].filter(node => node && !node.hidden),
|
|
323
|
+
dispose() {},
|
|
324
|
+
}) }),
|
|
325
|
+
});
|
|
326
|
+
for (const button of [annotateBtn]) {
|
|
327
|
+
button.setAttribute("aria-controls", markup.panel.id);
|
|
328
|
+
button.setAttribute("aria-expanded", "false");
|
|
329
|
+
}
|
|
330
|
+
function toggleMarkup(mode) {
|
|
331
|
+
const next = markup.mode === mode ? null : mode;
|
|
332
|
+
closePropertyPanel();
|
|
333
|
+
if (motion.enabled) setMotionEnabled(false);
|
|
334
|
+
gridPanelOpen = false; toolPanel.hidden = true; gridBtn.setAttribute("aria-expanded", "false");
|
|
335
|
+
cancelRulerDrag();
|
|
336
|
+
selectedElement = null; measuredRegion = null; inspectDrag = null; hideInspector();
|
|
337
|
+
markup.setMode(next);
|
|
338
|
+
}
|
|
339
|
+
for (const button of [xrayBtn, rulersBtn, gridBtn, pipetteBtn, typographyBtn, motionBtn, annotateBtn, screenshotBtn]) inspectorTools.appendChild(button);
|
|
297
340
|
|
|
298
341
|
const motion = { enabled: false, speed: 1, paused: false, managed: new Map(), shadowRoots: [], nextRootScan: 0 };
|
|
299
342
|
const motionPanel = document.createElement('section');
|
|
@@ -342,6 +385,7 @@ export function initComty(options = {}) {
|
|
|
342
385
|
motion.enabled = enabled; motionPanel.hidden = !enabled;
|
|
343
386
|
motionBtn.setAttribute('aria-pressed', String(enabled)); motionBtn.setAttribute('aria-expanded', String(enabled));
|
|
344
387
|
if (enabled) {
|
|
388
|
+
markup.setMode(null);
|
|
345
389
|
closePropertyPanel();
|
|
346
390
|
rulersEnabled = false;
|
|
347
391
|
syncInspectorTools();
|
|
@@ -600,6 +644,7 @@ export function initComty(options = {}) {
|
|
|
600
644
|
const visible = session.inspect;
|
|
601
645
|
measurements.setEnabled(visible && rulersEnabled && !motion.enabled);
|
|
602
646
|
overlayTools.hidden = !visible;
|
|
647
|
+
markup.setVisible(visible);
|
|
603
648
|
xrayBtn.setAttribute("aria-pressed", String(visible && xrayEnabled));
|
|
604
649
|
rulersBtn.setAttribute("aria-pressed", String(visible && rulersEnabled));
|
|
605
650
|
gridBtn.setAttribute("aria-pressed", String(visible && layoutGuides.some(guide => guide.visible)));
|
|
@@ -883,6 +928,7 @@ export function initComty(options = {}) {
|
|
|
883
928
|
}
|
|
884
929
|
|
|
885
930
|
function openPropertyPanel(kind, element, property, opener = null, sampledColor = null) {
|
|
931
|
+
markup.setMode(null);
|
|
886
932
|
propertyPicking = null;
|
|
887
933
|
colorMagnifier.setEnabled(false);
|
|
888
934
|
delete document.documentElement.dataset.comtyPropertyPicking;
|
|
@@ -1140,7 +1186,7 @@ export function initComty(options = {}) {
|
|
|
1140
1186
|
});
|
|
1141
1187
|
}
|
|
1142
1188
|
const rect = toolbar.getBoundingClientRect();
|
|
1143
|
-
for (const panel of [toolPanel, propertyPanel, sidepanel, motionPanel]) {
|
|
1189
|
+
for (const panel of [toolPanel, propertyPanel, sidepanel, motionPanel, markup.panel]) {
|
|
1144
1190
|
if (panel.hidden || (panel === sidepanel && !sidepanelOpen)) continue;
|
|
1145
1191
|
const above = rect.top - 16;
|
|
1146
1192
|
const below = innerHeight - rect.bottom - 16;
|
|
@@ -1157,9 +1203,10 @@ export function initComty(options = {}) {
|
|
|
1157
1203
|
panel.style.top = `${useAbove ? rect.top - panel.offsetHeight - 8 : rect.bottom + 8}px`;
|
|
1158
1204
|
panel.style.bottom = "auto";
|
|
1159
1205
|
}
|
|
1206
|
+
positionNotice();
|
|
1160
1207
|
}
|
|
1161
1208
|
const toolbarResize = new ResizeObserver(positionToolbar);
|
|
1162
|
-
[toolbar, toolPanel, propertyPanel, sidepanel, motionPanel].forEach(node => toolbarResize.observe(node));
|
|
1209
|
+
[toolbar, toolPanel, propertyPanel, sidepanel, motionPanel, markup.panel].forEach(node => toolbarResize.observe(node));
|
|
1163
1210
|
window.addEventListener("resize", positionToolbar);
|
|
1164
1211
|
|
|
1165
1212
|
toolbar.addEventListener("pointerdown", event => {
|
|
@@ -1494,7 +1541,7 @@ export function initComty(options = {}) {
|
|
|
1494
1541
|
|
|
1495
1542
|
function showAuthPrompt(forbidden = false, onReady = startEditSession) {
|
|
1496
1543
|
if (localOnly) {
|
|
1497
|
-
|
|
1544
|
+
showNotice({ title: "Comments are not configured", description: "Connect this site to a Comty project to leave shared comments." });
|
|
1498
1545
|
return;
|
|
1499
1546
|
}
|
|
1500
1547
|
const guestAllowed = allowGuestComments;
|
|
@@ -1628,6 +1675,7 @@ export function initComty(options = {}) {
|
|
|
1628
1675
|
inspectorTools.hidden = !session.inspect;
|
|
1629
1676
|
syncInspectorTools();
|
|
1630
1677
|
renderAgentPanel();
|
|
1678
|
+
positionNotice();
|
|
1631
1679
|
}
|
|
1632
1680
|
|
|
1633
1681
|
const inspectionLayer = document.createElement("div");
|
|
@@ -1940,7 +1988,7 @@ export function initComty(options = {}) {
|
|
|
1940
1988
|
});
|
|
1941
1989
|
return;
|
|
1942
1990
|
}
|
|
1943
|
-
if (propertyPicking || motion.enabled || (session.inspect && rulersEnabled)) {
|
|
1991
|
+
if (markup.mode || propertyPicking || motion.enabled || (session.inspect && rulersEnabled)) {
|
|
1944
1992
|
if (inspectionLayer.childNodes.length) hideInspector();
|
|
1945
1993
|
return;
|
|
1946
1994
|
}
|
|
@@ -2084,10 +2132,12 @@ export function initComty(options = {}) {
|
|
|
2084
2132
|
|
|
2085
2133
|
function startBrowseSession() {
|
|
2086
2134
|
setSession("browse", { renderComments: true });
|
|
2135
|
+
void loadComments({ force: true });
|
|
2087
2136
|
}
|
|
2088
2137
|
|
|
2089
2138
|
function startEditSession() {
|
|
2090
2139
|
setSession("edit", { renderComments: true });
|
|
2140
|
+
void loadComments({ force: true });
|
|
2091
2141
|
}
|
|
2092
2142
|
|
|
2093
2143
|
function startAgentSession() {
|
|
@@ -2095,6 +2145,7 @@ export function initComty(options = {}) {
|
|
|
2095
2145
|
if (session.agent) return;
|
|
2096
2146
|
closeBubbles();
|
|
2097
2147
|
setSession("agent", { renderComments: true });
|
|
2148
|
+
void loadComments({ force: true });
|
|
2098
2149
|
}
|
|
2099
2150
|
|
|
2100
2151
|
function toggleSidepanel() {
|
|
@@ -2832,6 +2883,7 @@ export function initComty(options = {}) {
|
|
|
2832
2883
|
function createCommentPin(comment, x, y) {
|
|
2833
2884
|
const pin = document.createElement("button");
|
|
2834
2885
|
pin.className = "comty-pin";
|
|
2886
|
+
pin.toggleAttribute('data-agent-task', Boolean(comment.local_agent));
|
|
2835
2887
|
pin.type = "button";
|
|
2836
2888
|
pin.style.left = `${x}px`;
|
|
2837
2889
|
pin.style.top = `${y}px`;
|
|
@@ -2849,10 +2901,52 @@ export function initComty(options = {}) {
|
|
|
2849
2901
|
const thread = document.createElement("div");
|
|
2850
2902
|
thread.className = "comty-thread";
|
|
2851
2903
|
thread.appendChild(createThreadItem(comment, { showMore: false, showLink: false }).item);
|
|
2904
|
+
if (comment.local_agent) {
|
|
2905
|
+
thread.appendChild(createThreadItem({ author_kind: 'agent', content: 'Modification failed. Open to retry or dismiss.' }, { showMore: false }).item);
|
|
2906
|
+
}
|
|
2852
2907
|
bubble.appendChild(thread);
|
|
2853
2908
|
return bubble;
|
|
2854
2909
|
}
|
|
2855
2910
|
|
|
2911
|
+
function openAgentTask(task, x, y) {
|
|
2912
|
+
closeBubbles();
|
|
2913
|
+
const bubble = document.createElement('div');
|
|
2914
|
+
bubble.className = 'comty-bubble comty-agent-task-thread';
|
|
2915
|
+
bubble.dataset.commentId = task.id;
|
|
2916
|
+
bubble.setAttribute('role', 'dialog');
|
|
2917
|
+
bubble.setAttribute('aria-label', 'Failed modification');
|
|
2918
|
+
const header = document.createElement('div'); header.className = 'comty-bubble-header';
|
|
2919
|
+
const title = document.createElement('strong'); title.textContent = 'Modification failed';
|
|
2920
|
+
const close = document.createElement('button'); close.type = 'button'; close.className = 'comty-icon-button';
|
|
2921
|
+
close.setAttribute('aria-label', 'Close failed modification');
|
|
2922
|
+
close.innerHTML = icon('<path d="m6 6 12 12M6 18 18 6"/>');
|
|
2923
|
+
close.onclick = () => closeBubbles();
|
|
2924
|
+
header.append(title, close);
|
|
2925
|
+
const thread = document.createElement('div'); thread.className = 'comty-thread';
|
|
2926
|
+
const list = document.createElement('div'); list.className = 'comty-thread-list';
|
|
2927
|
+
const issue = task.error ? describeAgentError(task.error) : null;
|
|
2928
|
+
list.append(
|
|
2929
|
+
createThreadItem(task, { showMore: false }).item,
|
|
2930
|
+
createThreadItem({ author_kind: 'agent', content: task.result?.summary || issue?.message || issue?.description || 'The agent could not complete this request.' }, { showMore: false }).item,
|
|
2931
|
+
);
|
|
2932
|
+
thread.appendChild(list);
|
|
2933
|
+
const actions = document.createElement('div'); actions.className = 'comty-agent-task-actions';
|
|
2934
|
+
for (const action of ['retry', 'dismiss']) {
|
|
2935
|
+
const button = document.createElement('button'); button.type = 'button'; button.className = 'comty-agent-action';
|
|
2936
|
+
button.textContent = action === 'retry' ? 'Retry' : 'Dismiss';
|
|
2937
|
+
button.onclick = async () => {
|
|
2938
|
+
for (const control of actions.children) control.disabled = true;
|
|
2939
|
+
try { await runAgentAction(`tasks/${task.id}/${action}`); }
|
|
2940
|
+
catch (error) { showAgentBridgeError(error.message); }
|
|
2941
|
+
finally { for (const control of actions.children) control.disabled = false; }
|
|
2942
|
+
};
|
|
2943
|
+
actions.appendChild(button);
|
|
2944
|
+
}
|
|
2945
|
+
bubble.append(header, thread, actions);
|
|
2946
|
+
root.appendChild(bubble);
|
|
2947
|
+
positionCommentBubble(bubble, x, y);
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2856
2950
|
function mountCaptureAnnotation(comment, { x, y }) {
|
|
2857
2951
|
const host = document.createElement("div");
|
|
2858
2952
|
host.dataset.comtyCaptureExclude = "";
|
|
@@ -3036,6 +3130,7 @@ export function initComty(options = {}) {
|
|
|
3036
3130
|
const id = isAgentTask ? data?.task?.id : data?.id;
|
|
3037
3131
|
if (!id) throw new Error("Save returned no ID");
|
|
3038
3132
|
if (isAgentTask) {
|
|
3133
|
+
requestedAgentTasks.add(id);
|
|
3039
3134
|
void loadComments({ force: true });
|
|
3040
3135
|
} else {
|
|
3041
3136
|
cachedComments = cachedComments.filter(item => String(item.id) !== pendingId);
|
|
@@ -3167,14 +3262,53 @@ export function initComty(options = {}) {
|
|
|
3167
3262
|
function agentHeaders() { return { "Content-Type": "application/json", "X-Comty-Token": agent?.token || "" }; }
|
|
3168
3263
|
|
|
3169
3264
|
function showAgentBridgeError(message = "Start your app with comty dev to use the local agent. Check the terminal if it is already running.") {
|
|
3170
|
-
|
|
3265
|
+
const issue = describeAgentError(message);
|
|
3266
|
+
showNotice({ title: 'Modification failed', description: issue.title === 'Task could not finish' ? 'The agent could not complete this request.' : issue.title + '.' });
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
function positionNotice() {
|
|
3270
|
+
const toast = root.querySelector(".comty-agent-task-toast");
|
|
3271
|
+
if (!toast) return;
|
|
3272
|
+
toast.style.removeProperty("left");
|
|
3273
|
+
toast.style.removeProperty("top");
|
|
3274
|
+
toast.toggleAttribute("data-toolbar", isSessionActive());
|
|
3275
|
+
if (!isSessionActive()) return;
|
|
3276
|
+
const rect = toolbar.getBoundingClientRect();
|
|
3277
|
+
const above = rect.top - toast.offsetHeight - 8;
|
|
3278
|
+
toast.style.left = `${Math.max(8, Math.min(rect.right - toast.offsetWidth, innerWidth - toast.offsetWidth - 8))}px`;
|
|
3279
|
+
toast.style.top = `${Math.max(8, Math.min(above >= 8 ? above : rect.bottom + 8, innerHeight - toast.offsetHeight - 8))}px`;
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
function showNotice(issue) {
|
|
3283
|
+
clearAgentToast();
|
|
3171
3284
|
const toast = document.createElement("div");
|
|
3172
|
-
toast.className = "comty-
|
|
3285
|
+
toast.className = "comty-agent-task-toast";
|
|
3173
3286
|
toast.setAttribute("role", "alert");
|
|
3174
|
-
const
|
|
3175
|
-
|
|
3287
|
+
const title = document.createElement("strong"); title.textContent = issue.title;
|
|
3288
|
+
const description = document.createElement("p"); description.textContent = issue.description;
|
|
3289
|
+
const close = document.createElement("button"); close.type = "button";
|
|
3290
|
+
close.setAttribute("aria-label", "Dismiss notification");
|
|
3291
|
+
close.innerHTML = icon('<path d="m6 6 12 12M6 18 18 6"/>');
|
|
3292
|
+
toast.append(title, description, close);
|
|
3293
|
+
let timer, leaving = false;
|
|
3294
|
+
const dismiss = () => {
|
|
3295
|
+
if (leaving) return;
|
|
3296
|
+
leaving = true;
|
|
3297
|
+
clearTimeout(timer);
|
|
3298
|
+
toast.dataset.leaving = "";
|
|
3299
|
+
timer = setTimeout(() => toast.remove(), reducedMotion.matches ? 0 : 200);
|
|
3300
|
+
};
|
|
3301
|
+
const schedule = () => { if (!leaving) { clearTimeout(timer); timer = setTimeout(dismiss, 6000); } };
|
|
3302
|
+
const pause = () => { if (!leaving) clearTimeout(timer); };
|
|
3303
|
+
clearAgentToast = () => { clearTimeout(timer); toast.remove(); };
|
|
3304
|
+
close.onclick = dismiss;
|
|
3305
|
+
toast.onmouseenter = pause;
|
|
3306
|
+
toast.onmouseleave = schedule;
|
|
3307
|
+
toast.onfocusin = pause;
|
|
3308
|
+
toast.onfocusout = schedule;
|
|
3176
3309
|
root.appendChild(toast);
|
|
3177
|
-
|
|
3310
|
+
positionNotice();
|
|
3311
|
+
schedule();
|
|
3178
3312
|
}
|
|
3179
3313
|
|
|
3180
3314
|
function describeAgentError(error) {
|
|
@@ -3203,7 +3337,7 @@ export function initComty(options = {}) {
|
|
|
3203
3337
|
} else if (/unauthorized|unauthenticated|authentication|not logged|sign.?in|log.?in|invalid.api.key|\b401\b/i.test(source)) {
|
|
3204
3338
|
title = 'Sign in to your agent'; description = `Open ${agentName} and sign in again, then retry this task.`;
|
|
3205
3339
|
} else if (/model.*(?:not found|does not exist|unavailable|not supported)|unsupported model|model_not_found/i.test(source)) {
|
|
3206
|
-
title = 'Model unavailable'; description = '
|
|
3340
|
+
title = 'Model unavailable'; description = 'The local agent rejected this model. Check its CLI version and signed-in account, then retry.';
|
|
3207
3341
|
} else if (/disconnected|ECONNREFUSED|ENOTFOUND|network|failed to fetch|development server stopped/i.test(source)) {
|
|
3208
3342
|
title = 'Connection lost'; description = 'Check that your local development server and coding agent are running, then reload this page.';
|
|
3209
3343
|
} else if (/timed? ?out|timeout|exceeded.*minutes/i.test(source)) {
|
|
@@ -3216,11 +3350,21 @@ export function initComty(options = {}) {
|
|
|
3216
3350
|
title = 'Result needs checking'; description = 'The agent could not confirm completion. Review the changes before retrying.';
|
|
3217
3351
|
} else {
|
|
3218
3352
|
title = 'Task could not finish';
|
|
3219
|
-
description = message && !/[{}]|\[object Object\]/.test(message) ? message.slice(0,
|
|
3353
|
+
description = message && !/[{}]|\[object Object\]/.test(message) ? message.slice(0, 160) + (message.length > 160 ? '…' : '') : 'The agent reported an unexpected error. Review the technical details and any changes before retrying.';
|
|
3220
3354
|
}
|
|
3221
3355
|
return { title, description, raw, message };
|
|
3222
3356
|
}
|
|
3223
3357
|
|
|
3358
|
+
async function runAgentAction(path, input = {}) {
|
|
3359
|
+
const response = await fetch(`${agentBridgeUrl}/${path}`, { method: 'POST', headers: agentHeaders(), body: JSON.stringify(input) });
|
|
3360
|
+
if (!response.ok) {
|
|
3361
|
+
const body = await response.json().catch(() => null);
|
|
3362
|
+
throw new Error(typeof body?.error === 'string' ? body.error : JSON.stringify(body?.error || { status: response.status, message: 'Request failed' }));
|
|
3363
|
+
}
|
|
3364
|
+
if (path.endsWith('/retry')) requestedAgentTasks.add(path.split('/')[1]);
|
|
3365
|
+
await loadComments({ force: true });
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3224
3368
|
function renderAgentPanel() {
|
|
3225
3369
|
if (!agent) return;
|
|
3226
3370
|
const visibleTasks = [];
|
|
@@ -3239,6 +3383,7 @@ export function initComty(options = {}) {
|
|
|
3239
3383
|
}
|
|
3240
3384
|
const open = Boolean(agent) && agentPanelOpen;
|
|
3241
3385
|
if (open) {
|
|
3386
|
+
clearAgentToast();
|
|
3242
3387
|
if (panel.hidden) {
|
|
3243
3388
|
const rect = toolbar.getBoundingClientRect();
|
|
3244
3389
|
panel.dataset.side = rect.left + rect.width / 2 < innerWidth / 2 ? "left" : "right";
|
|
@@ -3259,7 +3404,6 @@ export function initComty(options = {}) {
|
|
|
3259
3404
|
const signature = JSON.stringify([visibleTasks, localApprovals, agentConnectionError, agentActionError, agentPanelOpen]);
|
|
3260
3405
|
if (signature === agentPanelSignature) return;
|
|
3261
3406
|
agentPanelSignature = signature;
|
|
3262
|
-
root.querySelector('.comty-agent-error-popover')?.remove();
|
|
3263
3407
|
const expanded = new Set([...panel.querySelectorAll('details[data-detail-key][open]')].map(node => node.dataset.detailKey));
|
|
3264
3408
|
const focusKey = panel.contains(document.activeElement) ? document.activeElement.dataset.focusKey : null;
|
|
3265
3409
|
const scrollTop = panel.querySelector('.comty-agent-panel-content')?.scrollTop || 0;
|
|
@@ -3305,12 +3449,7 @@ export function initComty(options = {}) {
|
|
|
3305
3449
|
agentActionError = '';
|
|
3306
3450
|
agentPendingActions.add(key); button.disabled = true; button.textContent = 'Please wait…';
|
|
3307
3451
|
try {
|
|
3308
|
-
|
|
3309
|
-
if (!response.ok) {
|
|
3310
|
-
const body = await response.json().catch(() => null);
|
|
3311
|
-
throw new Error(typeof body?.error === 'string' ? body.error : JSON.stringify(body?.error || { status: response.status, message: 'Request failed' }));
|
|
3312
|
-
}
|
|
3313
|
-
await loadComments({ force: true });
|
|
3452
|
+
await runAgentAction(path, input);
|
|
3314
3453
|
} catch (error) { agentActionError = error.message; }
|
|
3315
3454
|
finally { agentPendingActions.delete(key); agentPanelSignature = ''; renderAgentPanel(); }
|
|
3316
3455
|
};
|
|
@@ -3331,9 +3470,7 @@ export function initComty(options = {}) {
|
|
|
3331
3470
|
section.appendChild(actions); content.appendChild(section);
|
|
3332
3471
|
}
|
|
3333
3472
|
const list = node('div', 'comty-agent-list'); content.appendChild(list);
|
|
3334
|
-
const
|
|
3335
|
-
content.addEventListener('scroll', hideErrorPopover, { passive: true });
|
|
3336
|
-
for (const [index, task] of visibleTasks.reverse().entries()) {
|
|
3473
|
+
for (const task of visibleTasks.reverse()) {
|
|
3337
3474
|
const failed = Boolean(task.error) || task.status === 'in_review';
|
|
3338
3475
|
const state = task.status === 'done' ? 'success' : failed ? 'error' : 'working';
|
|
3339
3476
|
const row = node('div', 'comty-agent-row'); row.dataset.taskId = task.id;
|
|
@@ -3349,24 +3486,12 @@ export function initComty(options = {}) {
|
|
|
3349
3486
|
if (task.status === 'in_review') action(actions, 'Dismiss', `tasks/${task.id}/dismiss`);
|
|
3350
3487
|
if (actions.childElementCount) row.appendChild(actions);
|
|
3351
3488
|
row.setAttribute('aria-label', `${task.content}, ${state === 'error' ? 'failed' : state === 'success' ? 'completed' : state === 'cancel' ? 'cancelled' : task.status === 'todo' ? 'starting' : 'working'}`);
|
|
3352
|
-
if (failed) {
|
|
3353
|
-
const tooltipId = `comty-agent-error-${index}`;
|
|
3354
|
-
const showErrorPopover = () => {
|
|
3355
|
-
hideErrorPopover();
|
|
3356
|
-
const issue = describeAgentError(task.error || task.result?.summary || task.progress || 'Completion was not verified');
|
|
3357
|
-
const popover = node('div', 'comty-agent-error-popover'); popover.id = tooltipId; popover.setAttribute('role', 'tooltip');
|
|
3358
|
-
popover.append(node('strong', '', task.content), node('p', '', issue.message || issue.description));
|
|
3359
|
-
root.appendChild(popover);
|
|
3360
|
-
const rowRect = row.getBoundingClientRect();
|
|
3361
|
-
const popoverRect = popover.getBoundingClientRect();
|
|
3362
|
-
const left = panel.dataset.side === 'right' ? rowRect.left - popoverRect.width - 8 : rowRect.right + 8;
|
|
3363
|
-
popover.style.left = `${Math.max(8, Math.min(left, innerWidth - popoverRect.width - 8))}px`;
|
|
3364
|
-
popover.style.top = `${Math.max(8, Math.min(rowRect.top - 4, innerHeight - popoverRect.height - 8))}px`;
|
|
3365
|
-
};
|
|
3366
|
-
row.addEventListener('mouseenter', showErrorPopover);
|
|
3367
|
-
row.addEventListener('mouseleave', hideErrorPopover);
|
|
3368
|
-
}
|
|
3369
3489
|
list.appendChild(row);
|
|
3490
|
+
if (task.error) {
|
|
3491
|
+
const details = node('div', 'comty-agent-item');
|
|
3492
|
+
errorMessage(details, task.error, task.id);
|
|
3493
|
+
list.appendChild(details);
|
|
3494
|
+
}
|
|
3370
3495
|
if (task.result?.summary) {
|
|
3371
3496
|
const answer = node('div', 'comty-agent-answer');
|
|
3372
3497
|
answer.append(brand(), node('p', '', task.result.summary));
|
|
@@ -3617,7 +3742,7 @@ export function initComty(options = {}) {
|
|
|
3617
3742
|
if (!(pin instanceof HTMLElement)) return;
|
|
3618
3743
|
const status = pin.dataset.status;
|
|
3619
3744
|
const shouldShow =
|
|
3620
|
-
!hasFilters || Boolean(status && selectedStatusFilters.has(status));
|
|
3745
|
+
pin.hasAttribute('data-agent-task') || !hasFilters || Boolean(status && selectedStatusFilters.has(status));
|
|
3621
3746
|
pin.classList.toggle("comty-filtered-out", !shouldShow);
|
|
3622
3747
|
});
|
|
3623
3748
|
}
|
|
@@ -3896,6 +4021,10 @@ export function initComty(options = {}) {
|
|
|
3896
4021
|
thread: { commentId: String(comment.id), transcript } }),
|
|
3897
4022
|
});
|
|
3898
4023
|
if (!response.ok) throw new Error((await response.json().catch(() => null))?.error || 'Could not start Agent');
|
|
4024
|
+
const data = await response.json();
|
|
4025
|
+
if (!data.task?.id) throw new Error('Agent returned no task ID');
|
|
4026
|
+
requestedAgentTasks.add(data.task.id);
|
|
4027
|
+
void loadComments({ force: true });
|
|
3899
4028
|
},
|
|
3900
4029
|
get canWrite() { return canWrite; },
|
|
3901
4030
|
get canManage() { return canManage; },
|
|
@@ -4011,6 +4140,7 @@ export function initComty(options = {}) {
|
|
|
4011
4140
|
|
|
4012
4141
|
pin.onclick = (event) => {
|
|
4013
4142
|
event.stopPropagation();
|
|
4143
|
+
if (comment.local_agent) { openAgentTask(comment, getPinPoint(pin).x, getPinPoint(pin).y); return; }
|
|
4014
4144
|
openCommentThread(
|
|
4015
4145
|
comment,
|
|
4016
4146
|
pin,
|
|
@@ -4135,7 +4265,7 @@ export function initComty(options = {}) {
|
|
|
4135
4265
|
}
|
|
4136
4266
|
}
|
|
4137
4267
|
|
|
4138
|
-
async function loadComments({ force = false } = {}) {
|
|
4268
|
+
async function loadComments({ force = false, tasksOnly = false } = {}) {
|
|
4139
4269
|
if (!projectId && !agent) return;
|
|
4140
4270
|
const pagePath = getPagePath();
|
|
4141
4271
|
if (!force && cachedPagePath === pagePath) { await renderComments(cachedComments); return; }
|
|
@@ -4145,7 +4275,7 @@ export function initComty(options = {}) {
|
|
|
4145
4275
|
}
|
|
4146
4276
|
pendingCommentsLoad = (async () => {
|
|
4147
4277
|
let comments = cachedPagePath === pagePath ? cachedComments.filter(comment => !comment.local_agent) : [];
|
|
4148
|
-
if (projectId) {
|
|
4278
|
+
if (projectId && !tasksOnly) {
|
|
4149
4279
|
try {
|
|
4150
4280
|
const paths = [...new Set([pagePath, window.location.pathname])];
|
|
4151
4281
|
const batches = await Promise.all(paths.map(async path => {
|
|
@@ -4166,15 +4296,20 @@ export function initComty(options = {}) {
|
|
|
4166
4296
|
const data = await res.json();
|
|
4167
4297
|
if (Array.isArray(data.tasks)) localTasks = data.tasks;
|
|
4168
4298
|
const failedTasks = localTasks.filter(task => task.status === 'in_review');
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4299
|
+
for (const task of failedTasks.filter(task => requestedAgentTasks.has(task.id))) {
|
|
4300
|
+
if (task.error) showAgentBridgeError(task.error);
|
|
4301
|
+
else showNotice({ title: 'Modification failed', description: 'The agent needs clarification.' });
|
|
4302
|
+
}
|
|
4172
4303
|
if (Array.isArray(data.approvals)) {
|
|
4173
4304
|
localApprovals = data.approvals;
|
|
4174
|
-
if (localApprovals.some(approval => !seenAgentApprovals.has(approval.id))) agentPanelOpen = true;
|
|
4305
|
+
if (localApprovals.some(approval => requestedAgentTasks.has(approval.taskId) && !seenAgentApprovals.has(approval.id))) agentPanelOpen = true;
|
|
4175
4306
|
seenAgentApprovals.clear();
|
|
4176
4307
|
for (const approval of localApprovals) seenAgentApprovals.add(approval.id);
|
|
4177
4308
|
}
|
|
4309
|
+
for (const id of requestedAgentTasks) {
|
|
4310
|
+
const task = localTasks.find(task => task.id === id);
|
|
4311
|
+
if (!task || ['done', 'cancel', 'in_review'].includes(task.status)) requestedAgentTasks.delete(id);
|
|
4312
|
+
}
|
|
4178
4313
|
agentConnectionError = "";
|
|
4179
4314
|
} catch (error) {
|
|
4180
4315
|
agentConnectionError = "Local agent disconnected. Check the development server, then reload this page.";
|
|
@@ -4183,7 +4318,7 @@ export function initComty(options = {}) {
|
|
|
4183
4318
|
for (const task of localTasks.filter(task => task.thread_comment_id && task.status === 'done' && task.result?.summary)) {
|
|
4184
4319
|
void deliverThreadReply(task);
|
|
4185
4320
|
}
|
|
4186
|
-
comments.push(...localTasks.filter(task => task.page_path === pagePath && !task.thread_comment_id &&
|
|
4321
|
+
comments.push(...localTasks.filter(task => task.page_path === pagePath && !task.thread_comment_id && task.status === 'in_review'));
|
|
4187
4322
|
renderAgentPanel();
|
|
4188
4323
|
}
|
|
4189
4324
|
if (pagePath !== getPagePath()) return;
|
|
@@ -4199,8 +4334,13 @@ export function initComty(options = {}) {
|
|
|
4199
4334
|
|
|
4200
4335
|
if (agent) {
|
|
4201
4336
|
setInterval(() => {
|
|
4202
|
-
|
|
4337
|
+
const active = requestedAgentTasks.size || localTasks.some(task =>
|
|
4338
|
+
['todo', 'in_progress'].includes(task.status) || (task.thread_comment_id && task.status === 'done' && task.result?.summary));
|
|
4339
|
+
if (active && !pendingCommentsLoad && document.visibilityState === "visible") void loadComments({ force: true, tasksOnly: true });
|
|
4203
4340
|
}, 2000);
|
|
4341
|
+
document.addEventListener('visibilitychange', () => {
|
|
4342
|
+
if (document.visibilityState === 'visible' && !pendingCommentsLoad) void loadComments({ force: true, tasksOnly: true });
|
|
4343
|
+
});
|
|
4204
4344
|
}
|
|
4205
4345
|
|
|
4206
4346
|
function getPinPoint(pin) {
|
|
@@ -4390,6 +4530,8 @@ export function initComty(options = {}) {
|
|
|
4390
4530
|
const next = getPagePath();
|
|
4391
4531
|
if (next === activePagePath) return;
|
|
4392
4532
|
activePagePath = next;
|
|
4533
|
+
markup.clear();
|
|
4534
|
+
markup.setMode(null);
|
|
4393
4535
|
closeBubbles();
|
|
4394
4536
|
commentTargets.clear();
|
|
4395
4537
|
reconcileComments([]);
|
|
@@ -4426,7 +4568,7 @@ export function initComty(options = {}) {
|
|
|
4426
4568
|
|
|
4427
4569
|
document.addEventListener("pointermove", (event) => {
|
|
4428
4570
|
if (!session.edit && !session.inspect) return;
|
|
4429
|
-
if (motion.enabled) return;
|
|
4571
|
+
if (motion.enabled || markup.mode) return;
|
|
4430
4572
|
if (propertyPicking) {
|
|
4431
4573
|
const target = event.target;
|
|
4432
4574
|
if (propertyPicking === "font" && target instanceof Element && !isComtyElement(target) && getElementOwnTextSignature(target)) {
|
|
@@ -4573,6 +4715,12 @@ export function initComty(options = {}) {
|
|
|
4573
4715
|
(target.isContentEditable ||
|
|
4574
4716
|
["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName));
|
|
4575
4717
|
|
|
4718
|
+
const markupMode = markup.mode;
|
|
4719
|
+
if (session.inspect && (e.key === "Escape" || !isEditable) && markup.keydown(e)) {
|
|
4720
|
+
e.preventDefault();
|
|
4721
|
+
if (!markup.mode) (markupMode === "annotate" ? annotateBtn : screenshotBtn).focus({ preventScroll: true });
|
|
4722
|
+
return;
|
|
4723
|
+
}
|
|
4576
4724
|
if (e.key === "Escape" && motion.enabled) {
|
|
4577
4725
|
e.preventDefault();
|
|
4578
4726
|
setMotionEnabled(false); motionBtn.focus({ preventScroll: true });
|
|
@@ -4587,7 +4735,7 @@ export function initComty(options = {}) {
|
|
|
4587
4735
|
return;
|
|
4588
4736
|
}
|
|
4589
4737
|
if (session.inspect && !isEditable && !e.repeat && !e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey) {
|
|
4590
|
-
const button = { x: xrayBtn, r: rulersBtn, g: gridBtn, p: pipetteBtn, t: typographyBtn, m: motionBtn }[e.key.toLowerCase()];
|
|
4738
|
+
const button = { x: xrayBtn, r: rulersBtn, g: gridBtn, p: pipetteBtn, t: typographyBtn, m: motionBtn, d: annotateBtn, s: screenshotBtn }[e.key.toLowerCase()];
|
|
4591
4739
|
if (button) { e.preventDefault(); button.click(); return; }
|
|
4592
4740
|
}
|
|
4593
4741
|
|
|
@@ -4595,6 +4743,11 @@ export function initComty(options = {}) {
|
|
|
4595
4743
|
measureAlt = true;
|
|
4596
4744
|
renderInspector();
|
|
4597
4745
|
}
|
|
4746
|
+
if (isLocalSite && !isEditable && !e.repeat && !e.isComposing && e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "a") {
|
|
4747
|
+
e.preventDefault();
|
|
4748
|
+
startAgentSession();
|
|
4749
|
+
return;
|
|
4750
|
+
}
|
|
4598
4751
|
if (!isEditable && !e.repeat && e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "i") {
|
|
4599
4752
|
e.preventDefault();
|
|
4600
4753
|
setSession(session.inspect ? "off" : "inspect");
|
package/src/capture.js
CHANGED
|
@@ -20,8 +20,10 @@ export function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () =>
|
|
|
20
20
|
finally { clearTimeout(timer); }
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async function captureViewportScreenshot({ context = captureContext(), annotation } = {}) {
|
|
23
|
+
async function captureViewportScreenshot({ context = captureContext(), annotation, region } = {}) {
|
|
24
24
|
if (document.visibilityState === "hidden") return null;
|
|
25
|
+
const area = region || { left: 0, top: 0, width: context.width, height: context.height };
|
|
26
|
+
if (![area.left, area.top, area.width, area.height].every(Number.isFinite) || area.left < 0 || area.top < 0 || area.width <= 0 || area.height <= 0 || area.left + area.width > context.width || area.top + area.height > context.height) throw new Error("Invalid capture area");
|
|
25
27
|
const controller = new AbortController();
|
|
26
28
|
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
27
29
|
const { signal } = controller;
|
|
@@ -35,7 +37,7 @@ export function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () =>
|
|
|
35
37
|
let lastYield = performance.now();
|
|
36
38
|
const checkContext = () => {
|
|
37
39
|
signal.throwIfAborted();
|
|
38
|
-
if (!frozen && (context.url !== location.href || context.width !== innerWidth || context.height !== innerHeight || context.scrollX !== scrollX || context.scrollY !== scrollY)) {
|
|
40
|
+
if ((!frozen || region) && (context.url !== location.href || context.width !== innerWidth || context.height !== innerHeight || context.scrollX !== scrollX || context.scrollY !== scrollY)) {
|
|
39
41
|
throw new Error("Page moved before its screenshot was ready");
|
|
40
42
|
}
|
|
41
43
|
};
|
|
@@ -234,21 +236,21 @@ export function createScreenshotCapture({ apiUrl, projectKey, getHeaders = () =>
|
|
|
234
236
|
image.src = svg;
|
|
235
237
|
await untilAborted(image.decode());
|
|
236
238
|
checkContext();
|
|
237
|
-
const scale = Math.min(2, devicePixelRatio || 1, 2560 /
|
|
239
|
+
const scale = Math.min(2, devicePixelRatio || 1, 2560 / area.width, 2560 / area.height);
|
|
238
240
|
const canvas = document.createElement("canvas");
|
|
239
|
-
canvas.width = Math.round(
|
|
240
|
-
canvas.height = Math.round(
|
|
241
|
+
canvas.width = Math.max(1, Math.round(area.width * scale));
|
|
242
|
+
canvas.height = Math.max(1, Math.round(area.height * scale));
|
|
241
243
|
const ctx = canvas.getContext("2d");
|
|
242
244
|
ctx.scale(scale, scale);
|
|
243
|
-
ctx.drawImage(image,
|
|
245
|
+
ctx.drawImage(image, -area.left, -area.top);
|
|
244
246
|
let blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/png")));
|
|
245
247
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
246
|
-
if (blob.size > 2800000) {
|
|
248
|
+
if (!region && blob.size > 2800000) {
|
|
247
249
|
blob = await untilAborted(new Promise(resolve => canvas.toBlob(resolve, "image/jpeg", 0.9)));
|
|
248
250
|
if (!blob) throw new Error("Screenshot encoding failed");
|
|
249
251
|
}
|
|
250
252
|
// CSS dimensions keep the eyedropper's coordinate system independent of DPR.
|
|
251
|
-
return { blob, width: canvas.width, height: canvas.height, cssWidth:
|
|
253
|
+
return { blob, width: canvas.width, height: canvas.height, cssWidth: area.width, cssHeight: area.height };
|
|
252
254
|
} finally {
|
|
253
255
|
clearTimeout(timeout);
|
|
254
256
|
controller.abort();
|
package/src/claude.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
3
3
|
import { createServer } from 'node:http';
|
|
4
|
-
import {
|
|
4
|
+
import { realpathSync } from 'node:fs';
|
|
5
5
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
6
6
|
import { tmpdir } from 'node:os';
|
|
7
|
-
import { join
|
|
7
|
+
import { join } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { parseReceipt, receiptSchema, taskPrompt } from './codex.mjs';
|
|
10
10
|
|
|
@@ -13,7 +13,6 @@ const permissionScript = fileURLToPath(new URL('./claude-permission.mjs', import
|
|
|
13
13
|
export async function runClaude({ cwd, task, signal, approve, progress, model, executable = 'claude', args = [] }) {
|
|
14
14
|
signal.throwIfAborted();
|
|
15
15
|
cwd = realpathSync(cwd);
|
|
16
|
-
if (executable === 'claude' && existsSync(resolve(cwd, 'node_modules/.bin/claude'))) executable = resolve(cwd, 'node_modules/.bin/claude');
|
|
17
16
|
|
|
18
17
|
const token = randomBytes(32).toString('hex');
|
|
19
18
|
const sockets = new Set();
|