appduct 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -0
  3. package/bin.js +2 -0
  4. package/dist/artifact-inspect.d.ts +110 -0
  5. package/dist/artifact-inspect.d.ts.map +1 -0
  6. package/dist/artifact-inspect.js +315 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +4 -0
  10. package/dist/cli/command-options.d.ts +44 -0
  11. package/dist/cli/command-options.d.ts.map +1 -0
  12. package/dist/cli/command-options.js +95 -0
  13. package/dist/cli/create-cli.d.ts +2 -0
  14. package/dist/cli/create-cli.d.ts.map +1 -0
  15. package/dist/cli/create-cli.js +56 -0
  16. package/dist/cli/dispatch.d.ts +3 -0
  17. package/dist/cli/dispatch.d.ts.map +1 -0
  18. package/dist/cli/dispatch.js +275 -0
  19. package/dist/cli/open-target.d.ts +112 -0
  20. package/dist/cli/open-target.d.ts.map +1 -0
  21. package/dist/cli/open-target.js +438 -0
  22. package/dist/cli/result-types.d.ts +194 -0
  23. package/dist/cli/result-types.d.ts.map +1 -0
  24. package/dist/cli/result-types.js +6 -0
  25. package/dist/cli/runner.d.ts +35 -0
  26. package/dist/cli/runner.d.ts.map +1 -0
  27. package/dist/cli/runner.js +111 -0
  28. package/dist/cli/types.d.ts +20 -0
  29. package/dist/cli/types.d.ts.map +1 -0
  30. package/dist/cli/types.js +10 -0
  31. package/dist/cli.d.ts +4 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +2 -0
  34. package/dist/client/app-client.d.ts +126 -0
  35. package/dist/client/app-client.d.ts.map +1 -0
  36. package/dist/client/app-client.js +223 -0
  37. package/dist/client/bootstrap.d.ts +48 -0
  38. package/dist/client/bootstrap.d.ts.map +1 -0
  39. package/dist/client/bootstrap.js +108 -0
  40. package/dist/client/connect.d.ts +17 -0
  41. package/dist/client/connect.d.ts.map +1 -0
  42. package/dist/client/connect.js +38 -0
  43. package/dist/client/errors.d.ts +26 -0
  44. package/dist/client/errors.d.ts.map +1 -0
  45. package/dist/client/errors.js +40 -0
  46. package/dist/client/index.d.ts +19 -0
  47. package/dist/client/index.d.ts.map +1 -0
  48. package/dist/client/index.js +16 -0
  49. package/dist/commands/daemon.d.ts +22 -0
  50. package/dist/commands/daemon.d.ts.map +1 -0
  51. package/dist/commands/daemon.js +128 -0
  52. package/dist/commands/doctor.d.ts +23 -0
  53. package/dist/commands/doctor.d.ts.map +1 -0
  54. package/dist/commands/doctor.js +42 -0
  55. package/dist/commands/events.d.ts +37 -0
  56. package/dist/commands/events.d.ts.map +1 -0
  57. package/dist/commands/events.js +76 -0
  58. package/dist/commands/init.d.ts +51 -0
  59. package/dist/commands/init.d.ts.map +1 -0
  60. package/dist/commands/init.js +231 -0
  61. package/dist/commands/invoke.d.ts +22 -0
  62. package/dist/commands/invoke.d.ts.map +1 -0
  63. package/dist/commands/invoke.js +54 -0
  64. package/dist/commands/keygen.d.ts +23 -0
  65. package/dist/commands/keygen.d.ts.map +1 -0
  66. package/dist/commands/keygen.js +46 -0
  67. package/dist/commands/link.d.ts +30 -0
  68. package/dist/commands/link.d.ts.map +1 -0
  69. package/dist/commands/link.js +43 -0
  70. package/dist/commands/ls.d.ts +9 -0
  71. package/dist/commands/ls.d.ts.map +1 -0
  72. package/dist/commands/ls.js +7 -0
  73. package/dist/commands/mcp.d.ts +49 -0
  74. package/dist/commands/mcp.d.ts.map +1 -0
  75. package/dist/commands/mcp.js +98 -0
  76. package/dist/commands/revoke.d.ts +12 -0
  77. package/dist/commands/revoke.d.ts.map +1 -0
  78. package/dist/commands/revoke.js +7 -0
  79. package/dist/commands/tools.d.ts +23 -0
  80. package/dist/commands/tools.d.ts.map +1 -0
  81. package/dist/commands/tools.js +52 -0
  82. package/dist/daemon/address.d.ts +30 -0
  83. package/dist/daemon/address.d.ts.map +1 -0
  84. package/dist/daemon/address.js +71 -0
  85. package/dist/daemon/audit.d.ts +107 -0
  86. package/dist/daemon/audit.d.ts.map +1 -0
  87. package/dist/daemon/audit.js +237 -0
  88. package/dist/daemon/calls.d.ts +84 -0
  89. package/dist/daemon/calls.d.ts.map +1 -0
  90. package/dist/daemon/calls.js +158 -0
  91. package/dist/daemon/config.d.ts +79 -0
  92. package/dist/daemon/config.d.ts.map +1 -0
  93. package/dist/daemon/config.js +170 -0
  94. package/dist/daemon/daemon.d.ts +43 -0
  95. package/dist/daemon/daemon.d.ts.map +1 -0
  96. package/dist/daemon/daemon.js +560 -0
  97. package/dist/daemon/event-bus.d.ts +49 -0
  98. package/dist/daemon/event-bus.d.ts.map +1 -0
  99. package/dist/daemon/event-bus.js +111 -0
  100. package/dist/daemon/links.d.ts +54 -0
  101. package/dist/daemon/links.d.ts.map +1 -0
  102. package/dist/daemon/links.js +118 -0
  103. package/dist/daemon/listener.d.ts +34 -0
  104. package/dist/daemon/listener.d.ts.map +1 -0
  105. package/dist/daemon/listener.js +132 -0
  106. package/dist/daemon/log-rotation.d.ts +82 -0
  107. package/dist/daemon/log-rotation.d.ts.map +1 -0
  108. package/dist/daemon/log-rotation.js +136 -0
  109. package/dist/daemon/pidfile.d.ts +38 -0
  110. package/dist/daemon/pidfile.d.ts.map +1 -0
  111. package/dist/daemon/pidfile.js +90 -0
  112. package/dist/daemon/policy.d.ts +22 -0
  113. package/dist/daemon/policy.d.ts.map +1 -0
  114. package/dist/daemon/policy.js +24 -0
  115. package/dist/daemon/registry.d.ts +19 -0
  116. package/dist/daemon/registry.d.ts.map +1 -0
  117. package/dist/daemon/registry.js +31 -0
  118. package/dist/daemon/rpc-server.d.ts +54 -0
  119. package/dist/daemon/rpc-server.d.ts.map +1 -0
  120. package/dist/daemon/rpc-server.js +203 -0
  121. package/dist/daemon/sessions.d.ts +86 -0
  122. package/dist/daemon/sessions.d.ts.map +1 -0
  123. package/dist/daemon/sessions.js +429 -0
  124. package/dist/daemon/socket-probe.d.ts +16 -0
  125. package/dist/daemon/socket-probe.d.ts.map +1 -0
  126. package/dist/daemon/socket-probe.js +28 -0
  127. package/dist/daemon/state-dir.d.ts +31 -0
  128. package/dist/daemon/state-dir.d.ts.map +1 -0
  129. package/dist/daemon/state-dir.js +43 -0
  130. package/dist/daemon/timers.d.ts +16 -0
  131. package/dist/daemon/timers.d.ts.map +1 -0
  132. package/dist/daemon/timers.js +12 -0
  133. package/dist/daemon/tls.d.ts +58 -0
  134. package/dist/daemon/tls.d.ts.map +1 -0
  135. package/dist/daemon/tls.js +79 -0
  136. package/dist/errors.d.ts +28 -0
  137. package/dist/errors.d.ts.map +1 -0
  138. package/dist/errors.js +162 -0
  139. package/dist/host-certificate.d.ts +8 -0
  140. package/dist/host-certificate.d.ts.map +1 -0
  141. package/dist/host-certificate.js +111 -0
  142. package/dist/index.d.ts +4 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +3 -0
  145. package/dist/key-material.d.ts +14 -0
  146. package/dist/key-material.d.ts.map +1 -0
  147. package/dist/key-material.js +42 -0
  148. package/dist/link.d.ts +76 -0
  149. package/dist/link.d.ts.map +1 -0
  150. package/dist/link.js +117 -0
  151. package/dist/mcp/connect-tool.d.ts +115 -0
  152. package/dist/mcp/connect-tool.d.ts.map +1 -0
  153. package/dist/mcp/connect-tool.js +396 -0
  154. package/dist/mcp/daemon-tools.d.ts +13 -0
  155. package/dist/mcp/daemon-tools.d.ts.map +1 -0
  156. package/dist/mcp/daemon-tools.js +29 -0
  157. package/dist/mcp/events-tool.d.ts +111 -0
  158. package/dist/mcp/events-tool.d.ts.map +1 -0
  159. package/dist/mcp/events-tool.js +322 -0
  160. package/dist/mcp/server.d.ts +58 -0
  161. package/dist/mcp/server.d.ts.map +1 -0
  162. package/dist/mcp/server.js +538 -0
  163. package/dist/mcp/tool-mapping.d.ts +58 -0
  164. package/dist/mcp/tool-mapping.d.ts.map +1 -0
  165. package/dist/mcp/tool-mapping.js +143 -0
  166. package/dist/mcp/tool-namespace.d.ts +40 -0
  167. package/dist/mcp/tool-namespace.d.ts.map +1 -0
  168. package/dist/mcp/tool-namespace.js +65 -0
  169. package/dist/native-scheme.d.ts +59 -0
  170. package/dist/native-scheme.d.ts.map +1 -0
  171. package/dist/native-scheme.js +465 -0
  172. package/dist/output.d.ts +28 -0
  173. package/dist/output.d.ts.map +1 -0
  174. package/dist/output.js +342 -0
  175. package/dist/package-version.d.ts +30 -0
  176. package/dist/package-version.d.ts.map +1 -0
  177. package/dist/package-version.js +47 -0
  178. package/dist/qr-terminal.d.ts +13 -0
  179. package/dist/qr-terminal.d.ts.map +1 -0
  180. package/dist/qr-terminal.js +45 -0
  181. package/dist/rpc/client.d.ts +137 -0
  182. package/dist/rpc/client.d.ts.map +1 -0
  183. package/dist/rpc/client.js +819 -0
  184. package/dist/scheme.d.ts +181 -0
  185. package/dist/scheme.d.ts.map +1 -0
  186. package/dist/scheme.js +321 -0
  187. package/dist/spki-pin.d.ts +3 -0
  188. package/dist/spki-pin.d.ts.map +1 -0
  189. package/dist/spki-pin.js +14 -0
  190. package/package.json +65 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Per-session tool-invocation manager (ARCHITECTURE.md §5 `tools.call`, §7 `tool_call` /
3
+ * `tool_result` / `tool_error` / `tool_call_progress`). This is the v2 replacement for v1's
4
+ * `pendingCalls` map: the correlation pattern (`call_<random>` ids, resolve on `tool_result`,
5
+ * reject on `tool_error`) was sound and is kept. v1's defect was error flattening — the app's
6
+ * `error.type` got re-wrapped twice and ended up buried two `details` levels deep. Here the type is
7
+ * preserved verbatim: {@link RpcApplicationError.type} is set directly from the app's
8
+ * `tool_error.error.type`, never a generic wrapper.
9
+ *
10
+ * This module knows nothing about sockets or the session state machine — `sessions.ts` (via the
11
+ * composition root in `daemon.ts`) supplies a `send` function and routes incoming
12
+ * `tool_result`/`tool_error`/`tool_call_progress` frames and suspend/revoke/expiry transitions back
13
+ * in. That keeps a single seam (`call`) that a policy layer can wrap.
14
+ */
15
+ import { type ToolCallMessage, type ToolCallProgressMessage, type ToolCancelMessage, type ToolErrorMessage, type ToolResultMessage } from "@appduct/shared";
16
+ import type { EventBus } from "./event-bus.js";
17
+ import { type TimerFns } from "./timers.js";
18
+ /** Re-exported under this module's historical names; the values live in `@appduct/shared` so the
19
+ * RN SDK's app-side abort timer and this timer cannot drift apart (issue #25). */
20
+ export declare const DEFAULT_CALL_TIMEOUT_MS = 10000;
21
+ export declare const MIN_CALL_TIMEOUT_MS = 1000;
22
+ export declare const MAX_CALL_TIMEOUT_MS = 600000;
23
+ /** Identifies the session a call belongs to; carried through so progress/lifecycle events can be
24
+ * tagged with the alias without this module needing to look sessions back up. */
25
+ export type CallSession = {
26
+ sessionId: string;
27
+ alias: string;
28
+ };
29
+ /** Sends a `tool_call` frame to the session's active socket; `false` if there is none (the caller
30
+ * must already have checked the session is ACTIVE, but the socket can still vanish mid-flight —
31
+ * the resulting rejection comes from the `session_suspended` transition below, not this return). */
32
+ export type SendToolCall = (sessionId: string, message: ToolCallMessage) => boolean;
33
+ /** Sends a `tool_cancel` frame to the session's active socket; `false` if there is none. */
34
+ export type SendToolCancel = (sessionId: string, message: ToolCancelMessage) => boolean;
35
+ export type CallsManagerOptions = {
36
+ send: SendToolCall;
37
+ sendCancel: SendToolCancel;
38
+ eventBus: EventBus;
39
+ timers?: TimerFns;
40
+ };
41
+ /** The call id (available synchronously, before the app has answered) plus the eventual result —
42
+ * lets the caller correlate its own `tools.call` with `tool_call_started`/`tool_call_progress`
43
+ * events carrying the same `callId` before it has anything to await. */
44
+ export type StartedCall = {
45
+ callId: string;
46
+ result: Promise<unknown>;
47
+ };
48
+ export type CallsManager = {
49
+ /** Sends `tool_call` with a fresh `call_<random>` id, tracks it in a per-session pending map,
50
+ * resolves on the matching `tool_result`, rejects on `tool_error` (type preserved verbatim) or
51
+ * after `timeoutMs` (clamped to [{@link MIN_CALL_TIMEOUT_MS}, {@link MAX_CALL_TIMEOUT_MS}],
52
+ * default {@link DEFAULT_CALL_TIMEOUT_MS}). Returns the `callId` immediately (synchronously)
53
+ * alongside the result promise. */
54
+ call: (session: CallSession, name: string, args: Record<string, unknown>, timeoutMs?: number) => StartedCall;
55
+ /** Routes a validated `tool_result` frame; unknown correlation ids are dropped. */
56
+ handleToolResult: (message: ToolResultMessage) => void;
57
+ /** Routes a validated `tool_error` frame; unknown correlation ids are dropped. */
58
+ handleToolError: (message: ToolErrorMessage) => void;
59
+ /** Routes a validated `tool_call_progress` frame to the event bus; unknown correlation ids are
60
+ * dropped (no event is emitted). */
61
+ handleToolCallProgress: (message: ToolCallProgressMessage) => void;
62
+ /** Sends `tool_cancel` for a still-pending call. Does not itself resolve/reject the pending
63
+ * call — the app's eventual `tool_error` (`tool_cancelled`) or a late `tool_result` still wins
64
+ * normally, and the existing timeout remains a backstop if the app never replies. Returns
65
+ * `false` (a no-op, not an error) when `callId` is unknown/already finished, or when the
66
+ * session has no active socket to send the frame on right now. */
67
+ cancel: (sessionId: string, callId: string, reason: string) => boolean;
68
+ /** Rejects every pending call for a session immediately (suspend/revoke/expiry transitions). */
69
+ rejectSession: (sessionId: string, errorType: "session_suspended" | "unknown_session") => void;
70
+ /** Rejects and clears every pending call across every session (daemon shutdown). */
71
+ disposeAll: () => void;
72
+ };
73
+ /** Extra headroom added on top of the daemon-side deadline so a caller's transport timeout never
74
+ * beats the daemon's own `tool_timeout` — mirrored (deliberately duplicated) in
75
+ * `client/app-client.ts`, which must not import daemon-internal modules. */
76
+ export declare const CALL_TRANSPORT_TIMEOUT_SLACK_MS = 5000;
77
+ /** The deadline the daemon will actually enforce for a `tools.call` with this `timeoutMs`. */
78
+ export declare const clampTimeout: (timeoutMs: number | undefined) => number;
79
+ /** Transport timeout a caller should use for a `tools.call` that will be given `timeoutMs`
80
+ * daemon-side. Always clamps first: adding slack to an unclamped value could overflow Node's
81
+ * 32-bit timer range (~24.8 days), which silently fires the timer immediately. */
82
+ export declare const deriveCallTransportTimeoutMs: (timeoutMs: number | undefined) => number;
83
+ export declare const createCallsManager: (options: CallsManagerOptions) => CallsManager;
84
+ //# sourceMappingURL=calls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/daemon/calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAgB,KAAK,QAAQ,EAAoB,MAAM,aAAa,CAAC;AAE5E;kFACkF;AAClF,eAAO,MAAM,uBAAuB,QAA0B,CAAC;AAC/D,eAAO,MAAM,mBAAmB,OAAsB,CAAC;AACvD,eAAO,MAAM,mBAAmB,SAAsB,CAAC;AAEvD;iFACiF;AACjF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;oGAEoG;AACpG,MAAM,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC;AAEpF,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC;AAExF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;wEAEwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB;;;;uCAImC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;IAC7G,mFAAmF;IACnF,gBAAgB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD,kFAAkF;IAClF,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACrD;wCACoC;IACpC,sBAAsB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACnE;;;;sEAIkE;IAClE,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACvE,gGAAgG;IAChG,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,GAAG,iBAAiB,KAAK,IAAI,CAAC;IAC/F,oFAAoF;IACpF,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAWF;;4EAE4E;AAC5E,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAErD,8FAA8F;AAC9F,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,GAAG,SAAS,KAAG,MAM5D,CAAC;AAEF;;kFAEkF;AAClF,eAAO,MAAM,4BAA4B,GAAI,WAAW,MAAM,GAAG,SAAS,KAAG,MAE5E,CAAC;AAYF,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,KAAG,YAoJjE,CAAC"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Per-session tool-invocation manager (ARCHITECTURE.md §5 `tools.call`, §7 `tool_call` /
3
+ * `tool_result` / `tool_error` / `tool_call_progress`). This is the v2 replacement for v1's
4
+ * `pendingCalls` map: the correlation pattern (`call_<random>` ids, resolve on `tool_result`,
5
+ * reject on `tool_error`) was sound and is kept. v1's defect was error flattening — the app's
6
+ * `error.type` got re-wrapped twice and ended up buried two `details` levels deep. Here the type is
7
+ * preserved verbatim: {@link RpcApplicationError.type} is set directly from the app's
8
+ * `tool_error.error.type`, never a generic wrapper.
9
+ *
10
+ * This module knows nothing about sockets or the session state machine — `sessions.ts` (via the
11
+ * composition root in `daemon.ts`) supplies a `send` function and routes incoming
12
+ * `tool_result`/`tool_error`/`tool_call_progress` frames and suspend/revoke/expiry transitions back
13
+ * in. That keeps a single seam (`call`) that a policy layer can wrap.
14
+ */
15
+ import { randomBytes } from "node:crypto";
16
+ import { clampToolTimeoutMs, DEFAULT_TOOL_TIMEOUT_MS, MAX_TOOL_TIMEOUT_MS, MIN_TOOL_TIMEOUT_MS, } from "@appduct/shared";
17
+ import { RpcApplicationError } from "./rpc-server.js";
18
+ import { systemTimers } from "./timers.js";
19
+ /** Re-exported under this module's historical names; the values live in `@appduct/shared` so the
20
+ * RN SDK's app-side abort timer and this timer cannot drift apart (issue #25). */
21
+ export const DEFAULT_CALL_TIMEOUT_MS = DEFAULT_TOOL_TIMEOUT_MS;
22
+ export const MIN_CALL_TIMEOUT_MS = MIN_TOOL_TIMEOUT_MS;
23
+ export const MAX_CALL_TIMEOUT_MS = MAX_TOOL_TIMEOUT_MS;
24
+ const generateCallId = () => `call_${randomBytes(16).toString("base64url")}`;
25
+ /** Extra headroom added on top of the daemon-side deadline so a caller's transport timeout never
26
+ * beats the daemon's own `tool_timeout` — mirrored (deliberately duplicated) in
27
+ * `client/app-client.ts`, which must not import daemon-internal modules. */
28
+ export const CALL_TRANSPORT_TIMEOUT_SLACK_MS = 5_000;
29
+ /** The deadline the daemon will actually enforce for a `tools.call` with this `timeoutMs`. */
30
+ export const clampTimeout = (timeoutMs) => {
31
+ if (timeoutMs === undefined || !Number.isFinite(timeoutMs)) {
32
+ return DEFAULT_CALL_TIMEOUT_MS;
33
+ }
34
+ return clampToolTimeoutMs(timeoutMs);
35
+ };
36
+ /** Transport timeout a caller should use for a `tools.call` that will be given `timeoutMs`
37
+ * daemon-side. Always clamps first: adding slack to an unclamped value could overflow Node's
38
+ * 32-bit timer range (~24.8 days), which silently fires the timer immediately. */
39
+ export const deriveCallTransportTimeoutMs = (timeoutMs) => {
40
+ return clampTimeout(timeoutMs) + CALL_TRANSPORT_TIMEOUT_SLACK_MS;
41
+ };
42
+ /** Event kinds that terminate every pending call for a session (ARCHITECTURE.md §6/§5: "On
43
+ * suspend/revoke/expiry, every pending call rejects immediately with session_suspended (or
44
+ * unknown_session on revoke)"). Expiry only ever follows suspend (pending calls are already gone
45
+ * by then), but it is handled defensively with the same `unknown_session` type as revoke. */
46
+ const TERMINATING_KIND_ERROR_TYPES = {
47
+ session_suspended: "session_suspended",
48
+ session_revoked: "unknown_session",
49
+ session_expired: "unknown_session",
50
+ };
51
+ export const createCallsManager = (options) => {
52
+ const timers = options.timers ?? systemTimers;
53
+ // sessionId -> callId -> PendingCall. Concurrent calls to the same session get independent
54
+ // entries here, keyed by their own call id, so out-of-order tool_result/tool_error frames still
55
+ // resolve the right caller.
56
+ const pendingBySession = new Map();
57
+ const takePending = (sessionId, callId) => {
58
+ const sessionMap = pendingBySession.get(sessionId);
59
+ const pending = sessionMap?.get(callId);
60
+ if (!sessionMap || !pending) {
61
+ return undefined;
62
+ }
63
+ sessionMap.delete(callId);
64
+ if (sessionMap.size === 0) {
65
+ pendingBySession.delete(sessionId);
66
+ }
67
+ timers.clearTimeout(pending.timer);
68
+ return pending;
69
+ };
70
+ const call = (session, name, args, timeoutMs) => {
71
+ const callId = generateCallId();
72
+ const result = new Promise((resolve, reject) => {
73
+ const effectiveTimeoutMs = clampTimeout(timeoutMs);
74
+ const timer = timers.setTimeout(() => {
75
+ const pending = takePending(session.sessionId, callId);
76
+ pending?.reject(new RpcApplicationError("tool_timeout", `Tool call "${name}" timed out after ${effectiveTimeoutMs}ms.`));
77
+ }, effectiveTimeoutMs);
78
+ let sessionMap = pendingBySession.get(session.sessionId);
79
+ if (!sessionMap) {
80
+ sessionMap = new Map();
81
+ pendingBySession.set(session.sessionId, sessionMap);
82
+ }
83
+ sessionMap.set(callId, { alias: session.alias, resolve, reject, timer });
84
+ const message = {
85
+ type: "tool_call",
86
+ session_id: session.sessionId,
87
+ id: callId,
88
+ name,
89
+ args,
90
+ };
91
+ const sent = options.send(session.sessionId, message);
92
+ if (!sent) {
93
+ const pending = takePending(session.sessionId, callId);
94
+ pending?.reject(new RpcApplicationError("session_not_active", "Session has no active socket."));
95
+ }
96
+ });
97
+ return { callId, result };
98
+ };
99
+ const handleToolResult = (message) => {
100
+ const pending = takePending(message.session_id, message.id);
101
+ pending?.resolve(message.result);
102
+ };
103
+ const handleToolError = (message) => {
104
+ const pending = takePending(message.session_id, message.id);
105
+ // Verbatim: `error.type` comes straight from the app's frame, never re-wrapped (v1's defect).
106
+ pending?.reject(new RpcApplicationError(message.error.type, message.error.message, -32000, message.error.details));
107
+ };
108
+ const handleToolCallProgress = (message) => {
109
+ const sessionMap = pendingBySession.get(message.session_id);
110
+ const pending = sessionMap?.get(message.id);
111
+ if (!pending) {
112
+ // Unknown correlation id: dropped (ARCHITECTURE.md task notes say "log at debug level"; a
113
+ // full logging seam doesn't exist yet in the daemon, so this is a silent, intentional no-op).
114
+ return;
115
+ }
116
+ options.eventBus.emit({
117
+ // Its own EventKind (ARCHITECTURE.md §5), distinct from `tool_call_started`/
118
+ // `tool_call_finished` — subscribers correlate all three by `callId` in `data` without
119
+ // needing to sniff which kind carries a `progress`/`message` field.
120
+ kind: "tool_call_progress",
121
+ sessionId: message.session_id,
122
+ alias: pending.alias,
123
+ data: { callId: message.id, progress: message.progress, message: message.message },
124
+ });
125
+ };
126
+ const cancel = (sessionId, callId, reason) => {
127
+ const pending = pendingBySession.get(sessionId)?.get(callId);
128
+ if (!pending) {
129
+ return false;
130
+ }
131
+ const message = { type: "tool_cancel", session_id: sessionId, id: callId, reason };
132
+ return options.sendCancel(sessionId, message);
133
+ };
134
+ const rejectSession = (sessionId, errorType) => {
135
+ const sessionMap = pendingBySession.get(sessionId);
136
+ if (!sessionMap) {
137
+ return;
138
+ }
139
+ pendingBySession.delete(sessionId);
140
+ for (const pending of sessionMap.values()) {
141
+ timers.clearTimeout(pending.timer);
142
+ pending.reject(new RpcApplicationError(errorType, `Session transitioned while the call was pending.`));
143
+ }
144
+ };
145
+ const unsubscribeFromEventBus = options.eventBus.subscribe((event) => {
146
+ const errorType = TERMINATING_KIND_ERROR_TYPES[event.kind];
147
+ if (errorType && event.sessionId) {
148
+ rejectSession(event.sessionId, errorType);
149
+ }
150
+ });
151
+ const disposeAll = () => {
152
+ unsubscribeFromEventBus();
153
+ for (const sessionId of Array.from(pendingBySession.keys())) {
154
+ rejectSession(sessionId, "unknown_session");
155
+ }
156
+ };
157
+ return { call, handleToolResult, handleToolError, handleToolCallProgress, cancel, rejectSession, disposeAll };
158
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `config.json` loading and validation (ARCHITECTURE.md §3). All fields are optional; unknown
3
+ * top-level keys produce a warning (not an error); invalid values throw a clear error naming the
4
+ * offending key.
5
+ */
6
+ import type { StateDirPaths } from "./state-dir.js";
7
+ export type PolicyDecision = "allow" | "deny" | "prompt";
8
+ /**
9
+ * `config.json`'s `policy` shape (ARCHITECTURE.md §12): `default` applies to tools whose
10
+ * descriptor has no `annotations.destructiveHint`; `destructive` applies when it is `true`;
11
+ * `tools["<alias>/<name>"]` overrides both for one specific tool on one specific session alias.
12
+ * `"prompt"` means "a human gate is required; if one cannot be guaranteed, deny" — today there are
13
+ * two such gates, both MCP-only (ARCHITECTURE.md §9/§12): an MCP client that declares the
14
+ * `elicitation` capability at `initialize` (issue #10, preferred whenever available) and an MCP
15
+ * client that honors `_meta["anthropic/requiresUserInteraction"]` (issue #14, Claude Code ≥
16
+ * v2.1.199, used only as a fallback for a client that doesn't declare elicitation). Every other
17
+ * caller (CLI, an MCP client on neither channel) gets `policy_denied` with reason
18
+ * `no_consent_channel`.
19
+ */
20
+ export type AppductPolicyConfig = {
21
+ default: PolicyDecision;
22
+ destructive: PolicyDecision;
23
+ tools?: Record<string, PolicyDecision>;
24
+ };
25
+ export type AppductConfig = {
26
+ wssPort: number;
27
+ keyPath: string;
28
+ graceSeconds: number;
29
+ linkTtlSeconds: number;
30
+ keepaliveIntervalSeconds: number;
31
+ /** Max retained `app_event`/etc. events per session (ARCHITECTURE.md §5's `events.since`
32
+ * retention buffer); default 256. */
33
+ eventBufferSize: number;
34
+ /** Days of `audit/<YYYY-MM-DD>.jsonl` history to keep; files older than this are pruned on
35
+ * daemon start and once a day thereafter (ARCHITECTURE.md §3). Default 30. */
36
+ auditRetentionDays: number;
37
+ /** Size at which `daemon.log` is rotated to `daemon.log.1` before a daemon is spawned
38
+ * (ARCHITECTURE.md §3/§4). Default 10 MiB. */
39
+ daemonLogMaxBytes: number;
40
+ policy: AppductPolicyConfig;
41
+ /**
42
+ * When the CLI/MCP client finds the running daemon on a different Appduct version, restart it
43
+ * even if that drops live sessions (issue #30, ARCHITECTURE.md §4's "Version drift"). Default
44
+ * `false`: with sessions connected the command fails with both versions and the remedy instead.
45
+ * The `--daemon-restart` flag and `APPDUCT_DAEMON_RESTART=1` force the same thing per run.
46
+ */
47
+ restartDaemonOnVersionMismatch: boolean;
48
+ /** Operator override for advertised-address detection (daemon/address.ts); undefined = auto-detect. */
49
+ advertisedIp?: string;
50
+ /**
51
+ * Deep-link URI scheme used to compose `appduct link`'s output (ARCHITECTURE.md §10: "taken
52
+ * from the flag, else `config.json`, else the CLI errors with a clear message"). Not part of the
53
+ * `config.json` shape enumerated in ARCHITECTURE.md §3 (which only covers daemon-side settings),
54
+ * but `config.json` is explicitly "all fields optional" there and this is the natural home for a
55
+ * CLI-side setting an operator wants to set once rather than pass with every `link` invocation.
56
+ */
57
+ scheme?: string;
58
+ /**
59
+ * The app's iOS bundle id, used only by the experimental `--open ios-device` /
60
+ * `appduct_connect` `target: "ios-device"` delivery path (issue #31): `xcrun devicectl device
61
+ * process launch` needs to be told which installed app to hand the URL to, and there is nothing
62
+ * in the deep link itself that says so. Overridable per invocation by `--bundle-id` (CLI) or
63
+ * `bundleId` (MCP). CLI-side like `scheme`, not a daemon setting.
64
+ */
65
+ iosBundleId?: string;
66
+ };
67
+ export declare const DEFAULT_AUDIT_RETENTION_DAYS = 30;
68
+ export declare const DEFAULT_DAEMON_LOG_MAX_BYTES: number;
69
+ export declare class AppductConfigError extends Error {
70
+ readonly key: string;
71
+ constructor(key: string, message: string);
72
+ }
73
+ export declare const defaultConfig: (paths: StateDirPaths) => AppductConfig;
74
+ export type ConfigWarnFn = (message: string) => void;
75
+ /** Loads and validates `<state-dir>/config.json`, falling back to defaults when it is missing. */
76
+ export declare const loadConfig: (paths: StateDirPaths, options?: {
77
+ warn?: ConfigWarnFn;
78
+ }) => Promise<AppductConfig>;
79
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC;yCACqC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB;kFAC8E;IAC9E,kBAAkB,EAAE,MAAM,CAAC;IAC3B;kDAC8C;IAC9C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;;;OAKG;IACH,8BAA8B,EAAE,OAAO,CAAC;IACxC,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAsBF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,4BAA4B,QAAmB,CAAC;AAE7D,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAAX,GAAG,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM;CAKlB;AAsCD,eAAO,MAAM,aAAa,GAAI,OAAO,aAAa,KAAG,aAgBpD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAErD,kGAAkG;AAClG,eAAO,MAAM,UAAU,GACrB,OAAO,aAAa,EACpB,UAAS;IAAE,IAAI,CAAC,EAAE,YAAY,CAAA;CAAO,KACpC,OAAO,CAAC,aAAa,CA4HvB,CAAC"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * `config.json` loading and validation (ARCHITECTURE.md §3). All fields are optional; unknown
3
+ * top-level keys produce a warning (not an error); invalid values throw a clear error naming the
4
+ * offending key.
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ const KNOWN_TOP_LEVEL_KEYS = new Set([
8
+ "wssPort",
9
+ "keyPath",
10
+ "graceSeconds",
11
+ "linkTtlSeconds",
12
+ "keepaliveIntervalSeconds",
13
+ "eventBufferSize",
14
+ "auditRetentionDays",
15
+ "daemonLogMaxBytes",
16
+ "policy",
17
+ "advertisedIp",
18
+ "scheme",
19
+ "iosBundleId",
20
+ "restartDaemonOnVersionMismatch",
21
+ ]);
22
+ const KNOWN_POLICY_KEYS = new Set(["default", "destructive", "tools"]);
23
+ const POLICY_DECISIONS = new Set(["allow", "deny", "prompt"]);
24
+ export const DEFAULT_AUDIT_RETENTION_DAYS = 30;
25
+ export const DEFAULT_DAEMON_LOG_MAX_BYTES = 10 * 1024 * 1024;
26
+ export class AppductConfigError extends Error {
27
+ key;
28
+ constructor(key, message) {
29
+ super(message);
30
+ this.key = key;
31
+ this.name = "AppductConfigError";
32
+ }
33
+ }
34
+ const configError = (key, message) => {
35
+ return new AppductConfigError(key, `Invalid Appduct config value for "${key}": ${message}`);
36
+ };
37
+ const requirePositiveInteger = (value, key) => {
38
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
39
+ throw configError(key, "must be a positive integer.");
40
+ }
41
+ return value;
42
+ };
43
+ const requireNonEmptyString = (value, key) => {
44
+ if (typeof value !== "string" || value.length === 0) {
45
+ throw configError(key, "must be a non-empty string.");
46
+ }
47
+ return value;
48
+ };
49
+ const requireBoolean = (value, key) => {
50
+ if (typeof value !== "boolean") {
51
+ throw configError(key, "must be a boolean.");
52
+ }
53
+ return value;
54
+ };
55
+ const requirePolicyDecision = (value, key) => {
56
+ if (typeof value !== "string" || !POLICY_DECISIONS.has(value)) {
57
+ throw configError(key, 'must be "allow", "deny", or "prompt".');
58
+ }
59
+ return value;
60
+ };
61
+ export const defaultConfig = (paths) => {
62
+ return {
63
+ wssPort: 8443,
64
+ keyPath: paths.keyPath,
65
+ graceSeconds: 600,
66
+ linkTtlSeconds: 300,
67
+ keepaliveIntervalSeconds: 15,
68
+ eventBufferSize: 256,
69
+ auditRetentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
70
+ daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
71
+ restartDaemonOnVersionMismatch: false,
72
+ policy: {
73
+ default: "allow",
74
+ destructive: "allow",
75
+ },
76
+ };
77
+ };
78
+ /** Loads and validates `<state-dir>/config.json`, falling back to defaults when it is missing. */
79
+ export const loadConfig = async (paths, options = {}) => {
80
+ const warn = options.warn ?? (() => { });
81
+ const config = defaultConfig(paths);
82
+ let raw;
83
+ try {
84
+ raw = JSON.parse(await readFile(paths.configPath, "utf8"));
85
+ }
86
+ catch (error) {
87
+ if (error.code === "ENOENT") {
88
+ return config;
89
+ }
90
+ throw configError("config.json", `could not be read/parsed (${error.message}).`);
91
+ }
92
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
93
+ throw configError("config.json", "must be a JSON object.");
94
+ }
95
+ const parsed = raw;
96
+ for (const key of Object.keys(parsed)) {
97
+ if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
98
+ warn(`Unknown Appduct config key "${key}" is ignored.`);
99
+ }
100
+ }
101
+ if (parsed.wssPort !== undefined) {
102
+ config.wssPort = requirePositiveInteger(parsed.wssPort, "wssPort");
103
+ }
104
+ if (parsed.keyPath !== undefined) {
105
+ config.keyPath = requireNonEmptyString(parsed.keyPath, "keyPath");
106
+ }
107
+ if (parsed.advertisedIp !== undefined) {
108
+ config.advertisedIp = requireNonEmptyString(parsed.advertisedIp, "advertisedIp");
109
+ }
110
+ if (parsed.scheme !== undefined) {
111
+ config.scheme = requireNonEmptyString(parsed.scheme, "scheme");
112
+ }
113
+ // Only a non-empty string here, like `scheme` — deliberately *not* the bundle-id charset check.
114
+ // This loader runs on every daemon start, and a typo in a CLI-side convenience key must not stop
115
+ // the daemon from starting. The charset is enforced where the value is actually used (`mintLink`,
116
+ // `handleConnectTool`, and `deliverToOpenTarget` itself), which is where it can be reported as a
117
+ // usage error against the command that needed it.
118
+ if (parsed.iosBundleId !== undefined) {
119
+ config.iosBundleId = requireNonEmptyString(parsed.iosBundleId, "iosBundleId");
120
+ }
121
+ if (parsed.graceSeconds !== undefined) {
122
+ config.graceSeconds = requirePositiveInteger(parsed.graceSeconds, "graceSeconds");
123
+ }
124
+ if (parsed.linkTtlSeconds !== undefined) {
125
+ config.linkTtlSeconds = requirePositiveInteger(parsed.linkTtlSeconds, "linkTtlSeconds");
126
+ }
127
+ if (parsed.keepaliveIntervalSeconds !== undefined) {
128
+ config.keepaliveIntervalSeconds = requirePositiveInteger(parsed.keepaliveIntervalSeconds, "keepaliveIntervalSeconds");
129
+ }
130
+ if (parsed.eventBufferSize !== undefined) {
131
+ config.eventBufferSize = requirePositiveInteger(parsed.eventBufferSize, "eventBufferSize");
132
+ }
133
+ if (parsed.auditRetentionDays !== undefined) {
134
+ config.auditRetentionDays = requirePositiveInteger(parsed.auditRetentionDays, "auditRetentionDays");
135
+ }
136
+ if (parsed.daemonLogMaxBytes !== undefined) {
137
+ config.daemonLogMaxBytes = requirePositiveInteger(parsed.daemonLogMaxBytes, "daemonLogMaxBytes");
138
+ }
139
+ if (parsed.restartDaemonOnVersionMismatch !== undefined) {
140
+ config.restartDaemonOnVersionMismatch = requireBoolean(parsed.restartDaemonOnVersionMismatch, "restartDaemonOnVersionMismatch");
141
+ }
142
+ if (parsed.policy !== undefined) {
143
+ if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
144
+ throw configError("policy", "must be an object.");
145
+ }
146
+ const policy = parsed.policy;
147
+ for (const key of Object.keys(policy)) {
148
+ if (!KNOWN_POLICY_KEYS.has(key)) {
149
+ warn(`Unknown Appduct config key "policy.${key}" is ignored.`);
150
+ }
151
+ }
152
+ if (policy.default !== undefined) {
153
+ config.policy.default = requirePolicyDecision(policy.default, "policy.default");
154
+ }
155
+ if (policy.destructive !== undefined) {
156
+ config.policy.destructive = requirePolicyDecision(policy.destructive, "policy.destructive");
157
+ }
158
+ if (policy.tools !== undefined) {
159
+ if (typeof policy.tools !== "object" || policy.tools === null || Array.isArray(policy.tools)) {
160
+ throw configError("policy.tools", "must be an object.");
161
+ }
162
+ const tools = {};
163
+ for (const [toolKey, toolValue] of Object.entries(policy.tools)) {
164
+ tools[toolKey] = requirePolicyDecision(toolValue, `policy.tools["${toolKey}"]`);
165
+ }
166
+ config.policy.tools = tools;
167
+ }
168
+ }
169
+ return config;
170
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Daemon composition root (ARCHITECTURE.md §4): state dir → config → pidfile → TLS → session
3
+ * engine → listener → RPC server. Implements `daemon.status`/`daemon.shutdown`, the session RPC
4
+ * surface (`link.create`, `sessions.list`, `sessions.describe`, `sessions.revoke`), and the
5
+ * invocation surface (`tools.list`, `tools.call`, `events.subscribe`), and policy + audit
6
+ * (ARCHITECTURE.md §12): every `tools.call` attempt is evaluated against `config.policy` and
7
+ * recorded to the audit log inline in the `tools.call` handler below.
8
+ */
9
+ import type { Clock } from "../cli/types.js";
10
+ import { detectAdvertisedAddress } from "./address.js";
11
+ import { type AppductConfig, type ConfigWarnFn } from "./config.js";
12
+ import { type EventBus } from "./event-bus.js";
13
+ import { type DaemonListener } from "./listener.js";
14
+ import { type RpcServer } from "./rpc-server.js";
15
+ import { type StateDirPaths } from "./state-dir.js";
16
+ import { type TimerFns } from "./timers.js";
17
+ /** How often audit retention runs after the startup prune (ARCHITECTURE.md §3: "once a day"). */
18
+ export declare const AUDIT_PRUNE_INTERVAL_MS: number;
19
+ export type DaemonOptions = {
20
+ stateDir: string;
21
+ clock?: Clock;
22
+ warn?: ConfigWarnFn;
23
+ /** Timer seam for the daily audit-retention sweep; defaults to real timers. */
24
+ timers?: TimerFns;
25
+ /** Advertised-address detector; defaults to `detectAdvertisedAddress` honoring
26
+ * `config.advertisedIp`. Overridable so tests can simulate a network change between two
27
+ * `link.create` calls without mocking `os.networkInterfaces()` process-wide. */
28
+ detectAddress?: () => ReturnType<typeof detectAdvertisedAddress>;
29
+ };
30
+ export type RunningDaemon = {
31
+ paths: StateDirPaths;
32
+ config: AppductConfig;
33
+ startedAt: Date;
34
+ server: RpcServer;
35
+ listener: DaemonListener;
36
+ eventBus: EventBus;
37
+ /** Resolves once graceful teardown (sockets closed, RPC/listener closed, pidfile released) completes. */
38
+ exited: Promise<void>;
39
+ /** Idempotent graceful teardown. */
40
+ shutdown: () => Promise<void>;
41
+ };
42
+ export declare const startDaemon: (options: DaemonOptions) => Promise<RunningDaemon>;
43
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/daemon/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8BH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAGvD,OAAO,EAAc,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGnE,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtF,OAAO,EAAmD,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACrG,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmB/E,iGAAiG;AACjG,eAAO,MAAM,uBAAuB,QAAsB,CAAC;AAE3D,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB;;oFAEgF;IAChF,aAAa,CAAC,EAAE,MAAM,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,yGAAyG;IACzG,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CAAC;AAkOF,eAAO,MAAM,WAAW,GAAU,SAAS,aAAa,KAAG,OAAO,CAAC,aAAa,CAkb/E,CAAC"}