@beeos-ai/device-agent 0.4.3 → 0.5.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 (200) hide show
  1. package/.pi/extensions/android-tools.ts +146 -15
  2. package/dist/adapters/agent-gateway-client.d.ts +37 -0
  3. package/dist/adapters/agent-gateway-client.js +45 -0
  4. package/dist/adapters/agent-gateway-client.js.map +1 -1
  5. package/dist/adapters/agentcore-pi.d.ts +22 -0
  6. package/dist/adapters/agentcore-pi.js +48 -0
  7. package/dist/adapters/agentcore-pi.js.map +1 -1
  8. package/dist/adapters/device-android-adb.d.ts +47 -0
  9. package/dist/adapters/device-android-adb.js +50 -0
  10. package/dist/adapters/device-android-adb.js.map +1 -1
  11. package/dist/adapters/device-desktop.d.ts +12 -0
  12. package/dist/adapters/device-desktop.js +31 -0
  13. package/dist/adapters/device-desktop.js.map +1 -1
  14. package/dist/adapters/identity-file.d.ts +33 -5
  15. package/dist/adapters/identity-file.js +52 -6
  16. package/dist/adapters/identity-file.js.map +1 -1
  17. package/dist/agent-runtime/adapters/base.d.ts +20 -0
  18. package/dist/agent-runtime/adapters/base.js.map +1 -1
  19. package/dist/agent-runtime/agent-runner.d.ts +4 -2
  20. package/dist/agent-runtime/agent-runner.js.map +1 -1
  21. package/dist/agent-runtime/core.d.ts +60 -0
  22. package/dist/agent-runtime/core.js +190 -9
  23. package/dist/agent-runtime/core.js.map +1 -1
  24. package/dist/agent-runtime/feedback.d.ts +56 -0
  25. package/dist/agent-runtime/feedback.js +54 -0
  26. package/dist/agent-runtime/feedback.js.map +1 -0
  27. package/dist/agent-runtime/index.d.ts +1 -0
  28. package/dist/agent-runtime/index.js +1 -0
  29. package/dist/agent-runtime/index.js.map +1 -1
  30. package/dist/agent-runtime/plan-executor.js +24 -0
  31. package/dist/agent-runtime/plan-executor.js.map +1 -1
  32. package/dist/agent-runtime/planner/base.d.ts +10 -0
  33. package/dist/agent-runtime/planner/base.js.map +1 -1
  34. package/dist/agent-runtime/planner/llm-planner.js +1 -0
  35. package/dist/agent-runtime/planner/llm-planner.js.map +1 -1
  36. package/dist/agent-runtime/planner/prompts.d.ts +3 -0
  37. package/dist/agent-runtime/planner/prompts.js +12 -0
  38. package/dist/agent-runtime/planner/prompts.js.map +1 -1
  39. package/dist/agent-runtime/reflection.d.ts +25 -6
  40. package/dist/agent-runtime/reflection.js +64 -22
  41. package/dist/agent-runtime/reflection.js.map +1 -1
  42. package/dist/agent-runtime/reflectors/policy.d.ts +10 -4
  43. package/dist/agent-runtime/reflectors/policy.js +19 -6
  44. package/dist/agent-runtime/reflectors/policy.js.map +1 -1
  45. package/dist/agent-runtime/shortcuts/fs-store.d.ts +41 -0
  46. package/dist/agent-runtime/shortcuts/fs-store.js +122 -0
  47. package/dist/agent-runtime/shortcuts/fs-store.js.map +1 -0
  48. package/dist/agent-runtime/shortcuts/in-memory-store.d.ts +20 -0
  49. package/dist/agent-runtime/shortcuts/in-memory-store.js +64 -0
  50. package/dist/agent-runtime/shortcuts/in-memory-store.js.map +1 -0
  51. package/dist/agent-runtime/shortcuts/index.d.ts +10 -0
  52. package/dist/agent-runtime/shortcuts/index.js +10 -0
  53. package/dist/agent-runtime/shortcuts/index.js.map +1 -0
  54. package/dist/agent-runtime/shortcuts/types.d.ts +69 -0
  55. package/dist/agent-runtime/shortcuts/types.js +17 -0
  56. package/dist/agent-runtime/shortcuts/types.js.map +1 -0
  57. package/dist/app/agent-host.d.ts +83 -0
  58. package/dist/app/agent-host.js +345 -32
  59. package/dist/app/agent-host.js.map +1 -1
  60. package/dist/app/session-manager.d.ts +33 -4
  61. package/dist/app/session-manager.js +27 -5
  62. package/dist/app/session-manager.js.map +1 -1
  63. package/dist/bootstrap.js +187 -3
  64. package/dist/bootstrap.js.map +1 -1
  65. package/dist/config/loader.js +22 -0
  66. package/dist/config/loader.js.map +1 -1
  67. package/dist/config/schema.d.ts +18 -0
  68. package/dist/config/schema.js +118 -0
  69. package/dist/config/schema.js.map +1 -1
  70. package/dist/fleet/adapters/process/node-process-manager.js +97 -17
  71. package/dist/fleet/adapters/process/node-process-manager.js.map +1 -1
  72. package/dist/fleet/adapters/state/beeos-cli-store.d.ts +75 -9
  73. package/dist/fleet/adapters/state/beeos-cli-store.js +201 -17
  74. package/dist/fleet/adapters/state/beeos-cli-store.js.map +1 -1
  75. package/dist/fleet/domain/device.d.ts +9 -0
  76. package/dist/fleet/domain/device.js.map +1 -1
  77. package/dist/fleet/domain/fsm.js +34 -0
  78. package/dist/fleet/domain/fsm.js.map +1 -1
  79. package/dist/fleet/installer/spawn-env.d.ts +3 -0
  80. package/dist/fleet/installer/spawn-env.js +186 -3
  81. package/dist/fleet/installer/spawn-env.js.map +1 -1
  82. package/dist/fleet/state/state-json.d.ts +116 -0
  83. package/dist/fleet/state/state-json.js +87 -0
  84. package/dist/fleet/state/state-json.js.map +1 -0
  85. package/dist/fleet/supervisor.js +14 -2
  86. package/dist/fleet/supervisor.js.map +1 -1
  87. package/dist/inbound/acp/handlers/session.js.map +1 -1
  88. package/dist/mcp-client/client.d.ts +6 -0
  89. package/dist/mcp-client/client.js +11 -0
  90. package/dist/mcp-client/client.js.map +1 -1
  91. package/dist/mcp-client/runtime.d.ts +13 -0
  92. package/dist/orchestrator-system-prompt.d.ts +26 -0
  93. package/dist/orchestrator-system-prompt.js +118 -2
  94. package/dist/orchestrator-system-prompt.js.map +1 -1
  95. package/dist/pi-context.d.ts +50 -0
  96. package/dist/pi-context.js.map +1 -1
  97. package/dist/ports/device.d.ts +18 -0
  98. package/dist/resources/augmenter.d.ts +48 -0
  99. package/dist/resources/augmenter.js +126 -0
  100. package/dist/resources/augmenter.js.map +1 -0
  101. package/dist/resources/cache.d.ts +57 -0
  102. package/dist/resources/cache.js +107 -0
  103. package/dist/resources/cache.js.map +1 -0
  104. package/dist/resources/index.d.ts +17 -0
  105. package/dist/resources/index.js +15 -0
  106. package/dist/resources/index.js.map +1 -0
  107. package/dist/resources/lifecycle/disk-pressure.d.ts +75 -0
  108. package/dist/resources/lifecycle/disk-pressure.js +148 -0
  109. package/dist/resources/lifecycle/disk-pressure.js.map +1 -0
  110. package/dist/resources/lifecycle/index.d.ts +7 -0
  111. package/dist/resources/lifecycle/index.js +7 -0
  112. package/dist/resources/lifecycle/index.js.map +1 -0
  113. package/dist/resources/lifecycle/manager.d.ts +38 -0
  114. package/dist/resources/lifecycle/manager.js +205 -0
  115. package/dist/resources/lifecycle/manager.js.map +1 -0
  116. package/dist/resources/lifecycle/sweeper.d.ts +36 -0
  117. package/dist/resources/lifecycle/sweeper.js +179 -0
  118. package/dist/resources/lifecycle/sweeper.js.map +1 -0
  119. package/dist/resources/lifecycle/types.d.ts +180 -0
  120. package/dist/resources/lifecycle/types.js +46 -0
  121. package/dist/resources/lifecycle/types.js.map +1 -0
  122. package/dist/resources/parsers.d.ts +28 -0
  123. package/dist/resources/parsers.js +88 -0
  124. package/dist/resources/parsers.js.map +1 -0
  125. package/dist/resources/registry.d.ts +47 -0
  126. package/dist/resources/registry.js +107 -0
  127. package/dist/resources/registry.js.map +1 -0
  128. package/dist/resources/resolvers/beeos-file-resolver.d.ts +62 -0
  129. package/dist/resources/resolvers/beeos-file-resolver.js +109 -0
  130. package/dist/resources/resolvers/beeos-file-resolver.js.map +1 -0
  131. package/dist/resources/resolvers/errors.d.ts +11 -0
  132. package/dist/resources/resolvers/errors.js +23 -0
  133. package/dist/resources/resolvers/errors.js.map +1 -0
  134. package/dist/resources/resolvers/mime-table.d.ts +41 -0
  135. package/dist/resources/resolvers/mime-table.js +92 -0
  136. package/dist/resources/resolvers/mime-table.js.map +1 -0
  137. package/dist/resources/resolvers/url-resolver.d.ts +85 -0
  138. package/dist/resources/resolvers/url-resolver.js +495 -0
  139. package/dist/resources/resolvers/url-resolver.js.map +1 -0
  140. package/dist/resources/shortcut-facade.d.ts +51 -0
  141. package/dist/resources/shortcut-facade.js +72 -0
  142. package/dist/resources/shortcut-facade.js.map +1 -0
  143. package/dist/resources/telemetry.d.ts +52 -0
  144. package/dist/resources/telemetry.js +59 -0
  145. package/dist/resources/telemetry.js.map +1 -0
  146. package/dist/resources/types.d.ts +249 -0
  147. package/dist/resources/types.js +29 -0
  148. package/dist/resources/types.js.map +1 -0
  149. package/dist/shortcuts/builtins/download-to-device.d.ts +83 -0
  150. package/dist/shortcuts/builtins/download-to-device.js +125 -0
  151. package/dist/shortcuts/builtins/download-to-device.js.map +1 -0
  152. package/dist/shortcuts/builtins/find-app-package.d.ts +79 -0
  153. package/dist/shortcuts/builtins/find-app-package.js +168 -0
  154. package/dist/shortcuts/builtins/find-app-package.js.map +1 -0
  155. package/dist/shortcuts/builtins/index.d.ts +32 -0
  156. package/dist/shortcuts/builtins/index.js +37 -0
  157. package/dist/shortcuts/builtins/index.js.map +1 -0
  158. package/dist/shortcuts/builtins/open-deeplink.d.ts +68 -0
  159. package/dist/shortcuts/builtins/open-deeplink.js +142 -0
  160. package/dist/shortcuts/builtins/open-deeplink.js.map +1 -0
  161. package/dist/shortcuts/builtins/set-clipboard.d.ts +65 -0
  162. package/dist/shortcuts/builtins/set-clipboard.js +112 -0
  163. package/dist/shortcuts/builtins/set-clipboard.js.map +1 -0
  164. package/dist/shortcuts/index.d.ts +11 -0
  165. package/dist/shortcuts/index.js +11 -0
  166. package/dist/shortcuts/index.js.map +1 -0
  167. package/dist/shortcuts/registry.d.ts +56 -0
  168. package/dist/shortcuts/registry.js +70 -0
  169. package/dist/shortcuts/registry.js.map +1 -0
  170. package/dist/shortcuts/router.d.ts +91 -0
  171. package/dist/shortcuts/router.js +158 -0
  172. package/dist/shortcuts/router.js.map +1 -0
  173. package/dist/shortcuts/types.d.ts +219 -0
  174. package/dist/shortcuts/types.js +46 -0
  175. package/dist/shortcuts/types.js.map +1 -0
  176. package/package.json +3 -3
  177. package/dist/agent-runtime/skills/sqlite-store.d.ts +0 -60
  178. package/dist/agent-runtime/skills/sqlite-store.js +0 -234
  179. package/dist/agent-runtime/skills/sqlite-store.js.map +0 -1
  180. package/dist/test-lan-autoglm.d.ts +0 -7
  181. package/dist/test-lan-autoglm.js +0 -46
  182. package/dist/test-lan-autoglm.js.map +0 -1
  183. package/dist/test-mem-breakdown.d.ts +0 -1
  184. package/dist/test-mem-breakdown.js +0 -103
  185. package/dist/test-mem-breakdown.js.map +0 -1
  186. package/dist/test-mem.d.ts +0 -1
  187. package/dist/test-mem.js +0 -60
  188. package/dist/test-mem.js.map +0 -1
  189. package/dist/test-mobile-use.d.ts +0 -1
  190. package/dist/test-mobile-use.js +0 -18
  191. package/dist/test-mobile-use.js.map +0 -1
  192. package/dist/test-screenshot-loop.d.ts +0 -1
  193. package/dist/test-screenshot-loop.js +0 -43
  194. package/dist/test-screenshot-loop.js.map +0 -1
  195. package/dist/test-screenshot.d.ts +0 -1
  196. package/dist/test-screenshot.js +0 -10
  197. package/dist/test-screenshot.js.map +0 -1
  198. package/dist/test-ui-xml-dump.d.ts +0 -15
  199. package/dist/test-ui-xml-dump.js +0 -49
  200. package/dist/test-ui-xml-dump.js.map +0 -1
@@ -0,0 +1,495 @@
1
+ /**
2
+ * URL Resolver — fetch HTTP(S) URLs and push them onto the device.
3
+ *
4
+ * Phase 1 / Pillar 1 of the GUI-Shortcut Hybrid Plan.
5
+ *
6
+ * Pipeline:
7
+ *
8
+ * 1. validate scheme — `http` / `https` only (allowedSchemes).
9
+ * 2. SSRF check — DNS resolve the hostname; reject any private
10
+ * / loopback / link-local / multicast IP.
11
+ * 3. fetch (streaming) — chunk-by-chunk with running size cap and
12
+ * AbortSignal merge. Validates Content-Type
13
+ * against the platform-supported MIME table.
14
+ * 4. DNS-rebind check — after the body has been read, re-resolve the
15
+ * hostname and assert at least one returned
16
+ * IP overlaps with the IP we accepted in (2).
17
+ * 5. choose ext — strictly via Content-Type → ext mapping.
18
+ * Path-derived extensions are NEVER trusted.
19
+ * 6. choose target — `ctx.device.resolveWellKnownDir(mime)` picks
20
+ * the platform-appropriate directory.
21
+ * 7. push — `ctx.mcp.fileWrite(remotePath, bytes)`.
22
+ * 8. media-scan — Android only: `touch -d now <path> &&
23
+ * am broadcast -a MEDIA_SCANNER_SCAN_FILE`
24
+ * so Gallery / WeChat / Photos see it as
25
+ * fresh and orderable. Best-effort; failure
26
+ * does NOT fail the resolve.
27
+ *
28
+ * All four SSRF defenses operate on the same `ResolverContext` so the
29
+ * AgentHost can flip them off in tests / lab environments via the
30
+ * feature flag.
31
+ */
32
+ import { Buffer } from "node:buffer";
33
+ import { promises as dns } from "node:dns";
34
+ import { createHash } from "node:crypto";
35
+ import { URL } from "node:url";
36
+ import { AGENT_HOST_ERROR_LABELS } from "./errors.js";
37
+ import { mapMimeToExt, ALLOWED_MIME_PREFIXES, ALLOWED_MIMES } from "./mime-table.js";
38
+ export class ResolverError extends Error {
39
+ code;
40
+ meta;
41
+ constructor(code, message, meta) {
42
+ super(message);
43
+ this.code = code;
44
+ this.meta = meta;
45
+ this.name = "ResolverError";
46
+ }
47
+ /** Promtheus-friendly outcome label (subset of error codes). */
48
+ get outcomeLabel() {
49
+ return AGENT_HOST_ERROR_LABELS[this.code];
50
+ }
51
+ }
52
+ const DEFAULT_FETCH_IMPL = (...args) => fetch(...args);
53
+ const DEFAULT_DNS_LOOKUP = async (hostname) => {
54
+ // Use `lookup` with `all: true` instead of `resolve4`+`resolve6` so we
55
+ // honor /etc/hosts and the OS resolver — same surface area Node's
56
+ // built-in fetch sees on a subsequent connect, which is essential for
57
+ // the DNS-rebind defence.
58
+ return dns.lookup(hostname, { all: true });
59
+ };
60
+ export class UrlResolver {
61
+ name = "url";
62
+ fetchImpl;
63
+ dnsLookup;
64
+ forceDeviceType;
65
+ constructor(opts = {}) {
66
+ this.fetchImpl = opts.fetchImpl ?? DEFAULT_FETCH_IMPL;
67
+ this.dnsLookup = opts.dnsLookup ?? DEFAULT_DNS_LOOKUP;
68
+ this.forceDeviceType = opts.forceDeviceType;
69
+ }
70
+ match(ref) {
71
+ return ref.kind === "url";
72
+ }
73
+ async resolve(ref, ctx) {
74
+ if (ref.kind !== "url") {
75
+ throw new ResolverError("fetch_error", `UrlResolver: unsupported ref kind ${ref.kind}`);
76
+ }
77
+ if (ctx.abortSignal.aborted) {
78
+ throw new ResolverError("aborted", "resolve aborted before start");
79
+ }
80
+ emit(ctx, { phase: "start", ref });
81
+ let asset;
82
+ try {
83
+ asset = await this.doResolve(ref, ctx);
84
+ emit(ctx, { phase: "end", ref, asset });
85
+ return asset;
86
+ }
87
+ catch (e) {
88
+ const err = e instanceof Error ? e : new Error(String(e));
89
+ emit(ctx, { phase: "end", ref, error: err });
90
+ throw err;
91
+ }
92
+ }
93
+ /* ---------------------------------------------------------------- */
94
+ /* Internal pipeline */
95
+ /* ---------------------------------------------------------------- */
96
+ async doResolve(ref, ctx) {
97
+ /* 1. scheme check */
98
+ let parsed;
99
+ try {
100
+ parsed = new URL(ref.url);
101
+ }
102
+ catch {
103
+ throw new ResolverError("disallowed_scheme", `invalid URL: ${ref.url}`);
104
+ }
105
+ const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
106
+ if (!ctx.allowedSchemes.includes(scheme)) {
107
+ throw new ResolverError("disallowed_scheme", `scheme "${scheme}" not in allow-list`, { scheme });
108
+ }
109
+ /* 2. SSRF — pre-fetch IP allow-list */
110
+ if (ctx.denyPrivateIp) {
111
+ const initial = await this.dnsLookup(parsed.hostname);
112
+ if (initial.length === 0) {
113
+ throw new ResolverError("fetch_error", `DNS resolved no addresses for ${parsed.hostname}`);
114
+ }
115
+ for (const a of initial) {
116
+ if (isPrivateOrLoopback(a.address, a.family)) {
117
+ throw new ResolverError("private_ip_blocked", `private/loopback IP ${a.address} for ${parsed.hostname}`);
118
+ }
119
+ }
120
+ }
121
+ /* 3. fetch (streaming, with running cap and timeout) */
122
+ const fetchController = new AbortController();
123
+ const onAbort = () => fetchController.abort();
124
+ if (ctx.abortSignal.aborted) {
125
+ throw new ResolverError("aborted", "aborted before fetch");
126
+ }
127
+ ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
128
+ const timer = setTimeout(() => fetchController.abort(), ctx.timeoutMs);
129
+ let response;
130
+ try {
131
+ response = await this.fetchImpl(parsed.toString(), {
132
+ signal: fetchController.signal,
133
+ redirect: "follow",
134
+ });
135
+ }
136
+ catch (e) {
137
+ ctx.abortSignal.removeEventListener("abort", onAbort);
138
+ clearTimeout(timer);
139
+ if (ctx.abortSignal.aborted) {
140
+ throw new ResolverError("aborted", "aborted during fetch", { cause: errString(e) });
141
+ }
142
+ throw new ResolverError("fetch_error", `fetch failed: ${errString(e)}`);
143
+ }
144
+ if (!response.ok) {
145
+ ctx.abortSignal.removeEventListener("abort", onAbort);
146
+ clearTimeout(timer);
147
+ throw new ResolverError("fetch_error", `fetch returned ${response.status} ${response.statusText}`, { status: response.status });
148
+ }
149
+ /* validate Content-Type early so we don't burn bandwidth on disallowed types */
150
+ const rawCt = response.headers.get("content-type") ?? "";
151
+ const mime = normaliseMime(rawCt);
152
+ if (!isAllowedMime(mime)) {
153
+ ctx.abortSignal.removeEventListener("abort", onAbort);
154
+ clearTimeout(timer);
155
+ throw new ResolverError("disallowed_mime", `Content-Type "${mime}" is not in the allow-list`, { mime });
156
+ }
157
+ /* declared Content-Length sanity check (still need streaming check) */
158
+ const declaredLen = parseInt(response.headers.get("content-length") ?? "", 10);
159
+ if (Number.isFinite(declaredLen) && declaredLen > ctx.maxBytes) {
160
+ ctx.abortSignal.removeEventListener("abort", onAbort);
161
+ clearTimeout(timer);
162
+ throw new ResolverError("oversized", `declared Content-Length ${declaredLen} exceeds maxBytes ${ctx.maxBytes}`, { declaredLen, maxBytes: ctx.maxBytes });
163
+ }
164
+ /* stream body, abort on cap overrun */
165
+ let bytes;
166
+ try {
167
+ bytes = await readBodyWithCap(response, ctx.maxBytes, Number.isFinite(declaredLen) ? declaredLen : undefined, (downloaded, total) => {
168
+ emit(ctx, {
169
+ phase: "update",
170
+ ref,
171
+ downloadedBytes: downloaded,
172
+ ...(total !== undefined ? { totalBytes: total } : {}),
173
+ });
174
+ });
175
+ }
176
+ catch (e) {
177
+ if (ctx.abortSignal.aborted) {
178
+ throw new ResolverError("aborted", "aborted during body read", { cause: errString(e) });
179
+ }
180
+ if (e instanceof ResolverError)
181
+ throw e;
182
+ throw new ResolverError("fetch_error", `body read failed: ${errString(e)}`);
183
+ }
184
+ finally {
185
+ ctx.abortSignal.removeEventListener("abort", onAbort);
186
+ clearTimeout(timer);
187
+ }
188
+ /* 4. DNS-rebind defence — re-resolve after the connection completed
189
+ * and require at least one IP overlap with the initial allow-list.
190
+ * Skipped when private-IP blocking is off (test/lab environments).
191
+ */
192
+ if (ctx.denyPrivateIp) {
193
+ const initial = await this.dnsLookup(parsed.hostname).catch(() => []);
194
+ const post = await this.dnsLookup(parsed.hostname).catch(() => []);
195
+ const initialIps = new Set(initial.map((a) => a.address));
196
+ const overlap = post.some((a) => initialIps.has(a.address));
197
+ if (!overlap && post.length > 0) {
198
+ throw new ResolverError("dns_rebind_blocked", `DNS for ${parsed.hostname} changed mid-fetch (rebind defence)`, { initial: [...initialIps], post: post.map((a) => a.address) });
199
+ }
200
+ // Also: if any post-fetch IP is private/loopback we treat that as
201
+ // a successful rebind and reject.
202
+ for (const a of post) {
203
+ if (isPrivateOrLoopback(a.address, a.family)) {
204
+ throw new ResolverError("dns_rebind_blocked", `post-fetch DNS now resolves to private IP ${a.address}`, { address: a.address });
205
+ }
206
+ }
207
+ }
208
+ /* 5. choose ext via MIME → ext mapping (NEVER from URL path) */
209
+ const ext = mapMimeToExt(mime);
210
+ if (!ext) {
211
+ throw new ResolverError("disallowed_mime", `MIME "${mime}" has no whitelisted extension mapping`, { mime });
212
+ }
213
+ /* 6. resolve well-known dir */
214
+ let baseDir;
215
+ try {
216
+ baseDir = await ctx.device.resolveWellKnownDir(mime);
217
+ }
218
+ catch (e) {
219
+ throw new ResolverError("push_error", `device cannot resolve well-known dir for ${mime}: ${errString(e)}`, { mime });
220
+ }
221
+ const sha = createHash("sha256").update(bytes).digest("hex");
222
+ const sha8 = sha.slice(0, 8);
223
+ const filename = `beeos_${Math.floor(Date.now() / 1000)}_${sha8}.${ext}`;
224
+ const remotePath = joinRemote(baseDir, filename);
225
+ /* 7a. ensure the sandbox directory exists. The production adapter
226
+ * resolves to a sub-folder like `/sdcard/Pictures/beeos/` (so the
227
+ * lifecycle sweepers have a safe-to-wipe boundary); on a freshly
228
+ * provisioned device that subdir does NOT exist yet and `fileWrite`
229
+ * would fail with `ENOENT`. We do this best-effort via the same
230
+ * MCP `executeCommand` channel the media-scan branch uses; on the
231
+ * legacy in-proc bridge the call is a no-op and `fileWrite` either
232
+ * succeeds (the subdir was already there) or fails the next step
233
+ * with a structured error. */
234
+ if (typeof ctx.mcp.executeCommand === "function") {
235
+ try {
236
+ await ctx.mcp.executeCommand(`mkdir -p ${shellQuote(baseDir)}`);
237
+ }
238
+ catch (e) {
239
+ ctx.log.warn({ baseDir, err: errString(e) }, "url-resolver: mkdir -p sandbox dir failed (continuing)");
240
+ }
241
+ }
242
+ /* 7b. disk-pressure guard (Phase 1.5 / lifecycle L3). Optional —
243
+ * when no guard is wired the resolver behaves exactly as before. */
244
+ if (ctx.diskPressure) {
245
+ try {
246
+ await ctx.diskPressure.assertCapacity({
247
+ mcp: ctx.mcp,
248
+ sandboxDir: baseDir,
249
+ incomingBytes: bytes.length,
250
+ log: ctx.log,
251
+ });
252
+ }
253
+ catch (e) {
254
+ if (e instanceof ResolverError)
255
+ throw e;
256
+ throw new ResolverError("push_error", `disk-pressure guard failed: ${errString(e)}`, { baseDir });
257
+ }
258
+ }
259
+ /* 7c. push via MCP file_write */
260
+ if (typeof ctx.mcp.fileWrite !== "function") {
261
+ throw new ResolverError("push_error", "MCP runtime does not implement fileWrite (legacy in-proc bridge?)");
262
+ }
263
+ try {
264
+ await ctx.mcp.fileWrite(remotePath, bytes);
265
+ }
266
+ catch (e) {
267
+ throw new ResolverError("push_error", `mcp.fileWrite failed: ${errString(e)}`, { remotePath });
268
+ }
269
+ /* 8. Android-only media-scan + touch-now */
270
+ let scanned = false;
271
+ const platform = await this.detectPlatform(ctx);
272
+ if (platform === "android" && typeof ctx.mcp.executeCommand === "function") {
273
+ try {
274
+ // touch -d now updates mtime so Gallery / Photos / WeChat sort
275
+ // it as the most recent file (otherwise the file's mtime is
276
+ // whatever ADB wrote — potentially seconds-old, but on some
277
+ // ROMs the mtime is preserved from the upload's source).
278
+ await ctx.mcp.executeCommand(`touch -d now ${shellQuote(remotePath)}`);
279
+ await ctx.mcp.executeCommand(`am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://${remotePath}`);
280
+ scanned = true;
281
+ }
282
+ catch (e) {
283
+ // Best-effort. Don't fail the resolve — agent can still
284
+ // reference the file by absolute path.
285
+ ctx.log.warn({ remotePath, err: errString(e) }, "url-resolver: media-scan trigger failed (non-fatal)");
286
+ }
287
+ }
288
+ return {
289
+ ref,
290
+ remotePath,
291
+ mime,
292
+ sizeBytes: bytes.length,
293
+ sha256: sha,
294
+ scannedToMediaStore: scanned,
295
+ sourceUrl: ref.url,
296
+ resolvedAt: Date.now(),
297
+ };
298
+ }
299
+ async detectPlatform(ctx) {
300
+ if (this.forceDeviceType)
301
+ return this.forceDeviceType.toLowerCase();
302
+ try {
303
+ const info = await ctx.device.getInfo();
304
+ return (info.os ?? info.type ?? "").toString().toLowerCase();
305
+ }
306
+ catch {
307
+ return "";
308
+ }
309
+ }
310
+ }
311
+ /* ------------------------------------------------------------------ */
312
+ /* Helpers */
313
+ /* ------------------------------------------------------------------ */
314
+ function emit(ctx, ev) {
315
+ if (!ctx.emitProgress)
316
+ return;
317
+ try {
318
+ ctx.emitProgress(ev);
319
+ }
320
+ catch {
321
+ /* swallow — telemetry must never break the pipeline */
322
+ }
323
+ }
324
+ function errString(e) {
325
+ if (e instanceof Error)
326
+ return e.message;
327
+ return String(e);
328
+ }
329
+ /**
330
+ * Read a `Response` body into a single Buffer, aborting once the
331
+ * accumulated size exceeds `maxBytes`. Reports progress through
332
+ * `onProgress` after each chunk so the AgentHost can render a download
333
+ * progress card.
334
+ */
335
+ async function readBodyWithCap(res, maxBytes, declaredLen, onProgress) {
336
+ if (!res.body) {
337
+ const ab = await res.arrayBuffer();
338
+ if (ab.byteLength > maxBytes) {
339
+ throw new ResolverError("oversized", `body ${ab.byteLength} exceeds maxBytes ${maxBytes}`);
340
+ }
341
+ onProgress(ab.byteLength, declaredLen);
342
+ return Buffer.from(ab);
343
+ }
344
+ const reader = res.body.getReader();
345
+ const chunks = [];
346
+ let received = 0;
347
+ try {
348
+ while (true) {
349
+ const { value, done } = await reader.read();
350
+ if (done)
351
+ break;
352
+ if (value) {
353
+ const chunk = Buffer.from(value);
354
+ received += chunk.length;
355
+ if (received > maxBytes) {
356
+ throw new ResolverError("oversized", `body exceeded maxBytes ${maxBytes} during streaming`);
357
+ }
358
+ chunks.push(chunk);
359
+ onProgress(received, declaredLen);
360
+ }
361
+ }
362
+ }
363
+ finally {
364
+ try {
365
+ reader.releaseLock();
366
+ }
367
+ catch {
368
+ /* noop */
369
+ }
370
+ }
371
+ return Buffer.concat(chunks);
372
+ }
373
+ function normaliseMime(raw) {
374
+ // strip ;charset=… and trim whitespace
375
+ const idx = raw.indexOf(";");
376
+ const body = idx >= 0 ? raw.slice(0, idx) : raw;
377
+ return body.trim().toLowerCase();
378
+ }
379
+ function isAllowedMime(mime) {
380
+ if (!mime)
381
+ return false;
382
+ if (ALLOWED_MIMES.has(mime))
383
+ return true;
384
+ for (const p of ALLOWED_MIME_PREFIXES) {
385
+ if (mime.startsWith(p))
386
+ return true;
387
+ }
388
+ return false;
389
+ }
390
+ /**
391
+ * RFC 1918 / RFC 4193 / loopback / link-local / multicast / etc check.
392
+ * We deliberately keep this exhaustive rather than rely on a third-party
393
+ * package — the SSRF surface is small and security-critical.
394
+ *
395
+ * IPv4 loopback — 127.0.0.0/8
396
+ * IPv4 RFC1918 private — 10/8, 172.16/12, 192.168/16
397
+ * IPv4 link-local — 169.254/16
398
+ * IPv4 CGNAT — 100.64/10
399
+ * IPv4 broadcast / multicast — 224/4, 255.255.255.255
400
+ * IPv4 0.0.0.0/8 — "this network"
401
+ * IPv6 loopback — ::1
402
+ * IPv6 link-local — fe80::/10
403
+ * IPv6 ULA — fc00::/7
404
+ * IPv6 multicast — ff00::/8
405
+ * IPv6 unspecified — ::
406
+ * IPv4-mapped IPv6 — ::ffff:0:0/96 (mapped to v4 above)
407
+ */
408
+ export function isPrivateOrLoopback(addr, family) {
409
+ if (!addr)
410
+ return true;
411
+ const a = addr.toLowerCase();
412
+ if (family === 4 || /^\d+\.\d+\.\d+\.\d+$/.test(a)) {
413
+ return isPrivateIpv4(a);
414
+ }
415
+ // IPv4-mapped IPv6 — ::ffff:1.2.3.4 / ::ffff:0102:0304
416
+ if (a.startsWith("::ffff:")) {
417
+ const tail = a.slice("::ffff:".length);
418
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(tail)) {
419
+ return isPrivateIpv4(tail);
420
+ }
421
+ // hex-form ::ffff:0102:0304 → 1.2.3.4
422
+ const mapped = hex32ToIpv4(tail);
423
+ if (mapped)
424
+ return isPrivateIpv4(mapped);
425
+ }
426
+ // IPv6 loopback / unspecified
427
+ if (a === "::1" || a === "::")
428
+ return true;
429
+ // IPv6 link-local fe80::/10
430
+ if (a.startsWith("fe8") || a.startsWith("fe9") || a.startsWith("fea") || a.startsWith("feb")) {
431
+ return true;
432
+ }
433
+ // IPv6 ULA fc00::/7 (covers fc.. and fd..)
434
+ if (a.startsWith("fc") || a.startsWith("fd"))
435
+ return true;
436
+ // IPv6 multicast ff00::/8
437
+ if (a.startsWith("ff"))
438
+ return true;
439
+ return false;
440
+ }
441
+ function isPrivateIpv4(a) {
442
+ const parts = a.split(".").map((s) => parseInt(s, 10));
443
+ if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n)))
444
+ return true;
445
+ const [b1, b2] = parts;
446
+ // 0.0.0.0/8 — "this host on this network"
447
+ if (b1 === 0)
448
+ return true;
449
+ if (b1 === 127)
450
+ return true; // 127/8 loopback
451
+ if (b1 === 10)
452
+ return true; // 10/8 private
453
+ if (b1 === 172 && b2 >= 16 && b2 <= 31)
454
+ return true; // 172.16/12 private
455
+ if (b1 === 192 && b2 === 168)
456
+ return true; // 192.168/16 private
457
+ if (b1 === 169 && b2 === 254)
458
+ return true; // 169.254/16 link-local
459
+ if (b1 === 100 && b2 >= 64 && b2 <= 127)
460
+ return true; // 100.64/10 CGNAT
461
+ if (b1 >= 224 && b1 <= 239)
462
+ return true; // 224/4 multicast
463
+ if (b1 >= 240)
464
+ return true; // 240/4 reserved + 255.255.255.255 broadcast
465
+ return false;
466
+ }
467
+ function hex32ToIpv4(s) {
468
+ // expects e.g. "0102:0304" or "0102:0304" or "102:304"
469
+ const parts = s.split(":");
470
+ if (parts.length !== 2)
471
+ return null;
472
+ const high = parseInt(parts[0], 16);
473
+ const low = parseInt(parts[1], 16);
474
+ if (!Number.isFinite(high) || !Number.isFinite(low))
475
+ return null;
476
+ const b1 = (high >> 8) & 0xff;
477
+ const b2 = high & 0xff;
478
+ const b3 = (low >> 8) & 0xff;
479
+ const b4 = low & 0xff;
480
+ return `${b1}.${b2}.${b3}.${b4}`;
481
+ }
482
+ function joinRemote(dir, filename) {
483
+ const left = dir.endsWith("/") ? dir.slice(0, -1) : dir;
484
+ return `${left}/${filename}`;
485
+ }
486
+ /**
487
+ * Quote a path for safe inclusion in a single-quoted shell argument.
488
+ * Path is generated server-side (no user input), but defensive
489
+ * quoting keeps us safe in case future filename schemes ever include
490
+ * exotic characters.
491
+ */
492
+ function shellQuote(s) {
493
+ return `'${s.replace(/'/g, "'\\''")}'`;
494
+ }
495
+ //# sourceMappingURL=url-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-resolver.js","sourceRoot":"","sources":["../../../src/resources/resolvers/url-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `BootstrapShortcutResourceFacade` — a `ShortcutResourceFacade` impl
3
+ * that delegates into the same `ResourceResolverRegistry` + cache the
4
+ * `AgentHostImpl` uses, so the `download_to_device` shortcut and the
5
+ * AgentHost auto-trigger share **one** download per URL. This closes
6
+ * the wiring gap that previously caused
7
+ * `download_to_device` → `permission_denied` in the Pi extension path.
8
+ *
9
+ * Design notes:
10
+ *
11
+ * - Owns no state of its own — it's a thin adapter. Cache writes still
12
+ * go through the shared `ResourceResolutionCache` so a subsequent
13
+ * AgentHost auto-trigger sees the result.
14
+ * - Re-uses the registry's `resolveAll(taskText, ctx)` entry point
15
+ * because `UrlParser` already knows how to extract a URL from a
16
+ * string. We pass the URL itself as the synthetic task — no parser
17
+ * change required.
18
+ * - Throws `ResolverError` (not a generic `Error`) so the
19
+ * `download_to_device` shortcut can map the code to the
20
+ * `ShortcutErrorCode` vocabulary deterministically.
21
+ */
22
+ import type { Logger } from "../logger/index.js";
23
+ import type { IDevice } from "../ports/device.js";
24
+ import type { IMcpRuntime } from "../mcp-client/runtime.js";
25
+ import type { AgentHostResourceConfig } from "../app/agent-host.js";
26
+ import type { ShortcutResourceFacade } from "../shortcuts/types.js";
27
+ import type { ResourceResolverRegistry } from "./registry.js";
28
+ import { ResourceResolutionCache } from "./cache.js";
29
+ import type { DiskPressureGuard } from "./types.js";
30
+ export interface BootstrapShortcutResourceFacadeOptions {
31
+ registry: ResourceResolverRegistry;
32
+ cache: ResourceResolutionCache;
33
+ device: IDevice;
34
+ mcpProvider: () => IMcpRuntime;
35
+ config: AgentHostResourceConfig;
36
+ diskPressure?: DiskPressureGuard;
37
+ log: Logger;
38
+ /** Optional shared abort signal — caller's host abort. Defaults to a fresh per-call controller. */
39
+ hostSignal?: AbortSignal;
40
+ }
41
+ export declare class BootstrapShortcutResourceFacade implements ShortcutResourceFacade {
42
+ private readonly opts;
43
+ constructor(opts: BootstrapShortcutResourceFacadeOptions);
44
+ resolveUrl(url: string): Promise<{
45
+ remotePath: string;
46
+ mime: string;
47
+ sizeBytes: number;
48
+ sha256: string;
49
+ sourceUrl: string;
50
+ }>;
51
+ }
@@ -0,0 +1,72 @@
1
+ import { ResourceResolutionCache } from "./cache.js";
2
+ import { ResolverError } from "./resolvers/url-resolver.js";
3
+ export class BootstrapShortcutResourceFacade {
4
+ opts;
5
+ constructor(opts) {
6
+ this.opts = opts;
7
+ }
8
+ async resolveUrl(url) {
9
+ if (!this.opts.config.enabled) {
10
+ throw new ResolverError("fetch_error", "resource resolution disabled (RESOURCE_RESOLUTION_ENABLED=false)");
11
+ }
12
+ const cacheKey = ResourceResolutionCache.keyForUrl(url);
13
+ const cached = this.opts.cache.get(cacheKey);
14
+ if (cached) {
15
+ // Validate the device-side file is still alive (operators
16
+ // occasionally clear well-known dirs between tasks). On a
17
+ // missing file we invalidate and fall through to a fresh
18
+ // resolve — same protocol the AgentHost uses.
19
+ const alive = await ResourceResolutionCache.validateAlive(cached, this.opts.mcpProvider()).catch(() => false);
20
+ if (alive)
21
+ return toFacadeOutput(cached);
22
+ this.opts.cache.invalidate(cacheKey);
23
+ }
24
+ const ctl = new AbortController();
25
+ const cleanup = [];
26
+ if (this.opts.hostSignal) {
27
+ if (this.opts.hostSignal.aborted)
28
+ ctl.abort();
29
+ else {
30
+ const onAbort = () => ctl.abort();
31
+ this.opts.hostSignal.addEventListener("abort", onAbort, { once: true });
32
+ cleanup.push(() => this.opts.hostSignal?.removeEventListener("abort", onAbort));
33
+ }
34
+ }
35
+ const timer = setTimeout(() => ctl.abort(), this.opts.config.timeoutMs);
36
+ cleanup.push(() => clearTimeout(timer));
37
+ try {
38
+ const out = await this.opts.registry.resolveAll(url, {
39
+ device: this.opts.device,
40
+ mcp: this.opts.mcpProvider(),
41
+ abortSignal: ctl.signal,
42
+ maxBytes: this.opts.config.maxBytes,
43
+ timeoutMs: this.opts.config.timeoutMs,
44
+ allowedSchemes: [...this.opts.config.allowedSchemes],
45
+ denyPrivateIp: this.opts.config.denyPrivateIp,
46
+ log: this.opts.log,
47
+ ...(this.opts.diskPressure ? { diskPressure: this.opts.diskPressure } : {}),
48
+ });
49
+ if (out.assets[0]) {
50
+ this.opts.cache.set(cacheKey, out.assets[0]);
51
+ return toFacadeOutput(out.assets[0]);
52
+ }
53
+ if (out.errors[0])
54
+ throw out.errors[0].error;
55
+ throw new ResolverError("fetch_error", `no resolver matched url: ${url}`);
56
+ }
57
+ finally {
58
+ for (const fn of cleanup)
59
+ fn();
60
+ }
61
+ }
62
+ }
63
+ function toFacadeOutput(asset) {
64
+ return {
65
+ remotePath: asset.remotePath,
66
+ mime: asset.mime,
67
+ sizeBytes: asset.sizeBytes,
68
+ sha256: asset.sha256,
69
+ sourceUrl: asset.sourceUrl,
70
+ };
71
+ }
72
+ //# sourceMappingURL=shortcut-facade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut-facade.js","sourceRoot":"","sources":["../../src/resources/shortcut-facade.ts"],"names":[],"mappings":"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"}