@oar-sh/oar 0.9.1 → 0.9.3
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/.github/extensions/web-relay/extension.mjs +0 -3
- package/.github/extensions/web-relay/polling/heartbeat.mjs +2 -55
- package/.github/extensions/web-relay/polling/polling-loop.mjs +0 -1
- package/.github/extensions/web-relay/runtime/api-client.mjs +2 -34
- package/.github/extensions/web-relay/runtime/config-loader.mjs +2 -104
- package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +2 -170
- package/.github/extensions/web-relay/runtime/session-io.mjs +1 -71
- package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +2 -325
- package/.github/extensions/web-relay/skills/prompt-context.mjs +2 -160
- package/CHANGELOG.md +269 -0
- package/README.md +407 -424
- package/bin/oar.js +142 -11
- package/package.json +8 -4
- package/server/README.md +59 -22
- package/server/claude-worker/claude-sdk-adapter.mjs +7 -2
- package/server/claude-worker/claude-session-process.mjs +1013 -45
- package/server/claude-worker/claude-session-worker.mjs +54 -9
- package/server/claude-worker/claude-turn-publisher.mjs +110 -19
- package/server/claude-worker/claude-worker-link-wiring.mjs +9 -0
- package/server/copilot-sdk-runtime.mjs +12 -3
- package/server/copilot-worker/copilot-ask-user-tool.mjs +89 -0
- package/server/copilot-worker/copilot-attachments.mjs +35 -2
- package/server/copilot-worker/copilot-continuation-signals.mjs +55 -0
- package/server/copilot-worker/copilot-model-switch.mjs +425 -0
- package/server/copilot-worker/copilot-prompt-context.mjs +20 -8
- package/server/copilot-worker/copilot-question-bridge.mjs +129 -16
- package/server/copilot-worker/copilot-sdk-adapter.mjs +59 -11
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +136 -12
- package/server/copilot-worker/copilot-sdk-session-process.mjs +2753 -527
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +94 -25
- package/server/cursor-worker/cursor-session-worker.mjs +5 -5
- package/server/cursor-worker/cursor-turn-runner.mjs +50 -4
- package/server/db-schema.mjs +61 -1
- package/server/features.mjs +155 -41
- package/server/grok-worker/grok-session-worker.mjs +5 -5
- package/server/grok-worker/grok-turn-runner.mjs +45 -4
- package/server/migrations/0004-steer-settle-markers.mjs +119 -0
- package/server/public/app/annotate-core.mjs +181 -0
- package/server/public/app/annotate-editor.js +617 -0
- package/server/public/app/api-client.js +86 -5
- package/server/public/app/ask-user-view.js +102 -13
- package/server/public/app/attachments-view.js +172 -3
- package/server/public/app/background-task-timeout-options.mjs +9 -0
- package/server/public/app/background-tasks-view.mjs +34 -19
- package/server/public/app/bootstrap.js +153 -69
- package/server/public/app/composer-control-state.mjs +52 -34
- package/server/public/app/context-tier-options.mjs +24 -4
- package/server/public/app/conversation-draft-sync.mjs +187 -0
- package/server/public/app/conversation-list-filter.mjs +86 -0
- package/server/public/app/conversation-preferences.mjs +47 -10
- package/server/public/app/conversation-provider-indicator.mjs +8 -0
- package/server/public/app/conversation-view.js +1032 -218
- package/server/public/app/emoji-view.js +2 -0
- package/server/public/app/font-scaling.js +4 -37
- package/server/public/app/journal-view.js +190 -24
- package/server/public/app/model-selector-options.mjs +185 -1
- package/server/public/app/new-conversation-model-choice.mjs +29 -9
- package/server/public/app/pwa-install.js +64 -136
- package/server/public/app/question-multi-select.mjs +52 -0
- package/server/public/app/reasoning-effort-labels.mjs +13 -3
- package/server/public/app/relay-prompt-sanitizer.mjs +26 -3
- package/server/public/app/router.js +120 -11
- package/server/public/app/settings-modal.js +225 -20
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +47 -4
- package/server/public/app/steered-turn-merge.mjs +100 -0
- package/server/public/app/store.js +159 -16
- package/server/public/app/sync-outbox.mjs +16 -8
- package/server/public/app/tool-activity-emoji.mjs +36 -0
- package/server/public/app/transcript-viewport-anchor.mjs +210 -0
- package/server/public/app/update-ui.js +194 -0
- package/server/public/index.html +483 -24
- package/server/public/sw.js +7 -0
- package/server/relay-tools.md +2 -0
- package/server/relay.mjs +23 -21
- package/server/repositories/message-repository.mjs +78 -21
- package/server/repositories/question-repository.mjs +19 -1
- package/server/repositories/session-repository.mjs +7 -31
- package/server/routes/ask-user-routes.mjs +10 -0
- package/server/routes/messages-routes.mjs +689 -159
- package/server/routes/sessions-routes.mjs +574 -80
- package/server/server-runtime.mjs +393 -925
- package/server/services/claude-auth-service.mjs +7 -1
- package/server/services/cli-install-service.mjs +8 -27
- package/server/services/cli-process-runner.mjs +30 -0
- package/server/services/copilot-model-discovery-service.mjs +202 -0
- package/server/services/data-dir-resolution.mjs +47 -0
- package/server/services/grok-auth-service.mjs +9 -1
- package/server/services/live-turn-picker.mjs +47 -0
- package/server/services/model-variant-catalog-service.mjs +1183 -0
- package/server/services/oar-cli-helpers.mjs +6 -2
- package/server/services/relay-prompt-sanitizer.mjs +30 -3
- package/server/services/relay-question-context.mjs +46 -0
- package/server/services/sdk-session-import-service.mjs +144 -16
- package/server/services/sdk-session-sync-service.mjs +84 -27
- package/server/services/session-worker-registry-service.mjs +116 -3
- package/server/services/session-worker-supervisor-service.mjs +7 -1
- package/server/services/session-worker-websocket-service.mjs +119 -24
- package/server/services/status-event-service.mjs +8 -2
- package/server/services/steer-resend.mjs +45 -0
- package/server/services/update-check-service.mjs +220 -0
- package/server/services/update-install-service.mjs +279 -0
- package/server/services/windows-autostart-service.mjs +42 -6
- package/server/services/windows-boot-autostart-service.mjs +487 -0
- package/shared/ask-user-bridge.mjs +37 -5
- package/shared/background-task-timeout.mjs +12 -4
- package/shared/claude-model-catalog.mjs +49 -0
- package/shared/context-window-fallbacks.mjs +1 -0
- package/shared/control-poller.mjs +4 -1
- package/shared/copilot-model-order.mjs +182 -0
- package/shared/media-embed-instructions.mjs +35 -0
- package/shared/model-descriptors.mjs +181 -30
- package/shared/model-id.mjs +6 -0
- package/shared/pwa-app-name.mjs +44 -0
- package/shared/question-multi-select.mjs +16 -0
- package/shared/relay-stop-failure.mjs +15 -0
- package/shared/slash-command-guard.mjs +76 -0
- package/shared/steer-note.mjs +66 -0
- package/shared/steer-settle-failure.mjs +89 -0
- package/shared/steer-settle-markers.mjs +18 -0
- package/shared/update-semver.mjs +50 -0
- package/shared/worker-crash-guard.mjs +40 -14
- package/shared/worker-runtime/api-client.mjs +34 -0
- package/shared/worker-runtime/config-loader.mjs +104 -0
- package/shared/worker-runtime/heartbeat.mjs +109 -0
- package/shared/worker-runtime/prompt-context.mjs +160 -0
- package/shared/worker-runtime/runner-link-wiring.mjs +44 -0
- package/shared/worker-runtime/send-and-wait-errors.mjs +170 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +436 -0
- package/server/claude-worker/claude-session-test-harness.mjs +0 -236
- package/server/claude-worker/fixtures/workflow-run-record.json +0 -1
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +0 -329
- package/server/copilot-worker/fixtures/abort-turn.json +0 -588
- package/server/copilot-worker/fixtures/ask-user-turn.json +0 -1154
- package/server/copilot-worker/fixtures/background-timer-continuation.json +0 -445
- package/server/copilot-worker/fixtures/background-timer-turn.json +0 -497
- package/server/copilot-worker/fixtures/happy-turn.json +0 -566
- package/server/copilot-worker/fixtures/quota-turn.json +0 -647
- package/server/copilot-worker/fixtures/reasoning-turn.json +0 -625
- package/server/copilot-worker/fixtures/resume-turn.json +0 -700
- package/server/copilot-worker/fixtures/subagent-turn.json +0 -1378
- package/server/copilot-worker/fixtures/tool-permission-turn.json +0 -1293
- package/server/routes/messages-routes-test-harness.mjs +0 -111
- package/server/services/fixtures/claude-auth-stub.sh +0 -62
- package/server/services/fixtures/cli-install-stub.sh +0 -75
- package/server/services/fixtures/grok-stub.sh +0 -86
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Pure annotation logic for the screenshot marker editor: palette, stroke
|
|
2
|
+
// widths, the undo stack, coordinate mapping, and the single drawing routine
|
|
3
|
+
// shared by the live overlay and the PNG export.
|
|
4
|
+
//
|
|
5
|
+
// Nothing here touches the DOM. The canvas/File constructors are injected the
|
|
6
|
+
// same way `browserDownscaleDeps` injects them in image-downscale.mjs, so the
|
|
7
|
+
// composition path can be exercised in Node.
|
|
8
|
+
|
|
9
|
+
export const ANNOTATE_COLORS = Object.freeze([
|
|
10
|
+
Object.freeze({ id: 'yellow', hex: '#ffdd00' }),
|
|
11
|
+
Object.freeze({ id: 'red', hex: '#ff3b30' }),
|
|
12
|
+
Object.freeze({ id: 'green', hex: '#34c759' }),
|
|
13
|
+
Object.freeze({ id: 'blue', hex: '#0a84ff' }),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const ANNOTATE_WIDTH_IDS = Object.freeze(['thin', 'medium', 'thick']);
|
|
17
|
+
|
|
18
|
+
// Marker translucency: dark enough to read as a highlight, light enough that
|
|
19
|
+
// black text under a mark stays legible.
|
|
20
|
+
export const MARKER_ALPHA = 0.4;
|
|
21
|
+
|
|
22
|
+
// Widths are a fraction of the image's longest side so a mark covers the same
|
|
23
|
+
// visual share of a phone screenshot and a 4K capture alike.
|
|
24
|
+
const WIDTH_FRACTIONS = Object.freeze({ thin: 0.008, medium: 0.016, thick: 0.032 });
|
|
25
|
+
const MIN_STROKE_PX = 4;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Stroke width in image pixels. Unknown ids fall back to 'medium' so a stale
|
|
29
|
+
* saved preference can never produce a zero-width (invisible) stroke.
|
|
30
|
+
*/
|
|
31
|
+
export function strokeWidthFor(widthId, imageLongestSide) {
|
|
32
|
+
const fraction = WIDTH_FRACTIONS[widthId] ?? WIDTH_FRACTIONS.medium;
|
|
33
|
+
const longest = Math.max(0, Number(imageLongestSide) || 0);
|
|
34
|
+
return Math.max(MIN_STROKE_PX, Math.round(longest * fraction));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The undo stack holds `{ kind: 'stroke', stroke }` and `{ kind: 'reset' }`
|
|
39
|
+
* entries. A reset is an entry rather than a truncation so 🗑️ stays undoable.
|
|
40
|
+
*/
|
|
41
|
+
export function createStrokeStack() {
|
|
42
|
+
return { entries: [] };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function pushStroke(stack, stroke) {
|
|
46
|
+
stack.entries.push({ kind: 'stroke', stroke });
|
|
47
|
+
return stack;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Records an undoable clear. Resetting an already-empty canvas would push an
|
|
52
|
+
* entry that undo has to eat before it can reach real work, so it is skipped.
|
|
53
|
+
*/
|
|
54
|
+
export function pushReset(stack) {
|
|
55
|
+
if (!visibleStrokes(stack).length) return false;
|
|
56
|
+
stack.entries.push({ kind: 'reset' });
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function undo(stack) {
|
|
61
|
+
if (!stack?.entries?.length) return false;
|
|
62
|
+
stack.entries.pop();
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function canUndo(stack) {
|
|
67
|
+
return Boolean(stack?.entries?.length);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Strokes drawn after the most recent reset, in draw order. */
|
|
71
|
+
export function visibleStrokes(stack) {
|
|
72
|
+
const entries = stack?.entries || [];
|
|
73
|
+
let start = 0;
|
|
74
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
75
|
+
if (entries[i]?.kind === 'reset') {
|
|
76
|
+
start = i + 1;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const out = [];
|
|
81
|
+
for (let i = start; i < entries.length; i += 1) {
|
|
82
|
+
if (entries[i]?.kind === 'stroke') out.push(entries[i].stroke);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function clamp(value, min, max) {
|
|
88
|
+
if (!(value > min)) return min;
|
|
89
|
+
return value > max ? max : value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Viewport point → image pixel. The overlay canvas carries the same CSS
|
|
94
|
+
* transform as the image, so its live bounding rect already encodes zoom and
|
|
95
|
+
* pan: no separate scale/pan bookkeeping is needed here. Points are clamped
|
|
96
|
+
* because a pointer can be dragged outside the image mid-stroke.
|
|
97
|
+
*/
|
|
98
|
+
export function clientPointToImagePoint(clientX, clientY, rect, naturalW, naturalH) {
|
|
99
|
+
const width = Math.max(0, Number(naturalW) || 0);
|
|
100
|
+
const height = Math.max(0, Number(naturalH) || 0);
|
|
101
|
+
const rectWidth = Number(rect?.width) || 0;
|
|
102
|
+
const rectHeight = Number(rect?.height) || 0;
|
|
103
|
+
const offsetX = (Number(clientX) || 0) - (Number(rect?.left) || 0);
|
|
104
|
+
const offsetY = (Number(clientY) || 0) - (Number(rect?.top) || 0);
|
|
105
|
+
return {
|
|
106
|
+
x: rectWidth > 0 ? clamp(offsetX * (width / rectWidth), 0, width) : 0,
|
|
107
|
+
y: rectHeight > 0 ? clamp(offsetY * (height / rectHeight), 0, height) : 0,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Draws strokes onto a context sized to the image's natural resolution. Used
|
|
113
|
+
* for both the live overlay and the export so what is drawn is what is saved.
|
|
114
|
+
*/
|
|
115
|
+
export function renderStrokes(ctx, strokes, { alpha = MARKER_ALPHA } = {}) {
|
|
116
|
+
if (!ctx || !Array.isArray(strokes) || !strokes.length) return;
|
|
117
|
+
const previousAlpha = ctx.globalAlpha;
|
|
118
|
+
ctx.globalAlpha = alpha;
|
|
119
|
+
try {
|
|
120
|
+
for (const stroke of strokes) {
|
|
121
|
+
const points = stroke?.points;
|
|
122
|
+
if (!Array.isArray(points) || !points.length) continue;
|
|
123
|
+
const widthPx = Math.max(1, Number(stroke.widthPx) || 1);
|
|
124
|
+
if (points.length === 1) {
|
|
125
|
+
// moveTo+lineTo to the same coordinate paints nothing in some engines,
|
|
126
|
+
// so a tap becomes an explicit dot the width of the nib.
|
|
127
|
+
ctx.beginPath();
|
|
128
|
+
ctx.fillStyle = stroke.color;
|
|
129
|
+
ctx.arc(points[0].x, points[0].y, widthPx / 2, 0, Math.PI * 2);
|
|
130
|
+
ctx.fill();
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
// One path for the whole stroke: stroking segment by segment would let a
|
|
134
|
+
// translucent marker darken everywhere it crosses itself.
|
|
135
|
+
ctx.beginPath();
|
|
136
|
+
ctx.strokeStyle = stroke.color;
|
|
137
|
+
ctx.lineWidth = widthPx;
|
|
138
|
+
ctx.lineCap = 'round';
|
|
139
|
+
ctx.lineJoin = 'round';
|
|
140
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
141
|
+
for (let i = 1; i < points.length; i += 1) ctx.lineTo(points[i].x, points[i].y);
|
|
142
|
+
ctx.stroke();
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
ctx.globalAlpha = previousAlpha;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function annotatedFileName(originalName) {
|
|
150
|
+
const base = String(originalName || '').trim().replace(/\.[^./\\]+$/, '') || 'screenshot';
|
|
151
|
+
return `${base}-annotated.png`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Flattens the decoded image plus its strokes into a PNG `File` at natural
|
|
156
|
+
* resolution. Returns null when the injected APIs are missing or the encode
|
|
157
|
+
* produces nothing; decode/encode exceptions propagate so the editor can show a
|
|
158
|
+
* notice and stay open. The bitmap belongs to the caller and is never closed.
|
|
159
|
+
*/
|
|
160
|
+
export async function composeAnnotatedImage({ bitmap, strokes, name }, deps = {}) {
|
|
161
|
+
const { createCanvas, createFile } = deps;
|
|
162
|
+
if (!bitmap) return null;
|
|
163
|
+
if (typeof createCanvas !== 'function' || typeof createFile !== 'function') return null;
|
|
164
|
+
|
|
165
|
+
const width = Math.max(0, Math.round(Number(bitmap.width) || 0));
|
|
166
|
+
const height = Math.max(0, Math.round(Number(bitmap.height) || 0));
|
|
167
|
+
if (!width || !height) return null;
|
|
168
|
+
|
|
169
|
+
const canvas = createCanvas(width, height);
|
|
170
|
+
const context = canvas?.getContext?.('2d');
|
|
171
|
+
if (!context) return null;
|
|
172
|
+
|
|
173
|
+
context.drawImage(bitmap, 0, 0, width, height);
|
|
174
|
+
renderStrokes(context, strokes);
|
|
175
|
+
|
|
176
|
+
const blob = await canvas.convertToBlob?.({ type: 'image/png' });
|
|
177
|
+
if (!blob) return null;
|
|
178
|
+
// createFile matches browserDownscaleDeps' shape — (blob, name, type), the
|
|
179
|
+
// dep itself wraps the blob into the File parts array.
|
|
180
|
+
return createFile(blob, annotatedFileName(name), 'image/png') || null;
|
|
181
|
+
}
|