@oar-sh/oar 0.9.0
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/README.md +190 -0
- package/.github/extensions/web-relay/extension.mjs +972 -0
- package/.github/extensions/web-relay/model-api/model-switching.mjs +338 -0
- package/.github/extensions/web-relay/polling/heartbeat.mjs +55 -0
- package/.github/extensions/web-relay/polling/polling-loop.mjs +1105 -0
- package/.github/extensions/web-relay/runtime/api-client.mjs +34 -0
- package/.github/extensions/web-relay/runtime/banner-state.mjs +126 -0
- package/.github/extensions/web-relay/runtime/config-loader.mjs +104 -0
- package/.github/extensions/web-relay/runtime/debug-log.mjs +27 -0
- package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +170 -0
- package/.github/extensions/web-relay/runtime/session-binding.mjs +78 -0
- package/.github/extensions/web-relay/runtime/session-io.mjs +349 -0
- package/.github/extensions/web-relay/runtime/session-join-retry.mjs +44 -0
- package/.github/extensions/web-relay/runtime/session-registry.mjs +29 -0
- package/.github/extensions/web-relay/runtime/session-runtime-manager.mjs +156 -0
- package/.github/extensions/web-relay/runtime/session-sync-bridge.mjs +74 -0
- package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +325 -0
- package/.github/extensions/web-relay/runtime/workspace-root.mjs +55 -0
- package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +386 -0
- package/.github/extensions/web-relay/skills/prompt-context.mjs +160 -0
- package/.github/extensions/web-relay/skills/question-bridge.mjs +141 -0
- package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +463 -0
- package/.github/extensions/web-relay/skills/reasoning-stream.mjs +285 -0
- package/.github/extensions/web-relay/skills/subagent-lifecycle.mjs +345 -0
- package/.github/extensions/web-relay/skills/tool-activity.mjs +243 -0
- package/.github/extensions/web-relay/utils/process-utils.mjs +25 -0
- package/.github/extensions/web-relay/utils/token-generation.mjs +58 -0
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +717 -0
- package/bin/bat/oar.bat +46 -0
- package/bin/oar.js +665 -0
- package/package.json +68 -0
- package/server/README.md +1379 -0
- package/server/claude-worker/claude-attachments.mjs +134 -0
- package/server/claude-worker/claude-preview-tool.mjs +57 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +420 -0
- package/server/claude-worker/claude-session-process.mjs +2391 -0
- package/server/claude-worker/claude-session-test-harness.mjs +236 -0
- package/server/claude-worker/claude-session-worker.mjs +186 -0
- package/server/claude-worker/claude-transcript-relocator.mjs +189 -0
- package/server/claude-worker/claude-turn-publisher.mjs +291 -0
- package/server/claude-worker/fixtures/workflow-run-record.json +1 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +629 -0
- package/server/claude-worker/workflow-progress-digest.mjs +269 -0
- package/server/copilot-sdk-runtime.mjs +126 -0
- package/server/copilot-worker/copilot-attachments.mjs +97 -0
- package/server/copilot-worker/copilot-byok-provider.mjs +121 -0
- package/server/copilot-worker/copilot-continuation-signals.mjs +349 -0
- package/server/copilot-worker/copilot-plan-board.mjs +110 -0
- package/server/copilot-worker/copilot-prompt-context.mjs +86 -0
- package/server/copilot-worker/copilot-question-bridge.mjs +216 -0
- package/server/copilot-worker/copilot-sdk-adapter.mjs +494 -0
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +800 -0
- package/server/copilot-worker/copilot-sdk-session-process.mjs +1729 -0
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +167 -0
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +329 -0
- package/server/copilot-worker/fixtures/abort-turn.json +588 -0
- package/server/copilot-worker/fixtures/ask-user-turn.json +1154 -0
- package/server/copilot-worker/fixtures/background-timer-continuation.json +445 -0
- package/server/copilot-worker/fixtures/background-timer-turn.json +497 -0
- package/server/copilot-worker/fixtures/happy-turn.json +566 -0
- package/server/copilot-worker/fixtures/quota-turn.json +647 -0
- package/server/copilot-worker/fixtures/reasoning-turn.json +625 -0
- package/server/copilot-worker/fixtures/resume-turn.json +700 -0
- package/server/copilot-worker/fixtures/subagent-turn.json +1378 -0
- package/server/copilot-worker/fixtures/tool-permission-turn.json +1293 -0
- package/server/cursor-worker/cursor-ask-user-tool.mjs +83 -0
- package/server/cursor-worker/cursor-attachments.mjs +88 -0
- package/server/cursor-worker/cursor-context-usage.mjs +85 -0
- package/server/cursor-worker/cursor-preview-tool.mjs +44 -0
- package/server/cursor-worker/cursor-sdk-adapter.mjs +554 -0
- package/server/cursor-worker/cursor-session-worker.mjs +145 -0
- package/server/cursor-worker/cursor-subagent-roster.mjs +81 -0
- package/server/cursor-worker/cursor-turn-runner.mjs +717 -0
- package/server/cursor-worker/sdk-message-normalizer.mjs +485 -0
- package/server/db-schema.mjs +932 -0
- package/server/features.mjs +106 -0
- package/server/grok-worker/acp-client.mjs +434 -0
- package/server/grok-worker/acp-host-services.mjs +277 -0
- package/server/grok-worker/grok-context-usage.mjs +77 -0
- package/server/grok-worker/grok-sdk-adapter.mjs +334 -0
- package/server/grok-worker/grok-session-worker.mjs +145 -0
- package/server/grok-worker/grok-turn-runner.mjs +523 -0
- package/server/grok-worker/sdk-message-normalizer.mjs +311 -0
- package/server/migrations/0001-add-structured-answer.mjs +80 -0
- package/server/migrations/0002-recent-workspace-roots-path-key.mjs +121 -0
- package/server/migrations/0003-push-subscriptions.mjs +87 -0
- package/server/public/app/action-confirmations.js +267 -0
- package/server/public/app/activity-replay-state.mjs +149 -0
- package/server/public/app/api-client.js +1082 -0
- package/server/public/app/ask-user-view.js +545 -0
- package/server/public/app/attachments-view.js +2112 -0
- package/server/public/app/auto-compact-window-options.mjs +59 -0
- package/server/public/app/background-tasks-view.mjs +494 -0
- package/server/public/app/bootstrap.js +4347 -0
- package/server/public/app/claude-auth-ui.js +394 -0
- package/server/public/app/claude-thinking-options.mjs +38 -0
- package/server/public/app/cli-install-ui.js +455 -0
- package/server/public/app/code-copy.mjs +85 -0
- package/server/public/app/composer-attachment-cache.mjs +151 -0
- package/server/public/app/composer-control-state.mjs +107 -0
- package/server/public/app/composer-paste.mjs +132 -0
- package/server/public/app/context-tier-options.mjs +68 -0
- package/server/public/app/context-usage-view.mjs +338 -0
- package/server/public/app/conversation-draft-timestamp-utils.mjs +11 -0
- package/server/public/app/conversation-preferences.mjs +91 -0
- package/server/public/app/conversation-provider-indicator.mjs +88 -0
- package/server/public/app/conversation-view.js +2898 -0
- package/server/public/app/cwd-menu-placement.mjs +85 -0
- package/server/public/app/cwd-picker.js +605 -0
- package/server/public/app/emoji-view.js +101 -0
- package/server/public/app/external-link-policy.mjs +80 -0
- package/server/public/app/file-preview-navigation.mjs +51 -0
- package/server/public/app/font-scaling.js +262 -0
- package/server/public/app/git-changes-view.js +339 -0
- package/server/public/app/git-diff-model.mjs +109 -0
- package/server/public/app/grok-auth-ui.js +387 -0
- package/server/public/app/image-downscale.mjs +174 -0
- package/server/public/app/in-flight-snapshot.mjs +40 -0
- package/server/public/app/infinite-loader.js +164 -0
- package/server/public/app/journal-view.js +1141 -0
- package/server/public/app/known-cwd-options.mjs +41 -0
- package/server/public/app/launch-cwd-path.mjs +23 -0
- package/server/public/app/live-message-dedupe.mjs +57 -0
- package/server/public/app/message-search-view.js +242 -0
- package/server/public/app/model-selector-options.mjs +31 -0
- package/server/public/app/model-vision-support.mjs +56 -0
- package/server/public/app/new-conversation-model-choice.mjs +67 -0
- package/server/public/app/plan-usage-view.mjs +307 -0
- package/server/public/app/preview-cards.mjs +273 -0
- package/server/public/app/preview-command.mjs +103 -0
- package/server/public/app/push-settings.js +370 -0
- package/server/public/app/pwa-install.js +456 -0
- package/server/public/app/question-schema-view.mjs +71 -0
- package/server/public/app/reasoning-effort-labels.mjs +42 -0
- package/server/public/app/relay-board-view.js +117 -0
- package/server/public/app/relay-dot-state.mjs +69 -0
- package/server/public/app/relay-error-ctas.mjs +52 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +114 -0
- package/server/public/app/relay-thoughts.mjs +98 -0
- package/server/public/app/relay-watchdog-policy.mjs +29 -0
- package/server/public/app/repo-browser-preferences.mjs +79 -0
- package/server/public/app/repo-browser-tree-state.mjs +97 -0
- package/server/public/app/router.js +657 -0
- package/server/public/app/selection-guard.mjs +93 -0
- package/server/public/app/settings-modal.js +1484 -0
- package/server/public/app/settings-tabs.js +129 -0
- package/server/public/app/slash-autocomplete.mjs +188 -0
- package/server/public/app/slash-commands.mjs +183 -0
- package/server/public/app/socket-handlers.js +703 -0
- package/server/public/app/status-store.mjs +242 -0
- package/server/public/app/status-view.mjs +315 -0
- package/server/public/app/store.js +1454 -0
- package/server/public/app/stream-state.mjs +73 -0
- package/server/public/app/streaming-dom-patch.mjs +23 -0
- package/server/public/app/sync-outbox.mjs +208 -0
- package/server/public/app/tap-actions.js +62 -0
- package/server/public/app/thread-order.mjs +54 -0
- package/server/public/app/tmux-inspector-view.js +422 -0
- package/server/public/app/transcript-separators.mjs +282 -0
- package/server/public/app-icon-192.png +0 -0
- package/server/public/app-icon-512.png +0 -0
- package/server/public/app-icon.svg +4 -0
- package/server/public/favicon.ico +0 -0
- package/server/public/index.html +5743 -0
- package/server/public/manifest.webmanifest +32 -0
- package/server/public/sw.js +307 -0
- package/server/relay-exit-codes.mjs +4 -0
- package/server/relay-self-restart.mjs +170 -0
- package/server/relay-tools.md +27 -0
- package/server/relay.mjs +1026 -0
- package/server/repositories/image-conversation-repository.mjs +317 -0
- package/server/repositories/message-repository.mjs +434 -0
- package/server/repositories/question-repository.mjs +161 -0
- package/server/repositories/session-repository.mjs +320 -0
- package/server/repositories/share-visibility-statements.mjs +24 -0
- package/server/routes/ask-user-routes.mjs +292 -0
- package/server/routes/cache-routes.mjs +44 -0
- package/server/routes/git-routes.mjs +92 -0
- package/server/routes/messages-routes-test-harness.mjs +111 -0
- package/server/routes/messages-routes.mjs +7107 -0
- package/server/routes/preview-routes.mjs +108 -0
- package/server/routes/push-routes.mjs +59 -0
- package/server/routes/relay-board-routes.mjs +268 -0
- package/server/routes/sessions-routes.mjs +6150 -0
- package/server/server-runtime.mjs +7463 -0
- package/server/server.js +24 -0
- package/server/services/ask-user-routing-service.mjs +117 -0
- package/server/services/cache-rebuild-service.mjs +236 -0
- package/server/services/claude-auth-service.mjs +578 -0
- package/server/services/claude-context-usage.mjs +240 -0
- package/server/services/claude-session-root-service.mjs +228 -0
- package/server/services/cli-install-service.mjs +1152 -0
- package/server/services/cli-process-runner.mjs +181 -0
- package/server/services/cloudflared-tunnel-service.mjs +358 -0
- package/server/services/context-snapshot-service.mjs +499 -0
- package/server/services/context-usage-view.mjs +160 -0
- package/server/services/conversation-preferences-service.mjs +208 -0
- package/server/services/cursor-dashboard-usage.mjs +217 -0
- package/server/services/cursor-session-root-service.mjs +62 -0
- package/server/services/delete-archive-service.mjs +154 -0
- package/server/services/drives-path-helpers.mjs +66 -0
- package/server/services/fixtures/claude-auth-stub.sh +62 -0
- package/server/services/fixtures/cli-install-stub.sh +75 -0
- package/server/services/fixtures/grok-stub.sh +86 -0
- package/server/services/generated-image-cleanup-service.mjs +114 -0
- package/server/services/git-changes-service.mjs +195 -0
- package/server/services/github-billing-usage.mjs +133 -0
- package/server/services/grok-auth-service.mjs +593 -0
- package/server/services/grok-billing-usage.mjs +120 -0
- package/server/services/image-context-service.mjs +106 -0
- package/server/services/image-operation-service.mjs +135 -0
- package/server/services/keyed-mutex-service.mjs +82 -0
- package/server/services/mime-sniffer.mjs +118 -0
- package/server/services/oar-cli-helpers.mjs +109 -0
- package/server/services/oar-state-migration-service.mjs +243 -0
- package/server/services/plan-usage-claude.mjs +387 -0
- package/server/services/plan-usage-contract.mjs +271 -0
- package/server/services/plan-usage-copilot.mjs +421 -0
- package/server/services/plan-usage-cursor.mjs +566 -0
- package/server/services/plan-usage-grok.mjs +436 -0
- package/server/services/plan-usage-service.mjs +487 -0
- package/server/services/preview-health-probe.mjs +82 -0
- package/server/services/preview-proxy-server.mjs +498 -0
- package/server/services/preview-registry-service.mjs +457 -0
- package/server/services/preview-static-handler.mjs +237 -0
- package/server/services/provider-model-selection.mjs +91 -0
- package/server/services/provider-reasoning-effort.mjs +94 -0
- package/server/services/push-active-device-service.mjs +57 -0
- package/server/services/push-dispatch-service.mjs +459 -0
- package/server/services/rate-limit-service.mjs +63 -0
- package/server/services/relay-bridge-owner-service.mjs +84 -0
- package/server/services/relay-cli-launcher-service.mjs +209 -0
- package/server/services/relay-prompt-sanitizer.mjs +137 -0
- package/server/services/relay-queue-gate-service.mjs +38 -0
- package/server/services/relay-restart-orchestrator-service.mjs +935 -0
- package/server/services/relay-singleton-guard.mjs +124 -0
- package/server/services/safe-served-content.mjs +57 -0
- package/server/services/sdk-session-import-service.mjs +271 -0
- package/server/services/sdk-session-sync-service.mjs +207 -0
- package/server/services/session-history-refresh-service.mjs +497 -0
- package/server/services/session-transcript-service.mjs +419 -0
- package/server/services/session-worker-launch-service.mjs +786 -0
- package/server/services/session-worker-process-service.mjs +319 -0
- package/server/services/session-worker-registry-service.mjs +194 -0
- package/server/services/session-worker-stop-service.mjs +141 -0
- package/server/services/session-worker-supervisor-service.mjs +898 -0
- package/server/services/session-worker-websocket-service.mjs +483 -0
- package/server/services/ssh-tunnel-manager-service.mjs +423 -0
- package/server/services/status-event-service.mjs +158 -0
- package/server/services/tmux-inspector-access-policy.mjs +77 -0
- package/server/services/tmux-inspector-socket-service.mjs +191 -0
- package/server/services/tmux-inspector-stream-service.mjs +552 -0
- package/server/services/tunnel-worker-path-guard.mjs +107 -0
- package/server/services/upload-sweep.mjs +60 -0
- package/server/services/usage-snapshot-helpers.mjs +209 -0
- package/server/services/windows-autostart-service.mjs +115 -0
- package/server/services/workspace-root-defaults-service.mjs +34 -0
- package/server/services/workspace-root-path-policy.mjs +196 -0
- package/server/services/workspace-root-relaunch-service.mjs +132 -0
- package/server/services/workspace-symlink-guard.mjs +54 -0
- package/server/tools/capture-proxy.mjs +173 -0
- package/server/tty-console-bootstrap.mjs +75 -0
- package/server/uploads/README.md +36 -0
- package/server/uploads/image_input.md +540 -0
- package/server/windows-terminal-launcher.mjs +29 -0
- package/server/workspace-root.mjs +87 -0
- package/shared/ask-user-bridge.mjs +131 -0
- package/shared/auto-compact-window.mjs +116 -0
- package/shared/background-task-timeout.mjs +52 -0
- package/shared/claude-thinking.mjs +101 -0
- package/shared/context-window-fallbacks.mjs +148 -0
- package/shared/control-poller.mjs +115 -0
- package/shared/empty-turn-completion.mjs +20 -0
- package/shared/model-catalog-freshness.mjs +11 -0
- package/shared/model-descriptors.mjs +118 -0
- package/shared/model-id.mjs +147 -0
- package/shared/model-refresh.mjs +24 -0
- package/shared/openai-reasoning.mjs +13 -0
- package/shared/plan-lines.mjs +13 -0
- package/shared/preview-instructions.mjs +77 -0
- package/shared/preview-tool-core.mjs +191 -0
- package/shared/provider-routing.mjs +29 -0
- package/shared/question-schema.mjs +315 -0
- package/shared/question-timeout.mjs +18 -0
- package/shared/stream-emit-gating.mjs +28 -0
- package/shared/subagent-run-id.mjs +17 -0
- package/shared/thought-cap.mjs +13 -0
- package/shared/tool-activity.mjs +98 -0
- package/shared/turn-ceiling.mjs +53 -0
- package/shared/worker-bootstrap.mjs +44 -0
- package/shared/worker-crash-guard.mjs +60 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
function normalizeText(value) {
|
|
2
|
+
const text = String(value || "").trim();
|
|
3
|
+
return text || null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function normalizePositiveInt(value) {
|
|
7
|
+
const numeric = Number(value);
|
|
8
|
+
if (!Number.isFinite(numeric)) return null;
|
|
9
|
+
const intValue = Math.trunc(numeric);
|
|
10
|
+
return intValue > 0 ? intValue : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveSocketStateValue(socket, stateName, fallback) {
|
|
14
|
+
const candidates = [
|
|
15
|
+
socket?.[stateName],
|
|
16
|
+
socket?.constructor?.[stateName],
|
|
17
|
+
globalThis?.WebSocket?.[stateName],
|
|
18
|
+
];
|
|
19
|
+
for (const value of candidates) {
|
|
20
|
+
const numeric = Number(value);
|
|
21
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
22
|
+
}
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toWebSocketUrl(serverUrl, token, getSessionId, getPid) {
|
|
27
|
+
const source = new URL(String(serverUrl || "http://localhost:3333"));
|
|
28
|
+
source.protocol = source.protocol === "https:" ? "wss:" : "ws:";
|
|
29
|
+
source.pathname = "/api/session-worker/ws";
|
|
30
|
+
source.search = "";
|
|
31
|
+
if (token) source.searchParams.set("token", token);
|
|
32
|
+
const sessionId = normalizeText(typeof getSessionId === "function" ? getSessionId() : null);
|
|
33
|
+
if (sessionId) source.searchParams.set("sessionId", sessionId);
|
|
34
|
+
const pid = normalizePositiveInt(typeof getPid === "function" ? getPid() : null);
|
|
35
|
+
if (pid) source.searchParams.set("pid", String(pid));
|
|
36
|
+
return source.toString();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createWorkerWebSocketLink({
|
|
40
|
+
serverUrl,
|
|
41
|
+
token,
|
|
42
|
+
dbg = () => {},
|
|
43
|
+
onDeliver = async () => {},
|
|
44
|
+
// Fire-and-forget control pushes from the server (e.g. stop a background
|
|
45
|
+
// task). Never blocks delivery; failures are the handler's to log.
|
|
46
|
+
onControl = () => {},
|
|
47
|
+
getSessionReady = () => false,
|
|
48
|
+
getSessionId = () => null,
|
|
49
|
+
getPid = () => null,
|
|
50
|
+
minBackoffMs = 1000,
|
|
51
|
+
maxBackoffMs = 8_000,
|
|
52
|
+
jitterMs = 250,
|
|
53
|
+
readyRefreshMs = 10_000,
|
|
54
|
+
staleConnectionMs = 30_000,
|
|
55
|
+
WebSocketImpl = globalThis.WebSocket,
|
|
56
|
+
setTimeoutImpl = setTimeout,
|
|
57
|
+
clearTimeoutImpl = clearTimeout,
|
|
58
|
+
setIntervalImpl = setInterval,
|
|
59
|
+
clearIntervalImpl = clearInterval,
|
|
60
|
+
now = () => Date.now(),
|
|
61
|
+
} = {}) {
|
|
62
|
+
let ws = null;
|
|
63
|
+
let stopped = false;
|
|
64
|
+
let reconnectTimer = null;
|
|
65
|
+
let readyRefreshTimer = null;
|
|
66
|
+
let reconnectAttempt = 0;
|
|
67
|
+
let reconnectDelayMs = 0;
|
|
68
|
+
let deliveryInFlight = null;
|
|
69
|
+
let lastOpenAt = null;
|
|
70
|
+
let lastMessageAt = null;
|
|
71
|
+
let lastHelloSentAt = null;
|
|
72
|
+
let lastReadySentAt = null;
|
|
73
|
+
let lastPingSentAt = null;
|
|
74
|
+
let lastPongAt = null;
|
|
75
|
+
let lastQueueChangedAt = null;
|
|
76
|
+
let lastDeliveryAt = null;
|
|
77
|
+
|
|
78
|
+
function getNowMs() {
|
|
79
|
+
const value = Number(now());
|
|
80
|
+
return Number.isFinite(value) ? value : Date.now();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isSocketOpen() {
|
|
84
|
+
return !!ws && ws.readyState === resolveSocketStateValue(ws, "OPEN", 1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function send(payload) {
|
|
88
|
+
if (!isSocketOpen()) return false;
|
|
89
|
+
try {
|
|
90
|
+
ws.send(JSON.stringify(payload));
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function notifyHello(reason = "worker-hello") {
|
|
98
|
+
if (!getSessionReady()) return false;
|
|
99
|
+
const sent = send({
|
|
100
|
+
type: "worker.hello",
|
|
101
|
+
reason,
|
|
102
|
+
sessionId: normalizeText(getSessionId()),
|
|
103
|
+
pid: normalizePositiveInt(getPid()),
|
|
104
|
+
});
|
|
105
|
+
if (sent) lastHelloSentAt = getNowMs();
|
|
106
|
+
return sent;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function notifyReady(reason = "worker-ready") {
|
|
110
|
+
if (!getSessionReady() || deliveryInFlight) return false;
|
|
111
|
+
const sent = send({
|
|
112
|
+
type: "worker.ready",
|
|
113
|
+
reason,
|
|
114
|
+
sessionId: normalizeText(getSessionId()),
|
|
115
|
+
pid: normalizePositiveInt(getPid()),
|
|
116
|
+
});
|
|
117
|
+
if (sent) lastReadySentAt = getNowMs();
|
|
118
|
+
return sent;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function notifyPing(reason = "readiness-refresh") {
|
|
122
|
+
if (!getSessionReady()) return false;
|
|
123
|
+
const sent = send({
|
|
124
|
+
type: "worker.ping",
|
|
125
|
+
reason,
|
|
126
|
+
sessionId: normalizeText(getSessionId()),
|
|
127
|
+
pid: normalizePositiveInt(getPid()),
|
|
128
|
+
});
|
|
129
|
+
if (sent) lastPingSentAt = getNowMs();
|
|
130
|
+
return sent;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function deliverPending(pending, reason = "queue-deliver") {
|
|
134
|
+
if (!getSessionReady()) return false;
|
|
135
|
+
if (deliveryInFlight) return deliveryInFlight;
|
|
136
|
+
deliveryInFlight = Promise.resolve()
|
|
137
|
+
.then(() => onDeliver(pending, reason))
|
|
138
|
+
.catch((error) => {
|
|
139
|
+
dbg("worker ws delivery failed", reason, error?.message || String(error));
|
|
140
|
+
return false;
|
|
141
|
+
})
|
|
142
|
+
.finally(() => {
|
|
143
|
+
lastDeliveryAt = getNowMs();
|
|
144
|
+
deliveryInFlight = null;
|
|
145
|
+
});
|
|
146
|
+
return deliveryInFlight;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function clearReconnectTimer() {
|
|
150
|
+
if (!reconnectTimer) return;
|
|
151
|
+
clearTimeoutImpl(reconnectTimer);
|
|
152
|
+
reconnectTimer = null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function clearReadyRefreshTimer() {
|
|
156
|
+
if (!readyRefreshTimer) return;
|
|
157
|
+
clearIntervalImpl(readyRefreshTimer);
|
|
158
|
+
readyRefreshTimer = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function closeStaleSocket(reason) {
|
|
162
|
+
if (!isSocketOpen()) return false;
|
|
163
|
+
dbg("worker ws stale connection closing", reason);
|
|
164
|
+
try { ws?.close?.(); } catch {}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function refreshReadiness(reason = "readiness-refresh") {
|
|
169
|
+
if (stopped || !getSessionReady() || !isSocketOpen()) return false;
|
|
170
|
+
const nowMs = getNowMs();
|
|
171
|
+
const staleMs = Math.max(0, Number(staleConnectionMs) || 0);
|
|
172
|
+
if (staleMs > 0 && lastMessageAt && (nowMs - lastMessageAt) > staleMs) {
|
|
173
|
+
return closeStaleSocket(`no-server-message:${reason}`);
|
|
174
|
+
}
|
|
175
|
+
notifyPing(reason);
|
|
176
|
+
if (deliveryInFlight) return true;
|
|
177
|
+
notifyHello(reason);
|
|
178
|
+
void notifyReady(reason);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function startReadyRefreshTimer() {
|
|
183
|
+
clearReadyRefreshTimer();
|
|
184
|
+
const intervalMs = Math.max(1000, Number(readyRefreshMs) || 10_000);
|
|
185
|
+
readyRefreshTimer = setIntervalImpl(() => {
|
|
186
|
+
refreshReadiness("readiness-refresh");
|
|
187
|
+
}, intervalMs);
|
|
188
|
+
if (typeof readyRefreshTimer?.unref === "function") readyRefreshTimer.unref();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function scheduleReconnect() {
|
|
192
|
+
if (stopped) return;
|
|
193
|
+
clearReconnectTimer();
|
|
194
|
+
reconnectAttempt += 1;
|
|
195
|
+
const backoff = Math.min(maxBackoffMs, minBackoffMs * (2 ** Math.max(0, reconnectAttempt - 1)));
|
|
196
|
+
const jitter = Math.floor(Math.random() * Math.max(0, jitterMs));
|
|
197
|
+
reconnectDelayMs = Math.min(maxBackoffMs, backoff + jitter);
|
|
198
|
+
dbg("worker ws reconnect scheduled", `attempt=${reconnectAttempt}`, `delayMs=${reconnectDelayMs}`);
|
|
199
|
+
reconnectTimer = setTimeoutImpl(() => {
|
|
200
|
+
reconnectTimer = null;
|
|
201
|
+
connect();
|
|
202
|
+
}, reconnectDelayMs);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function connect() {
|
|
206
|
+
if (stopped) return;
|
|
207
|
+
if (ws) {
|
|
208
|
+
const openState = resolveSocketStateValue(ws, "OPEN", 1);
|
|
209
|
+
const connectingState = resolveSocketStateValue(ws, "CONNECTING", 0);
|
|
210
|
+
if (ws.readyState === openState || ws.readyState === connectingState) return;
|
|
211
|
+
}
|
|
212
|
+
if (typeof WebSocketImpl !== "function") {
|
|
213
|
+
dbg("worker ws unavailable: global WebSocket is missing");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const url = toWebSocketUrl(serverUrl, token, getSessionId, getPid);
|
|
217
|
+
ws = new WebSocketImpl(url);
|
|
218
|
+
ws.addEventListener("open", () => {
|
|
219
|
+
const openedAt = getNowMs();
|
|
220
|
+
reconnectAttempt = 0;
|
|
221
|
+
reconnectDelayMs = 0;
|
|
222
|
+
lastOpenAt = openedAt;
|
|
223
|
+
lastMessageAt = openedAt;
|
|
224
|
+
dbg("worker ws connected");
|
|
225
|
+
notifyHello("ws-open");
|
|
226
|
+
void notifyReady("ws-open");
|
|
227
|
+
startReadyRefreshTimer();
|
|
228
|
+
});
|
|
229
|
+
ws.addEventListener("message", (event) => {
|
|
230
|
+
lastMessageAt = getNowMs();
|
|
231
|
+
let payload = null;
|
|
232
|
+
try {
|
|
233
|
+
payload = JSON.parse(String(event?.data || ""));
|
|
234
|
+
} catch {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (payload?.type === "queue.deliver") {
|
|
238
|
+
void deliverPending(payload.pending || null, String(payload.reason || "queue-deliver"))
|
|
239
|
+
.finally(() => notifyReady("delivery-complete"));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (payload?.type === "server.hello") {
|
|
243
|
+
void notifyReady(String(payload.reason || "server-hello"));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (payload?.type === "server.pong") {
|
|
247
|
+
lastPongAt = getNowMs();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (payload?.type === "server.draining") {
|
|
251
|
+
dbg("worker ws draining", payload.reason || "relay-shutdown");
|
|
252
|
+
try { ws?.close?.(); } catch {}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (payload?.type === "queue.changed") {
|
|
256
|
+
lastQueueChangedAt = getNowMs();
|
|
257
|
+
void notifyReady("queue-changed");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (payload?.type === "worker.control") {
|
|
261
|
+
try {
|
|
262
|
+
void onControl(payload.control || null);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
dbg("worker ws control handler failed", error?.message || String(error));
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (payload?.type === "queue.blocked") {
|
|
269
|
+
dbg("worker ws blocked", payload.reason || "blocked");
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
ws.addEventListener("close", () => {
|
|
273
|
+
clearReadyRefreshTimer();
|
|
274
|
+
if (stopped) return;
|
|
275
|
+
dbg("worker ws disconnected");
|
|
276
|
+
scheduleReconnect();
|
|
277
|
+
});
|
|
278
|
+
ws.addEventListener("error", (error) => {
|
|
279
|
+
dbg("worker ws error", error?.message || String(error));
|
|
280
|
+
try { ws?.close?.(); } catch {}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function start() {
|
|
285
|
+
stopped = false;
|
|
286
|
+
connect();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function stop() {
|
|
290
|
+
stopped = true;
|
|
291
|
+
clearReconnectTimer();
|
|
292
|
+
clearReadyRefreshTimer();
|
|
293
|
+
if (ws) {
|
|
294
|
+
try { ws.close(); } catch {}
|
|
295
|
+
ws = null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function status() {
|
|
300
|
+
// Some WebSocket implementations expose OPEN only on the constructor —
|
|
301
|
+
// the same reason every other check here goes through the resolver.
|
|
302
|
+
const connected = !!ws && ws.readyState === resolveSocketStateValue(ws, "OPEN", 1);
|
|
303
|
+
return {
|
|
304
|
+
connected,
|
|
305
|
+
delivering: !!deliveryInFlight,
|
|
306
|
+
reconnectAttempt,
|
|
307
|
+
reconnectDelayMs,
|
|
308
|
+
lastOpenAt,
|
|
309
|
+
lastMessageAt,
|
|
310
|
+
lastHelloSentAt,
|
|
311
|
+
lastReadySentAt,
|
|
312
|
+
lastPingSentAt,
|
|
313
|
+
lastPongAt,
|
|
314
|
+
lastQueueChangedAt,
|
|
315
|
+
lastDeliveryAt,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
notifyReady,
|
|
321
|
+
start,
|
|
322
|
+
stop,
|
|
323
|
+
status,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
function normalizePathValue(value) {
|
|
2
|
+
const text = String(value || "").trim();
|
|
3
|
+
return text || null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function firstPath(...candidates) {
|
|
7
|
+
for (const candidate of candidates) {
|
|
8
|
+
const text = normalizePathValue(candidate);
|
|
9
|
+
if (text) return text;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function nestedValue(source, keys = []) {
|
|
15
|
+
if (!source || typeof source !== "object") return null;
|
|
16
|
+
let current = source;
|
|
17
|
+
for (const key of keys) {
|
|
18
|
+
if (!current || typeof current !== "object" || !(key in current)) return null;
|
|
19
|
+
current = current[key];
|
|
20
|
+
}
|
|
21
|
+
return current;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveWorkspaceRootPath({
|
|
25
|
+
session = null,
|
|
26
|
+
env = process.env,
|
|
27
|
+
cwd = process.cwd(),
|
|
28
|
+
includeProcessCwd = true,
|
|
29
|
+
} = {}) {
|
|
30
|
+
const resolved = firstPath(
|
|
31
|
+
env?.COPILOT_WORKSPACE_ROOT,
|
|
32
|
+
env?.GITHUB_COPILOT_WORKSPACE_ROOT,
|
|
33
|
+
nestedValue(session, ["workspaceRootPath"]),
|
|
34
|
+
nestedValue(session, ["workspace_root_path"]),
|
|
35
|
+
nestedValue(session, ["cwd"]),
|
|
36
|
+
nestedValue(session, ["workingDirectory"]),
|
|
37
|
+
nestedValue(session, ["currentWorkingDirectory"]),
|
|
38
|
+
nestedValue(session, ["context", "workspaceRootPath"]),
|
|
39
|
+
nestedValue(session, ["context", "workspace_root_path"]),
|
|
40
|
+
nestedValue(session, ["context", "cwd"]),
|
|
41
|
+
nestedValue(session, ["context", "workingDirectory"]),
|
|
42
|
+
nestedValue(session, ["context", "currentWorkingDirectory"]),
|
|
43
|
+
nestedValue(session, ["state", "workspaceRootPath"]),
|
|
44
|
+
nestedValue(session, ["state", "workspace_root_path"]),
|
|
45
|
+
nestedValue(session, ["state", "cwd"]),
|
|
46
|
+
nestedValue(session, ["state", "workingDirectory"]),
|
|
47
|
+
nestedValue(session, ["state", "currentWorkingDirectory"]),
|
|
48
|
+
env?.INIT_CWD,
|
|
49
|
+
env?.PWD,
|
|
50
|
+
env?.ORIGINAL_PWD,
|
|
51
|
+
);
|
|
52
|
+
if (resolved) return resolved;
|
|
53
|
+
if (!includeProcessCwd) return null;
|
|
54
|
+
return normalizePathValue(cwd);
|
|
55
|
+
}
|