appduct 0.10.0 → 0.11.1

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 (230) hide show
  1. package/README.md +122 -52
  2. package/dist/artifact-inspect.d.ts +0 -1
  3. package/dist/bin.d.ts +0 -1
  4. package/dist/bin.js +1250 -2
  5. package/dist/cli/command-options.d.ts +8 -1
  6. package/dist/cli/create-cli.d.ts +0 -1
  7. package/dist/cli/dispatch.d.ts +10 -1
  8. package/dist/cli/envelope.d.ts +19 -0
  9. package/dist/cli/global-flags.d.ts +30 -0
  10. package/dist/cli/open-target.d.ts +25 -8
  11. package/dist/cli/result-types.d.ts +31 -5
  12. package/dist/cli/router.d.ts +78 -0
  13. package/dist/cli/routes/daemon/index.d.ts +9 -0
  14. package/dist/cli/routes/daemon/index.js +848 -0
  15. package/dist/cli/routes/daemon/run.d.ts +3 -0
  16. package/dist/cli/routes/daemon/run.js +3246 -0
  17. package/dist/cli/routes/daemon/start.d.ts +3 -0
  18. package/dist/cli/routes/daemon/start.js +1677 -0
  19. package/dist/cli/routes/daemon/status.d.ts +4 -0
  20. package/dist/cli/routes/daemon/status.js +1685 -0
  21. package/dist/cli/routes/daemon/stop.d.ts +4 -0
  22. package/dist/cli/routes/daemon/stop.js +1655 -0
  23. package/dist/cli/routes/doctor.d.ts +3 -0
  24. package/dist/cli/routes/doctor.js +1075 -0
  25. package/dist/cli/routes/events.d.ts +3 -0
  26. package/dist/cli/routes/events.js +1915 -0
  27. package/dist/cli/routes/init.d.ts +3 -0
  28. package/dist/cli/routes/init.js +1486 -0
  29. package/dist/cli/routes/invoke.d.ts +3 -0
  30. package/dist/cli/routes/invoke.js +1893 -0
  31. package/dist/cli/routes/keygen.d.ts +3 -0
  32. package/dist/cli/routes/keygen.js +928 -0
  33. package/dist/cli/routes/link.d.ts +3 -0
  34. package/dist/cli/routes/link.js +2714 -0
  35. package/dist/cli/routes/ls.d.ts +3 -0
  36. package/dist/cli/routes/ls.js +1668 -0
  37. package/dist/cli/routes/mcp.d.ts +5 -0
  38. package/dist/cli/routes/mcp.js +3929 -0
  39. package/dist/cli/routes/revoke.d.ts +3 -0
  40. package/dist/cli/routes/revoke.js +1684 -0
  41. package/dist/cli/routes/tools.d.ts +3 -0
  42. package/dist/cli/routes/tools.js +1860 -0
  43. package/dist/cli/runner.d.ts +3 -11
  44. package/dist/cli/types.d.ts +9 -6
  45. package/dist/cli/version-guard.d.ts +20 -0
  46. package/dist/cli.d.ts +0 -1
  47. package/dist/client/app-client.d.ts +4 -4
  48. package/dist/client/bootstrap.d.ts +4 -4
  49. package/dist/client/connect.d.ts +0 -1
  50. package/dist/client/errors.d.ts +0 -1
  51. package/dist/client/index.d.ts +1 -2
  52. package/dist/client/index.js +2389 -16
  53. package/dist/commands/daemon/context.d.ts +14 -0
  54. package/dist/commands/daemon/run.d.ts +11 -0
  55. package/dist/commands/daemon/start.d.ts +5 -0
  56. package/dist/commands/daemon/status.d.ts +5 -0
  57. package/dist/commands/daemon/stop.d.ts +5 -0
  58. package/dist/commands/doctor.d.ts +0 -1
  59. package/dist/commands/events.d.ts +0 -1
  60. package/dist/commands/init.d.ts +17 -4
  61. package/dist/commands/invoke.d.ts +0 -1
  62. package/dist/commands/keygen.d.ts +0 -1
  63. package/dist/commands/link.d.ts +1 -2
  64. package/dist/commands/ls.d.ts +0 -1
  65. package/dist/commands/mcp.d.ts +2 -4
  66. package/dist/commands/revoke.d.ts +0 -1
  67. package/dist/commands/tools.d.ts +19 -3
  68. package/dist/daemon/address.d.ts +0 -1
  69. package/dist/daemon/audit.d.ts +6 -11
  70. package/dist/daemon/calls.d.ts +0 -1
  71. package/dist/daemon/config.d.ts +13 -16
  72. package/dist/daemon/daemon.d.ts +0 -1
  73. package/dist/daemon/event-bus.d.ts +0 -1
  74. package/dist/daemon/links.d.ts +0 -1
  75. package/dist/daemon/listener.d.ts +0 -1
  76. package/dist/daemon/log-rotation.d.ts +0 -1
  77. package/dist/daemon/pidfile.d.ts +14 -8
  78. package/dist/daemon/policy.d.ts +0 -1
  79. package/dist/daemon/registry.d.ts +0 -1
  80. package/dist/daemon/rpc-errors.d.ts +16 -0
  81. package/dist/daemon/rpc-server.d.ts +0 -11
  82. package/dist/daemon/sessions.d.ts +0 -1
  83. package/dist/daemon/socket-probe.d.ts +0 -1
  84. package/dist/daemon/state-dir.d.ts +0 -1
  85. package/dist/daemon/timers.d.ts +0 -1
  86. package/dist/daemon/tls.d.ts +0 -1
  87. package/dist/errors.d.ts +0 -1
  88. package/dist/host-certificate.d.ts +0 -1
  89. package/dist/index.d.ts +0 -1
  90. package/dist/index.js +1307 -3
  91. package/dist/key-material.d.ts +0 -1
  92. package/dist/link.d.ts +8 -7
  93. package/dist/mcp/app-tools.d.ts +153 -0
  94. package/dist/mcp/connect-tool.d.ts +14 -8
  95. package/dist/mcp/daemon-tools.d.ts +2 -11
  96. package/dist/mcp/events-tool.d.ts +0 -1
  97. package/dist/mcp/server.d.ts +33 -14
  98. package/dist/native-scheme.d.ts +0 -1
  99. package/dist/output.d.ts +8 -11
  100. package/dist/package-root.d.ts +11 -0
  101. package/dist/package-version.d.ts +0 -1
  102. package/dist/qr-terminal.d.ts +0 -1
  103. package/dist/rpc/client.d.ts +3 -3
  104. package/dist/scheme.d.ts +60 -2
  105. package/dist/spki-pin.d.ts +0 -1
  106. package/package.json +6 -3
  107. package/dist/artifact-inspect.d.ts.map +0 -1
  108. package/dist/artifact-inspect.js +0 -334
  109. package/dist/bin.d.ts.map +0 -1
  110. package/dist/cli/command-options.d.ts.map +0 -1
  111. package/dist/cli/command-options.js +0 -95
  112. package/dist/cli/create-cli.d.ts.map +0 -1
  113. package/dist/cli/create-cli.js +0 -56
  114. package/dist/cli/dispatch.d.ts.map +0 -1
  115. package/dist/cli/dispatch.js +0 -275
  116. package/dist/cli/open-target.d.ts.map +0 -1
  117. package/dist/cli/open-target.js +0 -438
  118. package/dist/cli/result-types.d.ts.map +0 -1
  119. package/dist/cli/result-types.js +0 -6
  120. package/dist/cli/runner.d.ts.map +0 -1
  121. package/dist/cli/runner.js +0 -111
  122. package/dist/cli/types.d.ts.map +0 -1
  123. package/dist/cli/types.js +0 -10
  124. package/dist/cli.d.ts.map +0 -1
  125. package/dist/cli.js +0 -2
  126. package/dist/client/app-client.d.ts.map +0 -1
  127. package/dist/client/app-client.js +0 -223
  128. package/dist/client/bootstrap.d.ts.map +0 -1
  129. package/dist/client/bootstrap.js +0 -108
  130. package/dist/client/connect.d.ts.map +0 -1
  131. package/dist/client/connect.js +0 -38
  132. package/dist/client/errors.d.ts.map +0 -1
  133. package/dist/client/errors.js +0 -40
  134. package/dist/client/index.d.ts.map +0 -1
  135. package/dist/commands/daemon.d.ts +0 -22
  136. package/dist/commands/daemon.d.ts.map +0 -1
  137. package/dist/commands/daemon.js +0 -128
  138. package/dist/commands/doctor.d.ts.map +0 -1
  139. package/dist/commands/doctor.js +0 -42
  140. package/dist/commands/events.d.ts.map +0 -1
  141. package/dist/commands/events.js +0 -76
  142. package/dist/commands/init.d.ts.map +0 -1
  143. package/dist/commands/init.js +0 -231
  144. package/dist/commands/invoke.d.ts.map +0 -1
  145. package/dist/commands/invoke.js +0 -54
  146. package/dist/commands/keygen.d.ts.map +0 -1
  147. package/dist/commands/keygen.js +0 -46
  148. package/dist/commands/link.d.ts.map +0 -1
  149. package/dist/commands/link.js +0 -43
  150. package/dist/commands/ls.d.ts.map +0 -1
  151. package/dist/commands/ls.js +0 -7
  152. package/dist/commands/mcp.d.ts.map +0 -1
  153. package/dist/commands/mcp.js +0 -98
  154. package/dist/commands/revoke.d.ts.map +0 -1
  155. package/dist/commands/revoke.js +0 -7
  156. package/dist/commands/tools.d.ts.map +0 -1
  157. package/dist/commands/tools.js +0 -52
  158. package/dist/daemon/address.d.ts.map +0 -1
  159. package/dist/daemon/address.js +0 -71
  160. package/dist/daemon/audit.d.ts.map +0 -1
  161. package/dist/daemon/audit.js +0 -237
  162. package/dist/daemon/calls.d.ts.map +0 -1
  163. package/dist/daemon/calls.js +0 -158
  164. package/dist/daemon/config.d.ts.map +0 -1
  165. package/dist/daemon/config.js +0 -170
  166. package/dist/daemon/daemon.d.ts.map +0 -1
  167. package/dist/daemon/daemon.js +0 -560
  168. package/dist/daemon/event-bus.d.ts.map +0 -1
  169. package/dist/daemon/event-bus.js +0 -111
  170. package/dist/daemon/links.d.ts.map +0 -1
  171. package/dist/daemon/links.js +0 -118
  172. package/dist/daemon/listener.d.ts.map +0 -1
  173. package/dist/daemon/listener.js +0 -132
  174. package/dist/daemon/log-rotation.d.ts.map +0 -1
  175. package/dist/daemon/log-rotation.js +0 -136
  176. package/dist/daemon/pidfile.d.ts.map +0 -1
  177. package/dist/daemon/pidfile.js +0 -90
  178. package/dist/daemon/policy.d.ts.map +0 -1
  179. package/dist/daemon/policy.js +0 -24
  180. package/dist/daemon/registry.d.ts.map +0 -1
  181. package/dist/daemon/registry.js +0 -31
  182. package/dist/daemon/rpc-server.d.ts.map +0 -1
  183. package/dist/daemon/rpc-server.js +0 -203
  184. package/dist/daemon/sessions.d.ts.map +0 -1
  185. package/dist/daemon/sessions.js +0 -429
  186. package/dist/daemon/socket-probe.d.ts.map +0 -1
  187. package/dist/daemon/socket-probe.js +0 -28
  188. package/dist/daemon/state-dir.d.ts.map +0 -1
  189. package/dist/daemon/state-dir.js +0 -43
  190. package/dist/daemon/timers.d.ts.map +0 -1
  191. package/dist/daemon/timers.js +0 -12
  192. package/dist/daemon/tls.d.ts.map +0 -1
  193. package/dist/daemon/tls.js +0 -79
  194. package/dist/errors.d.ts.map +0 -1
  195. package/dist/errors.js +0 -162
  196. package/dist/host-certificate.d.ts.map +0 -1
  197. package/dist/host-certificate.js +0 -111
  198. package/dist/index.d.ts.map +0 -1
  199. package/dist/key-material.d.ts.map +0 -1
  200. package/dist/key-material.js +0 -42
  201. package/dist/link.d.ts.map +0 -1
  202. package/dist/link.js +0 -117
  203. package/dist/mcp/connect-tool.d.ts.map +0 -1
  204. package/dist/mcp/connect-tool.js +0 -396
  205. package/dist/mcp/daemon-tools.d.ts.map +0 -1
  206. package/dist/mcp/daemon-tools.js +0 -29
  207. package/dist/mcp/events-tool.d.ts.map +0 -1
  208. package/dist/mcp/events-tool.js +0 -322
  209. package/dist/mcp/server.d.ts.map +0 -1
  210. package/dist/mcp/server.js +0 -538
  211. package/dist/mcp/tool-mapping.d.ts +0 -58
  212. package/dist/mcp/tool-mapping.d.ts.map +0 -1
  213. package/dist/mcp/tool-mapping.js +0 -143
  214. package/dist/mcp/tool-namespace.d.ts +0 -40
  215. package/dist/mcp/tool-namespace.d.ts.map +0 -1
  216. package/dist/mcp/tool-namespace.js +0 -65
  217. package/dist/native-scheme.d.ts.map +0 -1
  218. package/dist/native-scheme.js +0 -465
  219. package/dist/output.d.ts.map +0 -1
  220. package/dist/output.js +0 -342
  221. package/dist/package-version.d.ts.map +0 -1
  222. package/dist/package-version.js +0 -47
  223. package/dist/qr-terminal.d.ts.map +0 -1
  224. package/dist/qr-terminal.js +0 -45
  225. package/dist/rpc/client.d.ts.map +0 -1
  226. package/dist/rpc/client.js +0 -819
  227. package/dist/scheme.d.ts.map +0 -1
  228. package/dist/scheme.js +0 -321
  229. package/dist/spki-pin.d.ts.map +0 -1
  230. package/dist/spki-pin.js +0 -14
@@ -11,4 +11,3 @@ export declare const generatePrivateKeyPem: () => string;
11
11
  * `path` still succeed, since `rename` replaces the destination on POSIX regardless of whether it
12
12
  * already existed — only the *temp* path must be exclusive. */
13
13
  export declare const writePrivateKeyAtomically: (path: string, keyPem: string) => Promise<void>;
14
- //# sourceMappingURL=key-material.d.ts.map
package/dist/link.d.ts CHANGED
@@ -11,10 +11,11 @@
11
11
  * The `pin` param is separate from the `appduct` bootstrap blob (opt-in hardening dev-mode) — it
12
12
  * is never part of that binary v2 payload, so old apps that don't know about it simply ignore it.
13
13
  * It carries the daemon's SPKI pin (same value as `daemon.status`'s `pinnedKeys[0]` / `appduct
14
- * keygen`'s output) so a debug build with no build-time `cliPins` configured can trust it for this
15
- * one connection instead of requiring a native rebuild just to test locally. The pin is standard
16
- * (non-URL-safe) base64 (`sha256/<44-char-base64>`, may contain `+`/`/`/`=`), so it is percent-
17
- * encoded here; native/JS parsers use `URLSearchParams`, which decodes it back.
14
+ * keygen`'s output) so any build that embeds no `cliPins` — whatever its build type, there is no
15
+ * build-type gate — can trust it for this one connection instead of requiring a native rebuild
16
+ * just to test locally. The pin is standard (non-URL-safe) base64
17
+ * (`sha256/<44-char-base64>`, may contain `+`/`/`/`=`), so it is percent-encoded here; native/JS
18
+ * parsers use `URLSearchParams`, which decodes it back.
18
19
  */
19
20
  import { type AgentEndpoint, type LinkCreateResult } from "@appduct/shared";
20
21
  import { type ExecFn, type OpenTarget } from "./cli/open-target.js";
@@ -40,8 +41,9 @@ export type MintLinkOptions = {
40
41
  /** An adb device serial (`target: "android"`), a simulator udid (`target: "ios-sim"`) or a
41
42
  * paired-device udid (`target: "ios-device"`). Only meaningful alongside a target. */
42
43
  device?: string;
43
- /** Overrides `config.json`'s `iosBundleId`. Only meaningful with `target: "ios-device"`. */
44
- bundleId?: string;
44
+ /** The installed app's id (Android package name / iOS bundle id) — highest-precedence source in
45
+ * `resolveAppId`'s order. Only meaningful with `target: "android"` or `target: "ios-device"`. */
46
+ appId?: string;
45
47
  /** `target: "ios-device"` only: terminate a running instance before launching
46
48
  * (`--terminate-existing`). Off by default. */
47
49
  relaunch?: boolean;
@@ -73,4 +75,3 @@ export type MintLinkResult = {
73
75
  * that straight through the CLI's own error path; `client/bootstrap.ts` converts it to a
74
76
  * `AppductError` via `toAppductError` (which already understands `AppductCliError`). */
75
77
  export declare const mintLink: (options: MintLinkOptions) => Promise<MintLinkResult>;
76
- //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The built-in `appduct_list_tools` / `appduct_describe_tool` / `appduct_call_tool` MCP tools
3
+ * (ARCHITECTURE.md §9). The app's own tools are never listed as MCP tools. An agent reaches them
4
+ * the way the CLI does: compact signatures first (`appduct tools`), one full schema on demand
5
+ * (`appduct tools <name>`), then a call by name (`appduct invoke`). A registry of hundreds of tools
6
+ * therefore costs a client three fixed tool definitions, and `tools/list` never changes while an
7
+ * agent works.
8
+ *
9
+ * Every lookup goes to the daemon live, with no caching: the session and its registry can change
10
+ * between any two calls, and the daemon is the single source of truth for both.
11
+ */
12
+ import { type EffectivePolicyDecision, type ListedToolDescriptor } from "@appduct/shared";
13
+ import type { DaemonCall } from "./daemon-tools.js";
14
+ export declare const LIST_TOOLS_TOOL_NAME = "appduct_list_tools";
15
+ export declare const DESCRIBE_TOOL_TOOL_NAME = "appduct_describe_tool";
16
+ export declare const CALL_TOOL_TOOL_NAME = "appduct_call_tool";
17
+ /** Applied when `appduct_list_tools` gets no `limit`, so the first call on a large app returns a
18
+ * page, not the whole registry; `total` tells the agent how much it left out. */
19
+ export declare const DEFAULT_LIST_TOOLS_LIMIT = 50;
20
+ export declare const LIST_TOOLS_TOOL_DESCRIPTOR: {
21
+ readonly name: "appduct_list_tools";
22
+ readonly description: string;
23
+ readonly inputSchema: {
24
+ readonly type: "object";
25
+ readonly properties: {
26
+ readonly selector: {
27
+ readonly type: "string";
28
+ readonly minLength: 1;
29
+ readonly description: "Session alias or id. Omit to target the sole active/suspended session.";
30
+ };
31
+ readonly group: {
32
+ readonly type: "string";
33
+ readonly pattern: string;
34
+ };
35
+ readonly filter: {
36
+ readonly type: "string";
37
+ readonly maxLength: 256;
38
+ };
39
+ readonly limit: {
40
+ readonly type: "integer";
41
+ readonly exclusiveMinimum: 0;
42
+ };
43
+ readonly offset: {
44
+ readonly type: "integer";
45
+ readonly minimum: 0;
46
+ };
47
+ };
48
+ readonly additionalProperties: false;
49
+ };
50
+ };
51
+ export declare const DESCRIBE_TOOL_TOOL_DESCRIPTOR: {
52
+ readonly name: "appduct_describe_tool";
53
+ readonly description: string;
54
+ readonly inputSchema: {
55
+ readonly type: "object";
56
+ readonly properties: {
57
+ readonly selector: {
58
+ readonly type: "string";
59
+ readonly minLength: 1;
60
+ readonly description: "Session alias or id. Omit to target the sole active/suspended session.";
61
+ };
62
+ readonly name: {
63
+ readonly type: "string";
64
+ readonly minLength: 1;
65
+ };
66
+ };
67
+ readonly required: readonly ["name"];
68
+ readonly additionalProperties: false;
69
+ };
70
+ };
71
+ export declare const CALL_TOOL_TOOL_DESCRIPTOR: {
72
+ readonly name: "appduct_call_tool";
73
+ readonly description: string;
74
+ readonly inputSchema: {
75
+ readonly type: "object";
76
+ readonly properties: {
77
+ readonly selector: {
78
+ readonly type: "string";
79
+ readonly minLength: 1;
80
+ readonly description: "Session alias or id. Omit to target the sole active/suspended session.";
81
+ };
82
+ readonly name: {
83
+ readonly type: "string";
84
+ readonly minLength: 1;
85
+ };
86
+ readonly args: {
87
+ readonly type: "object";
88
+ };
89
+ readonly timeoutMs: {
90
+ readonly type: "integer";
91
+ readonly minimum: 1000;
92
+ readonly maximum: 600000;
93
+ };
94
+ };
95
+ readonly required: readonly ["name"];
96
+ readonly additionalProperties: false;
97
+ };
98
+ };
99
+ /** One app tool resolved against a live session, with everything `appduct_call_tool` needs to
100
+ * run it: the session id the call, its progress subscription and any cancel are routed by, the
101
+ * alias shown to people, the descriptor (for its name and declared deadline), and the effective
102
+ * policy (for whether to ask for consent). */
103
+ export type ResolvedAppTool = {
104
+ sessionId: string;
105
+ alias: string;
106
+ descriptor: ListedToolDescriptor;
107
+ policy: EffectivePolicyDecision;
108
+ };
109
+ export type CallToolArgs = {
110
+ selector?: string;
111
+ name: string;
112
+ args: Record<string, unknown>;
113
+ timeoutMs?: number;
114
+ };
115
+ export declare const handleListToolsTool: (rawArgs: unknown, call: DaemonCall) => Promise<{
116
+ groups?: import("@appduct/shared").ToolGroupSummary[] | undefined;
117
+ tools: {
118
+ annotations?: import("@appduct/shared").ToolAnnotations | undefined;
119
+ name: string;
120
+ signature: string;
121
+ summary: string;
122
+ group: string | null;
123
+ policy: EffectivePolicyDecision;
124
+ }[];
125
+ offset?: {} | undefined;
126
+ limit: {};
127
+ filter?: {} | undefined;
128
+ group?: {} | undefined;
129
+ session: string;
130
+ total: number;
131
+ }>;
132
+ export declare const handleDescribeToolTool: (rawArgs: unknown, call: DaemonCall) => Promise<{
133
+ name: string;
134
+ description: string;
135
+ input_schema?: import("@appduct/shared").ToolSchemaDescriptor | undefined;
136
+ output_schema?: import("@appduct/shared").ToolSchemaDescriptor | undefined;
137
+ annotations?: import("@appduct/shared").ToolAnnotations | undefined;
138
+ timeout_ms?: number | undefined;
139
+ group: string | null;
140
+ session: string;
141
+ signature: string;
142
+ policy: EffectivePolicyDecision;
143
+ }>;
144
+ export declare const parseCallToolArgs: (rawArgs: unknown) => CallToolArgs;
145
+ /**
146
+ * The deadline a call runs under: the tool's own (`clampTimeout` folds in the 10 s default for a
147
+ * tool that declares none), or a shorter one the caller asked for. Never longer: the `tool_call`
148
+ * frame carries no deadline, so the app stops the handler at the tool's own deadline whatever the
149
+ * daemon waits for (docs/PROTOCOL.md), and a longer caller deadline would only turn a clear
150
+ * `tool_timeout` into the same timeout after a retry that ran the tool again.
151
+ */
152
+ export declare const resolveCallDeadline: (tool: ResolvedAppTool, requestedTimeoutMs: number | undefined) => number;
153
+ export declare const resolveAppTool: (call: DaemonCall, selector: string | undefined, name: string) => Promise<ResolvedAppTool>;
@@ -29,7 +29,7 @@ export declare const CONNECT_TOOL_DESCRIPTOR: {
29
29
  readonly device: {
30
30
  readonly type: "string";
31
31
  };
32
- readonly bundleId: {
32
+ readonly appId: {
33
33
  readonly type: "string";
34
34
  };
35
35
  readonly relaunch: {
@@ -63,10 +63,10 @@ export declare const WAIT_FOR_SESSION_TOOL_DESCRIPTOR: {
63
63
  };
64
64
  /** Errors from the built-in management tools reuse the daemon's wire `ErrorType` union
65
65
  * (`RpcApplicationError`-style: `{ type, message }`) so the MCP server's generic error-content
66
- * mapping (see `server.ts`) handles them the same way it handles a proxied device tool's error. */
66
+ * mapping (see `server.ts`) handles them the same way it handles an app tool's error. */
67
67
  export declare class McpBuiltinToolError extends Error {
68
- readonly type: "invalid_request" | "tool_timeout" | "tool_execution_error";
69
- constructor(type: "invalid_request" | "tool_timeout" | "tool_execution_error", message: string);
68
+ readonly type: "invalid_request" | "tool_not_found" | "tool_timeout" | "tool_cancelled" | "tool_execution_error" | "connection_error";
69
+ constructor(type: "invalid_request" | "tool_not_found" | "tool_timeout" | "tool_cancelled" | "tool_execution_error" | "connection_error", message: string);
70
70
  }
71
71
  export type ConnectToolDeps = {
72
72
  call: DaemonCall;
@@ -77,9 +77,16 @@ export type ConnectToolDeps = {
77
77
  /** Every location `scheme.ts` consulted, named in the failure below so an agent can tell its
78
78
  * human exactly where to put a scheme instead of guessing at one global file. */
79
79
  schemeTried?: string[];
80
- /** `config.json`'s `iosBundleId`, the default for the experimental `ios-device` target when the
81
- * call did not pass `bundleId`. Same source order as `scheme`. */
82
- iosBundleId?: string;
80
+ /** Where the project `.appduct/config.json` walk-up starts when resolving an `appId` for
81
+ * `target: "android"`/`"ios-device"` (`scheme.ts`'s `resolveAppId`) — unlike `scheme`, this is
82
+ * resolved fresh on every call rather than once at startup, since which platform's `appId.<key>`
83
+ * is needed depends on the call's (or auto-detection's) target. Defaults to `process.cwd()`. */
84
+ cwd?: string;
85
+ /** The state directory in use, so the `appId` walk-up can never mistake it for a project config
86
+ * — the same exclusion `resolveScheme` is given at server startup (`commands/mcp.ts`). Without
87
+ * it, an operator who points `--state-dir` at a directory on the walk-up path would have that
88
+ * *global* file read at the project tier, inverting the documented precedence. */
89
+ stateDirRoot?: string;
83
90
  exec?: ExecFn;
84
91
  env?: NodeJS.ProcessEnv;
85
92
  };
@@ -112,4 +119,3 @@ export type WaitForSessionToolResult = {
112
119
  alias: string;
113
120
  };
114
121
  export declare const handleWaitForSessionTool: (rawArgs: unknown, deps: WaitForSessionToolDeps) => Promise<WaitForSessionToolResult>;
115
- //# sourceMappingURL=connect-tool.d.ts.map
@@ -1,13 +1,4 @@
1
- /**
2
- * Fetches the effective (possibly namespaced) MCP tool list live from the daemon
3
- * (ARCHITECTURE.md §9): `sessions.list` for the live sessions, then `tools.list` per session.
4
- * There is no caching here — every `tools/list` request and every list-changed check re-fetches,
5
- * since the daemon's session/registry state is the single source of truth and can change between
6
- * any two calls.
7
- */
8
- import { type NamespacedTool } from "./tool-namespace.js";
9
1
  /** A method call against the daemon RPC — satisfied by both `callDaemon` (bound to a method+params)
10
- * and `DaemonStream.call` from `rpc/client.ts`. */
2
+ * and `DaemonStream.call` from `rpc/client.ts`. The MCP server's built-in tools take one of these
3
+ * rather than a whole stream, so they can be tested against a fake. */
11
4
  export type DaemonCall = <TResult>(method: string, params?: unknown) => Promise<TResult>;
12
- export declare const fetchEffectiveTools: (call: DaemonCall) => Promise<NamespacedTool[]>;
13
- //# sourceMappingURL=daemon-tools.d.ts.map
@@ -108,4 +108,3 @@ export type WaitForEventToolResult = {
108
108
  seq: number;
109
109
  };
110
110
  export declare const handleWaitForEventTool: (rawArgs: unknown, deps: WaitForEventToolDeps) => Promise<WaitForEventToolResult>;
111
- //# sourceMappingURL=events-tool.d.ts.map
@@ -1,28 +1,45 @@
1
1
  /**
2
- * The `appduct mcp` stdio MCP server (ARCHITECTURE.md §9): a thin proxy that maps the daemon RPC
3
- * surface (`rpc/client.ts`, same auto-spawning client the CLI uses) onto MCP's `tools/list`,
4
- * `tools/call`, `notifications/tools/list_changed`, progress notifications, and one resource
2
+ * The `appduct mcp` stdio MCP server (ARCHITECTURE.md §9): a thin proxy over the daemon RPC surface
3
+ * (`rpc/client.ts`, same auto-spawning client the CLI uses). `tools/list` is a fixed set of
4
+ * built-in tools; the app's own tools are reached through `appduct_list_tools`,
5
+ * `appduct_describe_tool` and `appduct_call_tool` (`app-tools.ts`), mirroring the CLI, never
6
+ * listed as MCP tools themselves. Also progress notifications, cancellation, and one resource
5
7
  * (`appduct://sessions`). All logging here goes to stderr only — stdout is reserved for the MCP
6
8
  * transport's protocol frames.
7
9
  *
8
10
  * One persistent daemon connection (`stream`) is used for everything except progress-correlated
9
- * `tools.call`s, which get their own short-lived connection (see `callProxiedTool` below) so the
11
+ * `tools.call`s, which get their own short-lived connection (see `callAppTool` below) so the
10
12
  * `tool_call_started` event that reveals the call's `callId` is unambiguous.
11
13
  *
12
- * This is also where both `"prompt"`-policy consent channels live (ARCHITECTURE.md §12):
13
- * `resolveToolCallConsent` prefers `elicitation/create` (issue #10, any client that declares the
14
- * `elicitation` capability) over the `_meta["anthropic/requiresUserInteraction"]` flag (issue #14,
15
- * Claude Code ≥ v2.1.199 only), and the two never arm for the same call — see
16
- * `clientSupportsElicitation` and the `tools/list` handler below.
14
+ * This is also where the `"prompt"`-policy consent channel lives (ARCHITECTURE.md §12):
15
+ * `resolveToolCallConsent` asks the human via `elicitation/create` (issue #10) when the client
16
+ * declared the `elicitation` capability. A client that didn't gets no consent marker, so the
17
+ * daemon's `"prompt"` gate denies the call (`policy_denied`, reason `no_consent_channel`).
17
18
  */
18
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
19
20
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
20
21
  import type { ExecFn } from "../cli/open-target.js";
21
- import { type SpawnFn, type VersionCheckOptions } from "../rpc/client.js";
22
+ import { type DaemonStream, type SpawnFn, type VersionCheckOptions } from "../rpc/client.js";
22
23
  export declare const SESSIONS_RESOURCE_URI = "appduct://sessions";
24
+ /**
25
+ * How this server opens a daemon connection. Both the startup stream and each short-lived
26
+ * progress stream go through it, so a caller can put something other than a real daemon on the
27
+ * other end. The only production implementation is {@link openDaemonStream}; the seam exists so
28
+ * the behaviour that is purely this module's own — the built-in tools, consent, progress and
29
+ * cancellation — can be tested without a TLS listener, a pidfile and a
30
+ * subprocess, none of which those behaviours depend on.
31
+ */
32
+ export type OpenDaemonStreamFn = (options: {
33
+ stateDir: string;
34
+ spawn?: SpawnFn;
35
+ checkVersion?: VersionCheckOptions;
36
+ }) => Promise<DaemonStream>;
23
37
  export type CreateMcpServerOptions = {
24
38
  stateDir: string;
25
39
  spawn?: SpawnFn;
40
+ /** Overrides how daemon connections are opened; defaults to {@link openDaemonStream}. Test-only
41
+ * seam — every real caller (`cli/mcp-command.ts`) leaves it unset. */
42
+ openStream?: OpenDaemonStreamFn;
26
43
  /**
27
44
  * Daemon/CLI version check (issue #30), applied to the startup stream only — never to the
28
45
  * short-lived progress streams below, which must never restart the daemon out from under a call
@@ -39,9 +56,12 @@ export type CreateMcpServerOptions = {
39
56
  /** Every location consulted while resolving {@link scheme}, so `appduct_connect` can name
40
57
  * them when it has to fail. Only meaningful when `scheme` is undefined. */
41
58
  schemeTried?: string[];
42
- /** `config.json`'s `iosBundleId`; the default bundle id for `appduct_connect`'s experimental
43
- * `ios-device` target (issue #31). */
44
- iosBundleId?: string;
59
+ /** Where `appduct_connect` resolves an `appId` from (its project `.appduct/config.json`
60
+ * walk-up); see `ConnectToolDeps.cwd`. Defaults to `process.cwd()`. */
61
+ cwd?: string;
62
+ /** The state directory in use, excluded from that walk-up exactly as it is from `scheme`'s.
63
+ * See `ConnectToolDeps.stateDirRoot`. */
64
+ stateDirRoot?: string;
45
65
  exec?: ExecFn;
46
66
  env?: NodeJS.ProcessEnv;
47
67
  /** Overrides `ELICITATION_TIMEOUT_MS` (ARCHITECTURE.md §12 / issue #10) — test-only seam so the
@@ -55,4 +75,3 @@ export type McpServerHandle = {
55
75
  close: () => Promise<void>;
56
76
  };
57
77
  export declare const createMcpServer: (options: CreateMcpServerOptions) => Promise<McpServerHandle>;
58
- //# sourceMappingURL=server.d.ts.map
@@ -56,4 +56,3 @@ export type NativeSchemeDiscovery = {
56
56
  * every location this looked without the caller needing to know which probes exist.
57
57
  */
58
58
  export declare const discoverNativeScheme: (root: string) => Promise<NativeSchemeDiscovery>;
59
- //# sourceMappingURL=native-scheme.d.ts.map
package/dist/output.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import { type EventNotification } from "@appduct/shared";
2
2
  import type { CliResult } from "./cli/result-types.js";
3
+ import { type GlobalFlags } from "./cli/global-flags.js";
3
4
  export type RenderOptions = {
4
5
  command: string;
5
- json: boolean;
6
- color: boolean;
6
+ flags: GlobalFlags;
7
+ /** "Now", for the one renderer (`ls`'s Age column) that needs a reference time absent `meta`
8
+ * (which is only attached under `--verbose`). Defaults to the wall clock when omitted, matching
9
+ * the pre-`--verbose` fallback behavior for a caller that doesn't care (e.g. non-`ls` tests). */
10
+ now?: Date;
7
11
  /** `link`-only: also render the deep link as terminal QR art (never affects `--json` output). */
8
12
  qr?: boolean;
9
13
  /** `tools`-only: render full schemas/annotations for every listed tool, not just name+description. */
@@ -14,15 +18,8 @@ export declare const renderResult: (result: CliResult<unknown>, options: RenderO
14
18
  stderr?: string;
15
19
  };
16
20
  /** Renders one `appduct events` line: NDJSON under `--json`, a compact human line otherwise. */
17
- export declare const renderEventLine: (event: EventNotification, options: {
18
- json: boolean;
19
- color: boolean;
20
- }) => string;
21
+ export declare const renderEventLine: (event: EventNotification, flags: GlobalFlags) => string;
21
22
  /** Renders the trailing cursor line for `appduct events --since` (issue #6): NDJSON under
22
23
  * `--json` so a scripted caller can parse the resume point without maxing `seq` over the printed
23
24
  * events (impossible when the response is empty), a human note otherwise. */
24
- export declare const renderEventsCursorLine: (cursor: number, options: {
25
- json: boolean;
26
- color: boolean;
27
- }) => string;
28
- //# sourceMappingURL=output.d.ts.map
25
+ export declare const renderEventsCursorLine: (cursor: number, flags: GlobalFlags) => string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Locates this package's root directory (the one holding `appduct`'s `package.json`) from
3
+ * wherever this module happens to be evaluated.
4
+ *
5
+ * The published build is bundled (`scripts/bundle.mjs`), so a module's location on disk is not
6
+ * its location in `src/`: this code may run from `src/…` under Vitest, from `dist/<entry>.js`, or
7
+ * from a shared chunk at `dist/<name>-<hash>.js`. A fixed `join(here, "..")` cannot be right in
8
+ * all three, so the root is found by walking up to the nearest `package.json` that names this
9
+ * package — at most a couple of `stat`s, done once per process.
10
+ */
11
+ export declare const getPackageRoot: () => string;
@@ -27,4 +27,3 @@ export declare const getPackageVersion: () => string;
27
27
  * started in-process by a test picks up the environment that test set.
28
28
  */
29
29
  export declare const getDaemonReportedVersion: (env?: NodeJS.ProcessEnv) => string;
30
- //# sourceMappingURL=package-version.d.ts.map
@@ -10,4 +10,3 @@ export type QrTerminalRenderOptions = {
10
10
  * Renders a QR payload as compact half-block terminal art (two modules per character row).
11
11
  */
12
12
  export declare const renderQrToTerminal: (content: string, options?: QrTerminalRenderOptions) => string;
13
- //# sourceMappingURL=qr-terminal.d.ts.map
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The auto-spawning RPC client library (ARCHITECTURE.md §4, §5) used by the CLI, MCP server, and
3
- * tests to talk to `daemon.sock`. On `ENOENT`/`ECONNREFUSED` it takes an exclusive spawn-lock,
4
- * spawns `daemon run` detached, polls the socket until ready, then retries the request once.
3
+ * tests to talk to `daemon.sock`. On `ENOENT`/`ECONNREFUSED` it creates the state dir if it is
4
+ * missing, takes an exclusive spawn-lock, spawns `daemon run` detached, polls the socket until
5
+ * ready, then retries the request once.
5
6
  */
6
7
  import { type RpcErrorData } from "@appduct/shared";
7
8
  export type SpawnContext = {
@@ -134,4 +135,3 @@ export type DaemonStream = {
134
135
  export declare const openDaemonStream: (options: Omit<CallDaemonOptions, "requestTimeoutMs"> & {
135
136
  requestTimeoutMs?: number;
136
137
  }) => Promise<DaemonStream>;
137
- //# sourceMappingURL=client.d.ts.map
package/dist/scheme.d.ts CHANGED
@@ -33,7 +33,9 @@
33
33
  * project file (`native-scheme.ts`'s doc comment has the detail). Dynamic-config projects use
34
34
  * `--scheme`, `APPDUCT_SCHEME`, or a project `.appduct/config.json` instead.
35
35
  *
36
- * A project `.appduct/config.json` carries client-side keys only (`scheme` today). It never
36
+ * A project `.appduct/config.json` carries client-side keys only — `scheme` and, since issue #63,
37
+ * `appId.<platform>` (see {@link resolveAppId} below, which mirrors this module's shape but with a
38
+ * shorter order: no environment-variable tier and no filesystem-discovery tier). It never
37
39
  * redirects the state directory — `--state-dir` / `APPDUCT_STATE_DIR` remain the only way to do
38
40
  * that — so a project file can never move the daemon's key, socket or audit log.
39
41
  */
@@ -142,6 +144,23 @@ export declare const findProjectConfig: (startDir: string, options?: ProjectConf
142
144
  * accepting (say) `keyPath` here would let a checked-in file redirect the daemon's key material.
143
145
  */
144
146
  export declare const readProjectConfigScheme: (path: string) => Promise<string | undefined>;
147
+ /** A target's platform, and the key it reads under `appId` in a project config — `"ios"` for
148
+ * `ios-device`, `"android"` for `android`. See `cli/open-target.ts`'s `platformOf`, the single
149
+ * place a delivery target is mapped to one of these. */
150
+ export type AppIdPlatform = "ios" | "android";
151
+ /**
152
+ * Reads `appId.<platform>` out of a project `.appduct/config.json`. Same stance as
153
+ * {@link readProjectConfigScheme}: this is Appduct's own file, so anything wrong with the `appId`
154
+ * block is a hard error rather than a silent fall-through, including an unknown key inside it — a
155
+ * typo'd `"andriod"` must read as "this file is broken", not as "no Android app id was recorded",
156
+ * which would otherwise look exactly like a project that simply hasn't set one up yet.
157
+ *
158
+ * Deliberately does *not* apply `cli/open-target.ts`'s `isValidAppId` charset check here: that
159
+ * check guards an argv/remote-shell hazard at the point a value is actually used, the same
160
+ * arrangement the old `iosBundleId` config key had. A structurally well-formed but wrong-for-its-
161
+ * platform id is caught there instead, with the command that needed it named in the error.
162
+ */
163
+ export declare const readProjectConfigAppId: (path: string, platform: AppIdPlatform) => Promise<string | undefined>;
145
164
  export type ResolveSchemeOptions = {
146
165
  /** `--scheme` (or a programmatic `scheme` option) — highest precedence. */
147
166
  flagScheme?: string;
@@ -178,4 +197,43 @@ export declare const resolveScheme: (options?: ResolveSchemeOptions) => Promise<
178
197
  export declare const describeMissingScheme: (tried: string[]) => string;
179
198
  /** {@link resolveScheme}, throwing {@link usageError} with {@link describeMissingScheme} when nothing matched. */
180
199
  export declare const resolveSchemeOrThrow: (options?: ResolveSchemeOptions) => Promise<string>;
181
- //# sourceMappingURL=scheme.d.ts.map
200
+ /** Which step of {@link resolveAppId}'s order produced the app id. */
201
+ export type AppIdSource = "flag" | "project-config";
202
+ export type ResolvedAppId = {
203
+ /** Undefined when no source produced one; `tried` then explains where we looked. */
204
+ appId?: string;
205
+ source?: AppIdSource;
206
+ /** Human-readable descriptions of every location consulted, in order, for error messages. */
207
+ tried: string[];
208
+ };
209
+ export type ResolveAppIdOptions = {
210
+ /** Which `appId.<platform>` key to read from a project config; see `cli/open-target.ts`'s
211
+ * `platformOf`. */
212
+ platform: AppIdPlatform;
213
+ /** `--app-id` (CLI `link`) / `appId` (MCP `appduct_connect`) / `appId` (`mintLink`, the
214
+ * `appduct/client` `link()`) — highest precedence, because the target is known at the call
215
+ * site. */
216
+ flagAppId?: string;
217
+ /** Where the project walk-up starts. Defaults to `process.cwd()`. */
218
+ cwd?: string;
219
+ /** The state directory root, so the walk-up never mistakes it for a project config. */
220
+ stateDirRoot?: string;
221
+ /** Overrides the home directory whose `.appduct` the walk-up skips (tests point this at a
222
+ * temp tree rather than the real `$HOME`). */
223
+ homeDir?: string;
224
+ };
225
+ /**
226
+ * Resolves an app id against the order the contract fixes: an explicit value first, then the
227
+ * nearest project `.appduct/config.json` declaring `appId.<platform>` (the same walk-up
228
+ * {@link resolveScheme} uses, via {@link findProjectConfigs}). Unlike {@link resolveScheme} there
229
+ * is no environment-variable tier and no filesystem-discovery tier (issue #63 leaves static
230
+ * discovery of an app id from `build.gradle`/`app.json` out of scope) — an app id that cannot be
231
+ * found either way is a plain usage error, not something worth guessing at.
232
+ */
233
+ export declare const resolveAppId: (options: ResolveAppIdOptions) => Promise<ResolvedAppId>;
234
+ /**
235
+ * The shared "no app id anywhere" message (issue #63). Every caller renders the same body — the
236
+ * CLI's `--app-id` guard, `mintLink`, and `appduct_connect` — so the three fixes it names (the
237
+ * flag, the MCP argument, and the project config `appduct init` writes) cannot drift between them.
238
+ */
239
+ export declare const describeMissingAppId: (platform: AppIdPlatform, tried: string[]) => string;
@@ -1,3 +1,2 @@
1
1
  export declare const createSpkiPin: (spkiDer: Buffer) => string;
2
2
  export declare const getSpkiPinFromPrivateKeyPem: (keyPem: string) => string;
3
- //# sourceMappingURL=spki-pin.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appduct",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Driving React Native tools without in-app debug UI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,11 +45,13 @@
45
45
  "reflect-metadata": "^0.2.2",
46
46
  "toqr": "^0.1.1",
47
47
  "ws": "^8.18.3",
48
- "@appduct/shared": "0.10.0"
48
+ "@appduct/shared": "0.11.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.10.0",
52
52
  "@types/ws": "^8.18.1",
53
+ "esbuild": "^0.25.12",
54
+ "eslint": "^10",
53
55
  "typescript": "~5.9.2"
54
56
  },
55
57
  "files": [
@@ -57,8 +59,9 @@
57
59
  "dist"
58
60
  ],
59
61
  "scripts": {
60
- "build": "tsc -p tsconfig.build.json",
62
+ "build": "tsc -p tsconfig.build.json && node scripts/bundle.mjs",
61
63
  "clean": "rm -rf dist",
64
+ "lint": "eslint src",
62
65
  "test": "vitest run --config ../../vitest.config.ts src/__tests__",
63
66
  "typecheck": "tsc -p tsconfig.json"
64
67
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"artifact-inspect.d.ts","sourceRoot":"","sources":["../src/artifact-inspect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AAQH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAAC;AACjD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAE3D,MAAM,MAAM,eAAe,GACvB,wBAAwB,GACxB,uBAAuB,GACvB,qBAAqB,GACrB,0BAA0B,GAC1B,4BAA4B,GAC5B,iCAAiC,CAAC;AAEtC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB;;mCAE+B;IAC/B,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,CAAC;AAoDF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;YAEY;AACZ,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE1F;;4EAE4E;AAC5E,eAAO,MAAM,iBAAiB,EAAE,YAgB/B,CAAC;AAiOF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF;;;mEAGmE;AACnE,eAAO,MAAM,eAAe,GAC1B,cAAc,MAAM,EACpB,UAAS,sBAA2B,KACnC,OAAO,CAAC,kBAAkB,CA+C5B,CAAC"}