@owlmeans/viable-sdk 0.1.18-rc.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 (166) hide show
  1. package/README.md +128 -0
  2. package/build/api/index.d.ts +2 -0
  3. package/build/api/index.d.ts.map +1 -0
  4. package/build/api/index.js +2 -0
  5. package/build/api/index.js.map +1 -0
  6. package/build/api/remote.d.ts +14 -0
  7. package/build/api/remote.d.ts.map +1 -0
  8. package/build/api/remote.js +123 -0
  9. package/build/api/remote.js.map +1 -0
  10. package/build/consts.d.ts +47 -0
  11. package/build/consts.d.ts.map +1 -0
  12. package/build/consts.js +47 -0
  13. package/build/consts.js.map +1 -0
  14. package/build/context/index.d.ts +22 -0
  15. package/build/context/index.d.ts.map +1 -0
  16. package/build/context/index.js +92 -0
  17. package/build/context/index.js.map +1 -0
  18. package/build/errors.d.ts +21 -0
  19. package/build/errors.d.ts.map +1 -0
  20. package/build/errors.js +36 -0
  21. package/build/errors.js.map +1 -0
  22. package/build/executor/boot-check.d.ts +73 -0
  23. package/build/executor/boot-check.d.ts.map +1 -0
  24. package/build/executor/boot-check.js +155 -0
  25. package/build/executor/boot-check.js.map +1 -0
  26. package/build/executor/env.d.ts +25 -0
  27. package/build/executor/env.d.ts.map +1 -0
  28. package/build/executor/env.js +77 -0
  29. package/build/executor/env.js.map +1 -0
  30. package/build/executor/errors.d.ts +69 -0
  31. package/build/executor/errors.d.ts.map +1 -0
  32. package/build/executor/errors.js +80 -0
  33. package/build/executor/errors.js.map +1 -0
  34. package/build/executor/files.d.ts +124 -0
  35. package/build/executor/files.d.ts.map +1 -0
  36. package/build/executor/files.js +529 -0
  37. package/build/executor/files.js.map +1 -0
  38. package/build/executor/git.d.ts +52 -0
  39. package/build/executor/git.d.ts.map +1 -0
  40. package/build/executor/git.js +353 -0
  41. package/build/executor/git.js.map +1 -0
  42. package/build/executor/health.d.ts +92 -0
  43. package/build/executor/health.d.ts.map +1 -0
  44. package/build/executor/health.js +98 -0
  45. package/build/executor/health.js.map +1 -0
  46. package/build/executor/index.d.ts +39 -0
  47. package/build/executor/index.d.ts.map +1 -0
  48. package/build/executor/index.js +173 -0
  49. package/build/executor/index.js.map +1 -0
  50. package/build/executor/integrity.d.ts +17 -0
  51. package/build/executor/integrity.d.ts.map +1 -0
  52. package/build/executor/integrity.js +59 -0
  53. package/build/executor/integrity.js.map +1 -0
  54. package/build/executor/layout.d.ts +61 -0
  55. package/build/executor/layout.d.ts.map +1 -0
  56. package/build/executor/layout.js +98 -0
  57. package/build/executor/layout.js.map +1 -0
  58. package/build/executor/shell.d.ts +89 -0
  59. package/build/executor/shell.d.ts.map +1 -0
  60. package/build/executor/shell.js +280 -0
  61. package/build/executor/shell.js.map +1 -0
  62. package/build/executor/spawn.d.ts +111 -0
  63. package/build/executor/spawn.d.ts.map +1 -0
  64. package/build/executor/spawn.js +314 -0
  65. package/build/executor/spawn.js.map +1 -0
  66. package/build/harness/index.d.ts +28 -0
  67. package/build/harness/index.d.ts.map +1 -0
  68. package/build/harness/index.js +83 -0
  69. package/build/harness/index.js.map +1 -0
  70. package/build/harness/templates.d.ts +30 -0
  71. package/build/harness/templates.d.ts.map +1 -0
  72. package/build/harness/templates.js +162 -0
  73. package/build/harness/templates.js.map +1 -0
  74. package/build/index.d.ts +13 -0
  75. package/build/index.d.ts.map +1 -0
  76. package/build/index.js +12 -0
  77. package/build/index.js.map +1 -0
  78. package/build/project/env.d.ts +108 -0
  79. package/build/project/env.d.ts.map +1 -0
  80. package/build/project/env.js +233 -0
  81. package/build/project/env.js.map +1 -0
  82. package/build/project/marker.d.ts +34 -0
  83. package/build/project/marker.d.ts.map +1 -0
  84. package/build/project/marker.js +54 -0
  85. package/build/project/marker.js.map +1 -0
  86. package/build/project/setup.d.ts +62 -0
  87. package/build/project/setup.d.ts.map +1 -0
  88. package/build/project/setup.js +190 -0
  89. package/build/project/setup.js.map +1 -0
  90. package/build/run/index.d.ts +75 -0
  91. package/build/run/index.d.ts.map +1 -0
  92. package/build/run/index.js +150 -0
  93. package/build/run/index.js.map +1 -0
  94. package/build/run/serve.d.ts +18 -0
  95. package/build/run/serve.d.ts.map +1 -0
  96. package/build/run/serve.js +148 -0
  97. package/build/run/serve.js.map +1 -0
  98. package/build/run/state.d.ts +75 -0
  99. package/build/run/state.d.ts.map +1 -0
  100. package/build/run/state.js +136 -0
  101. package/build/run/state.js.map +1 -0
  102. package/build/session/index.d.ts +17 -0
  103. package/build/session/index.d.ts.map +1 -0
  104. package/build/session/index.js +226 -0
  105. package/build/session/index.js.map +1 -0
  106. package/build/session/questions.d.ts +16 -0
  107. package/build/session/questions.d.ts.map +1 -0
  108. package/build/session/questions.js +17 -0
  109. package/build/session/questions.js.map +1 -0
  110. package/build/session/queue.d.ts +54 -0
  111. package/build/session/queue.d.ts.map +1 -0
  112. package/build/session/queue.js +102 -0
  113. package/build/session/queue.js.map +1 -0
  114. package/build/session/tasks.d.ts +15 -0
  115. package/build/session/tasks.d.ts.map +1 -0
  116. package/build/session/tasks.js +16 -0
  117. package/build/session/tasks.js.map +1 -0
  118. package/build/task/driver.d.ts +30 -0
  119. package/build/task/driver.d.ts.map +1 -0
  120. package/build/task/driver.js +120 -0
  121. package/build/task/driver.js.map +1 -0
  122. package/build/task/envelope.d.ts +35 -0
  123. package/build/task/envelope.d.ts.map +1 -0
  124. package/build/task/envelope.js +230 -0
  125. package/build/task/envelope.js.map +1 -0
  126. package/build/task/index.d.ts +4 -0
  127. package/build/task/index.d.ts.map +1 -0
  128. package/build/task/index.js +4 -0
  129. package/build/task/index.js.map +1 -0
  130. package/build/task/inquiry.d.ts +53 -0
  131. package/build/task/inquiry.d.ts.map +1 -0
  132. package/build/task/inquiry.js +232 -0
  133. package/build/task/inquiry.js.map +1 -0
  134. package/build/tools/catalogue.d.ts +17 -0
  135. package/build/tools/catalogue.d.ts.map +1 -0
  136. package/build/tools/catalogue.js +1169 -0
  137. package/build/tools/catalogue.js.map +1 -0
  138. package/build/tools/index.d.ts +7 -0
  139. package/build/tools/index.d.ts.map +1 -0
  140. package/build/tools/index.js +7 -0
  141. package/build/tools/index.js.map +1 -0
  142. package/build/tools/jobs.d.ts +12 -0
  143. package/build/tools/jobs.d.ts.map +1 -0
  144. package/build/tools/jobs.js +60 -0
  145. package/build/tools/jobs.js.map +1 -0
  146. package/build/tools/mcp.d.ts +43 -0
  147. package/build/tools/mcp.d.ts.map +1 -0
  148. package/build/tools/mcp.js +131 -0
  149. package/build/tools/mcp.js.map +1 -0
  150. package/build/tools/platform.d.ts +77 -0
  151. package/build/tools/platform.d.ts.map +1 -0
  152. package/build/tools/platform.js +316 -0
  153. package/build/tools/platform.js.map +1 -0
  154. package/build/tools/refusal.d.ts +66 -0
  155. package/build/tools/refusal.d.ts.map +1 -0
  156. package/build/tools/refusal.js +377 -0
  157. package/build/tools/refusal.js.map +1 -0
  158. package/build/tools/types.d.ts +83 -0
  159. package/build/tools/types.d.ts.map +1 -0
  160. package/build/tools/types.js +38 -0
  161. package/build/tools/types.js.map +1 -0
  162. package/build/types.d.ts +174 -0
  163. package/build/types.d.ts.map +1 -0
  164. package/build/types.js +2 -0
  165. package/build/types.js.map +1 -0
  166. package/package.json +105 -0
@@ -0,0 +1,190 @@
1
+ import fs from 'fs-extra';
2
+ import p from 'node:path';
3
+ import { spawn } from 'node:child_process';
4
+ import { hasWorker } from '../executor/layout.js';
5
+ import { CONNECT_ENV_BEGIN, CONNECT_ENV_END } from '@owlmeans/viable-common';
6
+ import { probeUrl, readEnv, ROOT_ENV_FILE } from './env.js';
7
+ /** Everything a connection string says except who is connecting. */
8
+ export const redactUrl = (url) => {
9
+ try {
10
+ const parsed = new URL(url);
11
+ if (parsed.username !== '' || parsed.password !== '') {
12
+ parsed.username = '***';
13
+ parsed.password = '';
14
+ }
15
+ return parsed.toString();
16
+ }
17
+ catch {
18
+ return url.replace(/\/\/[^@/]*@/, '//***@');
19
+ }
20
+ };
21
+ /** Whether a command can be run here. `--version` rather than `which`, which Windows lacks. */
22
+ export const probeCommand = async (command) => await new Promise(resolve => {
23
+ const child = spawn(command, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' });
24
+ const settle = (ok) => { resolve(ok); };
25
+ child.once('error', () => { settle(false); });
26
+ child.once('exit', code => { settle(code === 0); });
27
+ setTimeout(() => { child.kill(); settle(false); }, 4_000).unref?.();
28
+ });
29
+ const stateOf = async (url) => {
30
+ const configured = url != null && url.trim() !== '' && !url.includes('postgres:postgres@localhost:5432');
31
+ return {
32
+ configured,
33
+ reachable: configured ? await probeUrl(url) : false,
34
+ ...(configured ? { redacted: redactUrl(url) } : {}),
35
+ };
36
+ };
37
+ /** Whether the project's own `.env` is kept out of its history — it holds credentials. */
38
+ const envIsIgnored = async (dir) => {
39
+ const ignore = await fs.readFile(p.join(dir, '.gitignore'), 'utf-8').catch(() => '');
40
+ return /^\s*\.env\s*$/m.test(ignore) || /^\s*\*\.env\s*$/m.test(ignore) || /^\s*\.env\*/m.test(ignore);
41
+ };
42
+ export const readSetupReport = async (dir) => {
43
+ const { values } = await readEnv(dir);
44
+ const [database, queue, bun, git, docker, envIgnored] = await Promise.all([
45
+ stateOf(values.DATABASE_URL),
46
+ stateOf(values.VALKEY_URL),
47
+ probeCommand('bun'),
48
+ probeCommand('git'),
49
+ probeCommand('docker'),
50
+ envIsIgnored(dir),
51
+ ]);
52
+ return {
53
+ dir,
54
+ needsWorker: hasWorker(dir),
55
+ database,
56
+ queue,
57
+ tools: { bun, git, docker },
58
+ platform: process.platform,
59
+ envIgnored,
60
+ };
61
+ };
62
+ /** What is still missing before the application can start. */
63
+ export const missingServices = (report) => [
64
+ ...(report.database.reachable ? [] : ['database']),
65
+ ...(report.needsWorker && !report.queue.reachable ? ['queue'] : []),
66
+ ];
67
+ const dockerPostgres = (port) => `docker run -d --name viable-postgres -e POSTGRES_PASSWORD=viable -e POSTGRES_USER=viable`
68
+ + ` -e POSTGRES_DB=viable -p ${port}:5432 postgres:16-alpine`;
69
+ const dockerValkey = (port) => `docker run -d --name viable-valkey -p ${port}:6379 valkey/valkey:8-alpine`;
70
+ const nativeInstall = (platform) => platform === 'darwin'
71
+ ? [' brew install postgresql@16 && brew services start postgresql@16',
72
+ ' brew install valkey && brew services start valkey']
73
+ : platform === 'win32'
74
+ ? [' winget install PostgreSQL.PostgreSQL',
75
+ ' (a queue store is easiest through Docker Desktop on Windows)']
76
+ : [' sudo apt install postgresql redis-server # Debian/Ubuntu',
77
+ ' sudo systemctl enable --now postgresql redis-server'];
78
+ /**
79
+ * The answer a parent agent reads when the project cannot run yet.
80
+ *
81
+ * It is written as a QUESTION with three answers, because which one is right is the user's to
82
+ * decide and nobody else's: they may already run Postgres, they may want one installed here, or
83
+ * they may prefer a hosted free tier and never install anything. Guessing means either a container
84
+ * on somebody's machine they did not ask for, or a connection string invented for a database that
85
+ * does not exist — and the second surfaces minutes later as a boot error nobody can place.
86
+ */
87
+ export const renderSetupGuide = (report) => {
88
+ const missing = missingServices(report);
89
+ const lines = [`Project: ${report.dir}`, ''];
90
+ lines.push('What this machine provides:');
91
+ lines.push(` bun ${report.tools.bun ? 'yes' : 'NO — install it from https://bun.sh'}`);
92
+ lines.push(` git ${report.tools.git ? 'yes' : 'NO — the project keeps its own history'}`);
93
+ lines.push(` database ${report.database.reachable
94
+ ? `yes — ${report.database.redacted ?? 'configured'}`
95
+ : report.database.configured
96
+ ? `configured but NOT answering — ${report.database.redacted ?? ''}`
97
+ : 'not configured'}`);
98
+ lines.push(` queue ${report.needsWorker
99
+ ? report.queue.reachable
100
+ ? `yes — ${report.queue.redacted ?? 'configured'}`
101
+ : report.queue.configured ? 'configured but NOT answering' : 'not configured'
102
+ : 'not needed — this project has no background worker'}`);
103
+ lines.push('');
104
+ if (missing.length < 1) {
105
+ lines.push('Everything the application needs is in place. Call run_local to build and start it.');
106
+ return lines.join('\n');
107
+ }
108
+ const what = missing.map(one => one === 'database' ? 'a Postgres database' : 'a queue store (Valkey or Redis)');
109
+ lines.push(`ASK THE USER — this project still needs ${what.join(' and ')}, and only they can say how to`, 'provide it. Put these three choices to them in your own words and wait for an answer:', '', ' 1. "I already have one" — running here, on another machine, or a service you already pay', ' for. Ask for the connection string and nothing else.', ' 2. "Install one here" — the commands are below; run them only if they choose this.', ' 3. "Use a free hosted plan" — Supabase or Neon for Postgres, Upstash for Redis. They sign', ' up, create a project, and copy the connection string.', '', 'Then call set_local_service with what they gave you. Do not invent a connection string, and do', 'not install anything before they have chosen.', '');
110
+ if (missing.includes('database')) {
111
+ lines.push('--- 2. Install Postgres here ---');
112
+ lines.push(report.tools.docker
113
+ ? ` ${dockerPostgres(5432)}`
114
+ : ' Docker is not installed. Either install Docker, or use the native package:');
115
+ if (!report.tools.docker)
116
+ lines.push(nativeInstall(report.platform)[0]);
117
+ lines.push(' Then the connection string is:', ' postgres://viable:viable@localhost:5432/viable', '', '--- 3. Free hosted Postgres ---', ' Supabase: https://supabase.com → New project → Settings → Database → Connection string', ' (choose the "URI" form; it already carries the password)', ' Neon: https://neon.tech → New project → Connection string', ' Either gives a `postgres://…` URL that goes straight into set_local_service.', '');
118
+ }
119
+ if (missing.includes('queue')) {
120
+ lines.push('--- A queue store, for this project\'s worker ---');
121
+ lines.push(report.tools.docker
122
+ ? ` ${dockerValkey(6379)}`
123
+ : nativeInstall(report.platform)[1]);
124
+ lines.push(' Then: redis://localhost:6379', ' Free hosted: https://upstash.com → Redis → Create database → copy the `redis://…` URL.', '');
125
+ }
126
+ lines.push('--- Where the value goes ---', ` Into ${p.join(report.dir, ROOT_ENV_FILE)}, OUTSIDE the block marked "viable:managed".`, ' set_local_service writes it there for you. The platform never overwrites a key you set,', ' and the credential never reaches the project marker, your agent\'s configuration, or the', ' platform.', report.envIgnored
127
+ ? ' That file is git-ignored, so the credential stays out of the project\'s history.'
128
+ : ' WARNING: .env is NOT git-ignored in this project. Say so before writing a credential.');
129
+ return lines.join('\n');
130
+ };
131
+ /**
132
+ * Write the user's own service lines into their half of the `.env`.
133
+ *
134
+ * Replaces the key where it already exists outside the managed block and appends it otherwise, so
135
+ * running this twice leaves one line rather than two — a `.env` takes the LAST assignment, and two
136
+ * copies of a credential is one of them being quietly ignored.
137
+ */
138
+ export const setUserEnv = async (dir, values) => {
139
+ const file = p.join(dir, ROOT_ENV_FILE);
140
+ const existing = await fs.readFile(file, 'utf-8').catch(() => '');
141
+ const lines = existing === '' ? [] : existing.split('\n');
142
+ const written = [];
143
+ for (const [key, value] of Object.entries(values)) {
144
+ if (value.trim() === '')
145
+ continue;
146
+ written.push(key);
147
+ const line = `${key}=${value}`;
148
+ const pattern = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=`);
149
+ // Walked with the block's boundaries in hand, because only a line the USER owns may be
150
+ // rewritten: one inside the managed block belongs to the platform, which composes its half
151
+ // whole on every push.
152
+ let inside = false;
153
+ let replaced = false;
154
+ for (let i = 0; i < lines.length; ++i) {
155
+ if (lines[i].trim() === CONNECT_ENV_BEGIN) {
156
+ inside = true;
157
+ continue;
158
+ }
159
+ if (lines[i].trim() === CONNECT_ENV_END) {
160
+ inside = false;
161
+ continue;
162
+ }
163
+ if (inside || !pattern.test(lines[i]))
164
+ continue;
165
+ lines[i] = line;
166
+ replaced = true;
167
+ break;
168
+ }
169
+ // Appended AFTER the block when it is new. A `.env` takes the last assignment, so a value the
170
+ // user set outranks the platform's placeholder wherever the block happens to sit.
171
+ if (!replaced) {
172
+ if (lines.length > 0 && lines[lines.length - 1] === '')
173
+ lines.pop();
174
+ lines.push(line);
175
+ }
176
+ }
177
+ // Nothing to say, nothing to write: rewriting the file for an empty call would rewrite its
178
+ // trailing whitespace and show up as a change nobody made.
179
+ if (written.length < 1)
180
+ return { written, file };
181
+ while (lines.length > 0 && lines[lines.length - 1] === '')
182
+ lines.pop();
183
+ const next = lines.length > 0 ? `${lines.join('\n')}\n` : '';
184
+ if (next !== existing) {
185
+ await fs.ensureDir(p.dirname(file));
186
+ await fs.writeFile(file, next);
187
+ }
188
+ return { written, file };
189
+ };
190
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/project/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAA;AACzB,OAAO,CAAC,MAAM,WAAW,CAAA;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAE5E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAkC3D,oEAAoE;AACpE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;YACrD,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAA;YACvB,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAA;QACtB,CAAC;QAED,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC,CAAA;AAED,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,OAAe,EAAoB,EAAE,CACtE,MAAM,IAAI,OAAO,CAAU,OAAO,CAAC,EAAE;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAA;IACrG,MAAM,MAAM,GAAG,CAAC,EAAW,EAAQ,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC,CAAC,CAAA;IACrD,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;IAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;IAClD,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAA;AACpE,CAAC,CAAC,CAAA;AAEJ,MAAM,OAAO,GAAG,KAAK,EAAE,GAAuB,EAAyB,EAAE;IACvE,MAAM,UAAU,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,kCAAkC,CAAC,CAAA;IAExG,OAAO;QACL,UAAU;QACV,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK;QACnD,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,GAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrD,CAAA;AACH,CAAC,CAAA;AAED,0FAA0F;AAC1F,MAAM,YAAY,GAAG,KAAK,EAAE,GAAW,EAAoB,EAAE;IAC3D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAEpF,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AACxG,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,GAAW,EAAwB,EAAE;IACzE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;IACrC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1B,YAAY,CAAC,KAAK,CAAC;QACnB,YAAY,CAAC,KAAK,CAAC;QACnB,YAAY,CAAC,QAAQ,CAAC;QACtB,YAAY,CAAC,GAAG,CAAC;KAClB,CAAC,CAAA;IAEF,OAAO;QACL,GAAG;QACH,WAAW,EAAE,SAAS,CAAC,GAAG,CAAC;QAC3B,QAAQ;QACR,KAAK;QACL,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE;QAC3B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,UAAU;KACX,CAAA;AACH,CAAC,CAAA;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAmB,EAA+B,EAAE,CAAC;IACnF,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAmB,CAAC,CAAC;IAC3D,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;CAC7E,CAAA;AAED,MAAM,cAAc,GAAG,CAAC,IAAY,EAAU,EAAE,CAC9C,0FAA0F;MACxF,6BAA6B,IAAI,0BAA0B,CAAA;AAE/D,MAAM,YAAY,GAAG,CAAC,IAAY,EAAU,EAAE,CAC5C,yCAAyC,IAAI,8BAA8B,CAAA;AAE7E,MAAM,aAAa,GAAG,CAAC,QAAyB,EAAY,EAAE,CAC5D,QAAQ,KAAK,QAAQ;IACnB,CAAC,CAAC,CAAC,mEAAmE;QACnE,qDAAqD,CAAC;IACzD,CAAC,CAAC,QAAQ,KAAK,OAAO;QACpB,CAAC,CAAC,CAAC,wCAAwC;YACxC,gEAAgE,CAAC;QACpE,CAAC,CAAC,CAAC,8DAA8D;YAC9D,uDAAuD,CAAC,CAAA;AAEjE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAmB,EAAU,EAAE;IAC9D,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,KAAK,GAAa,CAAC,YAAY,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAA;IAEtD,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAA;IACzC,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,qCAAqC,EAAE,CAAC,CAAA;IAC5F,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,wCAAwC,EAAE,CAAC,CAAA;IAC/F,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,QAAQ,CAAC,SAAS;QAChD,CAAC,CAAC,SAAS,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,YAAY,EAAE;QACrD,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU;YAC1B,CAAC,CAAC,kCAAkC,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,EAAE;YACpE,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACzB,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,WAAW;QACzC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS;YACtB,CAAC,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,YAAY,EAAE;YAClD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,gBAAgB;QAC/E,CAAC,CAAC,oDAAoD,EAAE,CAAC,CAAA;IAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEd,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAA;QAEjG,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAA;IAC/G,KAAK,CAAC,IAAI,CACR,2CAA2C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAC7F,uFAAuF,EACvF,EAAE,EACF,6FAA6F,EAC7F,2DAA2D,EAC3D,yFAAyF,EACzF,6FAA6F,EAC7F,4DAA4D,EAC5D,EAAE,EACF,gGAAgG,EAChG,+CAA+C,EAC/C,EAAE,CACH,CAAA;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;QAC9C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;YAC5B,CAAC,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,EAAE;YAC7B,CAAC,CAAC,8EAA8E,CAAC,CAAA;QACnF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACvE,KAAK,CAAC,IAAI,CACR,kCAAkC,EAClC,oDAAoD,EACpD,EAAE,EACF,iCAAiC,EACjC,0FAA0F,EAC1F,sEAAsE,EACtE,iEAAiE,EACjE,gFAAgF,EAChF,EAAE,CACH,CAAA;IACH,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAA;QAC/D,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM;YAC5B,CAAC,CAAC,KAAK,YAAY,CAAC,IAAI,CAAC,EAAE;YAC3B,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACtC,KAAK,CAAC,IAAI,CACR,gCAAgC,EAChC,0FAA0F,EAC1F,EAAE,CACH,CAAA;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CACR,8BAA8B,EAC9B,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,8CAA8C,EACzF,2FAA2F,EAC3F,4FAA4F,EAC5F,aAAa,EACb,MAAM,CAAC,UAAU;QACf,CAAC,CAAC,oFAAoF;QACtF,CAAC,CAAC,yFAAyF,CAC9F,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,GAAW,EAAE,MAA8B,EACG,EAAE;IAChD,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IACjE,MAAM,KAAK,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACzD,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAQ;QACjC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEjB,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;QAC9B,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,uBAAuB,GAAG,OAAO,CAAC,CAAA;QAE7D,uFAAuF;QACvF,2FAA2F;QAC3F,uBAAuB;QACvB,IAAI,MAAM,GAAG,KAAK,CAAA;QAClB,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;gBAAC,MAAM,GAAG,IAAI,CAAC;gBAAC,SAAQ;YAAC,CAAC;YACtE,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,eAAe,EAAE,CAAC;gBAAC,MAAM,GAAG,KAAK,CAAC;gBAAC,SAAQ;YAAC,CAAC;YACrE,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAAE,SAAQ;YAC/C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;YACf,QAAQ,GAAG,IAAI,CAAA;YACf,MAAK;QACP,CAAC;QAED,8FAA8F;QAC9F,kFAAkF;QAClF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE;gBAAE,KAAK,CAAC,GAAG,EAAE,CAAA;YACnE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClB,CAAC;IACH,CAAC;IAED,2FAA2F;IAC3F,2DAA2D;IAC3D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAEhD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,CAAA;IACtE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IAC5D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QACnC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA"}
@@ -0,0 +1,75 @@
1
+ import type { LocalRunRecord } from './state.js';
2
+ export * from './state.js';
3
+ export * from './serve.js';
4
+ export interface RunLocalOptions {
5
+ /** Build before starting. On by default — starting a stale `dist/` is the confusing failure. */
6
+ build?: boolean;
7
+ log?: (line: string) => void;
8
+ }
9
+ export interface RunLocalResult {
10
+ record: LocalRunRecord | null;
11
+ /**
12
+ * The ports the run uses, so a caller can print an address without composing one.
13
+ *
14
+ * Stated even when a process did not come up: they are where the app WILL answer, and whether
15
+ * it does yet is what `error`, `buildError` and {@link localStatus} are for.
16
+ */
17
+ api: number;
18
+ web: number;
19
+ worker?: number;
20
+ /** The reason nothing was started, or the tree was refused. */
21
+ error?: string;
22
+ /** A build ran and failed. The web server still comes up so the state is visible. */
23
+ buildError?: string;
24
+ }
25
+ /** One supervised process, as a status read finds it. */
26
+ export interface LocalProcessStatus {
27
+ pid: number;
28
+ port: number;
29
+ alive: boolean;
30
+ }
31
+ export interface LocalRunStatus {
32
+ running: boolean;
33
+ startedAt?: string;
34
+ /** Ports, flat, for a caller composing an address or a one-line report. */
35
+ api?: number;
36
+ worker?: number;
37
+ web?: number;
38
+ /** The target's own boot phase, when it got far enough to report one. */
39
+ phase?: string;
40
+ /** Whether the target says it is serving — never the same question as "the pid is alive". */
41
+ ready?: boolean;
42
+ /** Why it is not ready. Absent when it is. */
43
+ reason?: string;
44
+ processes: {
45
+ api?: LocalProcessStatus;
46
+ worker?: LocalProcessStatus;
47
+ web?: LocalProcessStatus;
48
+ };
49
+ }
50
+ /**
51
+ * Build the target, start its processes, serve its browser app.
52
+ *
53
+ * A previous run is stopped first: both halves bind fixed ports, and a replacement that races the
54
+ * process it is replacing loses the bind and exits — with nothing announcing it, since a failed
55
+ * `listen` says nothing on its own.
56
+ */
57
+ export declare const runLocal: (dir: string, options?: RunLocalOptions) => Promise<RunLocalResult>;
58
+ /**
59
+ * Stop whatever the run record names, and forget it.
60
+ *
61
+ * Safe to call when nothing is running: the record is the only authority, and a stale one names
62
+ * pids that are simply not alive. The children are detached, so they outlive the process that
63
+ * started them — which is exactly why the record is a file and this is the only way to end them.
64
+ */
65
+ export declare const stopLocal: (dir: string) => Promise<void>;
66
+ /**
67
+ * What is running, and whether the target is actually serving.
68
+ *
69
+ * Two independent facts, because either one alone lies. A live pid says nothing about a target
70
+ * that bound its port and then failed to reach its database — it answers `200` with `db.ok:false`
71
+ * and would otherwise read as healthy. A health answer says nothing about a process that died
72
+ * while something else holds the port.
73
+ */
74
+ export declare const localStatus: (dir: string) => Promise<LocalRunStatus>;
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/run/index.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAgB1B,MAAM,WAAW,eAAe;IAC9B,gGAAgG;IAChG,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAA;IAC7B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6FAA6F;IAC7F,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE;QACT,GAAG,CAAC,EAAE,kBAAkB,CAAA;QACxB,MAAM,CAAC,EAAE,kBAAkB,CAAA;QAC3B,GAAG,CAAC,EAAE,kBAAkB,CAAA;KACzB,CAAA;CACF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,QACd,MAAM,YAAW,eAAe,KACpC,OAAO,CAAC,cAAc,CA4DxB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,QAAe,MAAM,KAAG,OAAO,CAAC,IAAI,CAezD,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,QAAe,MAAM,KAAG,OAAO,CAAC,cAAc,CAyCrE,CAAA"}
@@ -0,0 +1,150 @@
1
+ import fs from 'fs-extra';
2
+ import p from 'node:path';
3
+ import { SlotCommandType, SlotShellCommand, TARGET_API_PORT, TARGET_WEB_PORT, TARGET_WORKER_PORT } from '@owlmeans/viable-common';
4
+ import { makeLocalSlotExecutor } from '../executor/index.js';
5
+ import { readTargetHealth } from '../executor/health.js';
6
+ import { integrityRefusal } from '../executor/integrity.js';
7
+ import { apiPath, webPath } from '../executor/layout.js';
8
+ import { isAlive } from '../executor/spawn.js';
9
+ import { createLocalServer } from './serve.js';
10
+ import { apiRunning, clearRun, readRun, startApi, startWorker, stopProcess, writeRun } from './state.js';
11
+ export * from './state.js';
12
+ export * from './serve.js';
13
+ /**
14
+ * Run the generated application on the developer's own machine.
15
+ *
16
+ * The slot's equivalent is a pod: a publisher supervising two children and a static server, with
17
+ * Kubernetes underneath. None of that exists here, and the parts that were about the pod are
18
+ * deliberately not reproduced — there is no respawn ladder, no reconciler and no health route,
19
+ * because a developer watching their own terminal is the supervisor. What IS reproduced is the
20
+ * shape: the same ports, the same argv marker, the same environment, and the same "port ownership
21
+ * is the only proof" rule about restarts.
22
+ */
23
+ /** Servers this process is holding, so a stop can close what a run opened. */
24
+ const _servers = new Map();
25
+ /**
26
+ * Build the target, start its processes, serve its browser app.
27
+ *
28
+ * A previous run is stopped first: both halves bind fixed ports, and a replacement that races the
29
+ * process it is replacing loses the bind and exits — with nothing announcing it, since a failed
30
+ * `listen` says nothing on its own.
31
+ */
32
+ export const runLocal = async (dir, options = {}) => {
33
+ const root = p.resolve(dir);
34
+ const log = options.log ?? (() => undefined);
35
+ const ports = { api: TARGET_API_PORT, web: TARGET_WEB_PORT };
36
+ const refused = await integrityRefusal(root, 'Refused to run');
37
+ if (refused != null) {
38
+ return { record: null, ...ports, error: refused };
39
+ }
40
+ await stopLocal(root);
41
+ let buildError;
42
+ if (options.build !== false) {
43
+ log('Building the target project…');
44
+ const result = await makeLocalSlotExecutor(root, { log }).execute({
45
+ type: SlotCommandType.Shell, command: SlotShellCommand.Build,
46
+ });
47
+ if (result.result != null) {
48
+ buildError = result.result;
49
+ }
50
+ }
51
+ const record = { dir: root, startedAt: new Date().toISOString() };
52
+ // Only when the last build left something to spawn. Starting `bun dist/index.js` against a
53
+ // missing bundle fails with a message about a file rather than about the build that never
54
+ // produced it.
55
+ if (await fs.pathExists(p.resolve(apiPath(root), 'dist', 'index.js'))) {
56
+ record.api = await startApi(root);
57
+ log(`Target api started on port ${record.api.port}`);
58
+ }
59
+ else {
60
+ buildError ??= 'The target api has no build output — nothing was started.';
61
+ }
62
+ const worker = await startWorker(root);
63
+ if (worker != null) {
64
+ record.worker = worker;
65
+ log(`Target worker started on port ${worker.port}`);
66
+ }
67
+ const server = createLocalServer(() => p.resolve(webPath(root), 'dist'), { apiPort: TARGET_API_PORT });
68
+ await server.listen(TARGET_WEB_PORT);
69
+ _servers.set(root, server);
70
+ // The web server is this process, not a child: whoever called `runLocal` is holding it, and its
71
+ // pid is what a later `localStatus` from another process checks for liveness.
72
+ record.web = { pid: process.pid, port: TARGET_WEB_PORT };
73
+ log(`Target app served at http://localhost:${TARGET_WEB_PORT}`);
74
+ await writeRun(root, record);
75
+ return {
76
+ record,
77
+ ...ports,
78
+ ...(worker != null ? { worker: worker.port } : {}),
79
+ ...(buildError != null ? { buildError } : {}),
80
+ };
81
+ };
82
+ /**
83
+ * Stop whatever the run record names, and forget it.
84
+ *
85
+ * Safe to call when nothing is running: the record is the only authority, and a stale one names
86
+ * pids that are simply not alive. The children are detached, so they outlive the process that
87
+ * started them — which is exactly why the record is a file and this is the only way to end them.
88
+ */
89
+ export const stopLocal = async (dir) => {
90
+ const root = p.resolve(dir);
91
+ const server = _servers.get(root);
92
+ if (server != null) {
93
+ _servers.delete(root);
94
+ await server.close();
95
+ }
96
+ const record = await readRun(root);
97
+ if (record == null)
98
+ return;
99
+ await stopProcess(record.worker);
100
+ await stopProcess(record.api);
101
+ await clearRun(root);
102
+ };
103
+ /**
104
+ * What is running, and whether the target is actually serving.
105
+ *
106
+ * Two independent facts, because either one alone lies. A live pid says nothing about a target
107
+ * that bound its port and then failed to reach its database — it answers `200` with `db.ok:false`
108
+ * and would otherwise read as healthy. A health answer says nothing about a process that died
109
+ * while something else holds the port.
110
+ */
111
+ export const localStatus = async (dir) => {
112
+ const root = p.resolve(dir);
113
+ const record = await readRun(root);
114
+ if (record == null) {
115
+ return { running: false, processes: {} };
116
+ }
117
+ const status = {
118
+ running: apiRunning(record),
119
+ startedAt: record.startedAt,
120
+ processes: {},
121
+ };
122
+ if (record.api != null) {
123
+ const port = record.api.port ?? TARGET_API_PORT;
124
+ const health = await readTargetHealth(port, record.api.bootId ?? '', 2_000);
125
+ status.api = port;
126
+ status.ready = health.ready;
127
+ if (health.phase != null)
128
+ status.phase = health.phase;
129
+ if (!health.ready)
130
+ status.reason = health.reason;
131
+ status.processes.api = {
132
+ pid: record.api.pid, port, alive: isAlive(record.api.pid),
133
+ };
134
+ }
135
+ if (record.worker != null) {
136
+ const port = record.worker.port ?? TARGET_WORKER_PORT;
137
+ status.worker = port;
138
+ status.processes.worker = {
139
+ pid: record.worker.pid, port, alive: isAlive(record.worker.pid),
140
+ };
141
+ }
142
+ if (record.web != null) {
143
+ status.web = record.web.port;
144
+ status.processes.web = {
145
+ pid: record.web.pid, port: record.web.port, alive: isAlive(record.web.pid),
146
+ };
147
+ }
148
+ return status;
149
+ };
150
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/run/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface LocalServer {
2
+ listen: (port?: number, host?: string) => Promise<void>;
3
+ close: () => Promise<void>;
4
+ port: number;
5
+ }
6
+ export interface LocalServerOptions {
7
+ /** Where the target's api answers. The proxy target, not something the browser ever sees. */
8
+ apiPort?: number;
9
+ }
10
+ /**
11
+ * The static server, reading its directory per request.
12
+ *
13
+ * A getter and not a path, for the same reason every layout question here is a function: a
14
+ * re-initialization can move the web package under a running server, and a snapshot taken at
15
+ * construction would keep serving a directory that no longer exists.
16
+ */
17
+ export declare const createLocalServer: (getWebDir: () => string, options?: LocalServerOptions) => LocalServer;
18
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/run/serve.ts"],"names":[],"mappings":"AAgDA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,cACjB,MAAM,MAAM,YAAW,kBAAkB,KACnD,WAiHF,CAAA"}
@@ -0,0 +1,148 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import http from 'node:http';
4
+ import path from 'node:path';
5
+ import { TARGET_API_BASE, TARGET_API_PORT, TARGET_WEB_PORT } from '@owlmeans/viable-common';
6
+ /**
7
+ * Serve the target's built browser app, and put its `/api` behind the same origin.
8
+ *
9
+ * The slot does this with an HTTPRoute in front of two processes; a local run has no edge, so the
10
+ * proxy is what replaces it. It is not a convenience: the app and its API sharing one origin is
11
+ * what the generated app's own configuration assumes, and splitting them across two loopback ports
12
+ * would put the target's CORS setup in the path of a developer's first page load.
13
+ *
14
+ * Everything else the publisher's static server does — the crawler policy, the CSP, the preview
15
+ * error reporter, the build-state header — is about a PUBLIC hostname on a domain every customer
16
+ * shares. A loopback address has no reputation to spend and no crawler to answer, so none of it is
17
+ * reproduced here.
18
+ */
19
+ const MIME = {
20
+ '.html': 'text/html; charset=utf-8',
21
+ '.js': 'application/javascript; charset=utf-8',
22
+ '.mjs': 'application/javascript; charset=utf-8',
23
+ '.css': 'text/css; charset=utf-8',
24
+ '.json': 'application/json; charset=utf-8',
25
+ '.map': 'application/json; charset=utf-8',
26
+ '.webmanifest': 'application/manifest+json',
27
+ '.svg': 'image/svg+xml',
28
+ '.png': 'image/png',
29
+ '.jpg': 'image/jpeg',
30
+ '.jpeg': 'image/jpeg',
31
+ '.gif': 'image/gif',
32
+ '.webp': 'image/webp',
33
+ '.ico': 'image/x-icon',
34
+ '.woff': 'font/woff',
35
+ '.woff2': 'font/woff2',
36
+ '.ttf': 'font/ttf',
37
+ '.txt': 'text/plain; charset=utf-8',
38
+ };
39
+ const PLACEHOLDER = '<!doctype html><html lang="en"><head><meta charset="UTF-8"/>'
40
+ + '<meta name="viewport" content="width=device-width, initial-scale=1.0"/>'
41
+ + '<title>Not built yet</title></head><body style="font-family:sans-serif">'
42
+ + '<p style="padding:2rem">This project has not been built yet. Run a build and reload.</p>'
43
+ + '</body></html>';
44
+ /**
45
+ * The static server, reading its directory per request.
46
+ *
47
+ * A getter and not a path, for the same reason every layout question here is a function: a
48
+ * re-initialization can move the web package under a running server, and a snapshot taken at
49
+ * construction would keep serving a directory that no longer exists.
50
+ */
51
+ export const createLocalServer = (getWebDir, options = {}) => {
52
+ const apiPort = options.apiPort ?? TARGET_API_PORT;
53
+ const apiPrefix = `/${TARGET_API_BASE}`;
54
+ let server = null;
55
+ let port = TARGET_WEB_PORT;
56
+ const proxy = (req, res) => {
57
+ const upstream = http.request({
58
+ host: '127.0.0.1',
59
+ port: apiPort,
60
+ method: req.method,
61
+ path: req.url,
62
+ headers: { ...req.headers, host: `127.0.0.1:${apiPort}` },
63
+ }, answer => {
64
+ res.writeHead(answer.statusCode ?? 502, answer.headers);
65
+ answer.pipe(res);
66
+ });
67
+ // The api is a supervised child, so it is legitimately absent between a stop and a start. Say
68
+ // which of the two processes failed — an unexplained 502 here reads as a bug in the app.
69
+ upstream.on('error', error => {
70
+ if (res.headersSent) {
71
+ return res.destroy();
72
+ }
73
+ res.writeHead(502, { 'Content-Type': 'application/json; charset=utf-8' });
74
+ res.end(JSON.stringify({
75
+ error: `The target backend is not answering on port ${apiPort}: ${String(error)}`,
76
+ }));
77
+ });
78
+ req.pipe(upstream);
79
+ };
80
+ const serveFile = async (res, file, status = 200) => {
81
+ const body = await fsp.readFile(file).catch(() => null);
82
+ if (body == null)
83
+ return false;
84
+ res.writeHead(status, {
85
+ 'Content-Type': MIME[path.extname(file).toLowerCase()] ?? 'application/octet-stream',
86
+ // A local build is rewritten constantly and served under stable, unhashed names
87
+ // (`/bundle.js`), so a cached copy is always the previous build wearing the current URL.
88
+ 'Cache-Control': 'no-store',
89
+ 'Content-Length': body.byteLength,
90
+ });
91
+ res.end(body);
92
+ return true;
93
+ };
94
+ const handle = async (req, res) => {
95
+ const url = req.url ?? '/';
96
+ if (url === apiPrefix || url.startsWith(`${apiPrefix}/`)) {
97
+ return proxy(req, res);
98
+ }
99
+ const dir = getWebDir();
100
+ const pathname = decodeURIComponent(url.split('?')[0]);
101
+ const resolved = path.resolve(dir, `.${path.posix.normalize(pathname)}`);
102
+ // Confinement, exactly as on the file side: a resolved path is the only one that can be
103
+ // compared to the root, and this server answers requests from a browser.
104
+ if (resolved !== dir && !resolved.startsWith(`${dir}${path.sep}`)) {
105
+ res.writeHead(403);
106
+ res.end();
107
+ return;
108
+ }
109
+ if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {
110
+ if (await serveFile(res, resolved))
111
+ return;
112
+ }
113
+ // Client-side routing: any path the build did not emit a file for is a route inside the app.
114
+ const index = path.join(dir, 'index.html');
115
+ if (await serveFile(res, index))
116
+ return;
117
+ res.writeHead(200, { 'Content-Type': MIME['.html'], 'Cache-Control': 'no-store' });
118
+ res.end(PLACEHOLDER);
119
+ };
120
+ return {
121
+ listen: async (listenPort = TARGET_WEB_PORT, host = '127.0.0.1') => {
122
+ port = listenPort;
123
+ server = http.createServer((req, res) => {
124
+ void handle(req, res).catch(error => {
125
+ if (res.headersSent) {
126
+ res.destroy();
127
+ return;
128
+ }
129
+ res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
130
+ res.end(String(error));
131
+ });
132
+ });
133
+ await new Promise((resolve, reject) => {
134
+ server?.once('error', reject);
135
+ server?.listen(listenPort, host, () => resolve());
136
+ });
137
+ },
138
+ close: async () => {
139
+ const current = server;
140
+ server = null;
141
+ if (current == null)
142
+ return;
143
+ await new Promise(resolve => current.close(() => resolve()));
144
+ },
145
+ get port() { return port; },
146
+ };
147
+ };
148
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/run/serve.ts"],"names":[],"mappings":"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"}