@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.
Files changed (148) hide show
  1. package/dist/scripts/supervisor.js +51 -21
  2. package/dist/server/server/agent/agent-manager.js +22 -2
  3. package/dist/server/server/agent/agent-prompt.js +12 -0
  4. package/dist/server/server/agent/agent-sdk-types.d.ts +3 -0
  5. package/dist/server/server/agent/mcp-shared.d.ts +5 -1
  6. package/dist/server/server/agent/mcp-shared.js +2 -1
  7. package/dist/server/server/agent/provider-launch-config.d.ts +2 -2
  8. package/dist/server/server/agent/provider-launch-config.js +1 -1
  9. package/dist/server/server/agent/provider-registry.js +2 -2
  10. package/dist/server/server/agent/providers/acp-agent.js +20 -11
  11. package/dist/server/server/agent/providers/claude/agent.d.ts +1 -2
  12. package/dist/server/server/agent/providers/claude/agent.js +10 -9
  13. package/dist/server/server/agent/providers/claude/models.d.ts +1 -1
  14. package/dist/server/server/agent/providers/claude/models.js +1 -5
  15. package/dist/server/server/agent/providers/claude/project-dir.d.ts +1 -0
  16. package/dist/server/server/agent/providers/claude/project-dir.js +5 -1
  17. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +6 -1
  18. package/dist/server/server/agent/providers/codex-app-server-agent.js +10 -1
  19. package/dist/server/server/agent/providers/generic-acp-agent.js +3 -1
  20. package/dist/server/server/agent/providers/omp/agent.js +23 -6
  21. package/dist/server/server/agent/providers/omp/rpc-types.d.ts +1 -0
  22. package/dist/server/server/agent/providers/omp/rpc-types.js +1 -0
  23. package/dist/server/server/agent/providers/opencode/bridge.d.ts +5 -1
  24. package/dist/server/server/agent/providers/opencode/bridge.js +56 -5
  25. package/dist/server/server/agent/providers/opencode/http-error.d.ts +8 -0
  26. package/dist/server/server/agent/providers/opencode/http-error.js +13 -0
  27. package/dist/server/server/agent/providers/opencode/runtime-client.d.ts +38 -0
  28. package/dist/server/server/agent/providers/opencode/runtime-client.js +131 -0
  29. package/dist/server/server/agent/providers/opencode/runtime-notice.d.ts +3 -0
  30. package/dist/server/server/agent/providers/opencode/runtime-notice.js +47 -0
  31. package/dist/server/server/agent/providers/opencode/server-manager.js +19 -8
  32. package/dist/server/server/agent/providers/opencode/v2/agent.d.ts +36 -0
  33. package/dist/server/server/agent/providers/opencode/v2/agent.js +211 -0
  34. package/dist/server/server/agent/providers/opencode/v2/api.d.ts +18 -0
  35. package/dist/server/server/agent/providers/opencode/v2/api.js +2 -0
  36. package/dist/server/server/agent/providers/opencode/v2/bridge-plugin.bundle.mjs +6693 -0
  37. package/dist/server/server/agent/providers/opencode/v2/capabilities.d.ts +3 -0
  38. package/dist/server/server/agent/providers/opencode/v2/capabilities.js +13 -0
  39. package/dist/server/server/agent/providers/opencode/v2/children.d.ts +20 -0
  40. package/dist/server/server/agent/providers/opencode/v2/children.js +75 -0
  41. package/dist/server/server/agent/providers/opencode/v2/commands.d.ts +4 -0
  42. package/dist/server/server/agent/providers/opencode/v2/commands.js +36 -0
  43. package/dist/server/server/agent/providers/opencode/v2/configuration.d.ts +8 -0
  44. package/dist/server/server/agent/providers/opencode/v2/configuration.js +64 -0
  45. package/dist/server/server/agent/providers/opencode/v2/history.d.ts +4 -0
  46. package/dist/server/server/agent/providers/opencode/v2/history.js +15 -0
  47. package/dist/server/server/agent/providers/opencode/v2/mapping.d.ts +7 -0
  48. package/dist/server/server/agent/providers/opencode/v2/mapping.js +42 -0
  49. package/dist/server/server/agent/providers/opencode/v2/permissions.d.ts +18 -0
  50. package/dist/server/server/agent/providers/opencode/v2/permissions.js +157 -0
  51. package/dist/server/server/agent/providers/opencode/v2/readiness.d.ts +18 -0
  52. package/dist/server/server/agent/providers/opencode/v2/readiness.js +51 -0
  53. package/dist/server/server/agent/providers/opencode/v2/runtime.d.ts +36 -0
  54. package/dist/server/server/agent/providers/opencode/v2/runtime.js +230 -0
  55. package/dist/server/server/agent/providers/opencode/v2/session.d.ts +84 -0
  56. package/dist/server/server/agent/providers/opencode/v2/session.js +468 -0
  57. package/dist/server/server/agent/providers/opencode/v2/structured-output.d.ts +8 -0
  58. package/dist/server/server/agent/providers/opencode/v2/structured-output.js +32 -0
  59. package/dist/server/server/agent/providers/opencode/v2/timeline.d.ts +28 -0
  60. package/dist/server/server/agent/providers/opencode/v2/timeline.js +154 -0
  61. package/dist/server/server/agent/providers/opencode/v2/turns.d.ts +40 -0
  62. package/dist/server/server/agent/providers/opencode/v2/turns.js +226 -0
  63. package/dist/server/server/agent/providers/opencode-agent.d.ts +26 -7
  64. package/dist/server/server/agent/providers/opencode-agent.js +100 -40
  65. package/dist/server/server/agent/providers/pi/agent.d.ts +6 -6
  66. package/dist/server/server/agent/providers/pi/agent.js +114 -165
  67. package/dist/server/server/agent/providers/pi/extensions/child-session.d.ts +4 -0
  68. package/dist/server/server/agent/providers/pi/extensions/child-session.js +59 -0
  69. package/dist/server/server/agent/providers/pi/extensions/contract.d.ts +68 -0
  70. package/dist/server/server/agent/providers/pi/extensions/contract.js +2 -0
  71. package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.d.ts +3 -0
  72. package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.js +159 -0
  73. package/dist/server/server/agent/providers/pi/extensions/host.d.ts +28 -0
  74. package/dist/server/server/agent/providers/pi/extensions/host.js +94 -0
  75. package/dist/server/server/agent/providers/pi/extensions/index.d.ts +7 -0
  76. package/dist/server/server/agent/providers/pi/extensions/index.js +6 -0
  77. package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.d.ts +3 -0
  78. package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.js +138 -0
  79. package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.d.ts +3 -0
  80. package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.js +38 -0
  81. package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.d.ts +3 -0
  82. package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.js +63 -0
  83. package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.d.ts +3 -0
  84. package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.js +48 -0
  85. package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.d.ts +3 -0
  86. package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.js +131 -0
  87. package/dist/server/server/agent/providers/pi/extensions/registry.d.ts +2 -0
  88. package/dist/server/server/agent/providers/pi/extensions/registry.js +21 -0
  89. package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.d.ts +3 -0
  90. package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.js +180 -0
  91. package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.d.ts +3 -0
  92. package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.js +51 -0
  93. package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.d.ts +22 -0
  94. package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.js +66 -0
  95. package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.d.ts +4 -0
  96. package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.js +79 -0
  97. package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.d.ts +3 -0
  98. package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.js +149 -0
  99. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +7 -2
  100. package/dist/server/server/agent/providers/pi/history-mapper.js +78 -35
  101. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
  102. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +170 -0
  103. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +355 -0
  104. package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +1 -6
  105. package/dist/server/server/agent/providers/pi/tool-call-mapper.js +1 -97
  106. package/dist/server/server/agent/tools/paseo-tools.js +22 -15
  107. package/dist/server/server/auth.d.ts +1 -2
  108. package/dist/server/server/auth.js +8 -14
  109. package/dist/server/server/bootstrap.js +10 -2
  110. package/dist/server/server/daemon-instance.d.ts +1 -0
  111. package/dist/server/server/daemon-instance.js +1 -0
  112. package/dist/server/server/exports.d.ts +1 -1
  113. package/dist/server/server/exports.js +1 -1
  114. package/dist/server/server/local-credential.d.ts +6 -0
  115. package/dist/server/server/local-credential.js +82 -0
  116. package/dist/server/server/session-admission-auth.d.ts +16 -0
  117. package/dist/server/server/session-admission-auth.js +26 -0
  118. package/dist/server/server/websocket-server.d.ts +6 -1
  119. package/dist/server/server/websocket-server.js +132 -55
  120. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js} +1 -1
  121. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.br → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.br} +0 -0
  122. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.gz +0 -0
  123. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js → desktop-attachment-store-d5d3402a752965d3037191484127123b.js} +1 -1
  124. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.br → desktop-attachment-store-d5d3402a752965d3037191484127123b.js.br} +0 -0
  125. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-d5d3402a752965d3037191484127123b.js.gz +0 -0
  126. package/dist/server/web-ui/_expo/static/js/web/{index-8868523fc6d311c5da524c067d1cd426.js → index-136fa41d4adb7ec5c6392839cc354136.js} +1580 -1576
  127. package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.br +0 -0
  128. package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.gz +0 -0
  129. package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js} +1 -1
  130. package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.br → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.br} +0 -0
  131. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.gz +0 -0
  132. package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js → native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js} +3 -3
  133. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.br +0 -0
  134. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.gz +0 -0
  135. package/dist/server/web-ui/index.html +1 -1
  136. package/dist/server/web-ui/index.html.br +0 -0
  137. package/dist/server/web-ui/index.html.gz +0 -0
  138. package/dist/src/server/agent/provider-launch-config.js +1 -1
  139. package/dist/src/server/daemon-instance.js +1 -0
  140. package/dist/src/server/local-credential.js +82 -0
  141. package/package.json +8 -7
  142. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.gz +0 -0
  143. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.gz +0 -0
  144. package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.br +0 -0
  145. package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.gz +0 -0
  146. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.gz +0 -0
  147. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.br +0 -0
  148. 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
- app.use(createRequireBearerMiddleware(config.auth, (context) => {
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, config.auth, speechService, terminalManager, {
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, isBearerTokenValid } from "./auth.js";
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, isBearerTokenValid } from "./auth.js";
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, isBearerTokenValid, } from "./auth.js";
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
- if (password) {
527
- const requestMetadata = extractSocketRequestMetadata(request);
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 isAuthorized = isBearerTokenValid({ password, token });
531
- if (!isAuthorized) {
532
- const reason = token === null ? "Password required" : "Incorrect password";
533
- this.logger.warn({ ...requestMetadata, hasToken: token !== null }, "Rejected WebSocket connection with invalid daemon password");
534
- ws.close(WS_CLOSE_DAEMON_AUTH_FAILED, reason);
535
- return;
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 = OWNER_SESSION_ADMISSION, initialHello) {
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.principalId === principalId) {
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 = OWNER_SESSION_ADMISSION, initialHello) {
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.handleHello({ ws, message: initialHello, pending });
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 !== WS_PROTOCOL_VERSION) {
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
- try {
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(pending.admission.principalId, clientId);
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: pending.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.handleHello({
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
- const { rawPayload, parsedPayload } = this.decodeRawMessagePayloadForError(data);
1672
- const trimmedRawPayload = typeof rawPayload === "string" && rawPayload.length > 2000
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(parsedPayload);
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
- const buffer = bufferFromWsData(data);
1702
- rawPayload = buffer.toString();
1703
- parsedPayload = JSON.parse(rawPayload);
1784
+ return JSON.parse(bufferFromWsData(data).toString());
1704
1785
  }
1705
- catch (payloadError) {
1706
- rawPayload = rawPayload ?? "<unreadable>";
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])},5079,[4431,4432]);
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]);
@@ -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}'.`)}},5078,[4425]);
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]);