@oar-sh/oar 0.9.2 → 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/CHANGELOG.md +269 -0
- package/README.md +407 -424
- package/bin/oar.js +25 -10
- package/package.json +7 -4
- package/server/README.md +1 -1
- package/server/claude-worker/claude-session-process.mjs +981 -44
- package/server/claude-worker/claude-session-worker.mjs +45 -4
- package/server/claude-worker/claude-turn-publisher.mjs +81 -20
- package/server/claude-worker/claude-worker-link-wiring.mjs +9 -0
- package/server/copilot-worker/copilot-ask-user-tool.mjs +89 -0
- package/server/copilot-worker/copilot-continuation-signals.mjs +55 -0
- package/server/copilot-worker/copilot-question-bridge.mjs +34 -12
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +101 -12
- package/server/copilot-worker/copilot-sdk-session-process.mjs +2269 -492
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +82 -20
- package/server/db-schema.mjs +20 -0
- package/server/migrations/0004-steer-settle-markers.mjs +119 -0
- package/server/public/app/api-client.js +22 -2
- package/server/public/app/ask-user-view.js +102 -13
- package/server/public/app/attachments-view.js +37 -2
- package/server/public/app/background-task-timeout-options.mjs +9 -0
- package/server/public/app/background-tasks-view.mjs +33 -18
- package/server/public/app/bootstrap.js +34 -8
- package/server/public/app/composer-control-state.mjs +52 -34
- 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-provider-indicator.mjs +8 -0
- package/server/public/app/conversation-view.js +919 -194
- 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 +145 -5
- package/server/public/app/model-selector-options.mjs +1 -1
- package/server/public/app/question-multi-select.mjs +52 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +17 -2
- package/server/public/app/settings-modal.js +8 -6
- package/server/public/app/socket-handlers.js +19 -0
- package/server/public/app/steered-turn-merge.mjs +100 -0
- package/server/public/app/store.js +126 -15
- 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/index.html +206 -9
- package/server/public/sw.js +7 -0
- package/server/relay-tools.md +2 -0
- package/server/repositories/message-repository.mjs +61 -18
- package/server/repositories/question-repository.mjs +15 -0
- package/server/repositories/session-repository.mjs +7 -31
- package/server/routes/messages-routes.mjs +488 -46
- package/server/routes/sessions-routes.mjs +298 -55
- package/server/server-runtime.mjs +65 -25
- package/server/services/live-turn-picker.mjs +47 -0
- package/server/services/relay-prompt-sanitizer.mjs +17 -2
- package/server/services/relay-question-context.mjs +46 -0
- package/server/services/sdk-session-import-service.mjs +49 -7
- package/server/services/session-worker-registry-service.mjs +75 -0
- package/server/services/session-worker-supervisor-service.mjs +7 -1
- package/server/services/session-worker-websocket-service.mjs +23 -1
- package/server/services/steer-resend.mjs +45 -0
- package/shared/background-task-timeout.mjs +12 -4
- package/shared/control-poller.mjs +4 -1
- 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/worker-crash-guard.mjs +40 -25
- package/shared/worker-runtime/heartbeat.mjs +55 -1
- package/shared/worker-runtime/runner-link-wiring.mjs +44 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +119 -8
- package/server/claude-worker/claude-session-test-harness.mjs +0 -250
- package/server/claude-worker/fixtures/workflow-run-record.json +0 -1
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +0 -382
- 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.cmd +0 -1
- package/server/services/fixtures/claude-auth-stub.mjs +0 -76
- package/server/services/fixtures/claude-auth-stub.sh +0 -4
- package/server/services/fixtures/cli-install-stub.cmd +0 -1
- package/server/services/fixtures/cli-install-stub.mjs +0 -98
- package/server/services/fixtures/cli-install-stub.sh +0 -4
- package/server/services/fixtures/grok-stub.cmd +0 -1
- package/server/services/fixtures/grok-stub.mjs +0 -89
- package/server/services/fixtures/grok-stub.sh +0 -4
- package/shared/fixtures/copilot-catalog-raw-2026-09-07.json +0 -2363
package/bin/oar.js
CHANGED
|
@@ -17,7 +17,7 @@ function resolvePackageRoot(metaUrl = import.meta.url) {
|
|
|
17
17
|
return path.resolve(path.dirname(fileURLToPath(metaUrl)), '..');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function parsePort(argv = []) {
|
|
20
|
+
function parsePort(argv = [], fallback = 3333) {
|
|
21
21
|
const args = Array.isArray(argv) ? argv : [];
|
|
22
22
|
for (let index = 0; index < args.length; index += 1) {
|
|
23
23
|
const arg = String(args[index] || '');
|
|
@@ -30,7 +30,12 @@ function parsePort(argv = []) {
|
|
|
30
30
|
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) return parsed;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizePort(value) {
|
|
37
|
+
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
38
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : null;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
function readRelayLock(lockPath) {
|
|
@@ -184,7 +189,7 @@ function createDefaultConfig({ port = 3333, token = '' } = {}) {
|
|
|
184
189
|
};
|
|
185
190
|
}
|
|
186
191
|
|
|
187
|
-
function
|
|
192
|
+
function resolveConfigPaths({ packageRoot, env = process.env, layout = null } = {}) {
|
|
188
193
|
const envConfigPath = String(env.COPILOT_WEB_RELAY_CONFIG || '').trim();
|
|
189
194
|
const repoConfigPath = path.join(packageRoot, 'server', 'config.json');
|
|
190
195
|
const oarConfigPath = layout && !layout.checkout ? path.join(layout.configDir, 'config.json') : null;
|
|
@@ -195,7 +200,11 @@ function resolveLauncherConfig({ packageRoot, env = process.env, relayToken = ''
|
|
|
195
200
|
|| null
|
|
196
201
|
);
|
|
197
202
|
const managedPath = oarConfigPath || path.join(getLegacyManagedConfigDir(env), 'config.json');
|
|
198
|
-
|
|
203
|
+
return { seedPath, configPath: seedPath || managedPath };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function resolveLauncherConfig({ packageRoot, env = process.env, relayToken = '', relayPort = 3333, portOverride = null, layout = null } = {}) {
|
|
207
|
+
const { seedPath, configPath } = resolveConfigPaths({ packageRoot, env, layout });
|
|
199
208
|
const seedConfig = readJsonFile(seedPath) || {};
|
|
200
209
|
const runtimeConfig = {
|
|
201
210
|
...createDefaultConfig({ port: relayPort }),
|
|
@@ -203,10 +212,9 @@ function resolveLauncherConfig({ packageRoot, env = process.env, relayToken = ''
|
|
|
203
212
|
};
|
|
204
213
|
if (relayToken) runtimeConfig.authToken = relayToken;
|
|
205
214
|
if (!String(runtimeConfig.authToken || '').trim()) runtimeConfig.authToken = randomUUID();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
: 3333;
|
|
215
|
+
// An explicit --port wins over the config for this run: the server gets it
|
|
216
|
+
// on argv, and only a config this command creates saves it.
|
|
217
|
+
runtimeConfig.port = normalizePort(portOverride) ?? normalizePort(runtimeConfig.port) ?? normalizePort(relayPort) ?? 3333;
|
|
210
218
|
return {
|
|
211
219
|
configPath,
|
|
212
220
|
managed: !seedPath,
|
|
@@ -262,7 +270,7 @@ export async function launchRelay({
|
|
|
262
270
|
const forwardedArgs = separatorIdx === -1 ? [] : args.slice(separatorIdx + 1);
|
|
263
271
|
if (args.includes('--help') || args.includes('-h')) {
|
|
264
272
|
logger.log([
|
|
265
|
-
'Usage: oar [--port <port>] [--
|
|
273
|
+
'Usage: oar [--port <port>] [--migrate-from <old-checkout>] [--install-extension] [--no-install-extension] [-- [gh copilot args...]]',
|
|
266
274
|
'',
|
|
267
275
|
'Starts the web relay server if needed, then launches gh copilot in the current shell.',
|
|
268
276
|
'--install-extension installs/updates a user-global web-relay wrapper extension and exits.',
|
|
@@ -284,7 +292,7 @@ export async function launchRelay({
|
|
|
284
292
|
return { code: 0, reason: 'extension-installed' };
|
|
285
293
|
}
|
|
286
294
|
|
|
287
|
-
const
|
|
295
|
+
const cliPort = parsePort(launcherArgs, null);
|
|
288
296
|
const serverDir = path.join(packageRoot, 'server');
|
|
289
297
|
const layout = resolveStateLayout({ packageRoot, env });
|
|
290
298
|
|
|
@@ -311,6 +319,12 @@ export async function launchRelay({
|
|
|
311
319
|
const lockPath = layout.checkout
|
|
312
320
|
? path.join(serverDir, 'data', 'relay-server.lock')
|
|
313
321
|
: path.join(layout.dataDir, 'relay-server.lock');
|
|
322
|
+
// One port finds, launches and probes the relay: an explicit --port for this
|
|
323
|
+
// run, else the config's, else 3333. The probe used to take --port (or 3333)
|
|
324
|
+
// while the server started on the config's port, so any mismatch waited out
|
|
325
|
+
// the readiness deadline and killed the relay it had just started.
|
|
326
|
+
const { seedPath } = resolveConfigPaths({ packageRoot, env, layout });
|
|
327
|
+
const port = cliPort ?? normalizePort(readJsonFile(seedPath)?.port) ?? 3333;
|
|
314
328
|
const statusUrl = `http://localhost:${port}/api/status`;
|
|
315
329
|
const running = await detectRunningRelay({
|
|
316
330
|
lockPath,
|
|
@@ -323,6 +337,7 @@ export async function launchRelay({
|
|
|
323
337
|
env,
|
|
324
338
|
relayToken: running?.lock?.token || '',
|
|
325
339
|
relayPort: port,
|
|
340
|
+
portOverride: cliPort,
|
|
326
341
|
layout,
|
|
327
342
|
});
|
|
328
343
|
if (runtimeConfig.managed || !fs.existsSync(runtimeConfig.configPath)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oar-sh/oar",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OAR — Open Agent Relay: drive your local coding agents from any browser",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"bin/",
|
|
21
21
|
"server/",
|
|
22
22
|
"!server/**/*.test.mjs",
|
|
23
|
+
"!server/**/*-test-harness.mjs",
|
|
24
|
+
"!server/**/fixtures/",
|
|
23
25
|
"!server/data/",
|
|
24
26
|
"!server/logs/",
|
|
25
27
|
"!server/config.json",
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
"server/uploads/image_input.md",
|
|
29
31
|
"shared/",
|
|
30
32
|
"!shared/**/*.test.mjs",
|
|
33
|
+
"!shared/**/fixtures/",
|
|
31
34
|
".github/extensions/web-relay/",
|
|
32
35
|
"!.github/extensions/web-relay/**/*.test.mjs",
|
|
33
36
|
"README.md",
|
|
@@ -43,9 +46,9 @@
|
|
|
43
46
|
"test:e2e:raw": "node ./node_modules/@playwright/test/cli.js test --config tests/playwright.config.mjs"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
47
|
-
"@cursor/sdk": "^1.0.
|
|
48
|
-
"@github/copilot-sdk": "^1.0.
|
|
49
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.283",
|
|
50
|
+
"@cursor/sdk": "^1.0.32",
|
|
51
|
+
"@github/copilot-sdk": "^1.0.14",
|
|
49
52
|
"better-sqlite3": "^12.10.0",
|
|
50
53
|
"express": "^4.18.2",
|
|
51
54
|
"qrcode-terminal": "^0.12.0",
|
package/server/README.md
CHANGED
|
@@ -773,7 +773,7 @@ Queue metrics include `parkedCount` for turns deferred behind restart/rebind gat
|
|
|
773
773
|
| GET | `/api/search/messages` | Search message text across all conversations (`q`, `limit`, `offset`) |
|
|
774
774
|
| PATCH | `/api/conversation/:id` | Update a conversation title |
|
|
775
775
|
| POST | `/api/conversation/:id/compact` | Compact a conversation into a new one with carry-over summary seed |
|
|
776
|
-
| DELETE | `/api/conversation/:id` | Delete a conversation |
|
|
776
|
+
| DELETE | `/api/conversation/:id` | Delete a conversation: stops its session worker, deletes its CLI session, removes its rows. A Copilot session the relay only imported and never ran is hidden and tombstoned instead: nothing is stopped and its CLI session is kept. `409 conversation-active` (`reason: turn-running` / `background-tasks`) while it is still working; `409 worker-stop-failed` when its worker survives the stop (nothing deleted) |
|
|
777
777
|
| GET | `/api/sdk-session-delete/pending` | (CLI relay) Fetch next pending SDK session delete request |
|
|
778
778
|
| POST | `/api/sdk-session-delete/result` | (CLI relay) Report SDK session delete result |
|
|
779
779
|
| POST | `/api/session-sync` | (CLI relay) Sync conversation↔SDK binding and optionally confirm orchestrator rebind completion |
|