@bd7pil/ocrc 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/LICENSE +34 -0
- package/README.md +330 -0
- package/dist/cli/host-backends.js +216 -0
- package/dist/cli/host-backends.js.map +1 -0
- package/dist/cli/host.js +185 -0
- package/dist/cli/host.js.map +1 -0
- package/dist/cli/index.js +67 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.js +68 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/install.js +220 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/pair.js +11 -0
- package/dist/cli/pair.js.map +1 -0
- package/dist/cli/uninstall.js +75 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/connectivity/auth/cf-access.js +20 -0
- package/dist/connectivity/auth/cf-access.js.map +1 -0
- package/dist/connectivity/auth/index.js +2 -0
- package/dist/connectivity/auth/index.js.map +1 -0
- package/dist/connectivity/auth/select.js +19 -0
- package/dist/connectivity/auth/select.js.map +1 -0
- package/dist/connectivity/auth/token.js +157 -0
- package/dist/connectivity/auth/token.js.map +1 -0
- package/dist/connectivity/exposure/cloudflared.js +77 -0
- package/dist/connectivity/exposure/cloudflared.js.map +1 -0
- package/dist/connectivity/exposure/index.js +2 -0
- package/dist/connectivity/exposure/index.js.map +1 -0
- package/dist/connectivity/exposure/providers.js +49 -0
- package/dist/connectivity/exposure/providers.js.map +1 -0
- package/dist/connectivity/pairing.js +29 -0
- package/dist/connectivity/pairing.js.map +1 -0
- package/dist/core/agent/acp-backend.js +539 -0
- package/dist/core/agent/acp-backend.js.map +1 -0
- package/dist/core/agent/acp-connect.js +92 -0
- package/dist/core/agent/acp-connect.js.map +1 -0
- package/dist/core/agent/acp-normalizer.js +81 -0
- package/dist/core/agent/acp-normalizer.js.map +1 -0
- package/dist/core/agent/acp-replay.js +89 -0
- package/dist/core/agent/acp-replay.js.map +1 -0
- package/dist/core/agent/acp-store.js +129 -0
- package/dist/core/agent/acp-store.js.map +1 -0
- package/dist/core/agent/backend.js +2 -0
- package/dist/core/agent/backend.js.map +1 -0
- package/dist/core/agent/diff-util.js +70 -0
- package/dist/core/agent/diff-util.js.map +1 -0
- package/dist/core/agent/event.js +12 -0
- package/dist/core/agent/event.js.map +1 -0
- package/dist/core/agent/opencode-backend.js +598 -0
- package/dist/core/agent/opencode-backend.js.map +1 -0
- package/dist/core/agent/opencode-normalizer.js +69 -0
- package/dist/core/agent/opencode-normalizer.js.map +1 -0
- package/dist/core/agent/registry.js +55 -0
- package/dist/core/agent/registry.js.map +1 -0
- package/dist/core/agent/v2-backend.js +176 -0
- package/dist/core/agent/v2-backend.js.map +1 -0
- package/dist/core/card-bus.js +74 -0
- package/dist/core/card-bus.js.map +1 -0
- package/dist/core/channels.js +95 -0
- package/dist/core/channels.js.map +1 -0
- package/dist/core/history.js +91 -0
- package/dist/core/history.js.map +1 -0
- package/dist/core/list-files.js +50 -0
- package/dist/core/list-files.js.map +1 -0
- package/dist/core/opencode-events.js +29 -0
- package/dist/core/opencode-events.js.map +1 -0
- package/dist/core/primary-election.js +91 -0
- package/dist/core/primary-election.js.map +1 -0
- package/dist/core/push.js +152 -0
- package/dist/core/push.js.map +1 -0
- package/dist/core/relay.js +400 -0
- package/dist/core/relay.js.map +1 -0
- package/dist/core/scheduler.js +150 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/state.js +222 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/stream-accumulator.js +76 -0
- package/dist/core/stream-accumulator.js.map +1 -0
- package/dist/core/structured-card.js +2 -0
- package/dist/core/structured-card.js.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/opencode/global-events.js +57 -0
- package/dist/opencode/global-events.js.map +1 -0
- package/dist/opencode/list-sessions.js +122 -0
- package/dist/opencode/list-sessions.js.map +1 -0
- package/dist/opencode/submit.js +39 -0
- package/dist/opencode/submit.js.map +1 -0
- package/dist/opencode/workspaces.js +51 -0
- package/dist/opencode/workspaces.js.map +1 -0
- package/dist/plugin/config.js +128 -0
- package/dist/plugin/config.js.map +1 -0
- package/dist/plugin/control-plane.js +133 -0
- package/dist/plugin/control-plane.js.map +1 -0
- package/dist/plugin/entry.js +409 -0
- package/dist/plugin/entry.js.map +1 -0
- package/dist/plugin/v2/event-map.js +193 -0
- package/dist/plugin/v2/event-map.js.map +1 -0
- package/dist/plugin/v2/types.js +15 -0
- package/dist/plugin/v2/types.js.map +1 -0
- package/dist/transport/interface.js +2 -0
- package/dist/transport/interface.js.map +1 -0
- package/dist/transport/telegram/esc.js +9 -0
- package/dist/transport/telegram/esc.js.map +1 -0
- package/dist/transport/telegram/handlers/info-commands.js +101 -0
- package/dist/transport/telegram/handlers/info-commands.js.map +1 -0
- package/dist/transport/telegram/handlers.js +1348 -0
- package/dist/transport/telegram/handlers.js.map +1 -0
- package/dist/transport/telegram/i18n/en.js +538 -0
- package/dist/transport/telegram/i18n/en.js.map +1 -0
- package/dist/transport/telegram/i18n/index.js +83 -0
- package/dist/transport/telegram/i18n/index.js.map +1 -0
- package/dist/transport/telegram/i18n/zh.js +538 -0
- package/dist/transport/telegram/i18n/zh.js.map +1 -0
- package/dist/transport/telegram/index.js +792 -0
- package/dist/transport/telegram/index.js.map +1 -0
- package/dist/transport/telegram/main-keyboard.js +36 -0
- package/dist/transport/telegram/main-keyboard.js.map +1 -0
- package/dist/transport/telegram/managers/abort-suppression-manager.js +33 -0
- package/dist/transport/telegram/managers/abort-suppression-manager.js.map +1 -0
- package/dist/transport/telegram/managers/external-input-suppression-manager.js +51 -0
- package/dist/transport/telegram/managers/external-input-suppression-manager.js.map +1 -0
- package/dist/transport/telegram/managers/interaction-manager.js +254 -0
- package/dist/transport/telegram/managers/interaction-manager.js.map +1 -0
- package/dist/transport/telegram/managers/permission-manager.js +242 -0
- package/dist/transport/telegram/managers/permission-manager.js.map +1 -0
- package/dist/transport/telegram/managers/question-manager.js +191 -0
- package/dist/transport/telegram/managers/question-manager.js.map +1 -0
- package/dist/transport/telegram/managers/rename-manager.js +56 -0
- package/dist/transport/telegram/managers/rename-manager.js.map +1 -0
- package/dist/transport/telegram/menus.js +77 -0
- package/dist/transport/telegram/menus.js.map +1 -0
- package/dist/transport/telegram/permission-flow.js +156 -0
- package/dist/transport/telegram/permission-flow.js.map +1 -0
- package/dist/transport/telegram/question-flow.js +224 -0
- package/dist/transport/telegram/question-flow.js.map +1 -0
- package/dist/transport/telegram/render/block-parser.js +365 -0
- package/dist/transport/telegram/render/block-parser.js.map +1 -0
- package/dist/transport/telegram/render/block-plain-text.js +87 -0
- package/dist/transport/telegram/render/block-plain-text.js.map +1 -0
- package/dist/transport/telegram/render/block-splitter.js +152 -0
- package/dist/transport/telegram/render/block-splitter.js.map +1 -0
- package/dist/transport/telegram/render/chunker.js +71 -0
- package/dist/transport/telegram/render/chunker.js.map +1 -0
- package/dist/transport/telegram/render/limits.js +13 -0
- package/dist/transport/telegram/render/limits.js.map +1 -0
- package/dist/transport/telegram/render/markdown-normalizer.js +102 -0
- package/dist/transport/telegram/render/markdown-normalizer.js.map +1 -0
- package/dist/transport/telegram/render/markdown-to-telegram-v2.js +116 -0
- package/dist/transport/telegram/render/markdown-to-telegram-v2.js.map +1 -0
- package/dist/transport/telegram/render/part-signature.js +14 -0
- package/dist/transport/telegram/render/part-signature.js.map +1 -0
- package/dist/transport/telegram/render/pipeline.js +23 -0
- package/dist/transport/telegram/render/pipeline.js.map +1 -0
- package/dist/transport/telegram/render/rich-blocks.js +311 -0
- package/dist/transport/telegram/render/rich-blocks.js.map +1 -0
- package/dist/transport/telegram/render/text-splitter.js +64 -0
- package/dist/transport/telegram/render/text-splitter.js.map +1 -0
- package/dist/transport/telegram/render/types.js +2 -0
- package/dist/transport/telegram/render/types.js.map +1 -0
- package/dist/transport/telegram/renderer.js +305 -0
- package/dist/transport/telegram/renderer.js.map +1 -0
- package/dist/transport/telegram/streaming/duration-formatter.js +68 -0
- package/dist/transport/telegram/streaming/duration-formatter.js.map +1 -0
- package/dist/transport/telegram/streaming/finalize-assistant-response.js +37 -0
- package/dist/transport/telegram/streaming/finalize-assistant-response.js.map +1 -0
- package/dist/transport/telegram/streaming/response-streamer.js +400 -0
- package/dist/transport/telegram/streaming/response-streamer.js.map +1 -0
- package/dist/transport/telegram/streaming/running-tool-tracker.js +150 -0
- package/dist/transport/telegram/streaming/running-tool-tracker.js.map +1 -0
- package/dist/transport/telegram/streaming/stream-throttle.js +57 -0
- package/dist/transport/telegram/streaming/stream-throttle.js.map +1 -0
- package/dist/transport/telegram/streaming/tool-call-streamer.js +390 -0
- package/dist/transport/telegram/streaming/tool-call-streamer.js.map +1 -0
- package/dist/transport/telegram/streaming-render.js +182 -0
- package/dist/transport/telegram/streaming-render.js.map +1 -0
- package/dist/transport/telegram/types/interaction.js +2 -0
- package/dist/transport/telegram/types/interaction.js.map +1 -0
- package/dist/transport/telegram/types/model.js +25 -0
- package/dist/transport/telegram/types/model.js.map +1 -0
- package/dist/transport/telegram/types/permission.js +2 -0
- package/dist/transport/telegram/types/permission.js.map +1 -0
- package/dist/transport/telegram/types/question.js +2 -0
- package/dist/transport/telegram/types/question.js.map +1 -0
- package/dist/transport/telegram/types/rename.js +2 -0
- package/dist/transport/telegram/types/rename.js.map +1 -0
- package/dist/transport/telegram/types/scheduled-task.js +23 -0
- package/dist/transport/telegram/types/scheduled-task.js.map +1 -0
- package/dist/transport/telegram/types/session.js +2 -0
- package/dist/transport/telegram/types/session.js.map +1 -0
- package/dist/transport/telegram/ui.js +28 -0
- package/dist/transport/telegram/ui.js.map +1 -0
- package/dist/transport/web/index.js +148 -0
- package/dist/transport/web/index.js.map +1 -0
- package/dist/transport/web/middleware/cf-access.js +104 -0
- package/dist/transport/web/middleware/cf-access.js.map +1 -0
- package/dist/transport/web/opencode-config.js +15 -0
- package/dist/transport/web/opencode-config.js.map +1 -0
- package/dist/transport/web/routes/abort.js +12 -0
- package/dist/transport/web/routes/abort.js.map +1 -0
- package/dist/transport/web/routes/approval.js +16 -0
- package/dist/transport/web/routes/approval.js.map +1 -0
- package/dist/transport/web/routes/catalog.js +11 -0
- package/dist/transport/web/routes/catalog.js.map +1 -0
- package/dist/transport/web/routes/channels.js +53 -0
- package/dist/transport/web/routes/channels.js.map +1 -0
- package/dist/transport/web/routes/commands.js +15 -0
- package/dist/transport/web/routes/commands.js.map +1 -0
- package/dist/transport/web/routes/context.js +17 -0
- package/dist/transport/web/routes/context.js.map +1 -0
- package/dist/transport/web/routes/controls.js +34 -0
- package/dist/transport/web/routes/controls.js.map +1 -0
- package/dist/transport/web/routes/create-session.js +21 -0
- package/dist/transport/web/routes/create-session.js.map +1 -0
- package/dist/transport/web/routes/diff.js +7 -0
- package/dist/transport/web/routes/diff.js.map +1 -0
- package/dist/transport/web/routes/files.js +22 -0
- package/dist/transport/web/routes/files.js.map +1 -0
- package/dist/transport/web/routes/logs.js +9 -0
- package/dist/transport/web/routes/logs.js.map +1 -0
- package/dist/transport/web/routes/m8.js +69 -0
- package/dist/transport/web/routes/m8.js.map +1 -0
- package/dist/transport/web/routes/mcp.js +7 -0
- package/dist/transport/web/routes/mcp.js.map +1 -0
- package/dist/transport/web/routes/message.js +32 -0
- package/dist/transport/web/routes/message.js.map +1 -0
- package/dist/transport/web/routes/overrides.js +20 -0
- package/dist/transport/web/routes/overrides.js.map +1 -0
- package/dist/transport/web/routes/question.js +32 -0
- package/dist/transport/web/routes/question.js.map +1 -0
- package/dist/transport/web/routes/rename.js +12 -0
- package/dist/transport/web/routes/rename.js.map +1 -0
- package/dist/transport/web/routes/schedules.js +28 -0
- package/dist/transport/web/routes/schedules.js.map +1 -0
- package/dist/transport/web/routes/session.js +20 -0
- package/dist/transport/web/routes/session.js.map +1 -0
- package/dist/transport/web/routes/sessions.js +25 -0
- package/dist/transport/web/routes/sessions.js.map +1 -0
- package/dist/transport/web/routes/subagents.js +11 -0
- package/dist/transport/web/routes/subagents.js.map +1 -0
- package/dist/transport/web/routes/suggestions.js +7 -0
- package/dist/transport/web/routes/suggestions.js.map +1 -0
- package/dist/transport/web/routes/todo.js +7 -0
- package/dist/transport/web/routes/todo.js.map +1 -0
- package/dist/transport/web/routes/version.js +5 -0
- package/dist/transport/web/routes/version.js.map +1 -0
- package/dist/transport/web/routes/workspaces.js +7 -0
- package/dist/transport/web/routes/workspaces.js.map +1 -0
- package/dist/transport/web/server.js +111 -0
- package/dist/transport/web/server.js.map +1 -0
- package/dist/transport/web/session-summary.js +46 -0
- package/dist/transport/web/session-summary.js.map +1 -0
- package/dist/transport/web/ws-hub.js +83 -0
- package/dist/transport/web/ws-hub.js.map +1 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.js +129 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/version.js +44 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +91 -0
- package/web/dist/_app/env.js +1 -0
- package/web/dist/_app/immutable/assets/0.DxN3RsuG.css +1 -0
- package/web/dist/_app/immutable/assets/2.BNuGfmZP.css +1 -0
- package/web/dist/_app/immutable/assets/3.CTDprntu.css +1 -0
- package/web/dist/_app/immutable/chunks/BUpwHpMJ.js +1 -0
- package/web/dist/_app/immutable/chunks/BjY4dxcB.js +2 -0
- package/web/dist/_app/immutable/chunks/C2FuqD3s.js +1 -0
- package/web/dist/_app/immutable/chunks/CUHGq08X.js +2 -0
- package/web/dist/_app/immutable/chunks/CWj6FrbW.js +1 -0
- package/web/dist/_app/immutable/chunks/ClvoqtGj.js +1 -0
- package/web/dist/_app/immutable/chunks/DIEDQ_hk.js +1 -0
- package/web/dist/_app/immutable/entry/app.BoRpcVMj.js +2 -0
- package/web/dist/_app/immutable/entry/start.Cj-PQNTq.js +1 -0
- package/web/dist/_app/immutable/nodes/0.y9YEms5p.js +3 -0
- package/web/dist/_app/immutable/nodes/1.CPhhiRM2.js +1 -0
- package/web/dist/_app/immutable/nodes/2.B_c84Mc9.js +1 -0
- package/web/dist/_app/immutable/nodes/3.ClTd0J3D.js +64 -0
- package/web/dist/_app/version.json +1 -0
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/favicon.png +0 -0
- package/web/dist/favicon.svg +17 -0
- package/web/dist/icon-192.png +0 -0
- package/web/dist/icon-512-maskable.png +0 -0
- package/web/dist/icon-512.png +0 -0
- package/web/dist/index.html +73 -0
- package/web/dist/manifest.webmanifest +28 -0
- package/web/dist/service-worker.js +1 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
6
|
+
const OPENCODE_CONFIG_DIR = process.env.OPENCODE_CONFIG_DIR ?? join(homedir(), '.config', 'opencode');
|
|
7
|
+
const BRIDGE_FILE = join(OPENCODE_CONFIG_DIR, 'plugins', 'ocrc.js');
|
|
8
|
+
const GLOBAL_OPENCODE_JSON = join(OPENCODE_CONFIG_DIR, 'opencode.json');
|
|
9
|
+
/** Remove any legacy directory-path / package entry from opencode.json. */
|
|
10
|
+
function removeLegacyEntry() {
|
|
11
|
+
if (!existsSync(GLOBAL_OPENCODE_JSON))
|
|
12
|
+
return false;
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
config = JSON.parse(readFileSync(GLOBAL_OPENCODE_JSON, 'utf-8'));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const plugins = config.plugin ?? [];
|
|
21
|
+
const filtered = plugins.filter((e) => {
|
|
22
|
+
const name = Array.isArray(e) ? e[0] : e;
|
|
23
|
+
return !(name === REPO_ROOT || name.startsWith('opencode-remote-control'));
|
|
24
|
+
});
|
|
25
|
+
if (filtered.length === plugins.length)
|
|
26
|
+
return false;
|
|
27
|
+
if (filtered.length > 0)
|
|
28
|
+
config.plugin = filtered;
|
|
29
|
+
else
|
|
30
|
+
delete config.plugin;
|
|
31
|
+
writeFileSync(GLOBAL_OPENCODE_JSON, JSON.stringify(config, null, 2) + '\n');
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
export async function runUninstall() {
|
|
35
|
+
let removed = false;
|
|
36
|
+
if (existsSync(BRIDGE_FILE)) {
|
|
37
|
+
rmSync(BRIDGE_FILE);
|
|
38
|
+
console.log(`Removed plugin bridge: ${BRIDGE_FILE}`);
|
|
39
|
+
removed = true;
|
|
40
|
+
}
|
|
41
|
+
if (removeLegacyEntry()) {
|
|
42
|
+
console.log(`Removed legacy plugin entry from ${GLOBAL_OPENCODE_JSON}`);
|
|
43
|
+
removed = true;
|
|
44
|
+
}
|
|
45
|
+
if (!removed) {
|
|
46
|
+
console.log('opencode-remote-control was not installed (no bridge or config entry found).');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
console.log('\nUninstalled. Restart opencode to apply.');
|
|
50
|
+
console.log(`(Your .env at ${join(REPO_ROOT, '.env')} was left untouched.)`);
|
|
51
|
+
}
|
|
52
|
+
export async function main() {
|
|
53
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
54
|
+
console.log(`
|
|
55
|
+
opencode-remote-control uninstall (opencode 1.17+)
|
|
56
|
+
|
|
57
|
+
USAGE:
|
|
58
|
+
node dist/cli/uninstall.js
|
|
59
|
+
|
|
60
|
+
WHAT IT DOES:
|
|
61
|
+
- Removes the plugin bridge from ~/.config/opencode/plugins/
|
|
62
|
+
- Removes any legacy directory-path entry from opencode.json
|
|
63
|
+
- Leaves your .env untouched
|
|
64
|
+
`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await runUninstall();
|
|
68
|
+
}
|
|
69
|
+
if (process.argv[1]?.endsWith('uninstall.js') || process.argv[1]?.endsWith('uninstall.ts')) {
|
|
70
|
+
main().catch((err) => {
|
|
71
|
+
console.error(err);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/cli/uninstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRemoteJWKSet } from 'jose';
|
|
2
|
+
import { cfAccessMiddleware, verifyUpgradeJwt } from '../../transport/web/middleware/cf-access.js';
|
|
3
|
+
import { createLogger } from '../../utils/logger.js';
|
|
4
|
+
const log = createLogger('auth-cf-access');
|
|
5
|
+
/** Wrap the existing Cloudflare Access middleware/verifier as an AuthStrategy. */
|
|
6
|
+
export function createCfAccessAuth(opts) {
|
|
7
|
+
if (opts.devBypass) {
|
|
8
|
+
log.warn('⚠ CF Access devBypass is ON — requests from a loopback peer skip authentication entirely. ' +
|
|
9
|
+
'If cloudflared (or any same-host tunnel/proxy) fronts this server, ALL remote traffic arrives ' +
|
|
10
|
+
'from 127.0.0.1 and is let through UNAUTHENTICATED. Never combine devBypass with a tunnel.');
|
|
11
|
+
}
|
|
12
|
+
const jwks = opts.team
|
|
13
|
+
? createRemoteJWKSet(new URL(`https://${opts.team}.cloudflareaccess.com/cdn-cgi/access/certs`))
|
|
14
|
+
: undefined;
|
|
15
|
+
return {
|
|
16
|
+
httpMiddleware: () => cfAccessMiddleware(opts),
|
|
17
|
+
verifyUpgrade: (req) => verifyUpgradeJwt(req, opts, jwks),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=cf-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cf-access.js","sourceRoot":"","sources":["../../../src/connectivity/auth/cf-access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AACzC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAqB,MAAM,6CAA6C,CAAA;AACrH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAA;AAE1C,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CACN,4FAA4F;YAC5F,gGAAgG;YAChG,2FAA2F,CAC5F,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI;QACpB,CAAC,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,4CAA4C,CAAC,CAAC;QAC/F,CAAC,CAAC,SAAS,CAAA;IACb,OAAO;QACL,cAAc,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC;QAC9C,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;KAC1D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/connectivity/auth/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createTokenAuth } from './token.js';
|
|
2
|
+
import { createCfAccessAuth } from './cf-access.js';
|
|
3
|
+
export function selectAuthStrategy(sel) {
|
|
4
|
+
if (sel.mode === 'cf-access') {
|
|
5
|
+
const cf = sel.cfAccess;
|
|
6
|
+
if (!cf?.team || !cf?.aud) {
|
|
7
|
+
throw new Error('WEB_AUTH=cf-access requires WEB_CF_ACCESS_TEAM and WEB_CF_ACCESS_AUD to be set');
|
|
8
|
+
}
|
|
9
|
+
return createCfAccessAuth(cf);
|
|
10
|
+
}
|
|
11
|
+
return createTokenAuth({
|
|
12
|
+
token: sel.token,
|
|
13
|
+
tokenPath: sel.tokenPath,
|
|
14
|
+
devEmail: sel.devEmail,
|
|
15
|
+
devBypass: sel.devBypass,
|
|
16
|
+
host: sel.host,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../../src/connectivity/auth/select.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAanD,MAAM,UAAU,kBAAkB,CAAC,GAAkB;IACnD,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAA;QACvB,IAAI,CAAC,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAA;QACnG,CAAC;QACD,OAAO,kBAAkB,CAAC,EAAE,CAAC,CAAA;IAC/B,CAAC;IACD,OAAO,eAAe,CAAC;QACrB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync } from 'node:fs';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import { createLogger } from '../../utils/logger.js';
|
|
5
|
+
import { ocrcHome, legacyOpencodeHome } from '../../utils/paths.js';
|
|
6
|
+
const log = createLogger('auth-token');
|
|
7
|
+
const COOKIE = 'ocrc_token';
|
|
8
|
+
function defaultTokenPath() {
|
|
9
|
+
return join(ocrcHome(), 'token');
|
|
10
|
+
}
|
|
11
|
+
/** One-time migration: a token persisted by the pre-fork build at
|
|
12
|
+
* ${OPENCODE_CONFIG_DIR ?? ~/.opencode}/oprc-token is adopted so an upstream
|
|
13
|
+
* install keeps its paired devices after upgrading to ocrc. */
|
|
14
|
+
function adoptLegacyToken() {
|
|
15
|
+
const legacy = join(legacyOpencodeHome(), 'oprc-token');
|
|
16
|
+
try {
|
|
17
|
+
if (!existsSync(legacy))
|
|
18
|
+
return null;
|
|
19
|
+
const t = readFileSync(legacy, 'utf-8').trim();
|
|
20
|
+
if (!t)
|
|
21
|
+
return null;
|
|
22
|
+
const dest = defaultTokenPath();
|
|
23
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
24
|
+
writeFileSync(dest, t, { mode: 0o600 });
|
|
25
|
+
chmodSync(dest, 0o600);
|
|
26
|
+
log.info(`adopted legacy token from ${legacy} → ${dest}`);
|
|
27
|
+
return t;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the web token: explicit > existing file > legacy migration > freshly generated (persisted 0600). */
|
|
34
|
+
export function loadOrCreateToken(opts = {}) {
|
|
35
|
+
if (opts.token && opts.token.trim())
|
|
36
|
+
return opts.token.trim();
|
|
37
|
+
const path = opts.tokenPath ?? defaultTokenPath();
|
|
38
|
+
try {
|
|
39
|
+
if (existsSync(path)) {
|
|
40
|
+
const t = readFileSync(path, 'utf-8').trim();
|
|
41
|
+
if (t)
|
|
42
|
+
return t;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* fall through to generate */
|
|
47
|
+
}
|
|
48
|
+
const adopted = adoptLegacyToken();
|
|
49
|
+
if (adopted)
|
|
50
|
+
return adopted;
|
|
51
|
+
const token = randomBytes(32).toString('base64url');
|
|
52
|
+
try {
|
|
53
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
54
|
+
writeFileSync(path, token, { mode: 0o600 });
|
|
55
|
+
try {
|
|
56
|
+
chmodSync(path, 0o600);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* best effort */
|
|
60
|
+
}
|
|
61
|
+
log.info(`generated web token at ${path}`);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
log.warn(`could not persist token: ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
return token;
|
|
67
|
+
}
|
|
68
|
+
function isLoopbackAddr(addr) {
|
|
69
|
+
if (!addr)
|
|
70
|
+
return false;
|
|
71
|
+
const clean = addr.replace(/^::ffff:/, '').split('%')[0];
|
|
72
|
+
return clean === '127.0.0.1' || clean === '::1';
|
|
73
|
+
}
|
|
74
|
+
function tokenMatches(candidate, expected) {
|
|
75
|
+
if (!candidate)
|
|
76
|
+
return false;
|
|
77
|
+
const a = Buffer.from(candidate);
|
|
78
|
+
const b = Buffer.from(expected);
|
|
79
|
+
if (a.length !== b.length)
|
|
80
|
+
return false;
|
|
81
|
+
return timingSafeEqual(a, b);
|
|
82
|
+
}
|
|
83
|
+
function extractToken(headers, url) {
|
|
84
|
+
const auth = headers['authorization'];
|
|
85
|
+
if (typeof auth === 'string' && auth.startsWith('Bearer '))
|
|
86
|
+
return auth.slice(7).trim();
|
|
87
|
+
// Query-string tokens are accepted ONLY for the WS upgrade path (/ws) — the
|
|
88
|
+
// caller decides by passing `url`. Browsers can't set headers on a WebSocket,
|
|
89
|
+
// but a ?token= on a normal URL would leak into history/referer logs.
|
|
90
|
+
if (url && url.startsWith('/ws') && url.includes('?')) {
|
|
91
|
+
const q = new URLSearchParams(url.split('?')[1]);
|
|
92
|
+
const t = q.get('token');
|
|
93
|
+
if (t)
|
|
94
|
+
return t;
|
|
95
|
+
}
|
|
96
|
+
const cookie = headers['cookie'];
|
|
97
|
+
if (typeof cookie === 'string') {
|
|
98
|
+
const m = cookie.match(new RegExp(`(?:^|;\\s*)${COOKIE}=([^;]+)`));
|
|
99
|
+
if (m)
|
|
100
|
+
return decodeURIComponent(m[1]);
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
export function createTokenAuth(opts) {
|
|
105
|
+
const expected = loadOrCreateToken(opts);
|
|
106
|
+
const user = { email: opts.devEmail ?? 'you@local', sub: 'token' };
|
|
107
|
+
if (opts.devBypass) {
|
|
108
|
+
log.warn('⚠ token-auth devBypass is ON — requests from a loopback peer skip the token check entirely. ' +
|
|
109
|
+
'If cloudflared (or any same-host tunnel/proxy) fronts this server, ALL remote traffic arrives ' +
|
|
110
|
+
'from 127.0.0.1 and is let through UNAUTHENTICATED. Never combine devBypass with a tunnel.');
|
|
111
|
+
}
|
|
112
|
+
const bypass = (peer) => {
|
|
113
|
+
if (!opts.devBypass)
|
|
114
|
+
return false;
|
|
115
|
+
const peerKnown = peer !== undefined && peer !== '';
|
|
116
|
+
return peerKnown ? isLoopbackAddr(peer) : isLoopbackAddr(opts.host);
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
httpMiddleware() {
|
|
120
|
+
return async (c, next) => {
|
|
121
|
+
// c.env.incoming is the Node IncomingMessage (set by @hono/node-server); c.req.raw is the web-standard Request. Peer may be undefined under some adapters → bypass() then falls back to host.
|
|
122
|
+
const peer = (() => {
|
|
123
|
+
try {
|
|
124
|
+
return (c.env?.incoming ?? c.req?.raw)?.socket?.remoteAddress;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
if (bypass(peer)) {
|
|
131
|
+
c.set('user', user);
|
|
132
|
+
return next();
|
|
133
|
+
}
|
|
134
|
+
const raw = c.req.raw;
|
|
135
|
+
const rawHeaders = {};
|
|
136
|
+
try {
|
|
137
|
+
for (const [k, v] of (raw?.headers ?? new Headers()).entries())
|
|
138
|
+
rawHeaders[k] = v;
|
|
139
|
+
}
|
|
140
|
+
catch { /* ignore */ }
|
|
141
|
+
const candidate = extractToken(rawHeaders);
|
|
142
|
+
if (tokenMatches(candidate, expected)) {
|
|
143
|
+
c.set('user', user);
|
|
144
|
+
return next();
|
|
145
|
+
}
|
|
146
|
+
return c.json({ error: 'Unauthorized' }, 401);
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
async verifyUpgrade(req) {
|
|
150
|
+
if (bypass(req.socket?.remoteAddress))
|
|
151
|
+
return user;
|
|
152
|
+
const candidate = extractToken(req.headers, req.url);
|
|
153
|
+
return tokenMatches(candidate, expected) ? user : null;
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/connectivity/auth/token.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACvF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAEnE,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,CAAC,CAAA;AACtC,MAAM,MAAM,GAAG,YAAY,CAAA;AAgB3B,SAAS,gBAAgB;IACvB,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAA;AAClC,CAAC;AAED;;gEAEgE;AAChE,SAAS,gBAAgB;IACvB,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,EAAE,YAAY,CAAC,CAAA;IACvD,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;QACpC,MAAM,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;QAC9C,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QACnB,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAA;QAC/B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7C,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACvC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACtB,GAAG,CAAC,IAAI,CAAC,6BAA6B,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;QACzD,OAAO,CAAC,CAAA;IACV,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE;IAC3D,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,IAAI,gBAAgB,EAAE,CAAA;IACjD,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;YAC5C,IAAI,CAAC;gBAAE,OAAO,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAA;IAClC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAA;IAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IACnD,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7C,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC3C,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAA;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAA;IAChE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,cAAc,CAAC,IAAa;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACxD,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,KAAK,CAAA;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,SAA6B,EAAE,QAAgB;IACnE,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAA;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC/B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED,SAAS,YAAY,CACnB,OAAsD,EACtD,GAAY;IAEZ,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACvF,4EAA4E;IAC5E,8EAA8E;IAC9E,sEAAsE;IACtE,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACxB,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;IACjB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,cAAc,MAAM,UAAU,CAAC,CAAC,CAAA;QAClE,IAAI,CAAC;YAAE,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAsB;IACpD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACxC,MAAM,IAAI,GAAa,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;IAC5E,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CACN,8FAA8F;YAC9F,gGAAgG;YAChG,2FAA2F,CAC5F,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,IAAwB,EAAW,EAAE;QACnD,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAA;QACjC,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,CAAA;QACnD,OAAO,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrE,CAAC,CAAA;IAED,OAAO;QACL,cAAc;YACZ,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;gBACvB,8LAA8L;gBAC9L,MAAM,IAAI,GAAG,CAAC,GAAuB,EAAE;oBACrC,IAAI,CAAC;wBACH,OAAO,CAAE,CAAC,CAAC,GAAW,EAAE,QAAQ,IAAK,CAAC,CAAC,GAAW,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,aAAa,CAAA;oBACjF,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,SAAS,CAAA;oBAClB,CAAC;gBACH,CAAC,CAAC,EAAE,CAAA;gBACJ,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBAAC,OAAO,IAAI,EAAE,CAAA;gBAAC,CAAC;gBACxD,MAAM,GAAG,GAAI,CAAC,CAAC,GAAW,CAAC,GAA0B,CAAA;gBACrD,MAAM,UAAU,GAAuC,EAAE,CAAA;gBACzD,IAAI,CAAC;oBACH,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;wBAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBACnF,CAAC;gBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBACxB,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,CAAC,CAAA;gBAC1C,IAAI,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;oBAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBAAC,OAAO,IAAI,EAAE,CAAA;gBAAC,CAAC;gBAC7E,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAA;YAC/C,CAAC,CAAA;QACH,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,GAAG;YACrB,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC;gBAAE,OAAO,IAAI,CAAA;YAClD,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;YACpD,OAAO,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QACxD,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
function applyKv(item, text) {
|
|
5
|
+
const m = text.match(/^(hostname|service)\s*:\s*(.+?)\s*$/);
|
|
6
|
+
if (!m)
|
|
7
|
+
return;
|
|
8
|
+
const val = m[2].replace(/^["']|["']$/g, '');
|
|
9
|
+
if (m[1] === 'hostname')
|
|
10
|
+
item.hostname = val;
|
|
11
|
+
else
|
|
12
|
+
item.service = val;
|
|
13
|
+
}
|
|
14
|
+
function serviceMatchesPort(service, port) {
|
|
15
|
+
if (!service)
|
|
16
|
+
return false;
|
|
17
|
+
// Anchor on a non-digit (or end) after the port so 17081 ≠ 170810.
|
|
18
|
+
return new RegExp(`^https?://(localhost|127\\.0\\.0\\.1):${port}(?:[/?#]|$)`).test(service.trim());
|
|
19
|
+
}
|
|
20
|
+
/** Return the ingress hostname mapped to http(s)://localhost:<port>, if any. */
|
|
21
|
+
export function parseCloudflaredIngress(yamlText, port) {
|
|
22
|
+
const lines = yamlText.split(/\r?\n/);
|
|
23
|
+
let i = lines.findIndex((l) => /^\s*ingress\s*:\s*$/.test(l));
|
|
24
|
+
if (i < 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
const ingressIndent = lines[i].match(/^(\s*)/)[1].length;
|
|
27
|
+
const items = [];
|
|
28
|
+
let cur = null;
|
|
29
|
+
for (i = i + 1; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
if (line.trim() === '' || /^\s*#/.test(line))
|
|
32
|
+
continue;
|
|
33
|
+
const indent = line.match(/^(\s*)/)[1].length;
|
|
34
|
+
if (indent <= ingressIndent)
|
|
35
|
+
break; // dedented out of the ingress block
|
|
36
|
+
const dash = line.match(/^\s*-\s+(.*)$/);
|
|
37
|
+
if (dash) {
|
|
38
|
+
cur = {};
|
|
39
|
+
items.push(cur);
|
|
40
|
+
applyKv(cur, dash[1]);
|
|
41
|
+
}
|
|
42
|
+
else if (cur) {
|
|
43
|
+
applyKv(cur, line.trim());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return items.find((it) => it.hostname && serviceMatchesPort(it.service, port))?.hostname;
|
|
47
|
+
}
|
|
48
|
+
/** Scan ~/.cloudflared/*.{yml,yaml} for a tunnel hostname mapped to the web port. */
|
|
49
|
+
export function detectCloudflaredHostname(port, opts = {}) {
|
|
50
|
+
const dir = opts.configDir ?? join(homedir(), '.cloudflared');
|
|
51
|
+
const readDir = opts.readDir ?? ((d) => { try {
|
|
52
|
+
return readdirSync(d);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return [];
|
|
56
|
+
} });
|
|
57
|
+
const readFile = opts.readFile ?? ((p) => { try {
|
|
58
|
+
return readFileSync(p, 'utf8');
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return '';
|
|
62
|
+
} });
|
|
63
|
+
let files;
|
|
64
|
+
try {
|
|
65
|
+
files = readDir(dir).filter((f) => /\.ya?ml$/i.test(f));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
for (const f of files) {
|
|
71
|
+
const host = parseCloudflaredIngress(readFile(join(dir, f)), port);
|
|
72
|
+
if (host)
|
|
73
|
+
return host;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=cloudflared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../../src/connectivity/exposure/cloudflared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/connectivity/exposure/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { networkInterfaces } from 'node:os';
|
|
2
|
+
import { detectCloudflaredHostname } from './cloudflared.js';
|
|
3
|
+
function firstLanIpv4() {
|
|
4
|
+
const ifaces = networkInterfaces();
|
|
5
|
+
const skip = /^(utun|tun|tap|docker|br-|bridge|veth|vmnet|llw|awdl)/i;
|
|
6
|
+
const ipv4 = (name) => {
|
|
7
|
+
for (const ni of ifaces[name] ?? []) {
|
|
8
|
+
if (ni.family === 'IPv4' && !ni.internal)
|
|
9
|
+
return ni.address;
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
const names = Object.keys(ifaces);
|
|
14
|
+
// Prefer physical LAN interfaces (en*/eth*), then any non-virtual interface.
|
|
15
|
+
const preferred = names.filter((n) => /^(en|eth)/i.test(n));
|
|
16
|
+
for (const n of [...preferred, ...names]) {
|
|
17
|
+
if (!preferred.includes(n) && skip.test(n))
|
|
18
|
+
continue;
|
|
19
|
+
const ip = ipv4(n);
|
|
20
|
+
if (ip)
|
|
21
|
+
return ip;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the best public base URL:
|
|
27
|
+
* explicit publicUrl > auto-detected cloudflared hostname > LAN IP > loopback.
|
|
28
|
+
*/
|
|
29
|
+
export async function resolvePublicUrl(opts) {
|
|
30
|
+
return (await resolveExposure(opts)).url;
|
|
31
|
+
}
|
|
32
|
+
export async function resolveExposure(opts) {
|
|
33
|
+
if (opts.publicUrl && opts.publicUrl.trim()) {
|
|
34
|
+
let u = opts.publicUrl.trim().replace(/\/+$/, '');
|
|
35
|
+
if (!/^https?:\/\//i.test(u))
|
|
36
|
+
u = `https://${u}`;
|
|
37
|
+
return { url: u, provider: 'cf-tunnel' };
|
|
38
|
+
}
|
|
39
|
+
// No explicit URL: if a local cloudflared tunnel already maps a hostname to
|
|
40
|
+
// this port, prefer that reachable HTTPS URL over an unreachable LAN IP.
|
|
41
|
+
const cfHost = (opts.cloudflaredResolver ?? (() => detectCloudflaredHostname(opts.port)))();
|
|
42
|
+
if (cfHost)
|
|
43
|
+
return { url: `https://${cfHost.replace(/\/+$/, '')}`, provider: 'cf-tunnel' };
|
|
44
|
+
const lan = (opts.lanIpResolver ?? firstLanIpv4)();
|
|
45
|
+
if (lan)
|
|
46
|
+
return { url: `http://${lan}:${opts.port}`, provider: 'lan' };
|
|
47
|
+
return { url: `http://127.0.0.1:${opts.port}`, provider: 'loopback' };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../../src/connectivity/exposure/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAE3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAa5D,SAAS,YAAY;IACnB,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,MAAM,IAAI,GAAG,wDAAwD,CAAA;IACrE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAsB,EAAE;QAChD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,IAAI,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ;gBAAE,OAAO,EAAE,CAAC,OAAO,CAAA;QAC7D,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC,CAAA;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACjC,6EAA6E;IAC7E,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3D,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAQ;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QAClB,IAAI,EAAE;YAAE,OAAO,EAAE,CAAA;IACnB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAoB;IACzD,OAAO,CAAC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAoB;IACxD,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5C,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAA;QAChD,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;IAC1C,CAAC;IACD,4EAA4E;IAC5E,yEAAyE;IACzE,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IAC3F,IAAI,MAAM;QAAE,OAAO,EAAE,GAAG,EAAE,WAAW,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;IAC1F,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,EAAE,CAAA;IAClD,IAAI,GAAG;QAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACtE,OAAO,EAAE,GAAG,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;AACvE,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import QRCode from 'qrcode';
|
|
2
|
+
import { loadOrCreateToken } from './auth/token.js';
|
|
3
|
+
import { resolvePublicUrl } from './exposure/providers.js';
|
|
4
|
+
/** Build the pairing URL with the token in the fragment (never the query, so it
|
|
5
|
+
* isn't logged by proxies; the PWA reads it client-side then stores it). */
|
|
6
|
+
export function buildPairUrl(base, token) {
|
|
7
|
+
return `${base.replace(/\/+$/, '')}/#token=${encodeURIComponent(token)}`;
|
|
8
|
+
}
|
|
9
|
+
export async function buildPairCard(base, token) {
|
|
10
|
+
const url = buildPairUrl(base, token);
|
|
11
|
+
const qr = await QRCode.toString(url, { type: 'terminal', small: true });
|
|
12
|
+
const lines = [
|
|
13
|
+
'Scan to pair this device with opencode-remote-control:',
|
|
14
|
+
'',
|
|
15
|
+
url,
|
|
16
|
+
'',
|
|
17
|
+
'The token is stored on the device after the first open.',
|
|
18
|
+
];
|
|
19
|
+
return { url, qr, lines };
|
|
20
|
+
}
|
|
21
|
+
/** Resolve the current pairing token + best public URL from env (shared by the
|
|
22
|
+
* CLI `ocrc pair` and the Telegram `/pair` command). */
|
|
23
|
+
export async function buildPairContext() {
|
|
24
|
+
const port = Number(process.env.OCRC_WEB_PORT ?? process.env.WEB_PORT ?? 4099);
|
|
25
|
+
const token = loadOrCreateToken({ token: process.env.WEB_TOKEN });
|
|
26
|
+
const url = await resolvePublicUrl({ publicUrl: process.env.WEB_PUBLIC_URL, port });
|
|
27
|
+
return { token, url };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=pairing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/connectivity/pairing.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D;4EAC4E;AAC5E,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,KAAa;IACtD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAA;AAC1E,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa;IAC7D,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACxE,MAAM,KAAK,GAAG;QACZ,wDAAwD;QACxD,EAAE;QACF,GAAG;QACH,EAAE;QACF,yDAAyD;KAC1D,CAAA;IACD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;AAC3B,CAAC;AAOD;wDACwD;AACxD,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAA;IAC9E,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAA;IACjE,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAA;IACnF,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;AACvB,CAAC"}
|