@getpaseo/server 0.9.2 → 0.10.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/dist/scripts/supervisor.js +51 -21
- package/dist/server/server/agent/agent-manager.js +22 -2
- package/dist/server/server/agent/agent-prompt.js +12 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +3 -0
- package/dist/server/server/agent/mcp-shared.d.ts +5 -1
- package/dist/server/server/agent/mcp-shared.js +2 -1
- package/dist/server/server/agent/provider-launch-config.d.ts +2 -2
- package/dist/server/server/agent/provider-launch-config.js +1 -1
- package/dist/server/server/agent/provider-registry.js +2 -2
- package/dist/server/server/agent/providers/acp-agent.js +20 -11
- package/dist/server/server/agent/providers/claude/agent.d.ts +1 -2
- package/dist/server/server/agent/providers/claude/agent.js +10 -9
- package/dist/server/server/agent/providers/claude/models.d.ts +1 -1
- package/dist/server/server/agent/providers/claude/models.js +1 -5
- package/dist/server/server/agent/providers/claude/project-dir.d.ts +1 -0
- package/dist/server/server/agent/providers/claude/project-dir.js +5 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +6 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +10 -1
- package/dist/server/server/agent/providers/generic-acp-agent.js +3 -1
- package/dist/server/server/agent/providers/omp/agent.js +23 -6
- package/dist/server/server/agent/providers/omp/rpc-types.d.ts +1 -0
- package/dist/server/server/agent/providers/omp/rpc-types.js +1 -0
- package/dist/server/server/agent/providers/opencode/bridge.d.ts +5 -1
- package/dist/server/server/agent/providers/opencode/bridge.js +56 -5
- package/dist/server/server/agent/providers/opencode/http-error.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/http-error.js +13 -0
- package/dist/server/server/agent/providers/opencode/runtime-client.d.ts +38 -0
- package/dist/server/server/agent/providers/opencode/runtime-client.js +131 -0
- package/dist/server/server/agent/providers/opencode/runtime-notice.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/runtime-notice.js +47 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +19 -8
- package/dist/server/server/agent/providers/opencode/v2/agent.d.ts +36 -0
- package/dist/server/server/agent/providers/opencode/v2/agent.js +211 -0
- package/dist/server/server/agent/providers/opencode/v2/api.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/api.js +2 -0
- package/dist/server/server/agent/providers/opencode/v2/bridge-plugin.bundle.mjs +6693 -0
- package/dist/server/server/agent/providers/opencode/v2/capabilities.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/v2/capabilities.js +13 -0
- package/dist/server/server/agent/providers/opencode/v2/children.d.ts +20 -0
- package/dist/server/server/agent/providers/opencode/v2/children.js +75 -0
- package/dist/server/server/agent/providers/opencode/v2/commands.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode/v2/commands.js +36 -0
- package/dist/server/server/agent/providers/opencode/v2/configuration.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/v2/configuration.js +64 -0
- package/dist/server/server/agent/providers/opencode/v2/history.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode/v2/history.js +15 -0
- package/dist/server/server/agent/providers/opencode/v2/mapping.d.ts +7 -0
- package/dist/server/server/agent/providers/opencode/v2/mapping.js +42 -0
- package/dist/server/server/agent/providers/opencode/v2/permissions.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/permissions.js +157 -0
- package/dist/server/server/agent/providers/opencode/v2/readiness.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/readiness.js +51 -0
- package/dist/server/server/agent/providers/opencode/v2/runtime.d.ts +36 -0
- package/dist/server/server/agent/providers/opencode/v2/runtime.js +230 -0
- package/dist/server/server/agent/providers/opencode/v2/session.d.ts +84 -0
- package/dist/server/server/agent/providers/opencode/v2/session.js +468 -0
- package/dist/server/server/agent/providers/opencode/v2/structured-output.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/v2/structured-output.js +32 -0
- package/dist/server/server/agent/providers/opencode/v2/timeline.d.ts +28 -0
- package/dist/server/server/agent/providers/opencode/v2/timeline.js +154 -0
- package/dist/server/server/agent/providers/opencode/v2/turns.d.ts +40 -0
- package/dist/server/server/agent/providers/opencode/v2/turns.js +226 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +26 -7
- package/dist/server/server/agent/providers/opencode-agent.js +100 -40
- package/dist/server/server/agent/providers/pi/agent.d.ts +6 -6
- package/dist/server/server/agent/providers/pi/agent.js +114 -165
- package/dist/server/server/agent/providers/pi/extensions/child-session.d.ts +4 -0
- package/dist/server/server/agent/providers/pi/extensions/child-session.js +59 -0
- package/dist/server/server/agent/providers/pi/extensions/contract.d.ts +68 -0
- package/dist/server/server/agent/providers/pi/extensions/contract.js +2 -0
- package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.js +159 -0
- package/dist/server/server/agent/providers/pi/extensions/host.d.ts +28 -0
- package/dist/server/server/agent/providers/pi/extensions/host.js +94 -0
- package/dist/server/server/agent/providers/pi/extensions/index.d.ts +7 -0
- package/dist/server/server/agent/providers/pi/extensions/index.js +6 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.js +138 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.js +38 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.js +63 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.js +48 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.js +131 -0
- package/dist/server/server/agent/providers/pi/extensions/registry.d.ts +2 -0
- package/dist/server/server/agent/providers/pi/extensions/registry.js +21 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.js +180 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.js +51 -0
- package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.d.ts +22 -0
- package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.js +66 -0
- package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.d.ts +4 -0
- package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.js +79 -0
- package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.js +149 -0
- package/dist/server/server/agent/providers/pi/history-mapper.d.ts +7 -2
- package/dist/server/server/agent/providers/pi/history-mapper.js +78 -35
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +170 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +355 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +1 -6
- package/dist/server/server/agent/providers/pi/tool-call-mapper.js +1 -97
- package/dist/server/server/agent/tools/paseo-tools.js +22 -15
- package/dist/server/server/auth.d.ts +1 -2
- package/dist/server/server/auth.js +8 -14
- package/dist/server/server/bootstrap.js +10 -2
- package/dist/server/server/daemon-instance.d.ts +1 -0
- package/dist/server/server/daemon-instance.js +1 -0
- package/dist/server/server/exports.d.ts +1 -1
- package/dist/server/server/exports.js +1 -1
- package/dist/server/server/local-credential.d.ts +6 -0
- package/dist/server/server/local-credential.js +82 -0
- package/dist/server/server/session-admission-auth.d.ts +16 -0
- package/dist/server/server/session-admission-auth.js +26 -0
- package/dist/server/server/websocket-server.d.ts +6 -1
- package/dist/server/server/websocket-server.js +132 -55
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.br → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js → desktop-attachment-store-d5d3402a752965d3037191484127123b.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.br → desktop-attachment-store-d5d3402a752965d3037191484127123b.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-d5d3402a752965d3037191484127123b.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-8868523fc6d311c5da524c067d1cd426.js → index-136fa41d4adb7ec5c6392839cc354136.js} +1580 -1576
- package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.br → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js → native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js} +3 -3
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.gz +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/src/server/agent/provider-launch-config.js +1 -1
- package/dist/src/server/daemon-instance.js +1 -0
- package/dist/src/server/local-credential.js +82 -0
- package/package.json +8 -7
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.gz +0 -0
|
@@ -141,6 +141,7 @@ import { terminateWithTreeKill } from "../utils/tree-kill.js";
|
|
|
141
141
|
import { withTimeout } from "../utils/promise-timeout.js";
|
|
142
142
|
import { isHostnameAllowed } from "./hostnames.js";
|
|
143
143
|
import { createRequireBearerMiddleware, isAgentMcpRequestAuthorized, } from "./auth.js";
|
|
144
|
+
import { deleteLocalCredential, writeLocalCredential } from "./local-credential.js";
|
|
144
145
|
import { createWebUiMiddleware } from "./web-ui.js";
|
|
145
146
|
import { WorkspaceAutoName } from "./workspace-auto-name.js";
|
|
146
147
|
import { createGitMutationService } from "./session/git-mutation/git-mutation-service.js";
|
|
@@ -486,7 +487,9 @@ export async function createPaseoDaemon(config, rootLogger, dependencies = {}) {
|
|
|
486
487
|
// static app files load without the daemon password while API/WebSocket calls
|
|
487
488
|
// remain protected.
|
|
488
489
|
mountWebUi(app, config, logger);
|
|
489
|
-
|
|
490
|
+
let localCredential = null;
|
|
491
|
+
const daemonAuth = { ...config.auth, localCredential: () => localCredential };
|
|
492
|
+
app.use(createRequireBearerMiddleware(daemonAuth, (context) => {
|
|
490
493
|
logger.warn(context, "Rejected HTTP request with invalid daemon password");
|
|
491
494
|
}));
|
|
492
495
|
app.use(express.json());
|
|
@@ -1170,6 +1173,7 @@ export async function createPaseoDaemon(config, rootLogger, dependencies = {}) {
|
|
|
1170
1173
|
const start = async () => {
|
|
1171
1174
|
let mainStarted = false;
|
|
1172
1175
|
try {
|
|
1176
|
+
localCredential = await writeLocalCredential(config.paseoHome);
|
|
1173
1177
|
if (serviceProxyListenTarget) {
|
|
1174
1178
|
const boundServiceProxyTarget = await serviceProxy.startStandalone({
|
|
1175
1179
|
listenTarget: serviceProxyListenTarget,
|
|
@@ -1241,7 +1245,7 @@ export async function createPaseoDaemon(config, rootLogger, dependencies = {}) {
|
|
|
1241
1245
|
daemonStatusRpc: dependencies.serverFeatureOverrides?.daemonStatusRpc,
|
|
1242
1246
|
relayConfig: dependencies.serverFeatureOverrides?.relayConfig,
|
|
1243
1247
|
startPaused: true,
|
|
1244
|
-
}, workspaceAutoName,
|
|
1248
|
+
}, workspaceAutoName, daemonAuth, speechService, terminalManager, {
|
|
1245
1249
|
finalTimeoutMs: config.dictationFinalTimeoutMs,
|
|
1246
1250
|
}, daemonVersion, (intent) => {
|
|
1247
1251
|
try {
|
|
@@ -1310,6 +1314,8 @@ export async function createPaseoDaemon(config, rootLogger, dependencies = {}) {
|
|
|
1310
1314
|
scriptHealthMonitor.start();
|
|
1311
1315
|
}
|
|
1312
1316
|
catch (error) {
|
|
1317
|
+
localCredential = null;
|
|
1318
|
+
await deleteLocalCredential(config.paseoHome);
|
|
1313
1319
|
unsubscribePluginProviders();
|
|
1314
1320
|
await pluginRuntime.stopAllPlugins().catch(() => undefined);
|
|
1315
1321
|
await serviceProxy.stopStandalone().catch(() => undefined);
|
|
@@ -1322,6 +1328,8 @@ export async function createPaseoDaemon(config, rootLogger, dependencies = {}) {
|
|
|
1322
1328
|
}
|
|
1323
1329
|
};
|
|
1324
1330
|
const stop = async () => {
|
|
1331
|
+
localCredential = null;
|
|
1332
|
+
await deleteLocalCredential(config.paseoHome);
|
|
1325
1333
|
// Stop tracking plugin provider registrations before anything tears plugins
|
|
1326
1334
|
// down, so plugin shutdown cannot withdraw a provider from under an agent
|
|
1327
1335
|
// that is still open. Plugins themselves are stopped once every session
|
|
@@ -2,6 +2,7 @@ import { type PidLockInfo } from "./pid-lock.js";
|
|
|
2
2
|
export { resolvePaseoHome } from "./paseo-home.js";
|
|
3
3
|
export { ensurePrivateDirectory } from "./private-files.js";
|
|
4
4
|
export { daemonLaunchEnvironment } from "./config-environment.js";
|
|
5
|
+
export { readLocalCredentialForTarget } from "./local-credential.js";
|
|
5
6
|
export { isSamePidLock as isSameDaemonInstance, type PidLockInfo as DaemonInstance, } from "./pid-lock.js";
|
|
6
7
|
export declare class DaemonInstanceError extends Error {
|
|
7
8
|
readonly code: string;
|
|
@@ -18,6 +18,7 @@ const killTree = (pid, signal) => new Promise((resolve, reject) => treeKill(pid,
|
|
|
18
18
|
export { resolvePaseoHome } from "./paseo-home.js";
|
|
19
19
|
export { ensurePrivateDirectory } from "./private-files.js";
|
|
20
20
|
export { daemonLaunchEnvironment } from "./config-environment.js";
|
|
21
|
+
export { readLocalCredentialForTarget } from "./local-credential.js";
|
|
21
22
|
export { isSamePidLock as isSameDaemonInstance, } from "./pid-lock.js";
|
|
22
23
|
export class DaemonInstanceError extends Error {
|
|
23
24
|
constructor(code, message) {
|
|
@@ -4,7 +4,7 @@ export { resolvePaseoHome } from "./paseo-home.js";
|
|
|
4
4
|
export { getOrCreateServerId } from "./server-id.js";
|
|
5
5
|
export { createRootLogger, type LogLevel, type LogFormat } from "./logger.js";
|
|
6
6
|
export { loadPersistedConfig, savePersistedConfig, type PersistedConfig, } from "./persisted-config.js";
|
|
7
|
-
export { hashDaemonPassword,
|
|
7
|
+
export { hashDaemonPassword, isBearerTokenValidAsync } from "./auth.js";
|
|
8
8
|
export { generateLocalPairingOffer, type LocalPairingOffer } from "./pairing-offer.js";
|
|
9
9
|
export { ConnectionOfferSchema, decodeOfferFragmentPayload, parseConnectionOfferFromUrl, type ConnectionOffer, } from "@getpaseo/protocol/connection-offer";
|
|
10
10
|
export { buildRelayWebSocketUrl } from "@getpaseo/protocol/daemon-endpoints";
|
|
@@ -5,7 +5,7 @@ export { resolvePaseoHome } from "./paseo-home.js";
|
|
|
5
5
|
export { getOrCreateServerId } from "./server-id.js";
|
|
6
6
|
export { createRootLogger } from "./logger.js";
|
|
7
7
|
export { loadPersistedConfig, savePersistedConfig, } from "./persisted-config.js";
|
|
8
|
-
export { hashDaemonPassword,
|
|
8
|
+
export { hashDaemonPassword, isBearerTokenValidAsync } from "./auth.js";
|
|
9
9
|
export { generateLocalPairingOffer } from "./pairing-offer.js";
|
|
10
10
|
export { ConnectionOfferSchema, decodeOfferFragmentPayload, parseConnectionOfferFromUrl, } from "@getpaseo/protocol/connection-offer";
|
|
11
11
|
export { buildRelayWebSocketUrl } from "@getpaseo/protocol/daemon-endpoints";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function writeLocalCredential(home: string): Promise<string>;
|
|
2
|
+
export declare function deleteLocalCredential(home: string): Promise<void>;
|
|
3
|
+
export declare function readLocalCredential(home: string): string | null;
|
|
4
|
+
export declare function matchesLocalCredential(expected: string, candidate: string): boolean;
|
|
5
|
+
export declare function readLocalCredentialForTarget(home: string, target: string): string | null;
|
|
6
|
+
//# sourceMappingURL=local-credential.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { chmod, rename, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { normalizeLoopbackToLocalhost, parseConnectionUri, } from "@getpaseo/protocol/daemon-endpoints";
|
|
6
|
+
const FILE_NAME = "local-credential";
|
|
7
|
+
export async function writeLocalCredential(home) {
|
|
8
|
+
const token = randomBytes(32).toString("base64url");
|
|
9
|
+
const temporary = join(home, `${FILE_NAME}.${randomUUID()}.tmp`);
|
|
10
|
+
try {
|
|
11
|
+
await writeFile(temporary, `${token}\n`, { mode: 0o600, flag: "wx" });
|
|
12
|
+
await chmod(temporary, 0o600);
|
|
13
|
+
await rename(temporary, join(home, FILE_NAME));
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await unlink(temporary).catch((error) => {
|
|
18
|
+
if (error.code !== "ENOENT")
|
|
19
|
+
throw error;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function deleteLocalCredential(home) {
|
|
24
|
+
await unlink(join(home, FILE_NAME)).catch((error) => {
|
|
25
|
+
if (error.code !== "ENOENT")
|
|
26
|
+
throw error;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function readLocalCredential(home) {
|
|
30
|
+
try {
|
|
31
|
+
const token = readFileSync(join(home, FILE_NAME), "utf8").trim();
|
|
32
|
+
return /^[A-Za-z0-9_-]{43}$/.test(token) ? token : null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function matchesLocalCredential(expected, candidate) {
|
|
39
|
+
const actual = Buffer.from(candidate);
|
|
40
|
+
const reference = Buffer.from(expected);
|
|
41
|
+
return actual.length === reference.length && timingSafeEqual(actual, reference);
|
|
42
|
+
}
|
|
43
|
+
function normalizeTarget(target) {
|
|
44
|
+
try {
|
|
45
|
+
const trimmed = target.trim();
|
|
46
|
+
if (trimmed.startsWith("tcp://")) {
|
|
47
|
+
const parsed = parseConnectionUri(trimmed);
|
|
48
|
+
const endpoint = parsed.isIpv6
|
|
49
|
+
? `[${parsed.host}]:${parsed.port}`
|
|
50
|
+
: `${parsed.host}:${parsed.port}`;
|
|
51
|
+
return normalizeLoopbackToLocalhost(endpoint);
|
|
52
|
+
}
|
|
53
|
+
if (trimmed.startsWith("unix://") || trimmed.startsWith("pipe://"))
|
|
54
|
+
return trimmed;
|
|
55
|
+
if (trimmed.startsWith("/"))
|
|
56
|
+
return `unix://${trimmed}`;
|
|
57
|
+
if (trimmed.startsWith("\\\\.\\pipe\\"))
|
|
58
|
+
return `pipe://${trimmed}`;
|
|
59
|
+
if (/^\d+$/.test(trimmed))
|
|
60
|
+
return `localhost:${trimmed}`;
|
|
61
|
+
return normalizeLoopbackToLocalhost(trimmed);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function readLocalCredentialForTarget(home, target) {
|
|
68
|
+
try {
|
|
69
|
+
const lock = JSON.parse(readFileSync(join(home, "paseo.pid"), "utf8"));
|
|
70
|
+
if (typeof lock.listen !== "string")
|
|
71
|
+
return null;
|
|
72
|
+
const selected = normalizeTarget(target);
|
|
73
|
+
const listening = normalizeTarget(lock.listen);
|
|
74
|
+
if (!selected || !listening || selected !== listening)
|
|
75
|
+
return null;
|
|
76
|
+
return readLocalCredential(home);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=local-credential.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WSHelloMessage } from "@getpaseo/protocol/messages";
|
|
2
|
+
import type { SessionAdmission } from "./websocket-server.js";
|
|
3
|
+
export type AdmissionFailure = "password_required" | "incorrect_password";
|
|
4
|
+
type AdmissionResolution = {
|
|
5
|
+
admission: SessionAdmission;
|
|
6
|
+
} | {
|
|
7
|
+
rejection: AdmissionFailure;
|
|
8
|
+
};
|
|
9
|
+
export declare function resolveSessionAdmission(input: {
|
|
10
|
+
credential: WSHelloMessage["auth"];
|
|
11
|
+
passwordHash: string | undefined;
|
|
12
|
+
localCredential: string | null;
|
|
13
|
+
transport: "direct" | "relay";
|
|
14
|
+
}): Promise<AdmissionResolution>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=session-admission-auth.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { compare } from "bcryptjs";
|
|
2
|
+
import { OWNER_PERMISSIONS } from "./authorization/index.js";
|
|
3
|
+
import { matchesLocalCredential } from "./local-credential.js";
|
|
4
|
+
export async function resolveSessionAdmission(input) {
|
|
5
|
+
const { credential, passwordHash, localCredential, transport } = input;
|
|
6
|
+
if (!passwordHash) {
|
|
7
|
+
return { admission: { principalId: "owner", permissions: OWNER_PERMISSIONS } };
|
|
8
|
+
}
|
|
9
|
+
if (!credential) {
|
|
10
|
+
// COMPAT(relayPasswordOptional): added in v0.9.1, remove once release N mobile builds are live on App Store and Play.
|
|
11
|
+
if (transport === "relay") {
|
|
12
|
+
return { admission: { principalId: "owner", permissions: OWNER_PERMISSIONS } };
|
|
13
|
+
}
|
|
14
|
+
return { rejection: "password_required" };
|
|
15
|
+
}
|
|
16
|
+
if (credential.kind === "localCredential") {
|
|
17
|
+
if (localCredential && matchesLocalCredential(localCredential, credential.token)) {
|
|
18
|
+
return { admission: { principalId: "owner", permissions: OWNER_PERMISSIONS } };
|
|
19
|
+
}
|
|
20
|
+
return { rejection: "incorrect_password" };
|
|
21
|
+
}
|
|
22
|
+
return (await compare(credential.password, passwordHash))
|
|
23
|
+
? { admission: { principalId: "owner", permissions: OWNER_PERMISSIONS } }
|
|
24
|
+
: { rejection: "incorrect_password" };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=session-admission-auth.js.map
|
|
@@ -91,6 +91,8 @@ export declare class VoiceAssistantWebSocketServer {
|
|
|
91
91
|
private readonly workspaceAutoName;
|
|
92
92
|
private readonly downloadTokenStore;
|
|
93
93
|
private readonly paseoHome;
|
|
94
|
+
private readonly passwordHash;
|
|
95
|
+
private readonly credentialSource;
|
|
94
96
|
private readonly worktreesRoot;
|
|
95
97
|
private readonly daemonConfigStore;
|
|
96
98
|
private readonly pushNotifications;
|
|
@@ -147,7 +149,7 @@ export declare class VoiceAssistantWebSocketServer {
|
|
|
147
149
|
publishProjectUpdate(update: ProjectUpdate): void;
|
|
148
150
|
publishSpeechReadiness(readiness: SpeechReadinessSnapshot | null): void;
|
|
149
151
|
updateServerCapabilities(capabilities: ServerCapabilities | null | undefined): void;
|
|
150
|
-
attachExternalSocket(ws: WebSocketLike, metadata?: ExternalSocketMetadata, admission?: SessionAdmission, initialHello?: WSHelloMessage): Promise<void>;
|
|
152
|
+
attachExternalSocket(ws: WebSocketLike, metadata?: ExternalSocketMetadata, admission?: SessionAdmission | null, initialHello?: WSHelloMessage): Promise<void>;
|
|
151
153
|
attachPluginSocket(pluginId: string, ws: WebSocketLike): Promise<{
|
|
152
154
|
closed: Promise<void>;
|
|
153
155
|
}>;
|
|
@@ -170,6 +172,9 @@ export declare class VoiceAssistantWebSocketServer {
|
|
|
170
172
|
private createSocketSession;
|
|
171
173
|
private clearPendingConnection;
|
|
172
174
|
private handleHello;
|
|
175
|
+
private handleHelloSafely;
|
|
176
|
+
private admitPendingHello;
|
|
177
|
+
private rejectHello;
|
|
173
178
|
private resumeSession;
|
|
174
179
|
private buildServerInfoStatusPayload;
|
|
175
180
|
private createServerInfoMessage;
|
|
@@ -19,7 +19,8 @@ import { createPushNotifications, } from "./push/index.js";
|
|
|
19
19
|
import { computeNotificationPlan, isPushEligibleAttentionReason, } from "./agent-attention-policy.js";
|
|
20
20
|
import { buildAgentAttentionNotificationPayload, findLatestPermissionRequest, } from "@getpaseo/protocol/agent-attention-notification";
|
|
21
21
|
import { createGitHubService } from "../services/github-service.js";
|
|
22
|
-
import { extractWsBearerProtocol, extractWsBearerToken,
|
|
22
|
+
import { extractHttpBearerToken, extractWsBearerProtocol, extractWsBearerToken, isBearerTokenValidAsync, } from "./auth.js";
|
|
23
|
+
import { resolveSessionAdmission } from "./session-admission-auth.js";
|
|
23
24
|
import { WebSocketRuntimeMetricsWindow, } from "./websocket/runtime-metrics.js";
|
|
24
25
|
import { ProviderUsageService } from "../services/quota-fetcher/service.js";
|
|
25
26
|
import { getProcessMemoryDiagnostics, getProcessUptimeSeconds } from "./process-diagnostics.js";
|
|
@@ -331,6 +332,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
331
332
|
throw new MissingDaemonVersionError();
|
|
332
333
|
}
|
|
333
334
|
this.daemonVersion = daemonVersion.trim();
|
|
335
|
+
this.credentialSource = auth;
|
|
334
336
|
this.daemonRuntimeConfig = daemonRuntimeConfig;
|
|
335
337
|
this.browserToolsBroker = browserToolsBroker ?? null;
|
|
336
338
|
this.hubRelationships = hubRelationships ?? null;
|
|
@@ -354,6 +356,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
354
356
|
this.workspaceAutoName = workspaceAutoName;
|
|
355
357
|
this.downloadTokenStore = downloadTokenStore;
|
|
356
358
|
this.paseoHome = paseoHome;
|
|
359
|
+
this.passwordHash = auth?.password;
|
|
357
360
|
this.worktreesRoot = daemonRuntimeConfig?.worktreesRoot;
|
|
358
361
|
this.daemonConfigStore = daemonConfigStore;
|
|
359
362
|
this.mcpBaseUrl = mcpBaseUrl;
|
|
@@ -523,19 +526,29 @@ export class VoiceAssistantWebSocketServer {
|
|
|
523
526
|
}
|
|
524
527
|
}
|
|
525
528
|
async attachAuthenticatedSocket(ws, request, password) {
|
|
526
|
-
|
|
527
|
-
|
|
529
|
+
// Header validation is asynchronous. Buffer frames until the socket has a
|
|
530
|
+
// pending hello handler so an eager client cannot lose its first message.
|
|
531
|
+
ws.pause();
|
|
532
|
+
try {
|
|
533
|
+
// COMPAT(headerAuth): added in v0.9.1, remove after 2027-03-24.
|
|
528
534
|
const protocol = extractWsBearerProtocol(request.headers["sec-websocket-protocol"]);
|
|
529
|
-
const token = extractWsBearerToken(protocol);
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
535
|
+
const token = extractHttpBearerToken(request.headers.authorization) ?? extractWsBearerToken(protocol);
|
|
536
|
+
const hasHeaderCredential = token !== null;
|
|
537
|
+
if (password && hasHeaderCredential) {
|
|
538
|
+
const requestMetadata = extractSocketRequestMetadata(request);
|
|
539
|
+
const isAuthorized = await isBearerTokenValidAsync({ password, token });
|
|
540
|
+
if (!isAuthorized) {
|
|
541
|
+
const reason = "Incorrect password";
|
|
542
|
+
this.logger.warn({ ...requestMetadata, hasToken: true }, "Rejected WebSocket connection with invalid daemon password");
|
|
543
|
+
ws.close(WS_CLOSE_DAEMON_AUTH_FAILED, reason);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
536
546
|
}
|
|
547
|
+
await this.attachSocket(ws, request, undefined, false, hasHeaderCredential ? OWNER_SESSION_ADMISSION : null);
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
ws.resume();
|
|
537
551
|
}
|
|
538
|
-
await this.attachSocket(ws, request);
|
|
539
552
|
}
|
|
540
553
|
broadcast(message) {
|
|
541
554
|
if (message.type === "session") {
|
|
@@ -567,7 +580,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
567
580
|
this.serverCapabilities = next;
|
|
568
581
|
this.broadcastCapabilitiesUpdate();
|
|
569
582
|
}
|
|
570
|
-
async attachExternalSocket(ws, metadata, admission =
|
|
583
|
+
async attachExternalSocket(ws, metadata, admission = null, initialHello) {
|
|
571
584
|
if (metadata?.transport === "relay") {
|
|
572
585
|
this.incrementRuntimeCounter("relayExternalSocketAttached");
|
|
573
586
|
}
|
|
@@ -584,7 +597,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
584
597
|
this.pluginSocketIds.set(ws, pluginId);
|
|
585
598
|
this.pluginSocketCleanup.set(ws, resolve);
|
|
586
599
|
try {
|
|
587
|
-
await this.attachSocket(ws, undefined, undefined, true);
|
|
600
|
+
await this.attachSocket(ws, undefined, undefined, true, OWNER_SESSION_ADMISSION);
|
|
588
601
|
}
|
|
589
602
|
catch (error) {
|
|
590
603
|
this.pluginSocketIds.delete(ws);
|
|
@@ -595,7 +608,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
595
608
|
}
|
|
596
609
|
updatePrincipalPermissions(principalId, permissions) {
|
|
597
610
|
for (const pending of this.pendingConnections.values()) {
|
|
598
|
-
if (pending.admission
|
|
611
|
+
if (pending.admission?.principalId === principalId) {
|
|
599
612
|
pending.admission = { ...pending.admission, permissions };
|
|
600
613
|
}
|
|
601
614
|
}
|
|
@@ -818,7 +831,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
818
831
|
this.sendBinaryToClient(ws, frame, false);
|
|
819
832
|
}
|
|
820
833
|
}
|
|
821
|
-
async attachSocket(ws, request, metadata, allowDuringStartup = false, admission =
|
|
834
|
+
async attachSocket(ws, request, metadata, allowDuringStartup = false, admission = null, initialHello) {
|
|
822
835
|
if (this.connectionLifecycle === "stopping" ||
|
|
823
836
|
(this.connectionLifecycle === "starting" && !allowDuringStartup)) {
|
|
824
837
|
try {
|
|
@@ -838,6 +851,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
838
851
|
helloTimeout: null,
|
|
839
852
|
identity,
|
|
840
853
|
admission,
|
|
854
|
+
authenticating: false,
|
|
841
855
|
};
|
|
842
856
|
const timeout = setTimeout(() => {
|
|
843
857
|
if (this.pendingConnections.get(ws) !== pending) {
|
|
@@ -862,9 +876,8 @@ export class VoiceAssistantWebSocketServer {
|
|
|
862
876
|
...toConnectionLogFields(identity),
|
|
863
877
|
totalPendingConnections: this.pendingConnections.size,
|
|
864
878
|
}, "Client connected; awaiting hello");
|
|
865
|
-
if (initialHello)
|
|
866
|
-
this.
|
|
867
|
-
}
|
|
879
|
+
if (initialHello)
|
|
880
|
+
await this.handleHelloSafely({ ws, message: initialHello, pending });
|
|
868
881
|
}
|
|
869
882
|
createSessionConnection(params) {
|
|
870
883
|
const { ws, clientId, appVersion, clientCapabilities, connectionLogger, lifecycle, admission } = params;
|
|
@@ -1040,22 +1053,20 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1040
1053
|
this.pendingConnections.delete(ws);
|
|
1041
1054
|
return pending;
|
|
1042
1055
|
}
|
|
1043
|
-
handleHello(params) {
|
|
1056
|
+
async handleHello(params) {
|
|
1044
1057
|
const { ws, message, pending } = params;
|
|
1045
|
-
if (message.protocolVersion
|
|
1058
|
+
if (message.protocolVersion < 1) {
|
|
1046
1059
|
this.clearPendingConnection(ws);
|
|
1047
1060
|
pending.connectionLogger.warn({
|
|
1048
1061
|
receivedProtocolVersion: message.protocolVersion,
|
|
1049
1062
|
expectedProtocolVersion: WS_PROTOCOL_VERSION,
|
|
1050
1063
|
}, "Rejected hello due to protocol version mismatch");
|
|
1051
|
-
|
|
1052
|
-
ws.close(WS_CLOSE_INCOMPATIBLE_PROTOCOL, "Incompatible protocol version");
|
|
1053
|
-
}
|
|
1054
|
-
catch {
|
|
1055
|
-
// ignore close errors
|
|
1056
|
-
}
|
|
1064
|
+
await this.rejectHello(ws, message, "incompatible_protocol");
|
|
1057
1065
|
return;
|
|
1058
1066
|
}
|
|
1067
|
+
pending.authenticating = true;
|
|
1068
|
+
if (!pending.admission && !(await this.admitPendingHello(ws, message, pending)))
|
|
1069
|
+
return;
|
|
1059
1070
|
const clientId = message.clientId.trim();
|
|
1060
1071
|
if (clientId.length === 0) {
|
|
1061
1072
|
this.clearPendingConnection(ws);
|
|
@@ -1078,11 +1089,14 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1078
1089
|
return;
|
|
1079
1090
|
}
|
|
1080
1091
|
this.clearPendingConnection(ws);
|
|
1092
|
+
const admitted = pending.admission;
|
|
1093
|
+
if (!admitted)
|
|
1094
|
+
throw new Error("Admitted hello has no session admission");
|
|
1081
1095
|
pending.identity.clientId = clientId;
|
|
1082
1096
|
if (message.appVersion) {
|
|
1083
1097
|
pending.identity.appVersion = message.appVersion;
|
|
1084
1098
|
}
|
|
1085
|
-
const sessionKey = sessionConnectionKey(
|
|
1099
|
+
const sessionKey = sessionConnectionKey(admitted.principalId, clientId);
|
|
1086
1100
|
const existing = pluginId ? undefined : this.externalSessionsByKey.get(sessionKey);
|
|
1087
1101
|
if (existing) {
|
|
1088
1102
|
this.resumeSession({ ws, message, pending, existing });
|
|
@@ -1097,7 +1111,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1097
1111
|
clientCapabilities: message.capabilities ?? null,
|
|
1098
1112
|
connectionLogger,
|
|
1099
1113
|
lifecycle: pluginId ? { kind: "ephemeral-plugin", pluginId } : { kind: "reconnectable" },
|
|
1100
|
-
admission:
|
|
1114
|
+
admission: admitted,
|
|
1101
1115
|
});
|
|
1102
1116
|
this.sessions.set(ws, connection);
|
|
1103
1117
|
if (connection.lifecycle === "reconnectable") {
|
|
@@ -1111,6 +1125,83 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1111
1125
|
totalSessions: this.sessions.size,
|
|
1112
1126
|
}, "Client connected via hello");
|
|
1113
1127
|
}
|
|
1128
|
+
handleHelloSafely(params) {
|
|
1129
|
+
return this.handleHello(params).catch((error) => {
|
|
1130
|
+
try {
|
|
1131
|
+
this.handleRawMessageError({
|
|
1132
|
+
ws: params.ws,
|
|
1133
|
+
data: "",
|
|
1134
|
+
error,
|
|
1135
|
+
log: params.pending.connectionLogger,
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
catch {
|
|
1139
|
+
// The error reporter must not turn a connection failure into a process failure.
|
|
1140
|
+
}
|
|
1141
|
+
finally {
|
|
1142
|
+
try {
|
|
1143
|
+
params.ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
|
|
1144
|
+
}
|
|
1145
|
+
catch {
|
|
1146
|
+
// The transport may already be closed.
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
async admitPendingHello(ws, message, pending) {
|
|
1152
|
+
if (pending.admission)
|
|
1153
|
+
return true;
|
|
1154
|
+
try {
|
|
1155
|
+
const resolved = await resolveSessionAdmission({
|
|
1156
|
+
credential: message.auth,
|
|
1157
|
+
passwordHash: this.passwordHash,
|
|
1158
|
+
localCredential: this.credentialSource?.localCredential?.() ?? null,
|
|
1159
|
+
transport: pending.identity.transport === "relay" ? "relay" : "direct",
|
|
1160
|
+
});
|
|
1161
|
+
if (this.pendingConnections.get(ws) !== pending)
|
|
1162
|
+
return false;
|
|
1163
|
+
if ("rejection" in resolved) {
|
|
1164
|
+
this.clearPendingConnection(ws);
|
|
1165
|
+
await this.rejectHello(ws, message, resolved.rejection);
|
|
1166
|
+
return false;
|
|
1167
|
+
}
|
|
1168
|
+
pending.admission = resolved.admission;
|
|
1169
|
+
return true;
|
|
1170
|
+
}
|
|
1171
|
+
catch (error) {
|
|
1172
|
+
pending.connectionLogger.error({ err: error }, "Failed to resolve hello credential");
|
|
1173
|
+
if (this.pendingConnections.get(ws) === pending) {
|
|
1174
|
+
this.clearPendingConnection(ws);
|
|
1175
|
+
await this.rejectHello(ws, message, "incorrect_password");
|
|
1176
|
+
}
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
async rejectHello(ws, hello, reason) {
|
|
1181
|
+
// Older clients discard unknown frames, then use the close code and reason.
|
|
1182
|
+
// They never send hello.auth, so avoid sending an unknown envelope to them.
|
|
1183
|
+
if (hello.auth || hello.capabilities?.[CLIENT_CAPS.helloRejection] === true) {
|
|
1184
|
+
try {
|
|
1185
|
+
await ws.send(JSON.stringify({ type: "hello.rejected", reason, accepts: ["password"] }));
|
|
1186
|
+
}
|
|
1187
|
+
catch {
|
|
1188
|
+
// The close reason remains the compatibility signal.
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const closeReason = {
|
|
1192
|
+
password_required: "Password required",
|
|
1193
|
+
incorrect_password: "Incorrect password",
|
|
1194
|
+
incompatible_protocol: "Incompatible protocol version",
|
|
1195
|
+
}[reason];
|
|
1196
|
+
try {
|
|
1197
|
+
ws.close(reason === "incompatible_protocol"
|
|
1198
|
+
? WS_CLOSE_INCOMPATIBLE_PROTOCOL
|
|
1199
|
+
: WS_CLOSE_DAEMON_AUTH_FAILED, closeReason);
|
|
1200
|
+
}
|
|
1201
|
+
catch {
|
|
1202
|
+
// Ignore a transport that closed while the rejection was sent.
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1114
1205
|
resumeSession(params) {
|
|
1115
1206
|
const { ws, message, pending, existing } = params;
|
|
1116
1207
|
this.incrementRuntimeCounter("helloResumed");
|
|
@@ -1145,6 +1236,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1145
1236
|
buildServerInfoStatusPayload(session) {
|
|
1146
1237
|
return {
|
|
1147
1238
|
status: "server_info",
|
|
1239
|
+
protocolVersion: WS_PROTOCOL_VERSION,
|
|
1148
1240
|
serverId: this.serverId,
|
|
1149
1241
|
hostname: getHostname(),
|
|
1150
1242
|
version: this.daemonVersion,
|
|
@@ -1535,8 +1627,8 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1535
1627
|
}
|
|
1536
1628
|
handlePendingConnectionMessage(params) {
|
|
1537
1629
|
const { ws, message, pendingConnection } = params;
|
|
1538
|
-
if (message.type === "hello") {
|
|
1539
|
-
this.
|
|
1630
|
+
if (message.type === "hello" && !pendingConnection.authenticating) {
|
|
1631
|
+
void this.handleHelloSafely({
|
|
1540
1632
|
ws,
|
|
1541
1633
|
message,
|
|
1542
1634
|
pending: pendingConnection,
|
|
@@ -1590,7 +1682,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1590
1682
|
}
|
|
1591
1683
|
const message = parsedMessage.data;
|
|
1592
1684
|
this.recordInboundMessageType(message.type);
|
|
1593
|
-
if (message.type === "ping") {
|
|
1685
|
+
if (message.type === "ping" && activeConnection) {
|
|
1594
1686
|
// A plugin socket is IPC to a child this daemon already supervises, not
|
|
1595
1687
|
// an abandonable application socket.
|
|
1596
1688
|
if (!this.pluginSocketIds.has(ws)) {
|
|
@@ -1599,9 +1691,6 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1599
1691
|
this.sendToClient(ws, { type: "pong" });
|
|
1600
1692
|
return;
|
|
1601
1693
|
}
|
|
1602
|
-
if (message.type === "recording_state") {
|
|
1603
|
-
return;
|
|
1604
|
-
}
|
|
1605
1694
|
if (pendingConnection) {
|
|
1606
1695
|
this.handlePendingConnectionMessage({
|
|
1607
1696
|
ws,
|
|
@@ -1610,6 +1699,9 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1610
1699
|
});
|
|
1611
1700
|
return;
|
|
1612
1701
|
}
|
|
1702
|
+
if (message.type === "recording_state") {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1613
1705
|
if (!activeConnection) {
|
|
1614
1706
|
this.incrementRuntimeCounter("missingConnectionForMessage");
|
|
1615
1707
|
this.logger.error("No connection found for websocket");
|
|
@@ -1668,15 +1760,8 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1668
1760
|
handleRawMessageError(params) {
|
|
1669
1761
|
const { ws, data, error, log } = params;
|
|
1670
1762
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
? `${rawPayload.slice(0, 2000)}... (truncated)`
|
|
1674
|
-
: rawPayload;
|
|
1675
|
-
log.error({
|
|
1676
|
-
err,
|
|
1677
|
-
rawPayload: trimmedRawPayload,
|
|
1678
|
-
parsedPayload,
|
|
1679
|
-
}, "Failed to parse/handle message");
|
|
1763
|
+
// Inbound frames can contain hello.auth.password or session secrets.
|
|
1764
|
+
log.error({ errorName: err.name }, "Failed to parse/handle message");
|
|
1680
1765
|
if (this.pendingConnections.has(ws)) {
|
|
1681
1766
|
this.clearPendingConnection(ws);
|
|
1682
1767
|
try {
|
|
@@ -1687,7 +1772,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1687
1772
|
}
|
|
1688
1773
|
return;
|
|
1689
1774
|
}
|
|
1690
|
-
const requestInfo = extractRequestInfoFromUnknownWsInbound(
|
|
1775
|
+
const requestInfo = extractRequestInfoFromUnknownWsInbound(this.decodeRawMessagePayloadForError(data));
|
|
1691
1776
|
this.sessions.get(ws)?.session.delivery.protocolFailure(ws, {
|
|
1692
1777
|
...requestInfo,
|
|
1693
1778
|
error: `Invalid message: ${err.message}`,
|
|
@@ -1695,20 +1780,12 @@ export class VoiceAssistantWebSocketServer {
|
|
|
1695
1780
|
});
|
|
1696
1781
|
}
|
|
1697
1782
|
decodeRawMessagePayloadForError(data) {
|
|
1698
|
-
let rawPayload = null;
|
|
1699
|
-
let parsedPayload = null;
|
|
1700
1783
|
try {
|
|
1701
|
-
|
|
1702
|
-
rawPayload = buffer.toString();
|
|
1703
|
-
parsedPayload = JSON.parse(rawPayload);
|
|
1784
|
+
return JSON.parse(bufferFromWsData(data).toString());
|
|
1704
1785
|
}
|
|
1705
|
-
catch
|
|
1706
|
-
|
|
1707
|
-
parsedPayload = parsedPayload ?? rawPayload;
|
|
1708
|
-
const payloadErr = payloadError instanceof Error ? payloadError : new Error(String(payloadError));
|
|
1709
|
-
this.logger.error({ err: payloadErr }, "Failed to decode raw payload");
|
|
1786
|
+
catch {
|
|
1787
|
+
return null;
|
|
1710
1788
|
}
|
|
1711
|
-
return { rawPayload, parsedPayload };
|
|
1712
1789
|
}
|
|
1713
1790
|
incrementRuntimeCounter(counter) {
|
|
1714
1791
|
this.runtimeMetrics.incrementCounter(counter);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},5083,[4432,4433]);
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},5082,[4426]);
|
|
Binary file
|