@xopcai/xopc 0.0.272 → 0.0.273
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/README.md +1 -5
- package/README.zh-CN.md +1 -5
- package/dist/browser-ext/dist/assets/{auth-C1QI-uMG.js → auth-DkwOUI6_.js} +101 -53
- package/dist/browser-ext/dist/background.js +1 -22
- package/dist/browser-ext/dist/sidepanel.html +1 -1
- package/dist/browser-ext/dist/sidepanel.js +24 -22
- package/dist/browser-ext/manifest.json +1 -12
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-DffqjeqQ.js → activity-store-cF7IX2RP.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Cwj4HCYY.js → agent-avatar-dicebear-BrgxX8nG.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-6CLfLxVc.js → agent-defaults-settings-panel-DvhuRCLA.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-B6mu64fY.js → agents-C0By2FIo.js} +1 -1
- package/dist/gateway/static/root/assets/{api-CGLdye4i.js → api-CYkdUpkx.js} +1 -1
- package/dist/gateway/static/root/assets/app-link-BiBXVEvp.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BLujSbaw.js → app-management-settings-panel-DfoNzWIa.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BXMcueb8.js → appearance-settings-czbLdCqj.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BEgy4hY3.js → apps-page-BSJXKWcE.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-D4bUampf.js → archive-plugin-D14P13mX.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CBGNA2Ys.js → attachment-load-Dok_B1Gv.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-dQyolNmy.js → automations-page-DM7ZEewI.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-DxRFrmrV.js → automations-workspace--1TsZf3O.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-bbvp81h2.js → binary-plugins-CxNg6InF.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C8e6Wri4.js → block-editor-D22VcYnC.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-oJWJtQa6.js → browser-automation-inputs-6NWTLEpd.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CAcJQSjN.js → browser-automations-page-BrqySdbW.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-TLAlqcPo.js → browser-settings-page-DOHgQer_.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-z-K_jrZz.js → capabilities-settings-panel-Bpse_5K-.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-Dzrk33aB.js → channels-settings-yHsY174W.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-CfOPNnVr.js → channels-status-swr-BUZimNmp.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-9ZOl92kh.js → chat-terminal-dock-CZPBNpMI.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-B1dIoRNi.js → connectors-page-B_wLKGnH.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-Dj6FRLF9.js → date-picker-Da_0QyLe.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-D1q89tVc.js → dependency-picker-D_djqzy7.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-DfqZBvhp.js → desktop-pet-Celmbw-T.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-HCEh2MOm.js → desktop-pet-settings-CqNNgF45.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-B5_w9qlm.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-C8cAWVoK.js → extension-debug-page-ku3kCjvH.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-5N87Vaym.js → extension-page-C7FpHwL4.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D85SAUFP.js → extension-settings-page-OMj2tU2_.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-BjsDnB_n.js → gateway-config-swr-6eb1vKE1.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-Dqx8PmcT.js → gateway-settings-CH-6vkgN.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BIf0eLWX.js → gateway-startup-retry-DUFFCU8H.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-BpDWGE1a.js → heartbeat-settings-CSFzOACM.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-DJaqpC8-.js → home-page-BJebAKB-.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-1m8xTVS_.js → image-generation-api-C1cHRBRp.js} +1 -1
- package/dist/gateway/static/root/assets/{index-C6PmD0cK.js → index-C6OWYFtQ.js} +9 -9
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Byy8mCSq.js → keyboard-shortcuts-settings-BuvD3iv8.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-l2AEiG2C.js → local-app-workbench-page-CaC4-THD.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BPo0wBgM.js → local-apps-page-DdJ8zwBp.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-CRNble2Z.js → locale-store-BBSCIl4I.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-X5FdLlXK.js → logs-page-BaR2he0K.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-DT6NoIjm.js → management-settings-r-iYWZFY.js} +1 -1
- package/dist/gateway/static/root/assets/markdown-view-c5vtnYe8.js +201 -0
- package/dist/gateway/static/root/assets/{media-plugins-Bzka9qXu.js → media-plugins-D_a6GDY6.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-DH_HCgmw.js → messages-DjuBdDCn.js} +2 -2
- package/dist/gateway/static/root/assets/{note-detail-panel-BUJPoaGZ.js → note-detail-panel-BV6nbgac.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-BwFpWRuo.js → notes-page-BmLKoWwi.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-Ys0p65Lt.js → page-tabs-C7Le9bSI.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-iknB5xEp.js → preview-open-alternatives-B2pOxJBK.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-card-BPZDgNNR.js → proactive-card-BlQ1vCtV.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-CxdZR-p2.js → proactive-page-CghKTfYa.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-W1o3f-kz.js → product-open-page-DpjiHgwu.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-BU1RKAcy.js → project-detail-page-CV8Lh296.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-z0X0tL8G.js → projects-page-COweOaJP.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-DmYFpjsH.js → registry-api-CyQ4k1Ux.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-NNTut2I8.js → remote-access-hub-ChPPGVU5.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DP1XyYO8.js → runtime-tools-settings-panel-Bl4ArLHf.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BMYKdzsH.js → schema-form-KQFmfWd4.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-B6SsHlz5.js → sessions-page-DgmHibOK.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DV7kMco2.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-2LGjz7bu.js → settings-form-section-Dbnls9ty.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-7DjpC9_C.js → settings-page-CDKirYRG.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-Dj9jFWJt.js → setup-status-panel-SQmtWoOs.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BxYdSi8c.js → share-preview-page-DwgLjS1m.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CMqE0KuK.js → shares-settings-Cs5bUrYw.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DyKrjq7F.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-DNEScMZH.js → skills-page-Bd0_ED_p.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-q88l65YU.js → skills-page.utils-rA-XT-YG.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-8NIssGKj.js → system-settings-panel-CSkMrHLz.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-L6dp9v2y.js → task-detail-page-CEQU-L_G.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-BXZG3S25.js → theme-store-DWpIEWen.js} +1 -1
- package/dist/gateway/static/root/assets/tunnel-api-COJcGnEi.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-BwQuoTQU.js → use-autosave-DkQAIAuB.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-D4BucMrZ.js → user-model-page-BQAU55lf.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-Bd03Ic0s.js → voice-api-key-field-BrqcH0P-.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-gggUp3u9.js → voice-config-api-BJ95Z3pT.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-DF8PR_pK.js → work-discovery-overlay-vfTWKm13.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BiAAWlir.js → workflow-run-setup-panel-CHdr_ng9.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DryKUMfA.js → workflows-page-hwRJD7vR.js} +1 -1
- package/dist/gateway/static/root/index.html +10 -9
- package/dist/package.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +6 -0
- package/dist/src/browser/providers/browser-ext-install.js +16 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +8 -5
- package/dist/src/cli/commands/gateway/service.js +4 -3
- package/dist/src/cli/commands/gateway/token.js +28 -1
- package/dist/src/gateway/device-routes.js +4 -3
- package/dist/src/gateway/hono/routes/browser.js +26 -0
- package/dist/src/storage/sqlite/device-pairing-approval.js +1 -3
- package/package.json +4 -4
- package/dist/browser-ext/dist/pairing-handoff.js +0 -51
- package/dist/gateway/static/root/assets/device-pairing-wizard-CPth92s6.js +0 -1
- package/dist/gateway/static/root/assets/markdown-view-AglreSOj.js +0 -201
- package/dist/gateway/static/root/assets/settings-advanced-gate-BQu8bAcN.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BXqSJymN.js +0 -1
- package/dist/gateway/static/root/assets/tunnel-api-CWsRPM84.js +0 -1
|
@@ -14,6 +14,7 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
import { spawn } from "node:child_process";
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
16
|
import { readFile, readdir, rm } from "node:fs/promises";
|
|
17
|
+
import AdmZip from "adm-zip";
|
|
17
18
|
//#region src/browser/providers/browser-ext-install.ts
|
|
18
19
|
/**
|
|
19
20
|
* Install bundled Chrome extension artifacts into {resolveBinDir()}/browser-ext/.
|
|
@@ -134,6 +135,20 @@ async function resolveBundledBrowserExtDir() {
|
|
|
134
135
|
}
|
|
135
136
|
return null;
|
|
136
137
|
}
|
|
138
|
+
/** Build a portable archive for installing the bundled extension on another computer. */
|
|
139
|
+
async function createBrowserExtensionArchive() {
|
|
140
|
+
const bundled = await resolveBundledBrowserExtDir();
|
|
141
|
+
if (!bundled) throw new Error("Bundled browser extension not found. Reinstall or rebuild xopc.");
|
|
142
|
+
const root = "xopc-browser-extension";
|
|
143
|
+
const zip = new AdmZip();
|
|
144
|
+
zip.addLocalFile(join(bundled.dir, "manifest.json"), root);
|
|
145
|
+
zip.addLocalFolder(join(bundled.dir, "dist"), `${root}/dist`);
|
|
146
|
+
zip.addLocalFolder(join(bundled.dir, "icons"), `${root}/icons`);
|
|
147
|
+
return {
|
|
148
|
+
filename: `xopc-browser-extension-${PACKAGE_VERSION}.zip`,
|
|
149
|
+
data: zip.toBuffer()
|
|
150
|
+
};
|
|
151
|
+
}
|
|
137
152
|
function computeNeedsRefresh(params) {
|
|
138
153
|
if (params.force) return true;
|
|
139
154
|
if (!params.installedPath || !validateBrowserExtLayout(params.installedPath)) return true;
|
|
@@ -471,4 +486,4 @@ async function openBrowserExtensionInstallUi(opts) {
|
|
|
471
486
|
};
|
|
472
487
|
}
|
|
473
488
|
//#endregion
|
|
474
|
-
export { BROWSER_EXT_REQUIRED_FILES, browserExtContentHash, browserExtDoctor, browserNativeHostDoctor, browserNativeManifestDirectories, computeNeedsRefresh, ensureBrowserExtensionArtifacts, ensureBrowserExtensionOnStartup, installBrowserNativeMessagingHost, openBrowserExtensionInstallUi, readInstalledExtensionDir, resolveBundledBrowserExtDir, resolveInstalledExtensionPath, resolveWindowsExtensionManager, validateBrowserExtLayout };
|
|
489
|
+
export { BROWSER_EXT_REQUIRED_FILES, browserExtContentHash, browserExtDoctor, browserNativeHostDoctor, browserNativeManifestDirectories, computeNeedsRefresh, createBrowserExtensionArchive, ensureBrowserExtensionArtifacts, ensureBrowserExtensionOnStartup, installBrowserNativeMessagingHost, openBrowserExtensionInstallUi, readInstalledExtensionDir, resolveBundledBrowserExtDir, resolveInstalledExtensionPath, resolveWindowsExtensionManager, validateBrowserExtLayout };
|
|
@@ -69,10 +69,13 @@ async function runGatewayFromCliOptions(options, ctx) {
|
|
|
69
69
|
const { resolveGatewayAuth, assertGatewayAuthConfigured } = await import("../../../gateway/auth.js");
|
|
70
70
|
const { assertGatewayRuntimeConfig } = await import("../../../gateway/runtime-config.js");
|
|
71
71
|
let auth = resolveGatewayAuth({ authConfig: effectiveConfig.gateway?.auth });
|
|
72
|
-
if (typeof options.token === "string" && options.token.trim().length > 0)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
if (typeof options.token === "string" && options.token.trim().length > 0) {
|
|
73
|
+
auth = {
|
|
74
|
+
mode: "token",
|
|
75
|
+
token: options.token.trim()
|
|
76
|
+
};
|
|
77
|
+
process.env.XOPC_GATEWAY_TOKEN = auth.token;
|
|
78
|
+
}
|
|
76
79
|
try {
|
|
77
80
|
assertGatewayAuthConfigured(auth);
|
|
78
81
|
assertGatewayRuntimeConfig({
|
|
@@ -126,7 +129,7 @@ async function runGatewayFromCliOptions(options, ctx) {
|
|
|
126
129
|
});
|
|
127
130
|
await server.start();
|
|
128
131
|
const displayHost = bindHost === "0.0.0.0" ? "localhost" : bindHost;
|
|
129
|
-
const token =
|
|
132
|
+
const token = auth.mode === "token" ? auth.token : void 0;
|
|
130
133
|
console.log("✅ Gateway started");
|
|
131
134
|
console.log(` URL: http://${displayHost}:${port}`);
|
|
132
135
|
if (token) console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);
|
|
@@ -9,8 +9,9 @@ init_paths();
|
|
|
9
9
|
* Create service install subcommand - actually installs the OS service
|
|
10
10
|
*/
|
|
11
11
|
function createInstallCommand() {
|
|
12
|
-
return new Command("install").description("Install gateway as OS service (LaunchAgent / systemd / Task)").option("--port <port>", "Gateway port").option("--token <token>", "Gateway auth token").option("--force", "Force reinstall if already installed").option("--json", "Output JSON").action(async (options) => {
|
|
12
|
+
return new Command("install").description("Install gateway as OS service (LaunchAgent / systemd / Task)").option("--port <port>", "Gateway port").option("--token <token>", "Gateway auth token").option("--force", "Force reinstall if already installed").option("--json", "Output JSON").action(async (options, command) => {
|
|
13
13
|
const configPath = getContextWithOpts().configPath || resolveConfigPath();
|
|
14
|
+
const force = options.force === true || command.optsWithGlobals().force === true;
|
|
14
15
|
const [{ loadConfig }, { resolveGatewayService, isDaemonAvailableAsync, getPlatformName }, { buildGatewayInstallArgs }] = await Promise.all([
|
|
15
16
|
import("../../../config/index.js"),
|
|
16
17
|
import("../../../daemon/service.js"),
|
|
@@ -30,7 +31,7 @@ function createInstallCommand() {
|
|
|
30
31
|
}
|
|
31
32
|
const service = await resolveGatewayService();
|
|
32
33
|
const loaded = await service.isLoaded({ env: process.env });
|
|
33
|
-
if (loaded && !
|
|
34
|
+
if (loaded && !force) {
|
|
34
35
|
if (options.json) console.log(JSON.stringify({
|
|
35
36
|
ok: true,
|
|
36
37
|
result: "already-installed"
|
|
@@ -38,7 +39,7 @@ function createInstallCommand() {
|
|
|
38
39
|
else console.log("ℹ️ Service already installed. Use --force to reinstall.");
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
|
-
if (loaded &&
|
|
42
|
+
if (loaded && force) try {
|
|
42
43
|
await service.uninstall({ env: process.env });
|
|
43
44
|
} catch {}
|
|
44
45
|
const installArgs = buildGatewayInstallArgs({
|
|
@@ -4,6 +4,17 @@ import crypto from "crypto";
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
//#region src/cli/commands/gateway/token.ts
|
|
6
6
|
init_paths();
|
|
7
|
+
async function readInstalledServiceToken() {
|
|
8
|
+
try {
|
|
9
|
+
const { isDaemonAvailableAsync, resolveGatewayService } = await import("../../../daemon/service.js");
|
|
10
|
+
if (!await isDaemonAvailableAsync()) return void 0;
|
|
11
|
+
const service = await resolveGatewayService();
|
|
12
|
+
if (!await service.isLoaded({ env: process.env })) return void 0;
|
|
13
|
+
return (await service.readCommand(process.env))?.environment?.XOPC_GATEWAY_TOKEN?.trim() || void 0;
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Create the token subcommand for managing gateway authentication token.
|
|
9
20
|
*/
|
|
@@ -14,6 +25,7 @@ function createTokenCommand() {
|
|
|
14
25
|
const log = createLogger("GatewayTokenCommand");
|
|
15
26
|
try {
|
|
16
27
|
const config = loadConfig(configPath);
|
|
28
|
+
const installedServiceToken = await readInstalledServiceToken();
|
|
17
29
|
if (options.generate) {
|
|
18
30
|
const newToken = crypto.randomBytes(24).toString("hex");
|
|
19
31
|
config.gateway = config.gateway || {};
|
|
@@ -31,9 +43,16 @@ function createTokenCommand() {
|
|
|
31
43
|
console.log("");
|
|
32
44
|
console.log("Or set environment variable:");
|
|
33
45
|
console.log(` export XOPC_GATEWAY_TOKEN=${newToken}`);
|
|
46
|
+
if (installedServiceToken && installedServiceToken !== newToken) {
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log("⚠️ The installed gateway service still uses its previous token.");
|
|
49
|
+
console.log(" Run `xopc gateway service install --force`, then restart the service.");
|
|
50
|
+
}
|
|
34
51
|
process.exit(0);
|
|
35
52
|
} else {
|
|
36
|
-
const
|
|
53
|
+
const configToken = config.gateway?.auth?.token?.trim();
|
|
54
|
+
const environmentToken = process.env.XOPC_GATEWAY_TOKEN?.trim();
|
|
55
|
+
const currentToken = installedServiceToken || environmentToken || configToken;
|
|
37
56
|
if ((config.gateway?.auth?.mode || "token") === "none") {
|
|
38
57
|
console.log("⚠️ Gateway authentication is disabled (mode: none)");
|
|
39
58
|
console.log("");
|
|
@@ -46,6 +65,14 @@ function createTokenCommand() {
|
|
|
46
65
|
console.log(` ${currentToken}`);
|
|
47
66
|
console.log("");
|
|
48
67
|
console.log(`Preview: ${tokenPreview}`);
|
|
68
|
+
if (installedServiceToken) console.log("Source: installed gateway service");
|
|
69
|
+
else if (environmentToken) console.log("Source: XOPC_GATEWAY_TOKEN");
|
|
70
|
+
else console.log(`Source: ${configPath}`);
|
|
71
|
+
if (installedServiceToken && configToken && installedServiceToken !== configToken) {
|
|
72
|
+
console.log("");
|
|
73
|
+
console.log("⚠️ Token drift detected: the installed service token overrides the config token.");
|
|
74
|
+
console.log(" Run `xopc gateway service install --force`, then restart the service to sync.");
|
|
75
|
+
}
|
|
49
76
|
console.log("");
|
|
50
77
|
console.log("Usage:");
|
|
51
78
|
console.log(` xopc gateway --token ${currentToken}`);
|
|
@@ -19,9 +19,6 @@ function routeId(kind, url) {
|
|
|
19
19
|
function resolveSecureDeviceRoutes(config) {
|
|
20
20
|
const tunnelStatus = getTunnelService().getStatus();
|
|
21
21
|
const candidates = [{
|
|
22
|
-
kind: "custom-https",
|
|
23
|
-
url: secureUrl(resolveReverseProxyPublicUrl(config))
|
|
24
|
-
}, {
|
|
25
22
|
kind: "xopc-secure-link",
|
|
26
23
|
url: tunnelStatus.state === "connected" ? secureUrl(tunnelStatus.publicUrl) : null
|
|
27
24
|
}];
|
|
@@ -30,6 +27,10 @@ function resolveSecureDeviceRoutes(config) {
|
|
|
30
27
|
kind: "tailscale",
|
|
31
28
|
url: secureUrl(`https://${tailscale.hostname}`)
|
|
32
29
|
});
|
|
30
|
+
candidates.push({
|
|
31
|
+
kind: "custom-https",
|
|
32
|
+
url: secureUrl(resolveReverseProxyPublicUrl(config))
|
|
33
|
+
});
|
|
33
34
|
const seen = /* @__PURE__ */ new Set();
|
|
34
35
|
const routes = [];
|
|
35
36
|
for (const candidate of candidates) {
|
|
@@ -49,6 +49,10 @@ function isLocalOwnerRequest(c, service) {
|
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
function isOwnerRequest(c) {
|
|
53
|
+
const principal = getGatewayPrincipal(c);
|
|
54
|
+
return principal.kind === "owner" || principal.kind === "trusted-proxy";
|
|
55
|
+
}
|
|
52
56
|
function chromeDevicePrincipal(c) {
|
|
53
57
|
const principal = getGatewayPrincipal(c);
|
|
54
58
|
if (principal.kind !== "device" || !principal.deviceId) return void 0;
|
|
@@ -235,6 +239,28 @@ function registerBrowserRoutes(authenticated, deps) {
|
|
|
235
239
|
}, 500);
|
|
236
240
|
}
|
|
237
241
|
});
|
|
242
|
+
authenticated.get("/api/browser/extension/archive", strictRateLimitMiddleware, async (c) => {
|
|
243
|
+
if (!isOwnerRequest(c)) return c.json({
|
|
244
|
+
ok: false,
|
|
245
|
+
error: "Owner access required."
|
|
246
|
+
}, 403);
|
|
247
|
+
try {
|
|
248
|
+
const { createBrowserExtensionArchive } = await import("../../../browser/providers/browser-ext-install.js");
|
|
249
|
+
const archive = await createBrowserExtensionArchive();
|
|
250
|
+
const body = new Uint8Array(archive.data.byteLength);
|
|
251
|
+
body.set(archive.data);
|
|
252
|
+
return c.body(body, 200, {
|
|
253
|
+
"Content-Type": "application/zip",
|
|
254
|
+
"Content-Disposition": `attachment; filename="${archive.filename}"`,
|
|
255
|
+
"Cache-Control": "private, no-store"
|
|
256
|
+
});
|
|
257
|
+
} catch (error) {
|
|
258
|
+
return c.json({
|
|
259
|
+
ok: false,
|
|
260
|
+
error: error instanceof Error ? error.message : String(error)
|
|
261
|
+
}, 500);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
238
264
|
authenticated.post("/api/browser/extension/open", strictRateLimitMiddleware, async (c) => {
|
|
239
265
|
if (!isLocalOwnerRequest(c, service)) return c.json({
|
|
240
266
|
ok: false,
|
|
@@ -8,7 +8,6 @@ import "../../browser/extension-identity.js";
|
|
|
8
8
|
import crypto from "node:crypto";
|
|
9
9
|
//#region src/storage/sqlite/device-pairing-approval.ts
|
|
10
10
|
const PROOF_WINDOW_MS = 5 * 6e4;
|
|
11
|
-
const APPROVAL_WINDOW_MS = 2 * 6e4;
|
|
12
11
|
const RECOVERY_WINDOW_MS = 1440 * 6e4;
|
|
13
12
|
var DevicePairingError = class extends Error {
|
|
14
13
|
constructor(code, status = 409) {
|
|
@@ -105,9 +104,8 @@ function submitDevicePairingRequest(body, now = Date.now()) {
|
|
|
105
104
|
}
|
|
106
105
|
if (setup.consumed_at !== null) return fail("PAIRING_CANCELLED");
|
|
107
106
|
if (setup.expires_at <= now) return fail("PAIRING_EXPIRED");
|
|
108
|
-
const expiresAt = Math.min(setup.expires_at, now + APPROVAL_WINDOW_MS);
|
|
109
107
|
db.prepare(`INSERT INTO device_pairing_requests (request_id, pairing_id, device_json, status,
|
|
110
|
-
confirmation_code, expires_at, recovery_until, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(String(body.requestId), setup.pairing_id, deviceJson, autoApprove ? "approved" : "pending", crypto.randomInt(0, 1e6).toString().padStart(6, "0"),
|
|
108
|
+
confirmation_code, expires_at, recovery_until, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(String(body.requestId), setup.pairing_id, deviceJson, autoApprove ? "approved" : "pending", crypto.randomInt(0, 1e6).toString().padStart(6, "0"), setup.expires_at, setup.expires_at + RECOVERY_WINDOW_MS, now);
|
|
111
109
|
return statusFromRow(requestRow(String(body.requestId), now), now);
|
|
112
110
|
});
|
|
113
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.273",
|
|
4
4
|
"description": "Local-first personal AI assistant with reviewable user understanding, durable tasks, workflows, automations, and local or cloud models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -166,10 +166,10 @@
|
|
|
166
166
|
"vitest": "4.1.5",
|
|
167
167
|
"vue": "^3.5.35",
|
|
168
168
|
"@xopcai/browser-control-contract": "0.1.0",
|
|
169
|
-
"@xopcai/realtime-client": "0.0.0",
|
|
170
|
-
"@xopcai/realtime-protocol": "0.0.0",
|
|
171
169
|
"@xopcai/gateway-contract": "0.0.0",
|
|
172
|
-
"@xopcai/endpoint-tools-protocol": "0.0.0"
|
|
170
|
+
"@xopcai/endpoint-tools-protocol": "0.0.0",
|
|
171
|
+
"@xopcai/realtime-client": "0.0.0",
|
|
172
|
+
"@xopcai/realtime-protocol": "0.0.0"
|
|
173
173
|
},
|
|
174
174
|
"scripts": {
|
|
175
175
|
"assets:brand": "node scripts/generate-brand-assets.mjs",
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const messageType = "browser/stage-pairing-invite";
|
|
2
|
-
function hasPairingPayload() {
|
|
3
|
-
return new URLSearchParams(location.hash.slice(1)).has("p");
|
|
4
|
-
}
|
|
5
|
-
async function stage(open) {
|
|
6
|
-
return chrome.runtime.sendMessage({
|
|
7
|
-
type: messageType,
|
|
8
|
-
pairingLink: location.href,
|
|
9
|
-
open
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
function mountOpenButton() {
|
|
13
|
-
if (document.getElementById("xopc-browser-pairing-handoff")) return;
|
|
14
|
-
const host = document.createElement("div");
|
|
15
|
-
host.id = "xopc-browser-pairing-handoff";
|
|
16
|
-
const root = host.attachShadow({ mode: "closed" });
|
|
17
|
-
const button = document.createElement("button");
|
|
18
|
-
button.type = "button";
|
|
19
|
-
button.textContent = "Open xopc extension";
|
|
20
|
-
button.style.cssText = [
|
|
21
|
-
"position:fixed",
|
|
22
|
-
"right:24px",
|
|
23
|
-
"bottom:24px",
|
|
24
|
-
"z-index:2147483647",
|
|
25
|
-
"border:0",
|
|
26
|
-
"border-radius:10px",
|
|
27
|
-
"padding:12px 18px",
|
|
28
|
-
"background:#2563eb",
|
|
29
|
-
"color:#fff",
|
|
30
|
-
"font:600 14px/20px system-ui,sans-serif",
|
|
31
|
-
"box-shadow:0 8px 24px rgba(15,23,42,.24)",
|
|
32
|
-
"cursor:pointer"
|
|
33
|
-
].join(";");
|
|
34
|
-
button.addEventListener("click", () => {
|
|
35
|
-
button.disabled = true;
|
|
36
|
-
button.textContent = "Opening…";
|
|
37
|
-
void stage(true).then((result) => {
|
|
38
|
-
button.textContent = result.ok ? "Opened — approve in Gateway" : result.error || "Could not open xopc";
|
|
39
|
-
button.disabled = false;
|
|
40
|
-
}).catch(() => {
|
|
41
|
-
button.textContent = "Could not open xopc";
|
|
42
|
-
button.disabled = false;
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
root.append(button);
|
|
46
|
-
document.documentElement.append(host);
|
|
47
|
-
}
|
|
48
|
-
if (hasPairingPayload()) {
|
|
49
|
-
void stage(false).catch(() => void 0);
|
|
50
|
-
mountOpenButton();
|
|
51
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e,n as t}from"./rolldown-runtime-aKtaBQYM.js";import{a as n,n as r,t as i}from"./vendor-react-BOUWij0V.js";import{a,i as o,o as s,r as c,s as l,t as u}from"./dist-CpzYSoz7.js";import{t as d}from"./circle-check-C23XjkUj.js";import{t as f}from"./x-B_N4OPjy.js";import{t as p}from"./messages-DH_HCgmw.js";import{n as m}from"./locale-store-CRNble2Z.js";import{t as h}from"./cn-JdwAhgNB.js";import{i as g}from"./vendor-swr-BIWyz7Rc.js";import{t as _}from"./button-B11EPpor.js";import{t as v}from"./skeleton-BwbyijEw.js";import{i as y,o as b}from"./settings-shell-dialog-layer-BU3UuiJr.js";import{B as x,Ic as S,Mi as C,R as w,St as T,ac as E,co as D,wt as O,xt as k,z as A}from"./index-C6PmD0cK.js";import{c as j,f as M,g as N,h as P,i as F,l as I,n as L,o as R,p as z,r as B,t as V,u as ee}from"./tunnel-api-CWsRPM84.js";import{t as H}from"./browser-BLJWD7WI.js";var U=e(n(),1),W=r(),G=e(H(),1),K={width:216,margin:4,errorCorrectionLevel:`M`,color:{dark:`#000000ff`,light:`#ffffffff`}};async function te(e){let t=await G.toString(e,{...K,type:`svg`});return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(t)}`}var q=i();function ne(){let e=p(m(e=>e.language)),t=e.endpointToolsSettings.deviceAccess.flow,n=e.tunnelSettings,{data:r,error:i,mutate:a}=g(`tunnel-status`,V,{refreshInterval:1500}),[o,s]=(0,U.useState)(!1),[c,l]=(0,U.useState)(!1),[u,d]=(0,U.useState)(!0),[f,h]=(0,U.useState)(`idle`),[y,b]=(0,U.useState)(!1),[S,C]=(0,U.useState)(``),E=(0,U.useRef)(!0);(0,U.useEffect)(()=>(E.current=!0,()=>{E.current=!1}),[]);let D=async()=>{if(!r)return;if(r.consentRequired&&!o){l(!0);return}let e=r.registrationSecret?.configured?null:x(),t,n=!1;b(!1);try{if(r.consentRequired&&await F(),!r.registrationSecret?.configured){h(`authorizing`);let r=sessionStorage.getItem(`device-pairing-oauth`),i=r?JSON.parse(r):null;t=i&&i.expiresAt>Date.now()?i.sessionId:(await O(`xopc-tunnel`)).sessionId,sessionStorage.setItem(`device-pairing-oauth`,JSON.stringify({sessionId:t,expiresAt:i&&i.expiresAt>Date.now()?i.expiresAt:Date.now()+5*6e4}));let a=``,o=Date.now()+5*6e4,s=!1;for(;E.current&&Date.now()<o;){let r=await T(t);if(r.authUrl&&r.authUrl!==a&&(a=r.authUrl,await A(a,e)||C(a)),r.status===`completed`){s=!0,n=!0;break}if(r.status===`failed`||r.status===`cancelled`)throw Error(`Authorization cancelled`);await new Promise(e=>setTimeout(e,1e3))}if(!E.current)return;if(!s)throw Error(`Authorization expired`);await B()}if(!E.current)return;h(`starting`),await R(),u&&await L({autoStart:!0}),await a(),n=!0}catch{n=!0,E.current&&b(!0)}finally{w(e),n&&(sessionStorage.removeItem(`device-pairing-oauth`),t&&k(t).catch(()=>{})),E.current&&(h(`idle`),C(``))}};if(!r&&!i)return(0,q.jsxs)(`div`,{className:`space-y-5`,children:[(0,q.jsx)(v,{className:`h-5 w-4/5`}),(0,q.jsx)(v,{className:`h-24 w-full`})]});let j=f===`starting`||r?.state===`connecting`||r?.state===`reconnecting`;return(0,q.jsxs)(`div`,{className:`flex min-h-full flex-col`,children:[(0,q.jsx)(`p`,{className:`text-sm leading-relaxed text-fg-muted`,children:j?t.preparingHint:f===`authorizing`?t.authorizing:t.enableHint}),S&&f===`authorizing`?(0,q.jsx)(`a`,{href:S,target:`_blank`,rel:`noopener noreferrer`,className:`mt-4 inline-flex min-h-11 items-center text-sm text-accent`,children:t.openAuthorization}):null,j?(0,q.jsxs)(`div`,{className:`mt-8 space-y-3`,"aria-label":t.preparing,children:[(0,q.jsx)(v,{className:`h-2 w-full`}),(0,q.jsx)(`p`,{className:`text-sm text-fg-muted`,children:t.preparing})]}):null,(c||r?.consentRequired)&&f===`idle`?(0,q.jsxs)(`div`,{className:`mt-6 space-y-4`,children:[(0,q.jsxs)(`details`,{open:c,className:`text-sm text-fg-muted`,children:[(0,q.jsx)(`summary`,{className:`cursor-pointer py-2`,children:t.details}),(0,q.jsx)(`p`,{className:`mt-2`,children:n.consentIntro}),(0,q.jsx)(`ul`,{className:`mt-3 list-disc space-y-2 pl-5`,children:[n.consentBullet1,n.consentBullet2,n.consentBullet3].map(e=>(0,q.jsx)(`li`,{children:e},e))})]}),(0,q.jsxs)(`label`,{className:`flex items-start gap-3 text-sm text-fg`,children:[(0,q.jsx)(`input`,{type:`checkbox`,checked:o,onChange:e=>s(e.target.checked),className:`mt-1 size-4 accent-accent`}),n.consentCheckbox]})]}):null,y||i||r?.state===`error`?(0,q.jsx)(`p`,{role:`alert`,className:`mt-4 text-sm text-danger`,children:t.failed}):null,(0,q.jsx)(`div`,{className:`flex-1`}),(0,q.jsxs)(`label`,{className:`my-5 flex items-center gap-3 text-sm text-fg-muted`,children:[(0,q.jsx)(`input`,{type:`checkbox`,checked:u,disabled:f!==`idle`,onChange:e=>d(e.target.checked),className:`size-4 accent-accent`}),t.autoStart]}),(0,q.jsx)(_,{className:`w-full`,variant:`primary`,disabled:!r||f!==`idle`||j||c&&!o,onClick:()=>void D(),children:f===`authorizing`?t.authorizing:j?t.preparing:y?t.retry:t.enable}),i&&!r?(0,q.jsx)(_,{variant:`ghost`,onClick:()=>void a(),children:t.retry}):null]})}var J=t({DevicePairingWizard:()=>Y});function Y(e){let t=(0,W.c)(15),{open:n,onOpenChange:r,onPaired:i,targetKind:a}=e,[o,s]=(0,U.useState)(a),c,l;t[0]===a?(c=t[1],l=t[2]):(c=()=>{s(a)},l=[a],t[0]=a,t[1]=c,t[2]=l),(0,U.useEffect)(c,l);let d;t[3]!==r||t[4]!==a?(d=e=>{e||s(a),r(e)},t[3]=r,t[4]=a,t[5]=d):d=t[5];let f=d,p;t[6]!==f||t[7]!==i||t[8]!==n||t[9]!==o?(p=n?o?(0,q.jsx)(re,{targetKind:o,onClose:()=>f(!1),onPaired:i}):(0,q.jsx)(X,{onChoose:s,onClose:()=>f(!1)}):null,t[6]=f,t[7]=i,t[8]=n,t[9]=o,t[10]=p):p=t[10];let m;return t[11]!==f||t[12]!==n||t[13]!==p?(m=(0,q.jsx)(u,{open:n,onOpenChange:f,children:p}),t[11]=f,t[12]=n,t[13]=p,t[14]=m):m=t[14],m}function X(e){let t=(0,W.c)(45),{onChoose:n,onClose:r}=e,i=m(Z),u;t[0]===i?u=t[1]:(u=p(i),t[0]=i,t[1]=u);let d=u.endpointToolsSettings.deviceAccess,g;t[2]===Symbol.for(`react.memo_cache_sentinel`)?(g=(0,q.jsx)(a,{className:h(`fixed inset-0 bg-scrim backdrop-blur-[1px]`,b)}),t[2]=g):g=t[2];let _;t[3]===Symbol.for(`react.memo_cache_sentinel`)?(_=h(`fixed left-1/2 top-1/2 flex h-[min(460px,calc(100dvh-48px))] w-[min(520px,calc(100vw-32px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-edge bg-surface-panel shadow-popover`,y),t[3]=_):_=t[3];let v;t[4]===Symbol.for(`react.memo_cache_sentinel`)?(v=(0,q.jsx)(`span`,{className:`text-lg font-semibold tracking-tight text-fg`,children:`xopc`}),t[4]=v):v=t[4];let x;t[5]===Symbol.for(`react.memo_cache_sentinel`)?(x=(0,q.jsx)(f,{className:`size-4`}),t[5]=x):x=t[5];let C;t[6]!==d.cancel||t[7]!==r?(C=(0,q.jsxs)(`header`,{className:`flex shrink-0 items-center justify-between border-b border-edge-subtle px-6 py-3`,children:[v,(0,q.jsx)(`button`,{type:`button`,className:`flex size-10 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover`,"aria-label":d.cancel,onClick:r,children:x})]}),t[6]=d.cancel,t[7]=r,t[8]=C):C=t[8];let w;t[9]===d.chooseTitle?w=t[10]:(w=(0,q.jsx)(l,{className:`text-xl font-semibold tracking-tight text-fg`,children:d.chooseTitle}),t[9]=d.chooseTitle,t[10]=w);let T;t[11]===d.chooseHint?T=t[12]:(T=(0,q.jsx)(o,{className:`mt-2 text-sm leading-relaxed text-fg-muted`,children:d.chooseHint}),t[11]=d.chooseHint,t[12]=T);let D;t[13]===n?D=t[14]:(D=()=>n(`mobile`),t[13]=n,t[14]=D);let O;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,q.jsx)(E,{className:`size-5 text-accent`}),t[15]=O):O=t[15];let k;t[16]===d.mobileTitle?k=t[17]:(k=(0,q.jsx)(`span`,{className:`mt-4 block text-sm font-semibold text-fg`,children:d.mobileTitle}),t[16]=d.mobileTitle,t[17]=k);let A;t[18]===d.mobileHint?A=t[19]:(A=(0,q.jsx)(`span`,{className:`mt-1 block text-xs leading-5 text-fg-muted`,children:d.mobileHint}),t[18]=d.mobileHint,t[19]=A);let j;t[20]!==k||t[21]!==A||t[22]!==D?(j=(0,q.jsxs)(`button`,{type:`button`,className:`rounded-xl border border-edge bg-surface-base p-5 text-left hover:border-edge-strong hover:bg-surface-hover`,onClick:D,children:[O,k,A]}),t[20]=k,t[21]=A,t[22]=D,t[23]=j):j=t[23];let M;t[24]===n?M=t[25]:(M=()=>n(`browser`),t[24]=n,t[25]=M);let N;t[26]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,q.jsx)(S,{className:`size-5 text-accent`}),t[26]=N):N=t[26];let P;t[27]===d.browserTitle?P=t[28]:(P=(0,q.jsx)(`span`,{className:`mt-4 block text-sm font-semibold text-fg`,children:d.browserTitle}),t[27]=d.browserTitle,t[28]=P);let F;t[29]===d.browserHint?F=t[30]:(F=(0,q.jsx)(`span`,{className:`mt-1 block text-xs leading-5 text-fg-muted`,children:d.browserHint}),t[29]=d.browserHint,t[30]=F);let I;t[31]!==M||t[32]!==P||t[33]!==F?(I=(0,q.jsxs)(`button`,{type:`button`,className:`rounded-xl border border-edge bg-surface-base p-5 text-left hover:border-edge-strong hover:bg-surface-hover`,onClick:M,children:[N,P,F]}),t[31]=M,t[32]=P,t[33]=F,t[34]=I):I=t[34];let L;t[35]!==j||t[36]!==I?(L=(0,q.jsxs)(`div`,{className:`mt-7 grid gap-3 sm:grid-cols-2`,children:[j,I]}),t[35]=j,t[36]=I,t[37]=L):L=t[37];let R;t[38]!==L||t[39]!==w||t[40]!==T?(R=(0,q.jsxs)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-8 py-7`,children:[w,T,L]}),t[38]=L,t[39]=w,t[40]=T,t[41]=R):R=t[41];let z;return t[42]!==R||t[43]!==C?(z=(0,q.jsxs)(s,{children:[g,(0,q.jsxs)(c,{className:_,children:[C,R]})]}),t[42]=R,t[43]=C,t[44]=z):z=t[44],z}function Z(e){return e.language}function re({targetKind:e,onClose:t,onPaired:n}){let r=p(m(e=>e.language)),i=r.endpointToolsSettings.deviceAccess,u=i.flow,x=g(`device-pairing-readiness`,M,{refreshInterval:1500}),[S,w]=(0,U.useState)(),[T,E]=(0,U.useState)(!1),[O,k]=(0,U.useState)(!1),[A,F]=(0,U.useState)(Date.now()),[L,R]=(0,U.useState)(null),B=(0,U.useRef)(!0),V=(0,U.useRef)(null),H=(0,U.useRef)(!1),W=(0,U.useRef)(!1),G=g(S?[`device-pairing-setup`,S.id]:null,()=>z(S.id),{refreshInterval:1500}),K=G.data?.request,J=K?.status===`completed`,Y=g(J&&window.electronAPI?.system?`system-behavior`:null,()=>window.electronAPI.system.getBehavior()),X=D(()=>te(S?.universalLink??``),[S?.universalLink],{enabled:!!(S&&e===`mobile`),initial:null,errorData:null});(0,U.useEffect)(()=>{B.current=!0;let e=setInterval(()=>F(Date.now()),1e3);return()=>{B.current=!1,clearInterval(e),V.current&&j(V.current).catch(()=>{})}},[]);let Z=(0,U.useCallback)(async()=>{if(!H.current){H.current=!0,k(!0),E(!1);try{V.current&&await j(V.current),V.current=null,w(void 0);let t=await I(e);if(t.kind===`ready`){if(!B.current){await j(t.setup.id);return}V.current=t.setup.id,w(t.setup)}else await x.mutate()}catch{B.current&&E(!0)}finally{H.current=!1,B.current&&k(!1)}}},[x,e]);(0,U.useEffect)(()=>{x.data?.ready&&!S&&!T&&!H.current&&Z()},[x.data,S,T,Z]),(0,U.useEffect)(()=>{J&&!W.current&&(W.current=!0,n?.())},[J,n]);let re=async e=>{if(K){k(!0),E(!1);try{await ee(K,e),await G.mutate()}catch{E(!0),await G.mutate()}finally{B.current&&k(!1)}}},ie=(0,U.useRef)(0);(0,U.useEffect)(()=>{G.data&&(ie.current=G.data.serverTime-Date.now())},[G.data]);let Q=A+ie.current,ae=S&&Q>=S.expiresAt&&!K,oe=K&&[`expired`,`rejected`,`cancelled`].includes(K.status),$=e===`mobile`?{connect:i.mobileScan,connectHint:i.mobileScanHint,allowTitle:i.mobileAllowTitle,success:i.mobileSuccess,successHint:i.mobileSuccessHint}:{connect:i.browserConnect,connectHint:i.browserConnectHint,allowTitle:i.browserAllowTitle,success:i.browserSuccess,successHint:i.browserSuccessHint},se=J?$.success:K?.status===`pending`?$.allowTitle:K?.status===`approved`?u.waiting:S?$.connect:u.title;return(0,q.jsxs)(s,{children:[(0,q.jsx)(a,{className:h(`fixed inset-0 bg-scrim backdrop-blur-[1px]`,b)}),(0,q.jsxs)(c,{className:h(`fixed left-1/2 top-1/2 flex h-[min(600px,calc(100dvh-48px))] w-[min(560px,calc(100vw-32px))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-edge bg-surface-panel shadow-popover`,y),children:[(0,q.jsxs)(`header`,{className:`flex shrink-0 items-center justify-between border-b border-edge-subtle px-6 py-3`,children:[(0,q.jsx)(`span`,{className:`text-lg font-semibold tracking-tight text-fg`,children:`xopc`}),(0,q.jsx)(`button`,{type:`button`,className:`flex size-10 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover`,"aria-label":u.cancel,onClick:t,children:(0,q.jsx)(f,{className:`size-4`})})]}),(0,q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto px-8 py-7`,children:[J?(0,q.jsx)(d,{className:`mb-5 size-7 text-success`}):null,(0,q.jsx)(l,{className:`text-xl font-semibold tracking-tight text-fg`,children:se}),(0,q.jsx)(o,{className:`mt-2 text-sm leading-relaxed text-fg-muted`,children:J?$.successHint:K?K.displayName:S?$.connectHint:u.intro}),T||x.error||G.error||X.error?(0,q.jsx)(`p`,{role:`alert`,className:`mt-4 text-sm text-danger`,children:u.failed}):null,x.data?J?(0,q.jsxs)(q.Fragment,{children:[Y.data?(0,q.jsxs)(`label`,{className:`mt-8 flex items-center justify-between gap-4 border-y border-edge-subtle py-5 text-sm text-fg`,children:[i.background,(0,q.jsx)(`input`,{type:`checkbox`,role:`switch`,"aria-checked":Y.data.runInBackground??!1,disabled:!Y.data.backgroundSupported,checked:Y.data.runInBackground??!1,className:`size-5 accent-accent`,onChange:e=>{window.electronAPI.system.setBehavior({runInBackground:e.target.checked}).then(e=>Y.mutate(e.behavior)).catch(()=>E(!0))}})]}):null,(0,q.jsx)(`p`,{className:`mt-4 text-xs text-fg-muted`,children:i.sleep})]}):oe||ae?(0,q.jsxs)(`div`,{className:`mt-10 space-y-5`,children:[(0,q.jsx)(`p`,{className:`text-sm text-fg-muted`,children:ae||K?.status===`expired`?u.expired:u.declined}),(0,q.jsx)(_,{variant:`primary`,onClick:()=>void Z(),disabled:O,children:u.refresh})]}):K?(0,q.jsxs)(q.Fragment,{children:[(0,q.jsx)(`p`,{className:`mt-2 text-xs text-fg-muted`,children:u.scope}),(0,q.jsxs)(`p`,{className:`mt-10 font-mono text-4xl tracking-[0.16em] text-fg`,"aria-live":`polite`,children:[K.confirmationCode.slice(0,3),` `,K.confirmationCode.slice(3)]}),(0,q.jsx)(`p`,{className:`mt-3 text-xs text-fg-muted`,children:u.compare}),(0,q.jsxs)(`details`,{className:`mt-6 text-xs text-fg-muted`,children:[(0,q.jsx)(`summary`,{className:`cursor-pointer py-2`,children:u.access}),(0,q.jsx)(`p`,{children:u.accessHint})]})]}):S&&e===`mobile`?(0,q.jsxs)(q.Fragment,{children:[(0,q.jsx)(`div`,{className:`my-6 flex justify-center`,children:X.data?(0,q.jsx)(`img`,{src:X.data,alt:i.qrAlt,width:216,height:216,className:`size-[216px] max-w-full rounded-lg bg-white`}):(0,q.jsx)(v,{className:`size-[216px]`})}),(0,q.jsx)(`p`,{className:`text-center text-xs text-fg-muted`,children:S.expiresAt-Q<6e4?`${Math.max(0,Math.ceil((S.expiresAt-Q)/1e3))}s`:u.valid}),(0,q.jsxs)(`details`,{className:`mt-5 text-xs text-fg-muted`,children:[(0,q.jsx)(`summary`,{className:`cursor-pointer py-2`,children:u.fallback}),(0,q.jsx)(C,{text:S.universalLink,labels:{copy:i.copy,copied:i.copied,copyFailed:r.clipboard.copyFailed}})]})]}):S?(0,q.jsxs)(q.Fragment,{children:[(0,q.jsx)(_,{className:`mt-8 w-full`,variant:`primary`,onClick:()=>window.open(S.universalLink,`_blank`,`noopener,noreferrer`),children:i.openBrowserInvitation}),(0,q.jsx)(`p`,{className:`mt-3 text-center text-xs leading-5 text-fg-muted`,children:i.openBrowserInvitationHint}),(0,q.jsx)(`p`,{className:`mt-2 text-center text-xs text-fg-muted`,children:S.expiresAt-Q<6e4?`${Math.max(0,Math.ceil((S.expiresAt-Q)/1e3))}s`:u.valid}),(0,q.jsxs)(`details`,{className:`mt-5 text-xs text-fg-muted`,children:[(0,q.jsx)(`summary`,{className:`cursor-pointer py-2`,children:i.browserInvitationFallback}),(0,q.jsx)(C,{text:S.universalLink,labels:{copy:i.copy,copied:i.copied,copyFailed:r.clipboard.copyFailed}})]})]}):x.data.ready?(0,q.jsx)(`div`,{className:`mt-8`,children:(0,q.jsx)(v,{className:`mx-auto size-[216px]`})}):(0,q.jsxs)(`div`,{className:`mt-6 flex flex-1 flex-col`,children:[L===`tailscale`?(0,q.jsx)(N,{embedded:!0}):L===`https`?(0,q.jsx)(P,{}):L===`choices`?(0,q.jsxs)(`div`,{className:`space-y-3`,children:[(0,q.jsx)(_,{variant:`secondary`,className:`w-full`,onClick:()=>R(`tailscale`),children:i.useTailscale}),(0,q.jsx)(_,{variant:`secondary`,className:`w-full`,onClick:()=>R(`https`),children:i.useOwnHttps})]}):(0,q.jsx)(ne,{}),(0,q.jsx)(_,{variant:`ghost`,className:`mt-3 w-full`,onClick:()=>R(L?null:`choices`),children:L?u.back:u.existing})]}):(0,q.jsxs)(`div`,{className:`mt-8 space-y-4`,children:[(0,q.jsx)(v,{className:`h-5 w-3/4`}),(0,q.jsx)(v,{className:`h-40 w-full`})]})]}),J||K?.status===`pending`||T?(0,q.jsx)(`footer`,{className:`shrink-0 px-8 pb-6`,children:J?(0,q.jsx)(_,{className:`w-full`,variant:`primary`,onClick:t,children:u.done}):K?.status===`pending`?(0,q.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,q.jsx)(_,{className:`w-full`,variant:`primary`,disabled:O,onClick:()=>void re(`approve`),children:u.allow}),(0,q.jsx)(_,{className:`w-full`,variant:`ghost`,disabled:O,onClick:()=>void re(`reject`),children:u.cancel})]}):(0,q.jsx)(_,{className:`w-full`,variant:`primary`,disabled:O,onClick:()=>void Z(),children:u.retry})}):null]})]})}export{J as n,Y as t};
|