@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,54 @@
|
|
|
1
|
+
// Defeat symlink escapes on the raw file-serve / preview path.
|
|
2
|
+
//
|
|
3
|
+
// The lexical checks in resolveWorkspaceFilePath (path.resolve + path.relative)
|
|
4
|
+
// only prove the *string* stays under the workspace root. A symlink inside the
|
|
5
|
+
// workspace — e.g. an AI worker running `ln -s /etc/passwd creds` — passes that
|
|
6
|
+
// check but resolves to a real path outside the root, and fs.createReadStream /
|
|
7
|
+
// fs.statSync follow it. The directory/tree walkers already reject symlinks with
|
|
8
|
+
// lstat; this makes the serve path match, by resolving the real path (of the
|
|
9
|
+
// deepest existing ancestor for not-yet-created files) and re-checking containment.
|
|
10
|
+
|
|
11
|
+
import fsDefault from 'fs';
|
|
12
|
+
import pathDefault from 'path';
|
|
13
|
+
|
|
14
|
+
export function isRealPathWithinRoot(
|
|
15
|
+
absolutePath,
|
|
16
|
+
rootPath,
|
|
17
|
+
{ fs = fsDefault, path = pathDefault, platform = process.platform } = {},
|
|
18
|
+
) {
|
|
19
|
+
const target0 = String(absolutePath || '');
|
|
20
|
+
const root0 = String(rootPath || '');
|
|
21
|
+
if (!target0 || !root0) return false;
|
|
22
|
+
|
|
23
|
+
// Windows (and macOS by default) have case-insensitive filesystems, so compare
|
|
24
|
+
// the resolved paths case-insensitively there. realpathSync already
|
|
25
|
+
// canonicalizes both sides, but this defends against short (8.3) names and
|
|
26
|
+
// subst/mapped drives that can resolve to differing case.
|
|
27
|
+
const caseInsensitive = platform === 'win32' || platform === 'darwin';
|
|
28
|
+
const norm = (value) => (caseInsensitive ? String(value).toLowerCase() : String(value));
|
|
29
|
+
|
|
30
|
+
let realRoot;
|
|
31
|
+
try {
|
|
32
|
+
realRoot = fs.realpathSync(root0);
|
|
33
|
+
} catch {
|
|
34
|
+
// The root itself doesn't resolve; the lexical check is the best we have.
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let target = path.resolve(target0);
|
|
39
|
+
while (true) {
|
|
40
|
+
try {
|
|
41
|
+
const realTarget = fs.realpathSync(target);
|
|
42
|
+
const a = norm(realTarget);
|
|
43
|
+
const b = norm(realRoot);
|
|
44
|
+
return a === b || a.startsWith(`${b}${path.sep}`);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error?.code !== 'ENOENT') return false;
|
|
47
|
+
// The path doesn't exist yet — check its nearest existing ancestor, so a
|
|
48
|
+
// symlinked parent can't smuggle a not-yet-created file outside the root.
|
|
49
|
+
const parent = path.dirname(target);
|
|
50
|
+
if (parent === target) return false;
|
|
51
|
+
target = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI BYOK Capture Proxy
|
|
4
|
+
*
|
|
5
|
+
* A lightweight HTTP→HTTPS forwarding proxy that logs the full request body
|
|
6
|
+
* sent to the OpenAI API. Use this to inspect what the Copilot CLI actually
|
|
7
|
+
* sends — specifically whether `store: false` is set (which would suppress
|
|
8
|
+
* entries in platform.openai.com/logs).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node server/tools/capture-proxy.mjs [--port 3035] [--target https://api.openai.com] [--force-store-true]
|
|
12
|
+
*
|
|
13
|
+
* Then in the relay settings UI, set the OpenAI Base URL to:
|
|
14
|
+
* http://localhost:3035/v1
|
|
15
|
+
*
|
|
16
|
+
* All requests will be logged here and forwarded to OpenAI.
|
|
17
|
+
* After testing, reset the base URL back to https://api.openai.com/v1.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
import http from 'http';
|
|
23
|
+
import https from 'https';
|
|
24
|
+
import { URL } from 'url';
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const portArgIdx = args.indexOf('--port');
|
|
28
|
+
const targetArgIdx = args.indexOf('--target');
|
|
29
|
+
const forceStoreTrue = args.includes('--force-store-true');
|
|
30
|
+
const PORT = portArgIdx !== -1 ? Number(args[portArgIdx + 1]) : 3035;
|
|
31
|
+
const TARGET = targetArgIdx !== -1 ? args[targetArgIdx + 1] : 'https://api.openai.com';
|
|
32
|
+
|
|
33
|
+
const targetUrl = new URL(TARGET);
|
|
34
|
+
const isHttps = targetUrl.protocol === 'https:';
|
|
35
|
+
const targetHost = targetUrl.hostname;
|
|
36
|
+
const targetPort = targetUrl.port ? Number(targetUrl.port) : (isHttps ? 443 : 80);
|
|
37
|
+
|
|
38
|
+
let requestCount = 0;
|
|
39
|
+
|
|
40
|
+
function truncate(str, max = 4000) {
|
|
41
|
+
if (typeof str !== 'string') return str;
|
|
42
|
+
if (str.length <= max) return str;
|
|
43
|
+
return str.slice(0, max) + `\n... [truncated ${str.length - max} chars]`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function tryParseJson(str) {
|
|
47
|
+
try { return JSON.parse(str); } catch { return null; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function maybeForceStoreTrue(body) {
|
|
51
|
+
if (!forceStoreTrue || !body || typeof body !== 'object' || Array.isArray(body)) return body;
|
|
52
|
+
return { ...body, store: true };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function logRequest(reqId, method, path, headers, bodyStr) {
|
|
56
|
+
const body = tryParseJson(bodyStr);
|
|
57
|
+
console.log('\n' + '═'.repeat(72));
|
|
58
|
+
console.log(`[proxy] Request #${reqId}: ${method} ${path}`);
|
|
59
|
+
console.log(`[proxy] Host: ${headers.host || targetHost}`);
|
|
60
|
+
console.log(`[proxy] Authorization: ${headers.authorization ? headers.authorization.replace(/Bearer\s+(\S{4})\S*/, 'Bearer $1...') : '(none)'}`);
|
|
61
|
+
if (body && typeof body === 'object') {
|
|
62
|
+
// Key fields to highlight
|
|
63
|
+
const highlighted = {};
|
|
64
|
+
for (const key of ['model', 'store', 'stream', 'temperature', 'max_tokens', 'max_completion_tokens', 'reasoning_effort']) {
|
|
65
|
+
if (key in body) highlighted[key] = body[key];
|
|
66
|
+
}
|
|
67
|
+
console.log(`[proxy] KEY FIELDS: ${JSON.stringify(highlighted, null, 2)}`);
|
|
68
|
+
if (body.messages) console.log(`[proxy] messages: ${body.messages.length} message(s)`);
|
|
69
|
+
if (body.input) console.log(`[proxy] input: ${Array.isArray(body.input) ? body.input.length + ' item(s)' : typeof body.input}`);
|
|
70
|
+
if (body.tools) console.log(`[proxy] tools: ${body.tools.length} tool(s)`);
|
|
71
|
+
console.log(`[proxy] FULL BODY:\n${truncate(JSON.stringify(body, null, 2))}`);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(`[proxy] RAW BODY:\n${truncate(bodyStr)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function logResponse(reqId, statusCode, headers, bodyStr) {
|
|
78
|
+
const body = tryParseJson(bodyStr);
|
|
79
|
+
console.log(`[proxy] Response #${reqId}: HTTP ${statusCode}`);
|
|
80
|
+
if (body && typeof body === 'object') {
|
|
81
|
+
const highlighted = {};
|
|
82
|
+
for (const key of ['id', 'object', 'model', 'store', 'usage']) {
|
|
83
|
+
if (key in body) highlighted[key] = body[key];
|
|
84
|
+
}
|
|
85
|
+
console.log(`[proxy] RESPONSE KEY FIELDS: ${JSON.stringify(highlighted, null, 2)}`);
|
|
86
|
+
}
|
|
87
|
+
console.log('─'.repeat(72));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const server = http.createServer((req, res) => {
|
|
91
|
+
const reqId = ++requestCount;
|
|
92
|
+
const chunks = [];
|
|
93
|
+
|
|
94
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
95
|
+
req.on('end', () => {
|
|
96
|
+
const originalBodyStr = Buffer.concat(chunks).toString('utf8');
|
|
97
|
+
const parsedOriginal = tryParseJson(originalBodyStr);
|
|
98
|
+
const rewrittenBody = maybeForceStoreTrue(parsedOriginal);
|
|
99
|
+
const bodyStr = (
|
|
100
|
+
rewrittenBody
|
|
101
|
+
&& typeof rewrittenBody === 'object'
|
|
102
|
+
&& !Array.isArray(rewrittenBody)
|
|
103
|
+
&& rewrittenBody !== parsedOriginal
|
|
104
|
+
)
|
|
105
|
+
? JSON.stringify(rewrittenBody)
|
|
106
|
+
: originalBodyStr;
|
|
107
|
+
logRequest(reqId, req.method, req.url, req.headers, bodyStr);
|
|
108
|
+
|
|
109
|
+
const forwardHeaders = { ...req.headers };
|
|
110
|
+
forwardHeaders.host = targetHost;
|
|
111
|
+
// Remove transfer-encoding so we can send a Content-Length
|
|
112
|
+
delete forwardHeaders['transfer-encoding'];
|
|
113
|
+
const bodyBuf = Buffer.from(bodyStr, 'utf8');
|
|
114
|
+
if (bodyBuf.length > 0) {
|
|
115
|
+
forwardHeaders['content-length'] = String(bodyBuf.length);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const options = {
|
|
119
|
+
hostname: targetHost,
|
|
120
|
+
port: targetPort,
|
|
121
|
+
path: req.url,
|
|
122
|
+
method: req.method,
|
|
123
|
+
headers: forwardHeaders,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const transport = isHttps ? https : http;
|
|
127
|
+
const proxyReq = transport.request(options, (proxyRes) => {
|
|
128
|
+
const respChunks = [];
|
|
129
|
+
proxyRes.on('data', (chunk) => respChunks.push(chunk));
|
|
130
|
+
proxyRes.on('end', () => {
|
|
131
|
+
const respStr = Buffer.concat(respChunks).toString('utf8');
|
|
132
|
+
logResponse(reqId, proxyRes.statusCode, proxyRes.headers, respStr);
|
|
133
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
134
|
+
res.end(respStr);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
proxyReq.on('error', (err) => {
|
|
139
|
+
console.error(`[proxy] Forward error #${reqId}:`, err.message);
|
|
140
|
+
res.writeHead(502);
|
|
141
|
+
res.end(JSON.stringify({ error: 'proxy-forward-failed', message: err.message }));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (bodyBuf.length > 0) proxyReq.write(bodyBuf);
|
|
145
|
+
proxyReq.end();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
150
|
+
console.log('╔' + '═'.repeat(70) + '╗');
|
|
151
|
+
console.log('║ OpenAI BYOK Capture Proxy ║');
|
|
152
|
+
console.log('╠' + '═'.repeat(70) + '╣');
|
|
153
|
+
console.log(`║ Listening : http://127.0.0.1:${PORT} ║`);
|
|
154
|
+
console.log(`║ Forwarding: ${TARGET} ║`);
|
|
155
|
+
console.log('╠' + '═'.repeat(70) + '╣');
|
|
156
|
+
console.log(`║ Force store:true: ${forceStoreTrue ? 'enabled' : 'disabled'} ║`);
|
|
157
|
+
console.log('╠' + '═'.repeat(70) + '╣');
|
|
158
|
+
console.log('║ SETUP: ║');
|
|
159
|
+
console.log(`║ 1. In Relay settings → OpenAI → Base URL, set: ║`);
|
|
160
|
+
console.log(`║ http://localhost:${PORT}/v1 ║`);
|
|
161
|
+
console.log('║ 2. Start a new BYOK conversation and send any message. ║');
|
|
162
|
+
console.log('║ 3. Look for "store" in the KEY FIELDS output below. ║');
|
|
163
|
+
console.log('║ store: false → no logs in platform.openai.com ║');
|
|
164
|
+
console.log('║ store: true (or absent) → logs should appear ║');
|
|
165
|
+
console.log('║ 4. Reset Base URL to https://api.openai.com/v1 when done. ║');
|
|
166
|
+
console.log('╚' + '═'.repeat(70) + '╝\n');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
process.on('SIGINT', () => {
|
|
170
|
+
console.log('\n[proxy] Shutting down.');
|
|
171
|
+
server.close();
|
|
172
|
+
process.exit(0);
|
|
173
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
|
|
5
|
+
function existingDirectories(paths = []) {
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const candidate of paths) {
|
|
8
|
+
const normalized = String(candidate || '').trim();
|
|
9
|
+
if (!normalized) continue;
|
|
10
|
+
try {
|
|
11
|
+
if (fs.statSync(normalized).isDirectory()) out.push(normalized);
|
|
12
|
+
} catch {}
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveTtyConsoleRoot() {
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
return path.dirname(require.resolve('tty-console'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function maybeStartTtyConsole({
|
|
23
|
+
serverDir,
|
|
24
|
+
logsDir,
|
|
25
|
+
logger = console,
|
|
26
|
+
} = {}) {
|
|
27
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
|
|
28
|
+
|
|
29
|
+
const resolvedServerDir = path.resolve(String(serverDir || process.cwd()).trim() || process.cwd());
|
|
30
|
+
const resolvedLogsDir = path.resolve(String(logsDir || path.join(resolvedServerDir, 'logs')).trim() || path.join(resolvedServerDir, 'logs'));
|
|
31
|
+
fs.mkdirSync(resolvedLogsDir, { recursive: true });
|
|
32
|
+
|
|
33
|
+
let ttyConsoleModule;
|
|
34
|
+
try {
|
|
35
|
+
ttyConsoleModule = await import('tty-console');
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const code = String(error?.code || '').trim();
|
|
38
|
+
if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {
|
|
39
|
+
logger?.log?.('[server] tty-console not installed; continuing without terminal console.');
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
logger?.warn?.(`[server] tty-console load failed: ${error?.message || error}`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ttyConsoleApi = ttyConsoleModule?.default || ttyConsoleModule;
|
|
47
|
+
const ConsoleCtor = ttyConsoleApi?.Console;
|
|
48
|
+
if (typeof ConsoleCtor !== 'function') {
|
|
49
|
+
logger?.warn?.('[server] tty-console loaded but Console export was unavailable.');
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ttyConsoleRoot = resolveTtyConsoleRoot();
|
|
54
|
+
const bundledCommandsDir = path.join(ttyConsoleRoot, 'commands');
|
|
55
|
+
const localCommandsDir = path.join(resolvedServerDir, 'commands');
|
|
56
|
+
const commandsDirs = existingDirectories([bundledCommandsDir, localCommandsDir]);
|
|
57
|
+
const configPath = path.join(resolvedLogsDir, 'console_config.json');
|
|
58
|
+
const historyPath = path.join(resolvedLogsDir, 'console_history.json');
|
|
59
|
+
|
|
60
|
+
const consoleOptions = {
|
|
61
|
+
configPath,
|
|
62
|
+
historyPath,
|
|
63
|
+
exitOnStop: false,
|
|
64
|
+
};
|
|
65
|
+
if (commandsDirs.length === 1) {
|
|
66
|
+
consoleOptions.commandsDir = commandsDirs[0];
|
|
67
|
+
} else if (commandsDirs.length > 1) {
|
|
68
|
+
consoleOptions.commandsDir = commandsDirs;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const runtime = new ConsoleCtor(consoleOptions);
|
|
72
|
+
runtime.start?.();
|
|
73
|
+
logger?.log?.(`[server] tty-console started (${commandsDirs.join(', ')})`);
|
|
74
|
+
return runtime;
|
|
75
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Uploads
|
|
2
|
+
|
|
3
|
+
This directory is reserved for file uploads.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This folder serves as a temporary or persistent storage location for files uploaded through the application. All uploaded files are stored here for processing, retrieval, or archival purposes.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
- **Temporary Storage**: Files may be stored temporarily during processing
|
|
12
|
+
- **User Uploads**: User-submitted files are saved to this directory
|
|
13
|
+
- **File Access**: Uploaded files can be retrieved through the application API or interface
|
|
14
|
+
|
|
15
|
+
## Important Notes
|
|
16
|
+
|
|
17
|
+
- **Cleanup**: Old or unused files should be periodically removed to manage disk space
|
|
18
|
+
- **Security**: Ensure proper file validation and virus scanning before processing uploads
|
|
19
|
+
- **Permissions**: Configure appropriate file permissions to prevent unauthorized access
|
|
20
|
+
- **Backups**: Consider backing up important uploaded files regularly
|
|
21
|
+
|
|
22
|
+
## File Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
uploads/
|
|
26
|
+
├── README.md
|
|
27
|
+
└── [uploaded files and subdirectories]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Maintenance
|
|
31
|
+
|
|
32
|
+
Regular maintenance tasks:
|
|
33
|
+
- Monitor directory size
|
|
34
|
+
- Remove expired uploads
|
|
35
|
+
- Verify file integrity
|
|
36
|
+
- Review access logs
|