koppler 0.0.0-stage → 0.0.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 (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -2
  3. package/bin/run-logged +16 -0
  4. package/bin/secret-list +7 -0
  5. package/bin/secret-psql +41 -0
  6. package/bin/secret-set +17 -0
  7. package/bin/secret-store +82 -0
  8. package/bin/secret-test +10 -0
  9. package/bin/with-secrets +25 -0
  10. package/dist/.built +1 -0
  11. package/dist/cli/agent.js +144 -0
  12. package/dist/cli/agent.js.map +1 -0
  13. package/dist/cli/doctor.js +173 -0
  14. package/dist/cli/doctor.js.map +1 -0
  15. package/dist/cli/mcp.js +204 -0
  16. package/dist/cli/mcp.js.map +1 -0
  17. package/dist/cli/output.js +37 -0
  18. package/dist/cli/output.js.map +1 -0
  19. package/dist/cli/records.js +121 -0
  20. package/dist/cli/records.js.map +1 -0
  21. package/dist/cli/running.js +270 -0
  22. package/dist/cli/running.js.map +1 -0
  23. package/dist/cli/shell.js +43 -0
  24. package/dist/cli/shell.js.map +1 -0
  25. package/dist/cli/wait.js +59 -0
  26. package/dist/cli/wait.js.map +1 -0
  27. package/dist/core/addresses.js +55 -0
  28. package/dist/core/addresses.js.map +1 -0
  29. package/dist/core/arrange.js +329 -0
  30. package/dist/core/arrange.js.map +1 -0
  31. package/dist/core/cables.js +158 -0
  32. package/dist/core/cables.js.map +1 -0
  33. package/dist/core/expressions.js +72 -0
  34. package/dist/core/expressions.js.map +1 -0
  35. package/dist/core/frameworks.js +13 -0
  36. package/dist/core/frameworks.js.map +1 -0
  37. package/dist/core/kinds.js +244 -0
  38. package/dist/core/kinds.js.map +1 -0
  39. package/dist/core/launch.js +181 -0
  40. package/dist/core/launch.js.map +1 -0
  41. package/dist/core/logfmt.js +144 -0
  42. package/dist/core/logfmt.js.map +1 -0
  43. package/dist/core/mock.js +27 -0
  44. package/dist/core/mock.js.map +1 -0
  45. package/dist/core/plan.js +147 -0
  46. package/dist/core/plan.js.map +1 -0
  47. package/dist/core/ports.js +155 -0
  48. package/dist/core/ports.js.map +1 -0
  49. package/dist/core/presets.js +186 -0
  50. package/dist/core/presets.js.map +1 -0
  51. package/dist/core/secret-types.js +3 -0
  52. package/dist/core/secret-types.js.map +1 -0
  53. package/dist/core/sign-in.js +30 -0
  54. package/dist/core/sign-in.js.map +1 -0
  55. package/dist/core/stack-path.js +64 -0
  56. package/dist/core/stack-path.js.map +1 -0
  57. package/dist/core/stack.js +509 -0
  58. package/dist/core/stack.js.map +1 -0
  59. package/dist/core/terraform.js +393 -0
  60. package/dist/core/terraform.js.map +1 -0
  61. package/dist/data.js +37 -0
  62. package/dist/data.js.map +1 -0
  63. package/dist/engine/actions.js +99 -0
  64. package/dist/engine/actions.js.map +1 -0
  65. package/dist/engine/app-commands.js +106 -0
  66. package/dist/engine/app-commands.js.map +1 -0
  67. package/dist/engine/app-start.js +167 -0
  68. package/dist/engine/app-start.js.map +1 -0
  69. package/dist/engine/app-state.js +43 -0
  70. package/dist/engine/app-state.js.map +1 -0
  71. package/dist/engine/boards.js +219 -0
  72. package/dist/engine/boards.js.map +1 -0
  73. package/dist/engine/branch.js +194 -0
  74. package/dist/engine/branch.js.map +1 -0
  75. package/dist/engine/browser-console.js +45 -0
  76. package/dist/engine/browser-console.js.map +1 -0
  77. package/dist/engine/calls.js +97 -0
  78. package/dist/engine/calls.js.map +1 -0
  79. package/dist/engine/checks.js +270 -0
  80. package/dist/engine/checks.js.map +1 -0
  81. package/dist/engine/clones.js +67 -0
  82. package/dist/engine/clones.js.map +1 -0
  83. package/dist/engine/config.js +26 -0
  84. package/dist/engine/config.js.map +1 -0
  85. package/dist/engine/deployed.js +488 -0
  86. package/dist/engine/deployed.js.map +1 -0
  87. package/dist/engine/devices.js +81 -0
  88. package/dist/engine/devices.js.map +1 -0
  89. package/dist/engine/discover.js +236 -0
  90. package/dist/engine/discover.js.map +1 -0
  91. package/dist/engine/down.js +253 -0
  92. package/dist/engine/down.js.map +1 -0
  93. package/dist/engine/history.js +215 -0
  94. package/dist/engine/history.js.map +1 -0
  95. package/dist/engine/investigate.js +278 -0
  96. package/dist/engine/investigate.js.map +1 -0
  97. package/dist/engine/libraries.js +129 -0
  98. package/dist/engine/libraries.js.map +1 -0
  99. package/dist/engine/linked.js +146 -0
  100. package/dist/engine/linked.js.map +1 -0
  101. package/dist/engine/listen.js +223 -0
  102. package/dist/engine/listen.js.map +1 -0
  103. package/dist/engine/locks.js +38 -0
  104. package/dist/engine/locks.js.map +1 -0
  105. package/dist/engine/messages.js +177 -0
  106. package/dist/engine/messages.js.map +1 -0
  107. package/dist/engine/needs.js +363 -0
  108. package/dist/engine/needs.js.map +1 -0
  109. package/dist/engine/processes.js +350 -0
  110. package/dist/engine/processes.js.map +1 -0
  111. package/dist/engine/query.js +241 -0
  112. package/dist/engine/query.js.map +1 -0
  113. package/dist/engine/queues.js +100 -0
  114. package/dist/engine/queues.js.map +1 -0
  115. package/dist/engine/recipes.js +248 -0
  116. package/dist/engine/recipes.js.map +1 -0
  117. package/dist/engine/reload.js +32 -0
  118. package/dist/engine/reload.js.map +1 -0
  119. package/dist/engine/repos.js +214 -0
  120. package/dist/engine/repos.js.map +1 -0
  121. package/dist/engine/research.js +134 -0
  122. package/dist/engine/research.js.map +1 -0
  123. package/dist/engine/run-logs.js +79 -0
  124. package/dist/engine/run-logs.js.map +1 -0
  125. package/dist/engine/run.js +54 -0
  126. package/dist/engine/run.js.map +1 -0
  127. package/dist/engine/runs.js +223 -0
  128. package/dist/engine/runs.js.map +1 -0
  129. package/dist/engine/session.js +130 -0
  130. package/dist/engine/session.js.map +1 -0
  131. package/dist/engine/share.js +124 -0
  132. package/dist/engine/share.js.map +1 -0
  133. package/dist/engine/stack-file.js +206 -0
  134. package/dist/engine/stack-file.js.map +1 -0
  135. package/dist/engine/stack-share.js +86 -0
  136. package/dist/engine/stack-share.js.map +1 -0
  137. package/dist/engine/stacks.js +143 -0
  138. package/dist/engine/stacks.js.map +1 -0
  139. package/dist/engine/sync.js +214 -0
  140. package/dist/engine/sync.js.map +1 -0
  141. package/dist/engine/traces.js +65 -0
  142. package/dist/engine/traces.js.map +1 -0
  143. package/dist/engine/up.js +621 -0
  144. package/dist/engine/up.js.map +1 -0
  145. package/dist/engine/watch.js +71 -0
  146. package/dist/engine/watch.js.map +1 -0
  147. package/dist/engine/wire.js +120 -0
  148. package/dist/engine/wire.js.map +1 -0
  149. package/dist/failure.js +16 -0
  150. package/dist/failure.js.map +1 -0
  151. package/dist/github.js +95 -0
  152. package/dist/github.js.map +1 -0
  153. package/dist/ids.js +6 -0
  154. package/dist/ids.js.map +1 -0
  155. package/dist/json-files.js +36 -0
  156. package/dist/json-files.js.map +1 -0
  157. package/dist/koppler.mjs +840 -0
  158. package/dist/koppler.mjs.map +1 -0
  159. package/dist/lambda-local.mjs +250 -0
  160. package/dist/lambda-local.mjs.map +1 -0
  161. package/dist/lambda-secrets-proxy.go +31 -0
  162. package/dist/mock-server.mjs +23 -0
  163. package/dist/mock-server.mjs.map +1 -0
  164. package/dist/page/assets/JetBrainsMono-cyrillic-D2jMJ_bk.woff2 +0 -0
  165. package/dist/page/assets/JetBrainsMono-greek-D2nYnAec.woff2 +0 -0
  166. package/dist/page/assets/JetBrainsMono-latin-Db4Uuiha.woff2 +0 -0
  167. package/dist/page/assets/JetBrainsMono-latin-ext-Bnd2h4Br.woff2 +0 -0
  168. package/dist/page/assets/JetBrainsMono-vietnamese-CNlsqbWb.woff2 +0 -0
  169. package/dist/page/assets/SchibstedGrotesk-latin-DxefFeRw.woff2 +0 -0
  170. package/dist/page/assets/SchibstedGrotesk-latin-ext-CnpJE18X.woff2 +0 -0
  171. package/dist/page/assets/index-Bkj_pX35.js +42 -0
  172. package/dist/page/assets/index-Dq45jswH.css +1 -0
  173. package/dist/page/index.html +14 -0
  174. package/dist/page/theme.js +8 -0
  175. package/dist/paths.js +19 -0
  176. package/dist/paths.js.map +1 -0
  177. package/dist/safe-env.js +73 -0
  178. package/dist/safe-env.js.map +1 -0
  179. package/dist/sam-local.mjs +135 -0
  180. package/dist/sam-local.mjs.map +1 -0
  181. package/dist/scrub.js +87 -0
  182. package/dist/scrub.js.map +1 -0
  183. package/dist/secret-check.mjs +262 -0
  184. package/dist/secret-check.mjs.map +1 -0
  185. package/dist/server/browser-console.js +117 -0
  186. package/dist/server/browser-console.js.map +1 -0
  187. package/dist/server/cable-test.js +365 -0
  188. package/dist/server/cable-test.js.map +1 -0
  189. package/dist/server/choices.js +51 -0
  190. package/dist/server/choices.js.map +1 -0
  191. package/dist/server/files.js +107 -0
  192. package/dist/server/files.js.map +1 -0
  193. package/dist/server/gate.js +128 -0
  194. package/dist/server/gate.js.map +1 -0
  195. package/dist/server/logs.js +76 -0
  196. package/dist/server/logs.js.map +1 -0
  197. package/dist/server/mobile.js +113 -0
  198. package/dist/server/mobile.js.map +1 -0
  199. package/dist/server/routes.js +326 -0
  200. package/dist/server/routes.js.map +1 -0
  201. package/dist/server/runs.js +111 -0
  202. package/dist/server/runs.js.map +1 -0
  203. package/dist/server/sanitize.js +242 -0
  204. package/dist/server/sanitize.js.map +1 -0
  205. package/dist/server/secrets.js +111 -0
  206. package/dist/server/secrets.js.map +1 -0
  207. package/dist/server/status.js +321 -0
  208. package/dist/server/status.js.map +1 -0
  209. package/dist/server/token.js +38 -0
  210. package/dist/server/token.js.map +1 -0
  211. package/dist/server/waiting.js +79 -0
  212. package/dist/server/waiting.js.map +1 -0
  213. package/dist/status-watch.mjs +63 -0
  214. package/dist/status-watch.mjs.map +1 -0
  215. package/package.json +73 -4
  216. package/skills/koppler-run/SKILL.md +169 -0
  217. package/skills/koppler-run/VERSION +1 -0
  218. package/skills/koppler-run/references/action-log.md +50 -0
  219. package/skills/koppler-run/references/permissions.md +25 -0
  220. package/skills/koppler-run/references/preparing-nodes.md +62 -0
  221. package/skills/koppler-run/references/reporting-bugs.md +76 -0
  222. package/skills/koppler-run/references/research.md +40 -0
  223. package/skills/koppler-run/references/secrets.md +56 -0
  224. package/skills/koppler-run/references/stack.md +197 -0
  225. package/skills/koppler-run/references/status.md +92 -0
@@ -0,0 +1,128 @@
1
+ // The canvas server's gate: only this machine's own page, with the token, sending JSON, reaches a route; every answer
2
+ // carries the security headers, and a route that fails is answered, not the end of the server.
3
+ import crypto from 'node:crypto';
4
+ import { JSON_HEADERS, serveFile } from "./files.js";
5
+ import { ROUTES } from "./routes.js";
6
+ import { serveConsole } from "./browser-console.js";
7
+ const MAX_BODY_BYTES = 256 * 1024;
8
+ // The page loads and calls nothing but this server, and no other page can frame it or learn its address.
9
+ const SECURITY_HEADERS = {
10
+ 'content-security-policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; frame-ancestors 'none'; base-uri 'none'; form-action 'none'",
11
+ 'referrer-policy': 'no-referrer',
12
+ 'x-content-type-options': 'nosniff',
13
+ 'x-frame-options': 'DENY',
14
+ 'cross-origin-resource-policy': 'same-origin',
15
+ };
16
+ export const logged = (error) => {
17
+ console.error(`status-watch: ${error?.stack ?? String(error)}`);
18
+ };
19
+ /** A request that fails (a full disk, a file that cannot be written) is answered and logged; the server keeps running. */
20
+ export function failRequest(res, error) {
21
+ logged(error);
22
+ try {
23
+ if (res.headersSent)
24
+ res.end();
25
+ else {
26
+ const said = error?.message ?? String(error);
27
+ res
28
+ .writeHead(500, JSON_HEADERS)
29
+ .end(JSON.stringify({ error: `Koppler could not do that: ${said.slice(0, 300)}` }));
30
+ }
31
+ }
32
+ catch {
33
+ // The connection went away.
34
+ }
35
+ }
36
+ /**
37
+ * The request's JSON body, or null when it is not JSON. Too large a body is dropped as it arrives and answered once it
38
+ * has all been sent: closing earlier would reach the sender as a broken connection instead of an answer. One that goes
39
+ * on far beyond the limit is cut off.
40
+ */
41
+ function readBody(req, res, onBody) {
42
+ let size = 0;
43
+ const chunks = [];
44
+ req.on('data', (chunk) => {
45
+ size += chunk.length;
46
+ if (size > MAX_BODY_BYTES * 256)
47
+ req.destroy();
48
+ else if (size <= MAX_BODY_BYTES)
49
+ chunks.push(chunk);
50
+ });
51
+ req.on('end', () => {
52
+ if (size > MAX_BODY_BYTES) {
53
+ res.writeHead(413).end();
54
+ return;
55
+ }
56
+ let body = null;
57
+ try {
58
+ body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
59
+ }
60
+ catch {
61
+ // Not JSON: each route says what it makes of nothing.
62
+ }
63
+ onBody(body);
64
+ });
65
+ }
66
+ export function gate(req, res, token, port) {
67
+ const prefix = `/s/${token}/`;
68
+ const url = new URL(req.url ?? '', 'http://127.0.0.1');
69
+ for (const [name, value] of Object.entries(SECURITY_HEADERS))
70
+ res.setHeader(name, value);
71
+ // Answered once the request has been read to its end, so the sender gets the answer and not a broken connection.
72
+ const refuse = (code) => {
73
+ const answer = () => res.writeHead(code).end();
74
+ req.resume();
75
+ if (req.complete)
76
+ answer();
77
+ else
78
+ req.once('end', answer);
79
+ };
80
+ // Only this machine's own page is answered: not another site's name pointed here, nor a request another site makes.
81
+ const own = ['127.0.0.1', 'localhost'].map(host => `${host}:${port}`);
82
+ const { host = '', origin } = req.headers;
83
+ // A web app's page sends its console here from its own origin: the app's key is checked instead of the token.
84
+ if (own.includes(host) && (url.pathname === '/console.js' || url.pathname === '/console')) {
85
+ serveConsole(req, res, url);
86
+ return;
87
+ }
88
+ if (!own.includes(host) || (origin !== undefined && !own.includes(origin.replace(/^http:\/\//, '')))) {
89
+ refuse(403);
90
+ return;
91
+ }
92
+ if (req.method === 'GET' && url.pathname.startsWith(prefix)) {
93
+ serveFile(url.pathname.slice(prefix.length), url, res);
94
+ return;
95
+ }
96
+ const given = Buffer.from(url.searchParams.get('token') ?? '');
97
+ const expected = Buffer.from(token);
98
+ const authorized = given.length === expected.length && crypto.timingSafeEqual(given, expected);
99
+ const route = Object.hasOwn(ROUTES, url.pathname) ? ROUTES[url.pathname] : undefined;
100
+ if (req.method !== 'POST' || !authorized || !route) {
101
+ refuse(404);
102
+ return;
103
+ }
104
+ // A form on another site cannot send JSON without asking first, and that question is never answered.
105
+ if (!/^application\/json\b/i.test(req.headers['content-type'] ?? '')) {
106
+ refuse(415);
107
+ return;
108
+ }
109
+ readBody(req, res, body => {
110
+ const fields = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
111
+ // A route that throws, now or later, is answered like any failed request.
112
+ new Promise(resolve => {
113
+ resolve(route(fields));
114
+ })
115
+ .then(answered => {
116
+ const { status, body: said, after } = answered;
117
+ if (said === undefined)
118
+ res.writeHead(status).end();
119
+ else
120
+ res.writeHead(status, JSON_HEADERS).end(JSON.stringify(said));
121
+ after?.();
122
+ })
123
+ .catch((error) => {
124
+ failRequest(res, error);
125
+ });
126
+ });
127
+ }
128
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/server/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ // The end of an app's log for its card, and the debug log of what the user did on the canvas.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { tempDir } from "../paths.js";
5
+ import { redact } from "../scrub.js";
6
+ const TAIL_BYTES = 96 * 1024;
7
+ const TAIL_LINES = 600;
8
+ /** The last lines of a file, secrets masked and colour codes taken out; null when it cannot be read. */
9
+ export function tail(file, bytes = TAIL_BYTES, lines = TAIL_LINES) {
10
+ try {
11
+ const { size } = fs.statSync(file);
12
+ const fd = fs.openSync(file, 'r');
13
+ const length = Math.min(size, bytes);
14
+ const buffer = Buffer.alloc(length);
15
+ fs.readSync(fd, buffer, 0, length, size - length);
16
+ fs.closeSync(fd);
17
+ // Color codes, as terminals send them or as `flutter logs` spells them out, are noise on a card.
18
+ // A read that starts mid-file starts mid-line: that piece goes, so a secret never shows without the name that masks it.
19
+ const text = buffer.toString('utf8');
20
+ const whole = size > length ? text.slice(text.indexOf('\n') + 1) : text;
21
+ const plain = whole.replace(/(?:\x1b|\\\^\[)\[[0-9;]*m/g, '');
22
+ const last = redact(plain).trimEnd().split('\n').slice(-lines);
23
+ return last.map(line => (line.length > 4000 ? `${line.slice(0, 4000)} …` : line)).join('\n');
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ // The debug log: what the user did on the canvas, one JSON line each, so a concern can be traced afterwards.
30
+ // Kept to the last MAX_DEBUG_LINES lines; every text goes through `redact`, and only known fields are kept.
31
+ const MAX_DEBUG_LINES = 5000;
32
+ const debugFile = () => path.join(tempDir(), 'debug.jsonl');
33
+ const shown = (value) => (typeof value === 'string' ? redact(value).slice(0, 300) : undefined);
34
+ const count = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : undefined);
35
+ export function cleanDebugEntry(given) {
36
+ if (!given || typeof given !== 'object')
37
+ return null;
38
+ const entry = given;
39
+ const board = entry['board'] && typeof entry['board'] === 'object' ? entry['board'] : null;
40
+ const detail = shown(entry['detail']);
41
+ const out = {
42
+ at: shown(entry['at']) ?? new Date().toISOString(),
43
+ kind: shown(entry['kind']) ?? 'event',
44
+ what: shown(entry['what']) ?? '',
45
+ ...(detail && { detail }),
46
+ ...(board && {
47
+ board: {
48
+ apps: count(board['apps']),
49
+ cables: count(board['cables']),
50
+ view: shown(board['view']),
51
+ env: shown(board['env']),
52
+ zoom: count(board['zoom']),
53
+ name: shown(board['name']),
54
+ },
55
+ }),
56
+ };
57
+ return out.what || out.detail ? out : null;
58
+ }
59
+ export function appendDebugLog(entries) {
60
+ const lines = (Array.isArray(entries) ? entries : [])
61
+ .slice(0, 500)
62
+ .flatMap(entry => cleanDebugEntry(entry) ?? [])
63
+ .map(entry => JSON.stringify(entry));
64
+ if (!lines.length)
65
+ return { kept: 0 };
66
+ fs.appendFileSync(debugFile(), lines.join('\n') + '\n', { mode: 0o600 });
67
+ const all = fs.readFileSync(debugFile(), 'utf8').split('\n').filter(Boolean);
68
+ if (all.length > MAX_DEBUG_LINES)
69
+ fs.writeFileSync(debugFile(), all.slice(-MAX_DEBUG_LINES).join('\n') + '\n', { mode: 0o600 });
70
+ return { kept: lines.length };
71
+ }
72
+ export function clearDebugLog() {
73
+ fs.rmSync(debugFile(), { force: true });
74
+ return { cleared: true };
75
+ }
76
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/server/logs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ // The mobile app has no port: Koppler looks for its process on booted iOS simulators and connected Android devices, and
2
+ // follows the log of one the user started from their IDE.
3
+ import { execFile } from 'node:child_process';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { readClones, readStack } from "../data.js";
7
+ import { runningNames } from "../engine/runs.js";
8
+ import { live } from "../engine/history.js";
9
+ import { CODE, stateDir } from "../paths.js";
10
+ const MOBILE_INTERVAL_MS = 5000;
11
+ const run = (command, args) => new Promise(resolve => {
12
+ execFile(command, args, { timeout: 4000, maxBuffer: 4 * 1024 * 1024 }, (error, stdout) => {
13
+ resolve(error ? '' : stdout);
14
+ });
15
+ });
16
+ const flavorIn = (command) => /--flavor[= ](\S+)/.exec(command)?.[1] ?? null;
17
+ export async function findMobileApp({ iosBundleId, androidPackage }) {
18
+ const found = [];
19
+ if (iosBundleId) {
20
+ let booted = [];
21
+ try {
22
+ const listed = JSON.parse(await run('xcrun', ['simctl', 'list', 'devices', 'booted', '-j']));
23
+ booted = Object.values(listed.devices).flat();
24
+ }
25
+ catch {
26
+ // No Xcode, or no simulator booted.
27
+ }
28
+ for (const simulator of booted) {
29
+ const services = await run('xcrun', ['simctl', 'spawn', simulator.udid, 'launchctl', 'list']);
30
+ const line = services.split('\n').find(one => one.includes(`UIKitApplication:${iosBundleId}[`));
31
+ const pid = Number(line?.split(/\s+/)[0]);
32
+ if (pid > 0)
33
+ found.push({ platform: 'ios', device: simulator.name, deviceId: simulator.udid, pid });
34
+ }
35
+ }
36
+ if (androidPackage) {
37
+ const devices = (await run('adb', ['devices'])).split('\n').slice(1);
38
+ const serials = devices
39
+ .map(line => line.split('\t'))
40
+ .flatMap(([id, state]) => (id && state?.trim() === 'device' ? [id] : []));
41
+ for (const serial of serials) {
42
+ const shell = (...args) => run('adb', ['-s', serial, 'shell', ...args]);
43
+ const pid = Number((await shell('pidof', androidPackage)).trim().split(/\s+/)[0]);
44
+ if (pid > 0) {
45
+ const model = (await shell('getprop', 'ro.product.model')).trim();
46
+ found.push({ platform: 'android', device: model || serial, deviceId: serial, pid });
47
+ }
48
+ }
49
+ }
50
+ const processes = (await run('ps', ['-Ao', 'command='])).split('\n');
51
+ const runs = processes.filter(line => line.includes('flutter_tools') && / run( |$)/.test(line));
52
+ for (const app of found) {
53
+ const mine = runs.find(line => line.includes(app.deviceId)) ?? (runs.length === 1 ? runs[0] : undefined);
54
+ if (!mine)
55
+ continue;
56
+ app.flavor = flavorIn(mine);
57
+ app.source = mine.includes('--machine') ? 'ide' : 'cli';
58
+ }
59
+ return found;
60
+ }
61
+ const recordOf = (id) => {
62
+ try {
63
+ return JSON.parse(fs.readFileSync(path.join(stateDir(), 'procs', `${id}.json`), 'utf8'));
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ };
69
+ /** An app Koppler launched keeps no `flutter run` attached, so its flavor and device come from Koppler's own record of the start. */
70
+ export function fromKoppler(id, found, record = recordOf(id)) {
71
+ const command = record?.command ?? '';
72
+ return found.map(app => app.flavor || !command.includes(app.deviceId) ? app : { ...app, flavor: flavorIn(command), source: 'cli' });
73
+ }
74
+ /** What was found for each mobile app at the last look. */
75
+ export const mobileState = {};
76
+ let checkedAt = 0;
77
+ /** Looks for the stack's mobile apps among `ids` again, at most every five seconds. */
78
+ export async function refreshMobile(ids) {
79
+ if (Date.now() - checkedAt < MOBILE_INTERVAL_MS)
80
+ return;
81
+ checkedAt = Date.now();
82
+ const apps = readStack()?.apps ?? {};
83
+ for (const id of ids) {
84
+ const mobile = apps[id]?.mobile;
85
+ if (mobile)
86
+ mobileState[id] = fromKoppler(id, await findMobileApp(mobile));
87
+ }
88
+ followIdeLogs();
89
+ }
90
+ const following = new Map();
91
+ /**
92
+ * A mobile app on the applied board that runs from the IDE: Koppler follows its output with `flutter logs` on that
93
+ * device, so its card shows the app's own log instead of an older run's. Started once per device; Stop and revert
94
+ * stops it too.
95
+ */
96
+ function followIdeLogs() {
97
+ const stack = readStack();
98
+ const board = live()?.nodes ?? {};
99
+ const running = runningNames();
100
+ const clones = readClones();
101
+ for (const [id, found] of Object.entries(mobileState)) {
102
+ const ide = found.find(app => app.source === 'ide');
103
+ const repo = clones[id]?.path;
104
+ if (!ide || !board[id] || !repo || running.includes(id) || following.get(id) === ide.deviceId)
105
+ continue;
106
+ following.set(id, ide.deviceId);
107
+ const fvm = stack?.apps[id]?.['flutter']?.fvm;
108
+ const logs = [...(fvm ? ['fvm', 'flutter'] : ['flutter']), 'logs', '-d', ide.deviceId];
109
+ const koppler = [path.join(CODE, 'koppler.mjs'), 'run', id, '--cwd', repo, '--restart', '--', ...logs];
110
+ execFile(process.execPath, koppler, { timeout: 30000 }, () => undefined);
111
+ }
112
+ }
113
+ //# sourceMappingURL=mobile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../src/server/mobile.ts"],"names":[],"mappings":"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"}