local-serverless-stack 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/CHANGELOG.md +125 -3
  2. package/README.md +92 -38
  3. package/bin/cli.js +218 -28
  4. package/bin/i18n.js +57 -3
  5. package/dist/client/index.d.ts +1 -1
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/namespaces/services.d.ts +46 -0
  8. package/dist/client/namespaces/services.d.ts.map +1 -1
  9. package/dist/client/namespaces/services.js +1 -0
  10. package/dist/client/namespaces/services.js.map +1 -1
  11. package/dist/mcp/tools.d.ts.map +1 -1
  12. package/dist/mcp/tools.js +17 -0
  13. package/dist/mcp/tools.js.map +1 -1
  14. package/dist/server/engine/backends/self-backend.d.ts +26 -1
  15. package/dist/server/engine/backends/self-backend.d.ts.map +1 -1
  16. package/dist/server/engine/backends/self-backend.js +104 -11
  17. package/dist/server/engine/backends/self-backend.js.map +1 -1
  18. package/dist/server/engine/emulators/dynamodb/expressions/parser.d.ts.map +1 -1
  19. package/dist/server/engine/emulators/dynamodb/expressions/parser.js +12 -1
  20. package/dist/server/engine/emulators/dynamodb/expressions/parser.js.map +1 -1
  21. package/dist/server/engine/emulators/dynamodb/expressions/reserved-words.d.ts +3 -0
  22. package/dist/server/engine/emulators/dynamodb/expressions/reserved-words.d.ts.map +1 -0
  23. package/dist/server/engine/emulators/dynamodb/expressions/reserved-words.js +78 -0
  24. package/dist/server/engine/emulators/dynamodb/expressions/reserved-words.js.map +1 -0
  25. package/dist/server/engine/emulators/dynamodb/index.d.ts +11 -0
  26. package/dist/server/engine/emulators/dynamodb/index.d.ts.map +1 -1
  27. package/dist/server/engine/emulators/dynamodb/index.js +101 -7
  28. package/dist/server/engine/emulators/dynamodb/index.js.map +1 -1
  29. package/dist/server/engine/emulators/dynamodb/query.d.ts +1 -0
  30. package/dist/server/engine/emulators/dynamodb/query.d.ts.map +1 -1
  31. package/dist/server/engine/emulators/dynamodb/query.js +34 -6
  32. package/dist/server/engine/emulators/dynamodb/query.js.map +1 -1
  33. package/dist/server/engine/emulators/s3/index.d.ts +10 -0
  34. package/dist/server/engine/emulators/s3/index.d.ts.map +1 -1
  35. package/dist/server/engine/emulators/s3/index.js +369 -25
  36. package/dist/server/engine/emulators/s3/index.js.map +1 -1
  37. package/dist/server/engine/emulators/sqs/index.d.ts +2 -0
  38. package/dist/server/engine/emulators/sqs/index.d.ts.map +1 -1
  39. package/dist/server/engine/emulators/sqs/index.js +70 -7
  40. package/dist/server/engine/emulators/sqs/index.js.map +1 -1
  41. package/dist/server/engine/emulators/sqs/queue.d.ts +2 -1
  42. package/dist/server/engine/emulators/sqs/queue.d.ts.map +1 -1
  43. package/dist/server/engine/emulators/sqs/queue.js +68 -35
  44. package/dist/server/engine/emulators/sqs/queue.js.map +1 -1
  45. package/dist/server/engine/emulators/ssm/index.d.ts +31 -0
  46. package/dist/server/engine/emulators/ssm/index.d.ts.map +1 -0
  47. package/dist/server/engine/emulators/ssm/index.js +670 -0
  48. package/dist/server/engine/emulators/ssm/index.js.map +1 -0
  49. package/dist/server/engine/http/errors.d.ts +1 -0
  50. package/dist/server/engine/http/errors.d.ts.map +1 -1
  51. package/dist/server/engine/http/errors.js +13 -5
  52. package/dist/server/engine/http/errors.js.map +1 -1
  53. package/dist/server/engine/http/is-aws-request.d.ts.map +1 -1
  54. package/dist/server/engine/http/is-aws-request.js +13 -3
  55. package/dist/server/engine/http/is-aws-request.js.map +1 -1
  56. package/dist/server/engine/http/protocols/aws-json.d.ts +1 -0
  57. package/dist/server/engine/http/protocols/aws-json.d.ts.map +1 -1
  58. package/dist/server/engine/http/protocols/aws-json.js +13 -0
  59. package/dist/server/engine/http/protocols/aws-json.js.map +1 -1
  60. package/dist/server/engine/http/reset.d.ts +4 -0
  61. package/dist/server/engine/http/reset.d.ts.map +1 -0
  62. package/dist/server/engine/http/reset.js +17 -0
  63. package/dist/server/engine/http/reset.js.map +1 -0
  64. package/dist/server/engine/http/router.d.ts +1 -0
  65. package/dist/server/engine/http/router.d.ts.map +1 -1
  66. package/dist/server/engine/http/router.js +185 -34
  67. package/dist/server/engine/http/router.js.map +1 -1
  68. package/dist/server/engine/types.d.ts +2 -1
  69. package/dist/server/engine/types.d.ts.map +1 -1
  70. package/dist/server/routes/apis.d.ts.map +1 -1
  71. package/dist/server/routes/apis.js +6 -0
  72. package/dist/server/routes/apis.js.map +1 -1
  73. package/dist/server/routes/config.d.ts.map +1 -1
  74. package/dist/server/routes/config.js +25 -1
  75. package/dist/server/routes/config.js.map +1 -1
  76. package/dist/server/routes/lambdas.d.ts.map +1 -1
  77. package/dist/server/routes/lambdas.js +12 -0
  78. package/dist/server/routes/lambdas.js.map +1 -1
  79. package/dist/server/routes/services.d.ts.map +1 -1
  80. package/dist/server/routes/services.js +66 -5
  81. package/dist/server/routes/services.js.map +1 -1
  82. package/dist/server/runtime/runtime-worker.js +40 -3
  83. package/dist/server/runtime/runtime-worker.js.map +1 -1
  84. package/dist/server/services/api-gateway-events.d.ts +14 -0
  85. package/dist/server/services/api-gateway-events.d.ts.map +1 -1
  86. package/dist/server/services/api-gateway-events.js +114 -8
  87. package/dist/server/services/api-gateway-events.js.map +1 -1
  88. package/dist/server/services/artifact-staleness.d.ts +20 -0
  89. package/dist/server/services/artifact-staleness.d.ts.map +1 -0
  90. package/dist/server/services/artifact-staleness.js +115 -0
  91. package/dist/server/services/artifact-staleness.js.map +1 -0
  92. package/dist/server/services/authorizer-service.d.ts +34 -5
  93. package/dist/server/services/authorizer-service.d.ts.map +1 -1
  94. package/dist/server/services/authorizer-service.js +0 -0
  95. package/dist/server/services/authorizer-service.js.map +1 -1
  96. package/dist/server/services/cache-manager.d.ts +5 -0
  97. package/dist/server/services/cache-manager.d.ts.map +1 -1
  98. package/dist/server/services/cache-manager.js.map +1 -1
  99. package/dist/server/services/cfn-intrinsics.d.ts +63 -0
  100. package/dist/server/services/cfn-intrinsics.d.ts.map +1 -0
  101. package/dist/server/services/cfn-intrinsics.js +403 -0
  102. package/dist/server/services/cfn-intrinsics.js.map +1 -0
  103. package/dist/server/services/cfn-physical-values.d.ts +51 -0
  104. package/dist/server/services/cfn-physical-values.d.ts.map +1 -0
  105. package/dist/server/services/cfn-physical-values.js +186 -0
  106. package/dist/server/services/cfn-physical-values.js.map +1 -0
  107. package/dist/server/services/cloudformation-parser.d.ts +26 -3
  108. package/dist/server/services/cloudformation-parser.d.ts.map +1 -1
  109. package/dist/server/services/cloudformation-parser.js +79 -7
  110. package/dist/server/services/cloudformation-parser.js.map +1 -1
  111. package/dist/server/services/config-manager.d.ts +49 -2
  112. package/dist/server/services/config-manager.d.ts.map +1 -1
  113. package/dist/server/services/config-manager.js +206 -24
  114. package/dist/server/services/config-manager.js.map +1 -1
  115. package/dist/server/services/gateway-manager.d.ts.map +1 -1
  116. package/dist/server/services/gateway-manager.js +52 -20
  117. package/dist/server/services/gateway-manager.js.map +1 -1
  118. package/dist/server/services/lambda-runtime-manager.d.ts +2 -0
  119. package/dist/server/services/lambda-runtime-manager.d.ts.map +1 -1
  120. package/dist/server/services/lambda-runtime-manager.js +49 -15
  121. package/dist/server/services/lambda-runtime-manager.js.map +1 -1
  122. package/dist/server/services/raw-api-assembler.d.ts +1 -1
  123. package/dist/server/services/raw-api-assembler.d.ts.map +1 -1
  124. package/dist/server/services/raw-api-assembler.js +18 -8
  125. package/dist/server/services/raw-api-assembler.js.map +1 -1
  126. package/dist/server/services/resource-provisioner.d.ts +31 -1
  127. package/dist/server/services/resource-provisioner.d.ts.map +1 -1
  128. package/dist/server/services/resource-provisioner.js +257 -25
  129. package/dist/server/services/resource-provisioner.js.map +1 -1
  130. package/dist/server/services/s3-explorer.js +1 -1
  131. package/dist/server/services/seed-manager.d.ts.map +1 -1
  132. package/dist/server/services/seed-manager.js +11 -6
  133. package/dist/server/services/seed-manager.js.map +1 -1
  134. package/dist/server/services/serverless-state-parser.d.ts +45 -2
  135. package/dist/server/services/serverless-state-parser.d.ts.map +1 -1
  136. package/dist/server/services/serverless-state-parser.js +217 -29
  137. package/dist/server/services/serverless-state-parser.js.map +1 -1
  138. package/dist/server/services/service-graph.d.ts +76 -0
  139. package/dist/server/services/service-graph.d.ts.map +1 -0
  140. package/dist/server/services/service-graph.js +914 -0
  141. package/dist/server/services/service-graph.js.map +1 -0
  142. package/dist/server/services/service-registrar.d.ts +17 -1
  143. package/dist/server/services/service-registrar.d.ts.map +1 -1
  144. package/dist/server/services/service-registrar.js +190 -66
  145. package/dist/server/services/service-registrar.js.map +1 -1
  146. package/dist/server/services/service-scanner.d.ts +7 -2
  147. package/dist/server/services/service-scanner.d.ts.map +1 -1
  148. package/dist/server/services/service-scanner.js +53 -7
  149. package/dist/server/services/service-scanner.js.map +1 -1
  150. package/dist/server/services/source-watcher.d.ts +1 -0
  151. package/dist/server/services/source-watcher.d.ts.map +1 -1
  152. package/dist/server/services/source-watcher.js +19 -1
  153. package/dist/server/services/source-watcher.js.map +1 -1
  154. package/dist/server/services/worker-runtime.d.ts +82 -0
  155. package/dist/server/services/worker-runtime.d.ts.map +1 -0
  156. package/dist/server/services/worker-runtime.js +303 -0
  157. package/dist/server/services/worker-runtime.js.map +1 -0
  158. package/dist/ui/assets/ApisPage-BkW-QZ0X.js +1 -0
  159. package/dist/ui/assets/BucketDetailPage-CJwTQJh7.js +1 -0
  160. package/dist/ui/assets/BucketsPage-B1xPVhZC.js +1 -0
  161. package/dist/ui/assets/DynamoPage-DvbpwZDc.js +1 -0
  162. package/dist/ui/assets/DynamoTablePage-xn3Lq3so.js +3 -0
  163. package/dist/ui/assets/LambdaDetailPage-BhUyO77H.js +4 -0
  164. package/dist/ui/assets/LambdasPage-CUF_4rOH.js +1 -0
  165. package/dist/ui/assets/OnboardingPage-U49THUzu.js +1 -0
  166. package/dist/ui/assets/OpenSearchCollectionPage-BJTVNCq4.js +1 -0
  167. package/dist/ui/assets/OpenSearchPage-4s-egkB9.js +1 -0
  168. package/dist/ui/assets/OverviewPage-DJeJQktG.js +1 -0
  169. package/dist/ui/assets/{OverviewPage-C1pBJ8aP.css → OverviewPage-DjQETmb_.css} +1 -1
  170. package/dist/ui/assets/QueueDetailPage-DWsy5Yt8.js +1 -0
  171. package/dist/ui/assets/QueuesPage-BZJSqman.js +1 -0
  172. package/dist/ui/assets/SecretsPage-YC9ua54G.js +1 -0
  173. package/dist/ui/assets/ServiceDetailPage-Cum5frP4.js +2 -0
  174. package/dist/ui/assets/ServicesPage-IpaPDuqx.js +2 -0
  175. package/dist/ui/assets/SettingsPage-BiCRU8O6.js +1 -0
  176. package/dist/ui/assets/index-BDlrAJhi.css +1 -0
  177. package/dist/ui/assets/index-BouKSS43.js +38 -0
  178. package/dist/ui/assets/resourceIcons-DZs28nbs.js +1 -0
  179. package/dist/ui/index.html +5 -2
  180. package/package.json +1 -1
  181. package/dist/ui/assets/ApisPage-BOuimwWz.js +0 -1
  182. package/dist/ui/assets/BucketDetailPage-vLYwhoQt.js +0 -1
  183. package/dist/ui/assets/BucketsPage-Cu5lU1Hp.js +0 -1
  184. package/dist/ui/assets/DynamoPage-y3TTEPUL.js +0 -1
  185. package/dist/ui/assets/DynamoTablePage-BnmSRZjc.js +0 -3
  186. package/dist/ui/assets/LambdaDetailPage-B5q_d27t.js +0 -4
  187. package/dist/ui/assets/LambdasPage-CJpRoEDx.js +0 -1
  188. package/dist/ui/assets/OnboardingPage-D02dymQi.js +0 -1
  189. package/dist/ui/assets/OpenSearchCollectionPage-B_wWTlfL.js +0 -1
  190. package/dist/ui/assets/OpenSearchPage-BLEgfF_g.js +0 -1
  191. package/dist/ui/assets/OverviewPage-EdJWiAt6.js +0 -1
  192. package/dist/ui/assets/QueueDetailPage-ByEYHvHe.js +0 -1
  193. package/dist/ui/assets/QueuesPage-CvwYHSll.js +0 -1
  194. package/dist/ui/assets/SecretsPage-DASw8Ntf.js +0 -1
  195. package/dist/ui/assets/ServiceDetailPage-BMsUDg6L.js +0 -2
  196. package/dist/ui/assets/ServicesPage-2JuiIiRr.js +0 -2
  197. package/dist/ui/assets/SettingsPage-DStO4bgN.js +0 -1
  198. package/dist/ui/assets/index-BIEznsi4.js +0 -37
  199. package/dist/ui/assets/index-BRMvNP3Y.css +0 -1
  200. package/dist/ui/assets/resourceIcons-baecADXA.js +0 -1
@@ -7,6 +7,7 @@ export interface SelfEngineConfig {
7
7
  memoryBudgetMb?: number;
8
8
  fsync?: boolean;
9
9
  fallbackEndpoint?: string | null;
10
+ fallbackEndpoints?: Record<string, string>;
10
11
  }
11
12
  export interface ResolvedSelfEngineConfig {
12
13
  port: number;
@@ -16,6 +17,7 @@ export interface ResolvedSelfEngineConfig {
16
17
  memoryBudgetMb: number;
17
18
  fsync: boolean;
18
19
  fallbackEndpoint: string | null;
20
+ fallbackEndpoints: Record<string, string>;
19
21
  persistence: boolean;
20
22
  region: string;
21
23
  }
@@ -24,12 +26,14 @@ export interface ServicePackageConfig {
24
26
  packageArgs?: string[];
25
27
  packageEnv?: Record<string, string>;
26
28
  packageTimeoutMs?: number;
29
+ packageCwd?: string;
27
30
  }
28
31
  export interface ResolvedPackageConfig {
29
32
  command: string;
30
33
  args: string[];
31
34
  env: Record<string, string>;
32
35
  timeoutMs: number;
36
+ cwd: string;
33
37
  }
34
38
  export type LambdaExecutionMode = 'auto' | 'artifact' | 'source';
35
39
  export interface LambdaRuntimeConfig {
@@ -48,6 +52,18 @@ export interface ServiceRuntimeConfig {
48
52
  invokePort?: number;
49
53
  execution?: LambdaExecutionMode;
50
54
  watch?: boolean;
55
+ env?: Record<string, string>;
56
+ execArgv?: string[];
57
+ functions?: Record<string, {
58
+ env?: Record<string, string>;
59
+ }>;
60
+ }
61
+ export interface ResolvedWorkerOverrides {
62
+ env: Record<string, string>;
63
+ execArgv: string[];
64
+ functions: Record<string, {
65
+ env?: Record<string, string>;
66
+ }>;
51
67
  }
52
68
  export interface ResolvedRuntimeConfig {
53
69
  enabled: boolean;
@@ -97,6 +113,7 @@ export interface LSSConfig {
97
113
  packageEnv?: Record<string, string>;
98
114
  servicePackaging?: Record<string, ServicePackageConfig>;
99
115
  packageTimeoutMs?: number;
116
+ scanIgnore?: string[];
100
117
  lambdaRuntime?: LambdaRuntimeConfig;
101
118
  serviceRuntime?: Record<string, ServiceRuntimeConfig>;
102
119
  selfEngine?: SelfEngineConfig;
@@ -144,6 +161,28 @@ export declare class ConfigManager {
144
161
  * as the global baseline.
145
162
  */
146
163
  getPackageConfigForService(servicePath: string): ResolvedPackageConfig;
164
+ /**
165
+ * Where a package command runs. `packageCwd` is resolved against the PROJECT
166
+ * ROOT (not the service dir — the point of the key is to reach a directory
167
+ * above the service) and fenced to it with the same realpath comparison the
168
+ * /install and /package endpoints apply to `servicePath`: this value is the
169
+ * cwd of a spawned child, so an unfenced `../..` would walk the packaging
170
+ * command out of the project entirely. A rejected value falls back to the
171
+ * service directory and says so, rather than silently packaging elsewhere.
172
+ */
173
+ private resolvePackageCwd;
174
+ /**
175
+ * Stacks the scan must not offer. A monorepo always holds a few that are
176
+ * never local by decision — a bootstrap stack deployed once per AWS account,
177
+ * a us-east-1 DNS/certificate stack the engine does not emulate — and they
178
+ * came back as pending work, with warnings, on every single scan. Permanent
179
+ * noise is how a real warning goes unread.
180
+ *
181
+ * Matched with the same key spellings as `servicePackaging`/`serviceRuntime`
182
+ * (path relative to the config file's directory or to the project root, or
183
+ * the directory basename), so one convention covers all three.
184
+ */
185
+ isScanIgnored(servicePath: string): boolean;
147
186
  /**
148
187
  * The keys a `servicePackaging`/`serviceRuntime` entry may be written under,
149
188
  * most specific first: the path relative to the config file's directory, the
@@ -174,13 +213,21 @@ export declare class ConfigManager {
174
213
  * (true for source, false for artifact).
175
214
  */
176
215
  getRuntimeConfigForService(servicePath: string): ResolvedRuntimeConfig;
216
+ /**
217
+ * The worker-process overrides for one service: `serviceRuntime.<svc>.env`,
218
+ * `.execArgv` and `.functions`. Looked up with the same key spellings as the
219
+ * rest of the entry. Never throws on a malformed hand-edited value — a
220
+ * non-object env or non-array execArgv reads as absent.
221
+ */
222
+ getWorkerOverridesForService(servicePath: string): ResolvedWorkerOverrides;
177
223
  getSelfEngineConfig(): ResolvedSelfEngineConfig;
178
224
  private homeStateDir;
179
225
  getEngineEndpoint(): string;
226
+ private getSelfEnginePort;
180
227
  /**
181
228
  * True when the orchestrator and the engine share one listener — the default,
182
- * since both ports default to the same value. Give them different values to
183
- * go back to two listeners.
229
+ * since the engine port follows serverPort. Give `selfEngine.port` a
230
+ * different value to go back to two listeners.
184
231
  */
185
232
  isSingleListener(): boolean;
186
233
  getConfigPath(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/server/services/config-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAazD,MAAM,WAAW,gBAAgB;IAG/B,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAGhB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAGD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAOD,MAAM,WAAW,oBAAoB;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAElC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAGhC,KAAK,CAAC,EAAE,OAAO,CAAC;IAGhB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAI1B,UAAU,CAAC,EAAE,MAAM,CAAC;IAMpB,IAAI,CAAC,EAAE,OAAO,CAAC;IAKf,aAAa,CAAC,EAAE,MAAM,CAAC;IAMvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAGD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,cAAc;IAE7B,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAIlB,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAIhC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC,WAAW,CAAC,EAAE;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;CACH;AAID,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACH;AAUD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAAC;AAEnD,MAAM,WAAW,SAAS;IAExB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAIhB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAQlB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAK1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAIlB,WAAW,CAAC,EAAE,OAAO,CAAC;IAUtB,cAAc,CAAC,EAAE,MAAM,CAAC;IAOxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAKvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAOpC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAGxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAGpC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAGtD,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAG9B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AA0gBD,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE;CAK9B;AAED,MAAM,WAAW,kBAAkB;IAEjC,IAAI,EAAE,MAAM,CAAC;IAGb,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAG5D,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAgB;IACvC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAc;IAEhC,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC,OAAO,CAAC,UAAU;IAqClB,OAAO,CAAC,WAAW;IA6EnB,SAAS,IAAI,SAAS;IAItB,aAAa,IAAI,MAAM;IAKvB,gBAAgB,IAAI,MAAM;IAI1B,mBAAmB,IAAI,OAAO;IAQ9B,kBAAkB,IAAI,MAAM;IAI5B,SAAS,IAAI,MAAM;IAInB,aAAa,IAAI,OAAO;IAOxB,OAAO,IAAI,OAAO;IAOlB,WAAW,IAAI,MAAM;IAMrB,iBAAiB,IAAI,MAAM;IAK3B,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;IAIjD,WAAW,IAAI,MAAM,GAAG,SAAS;IAMjC,aAAa,IAAI,OAAO;IAIxB,iBAAiB,IAAI,MAAM;IAI3B,mBAAmB,IAAI,MAAM;IAI7B;;;;;;OAMG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB;IAUtE;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,kBAAkB;IAW1B,sBAAsB,IAAI,OAAO;IAIjC,sBAAsB,IAAI,mBAAmB;IAI7C,mBAAmB,IAAI,MAAM;IAO7B,mBAAmB,IAAI,OAAO;IAK9B,sBAAsB,IAAI,MAAM;IAShC,uBAAuB,IAAI,MAAM;IAOjC,aAAa,IAAI,MAAM;IASvB;;;;;;OAMG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB;IAWtE,mBAAmB,IAAI,wBAAwB;IAsB/C,OAAO,CAAC,YAAY;IAQpB,iBAAiB,IAAI,MAAM;IAI3B;;;;OAIG;IACH,gBAAgB,IAAI,OAAO;IAI3B,aAAa,IAAI,MAAM;IAIvB;;;;;;OAMG;IACH,cAAc,IAAI,MAAM;IAYxB;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI;IAY5D,OAAO,CAAC,mBAAmB;IAW3B,WAAW,IAAI,gBAAgB;IAmB/B,oBAAoB,IAAI,MAAM,EAAE;IAYhC,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,MAAM,CAAC,QAAQ;IASvB,OAAO,CAAC,cAAc;IAMtB;;;;;;OAMG;IACH,cAAc,IAAI,kBAAkB;IAsBpC;;;;;;OAMG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB;IAyGhE;;OAEG;IACH,YAAY,IAAI,IAAI;IAwCpB,kBAAkB,IAAI,MAAM;CAI7B"}
1
+ {"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/server/services/config-manager.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
4
  import { projectCacheSegment } from './project-scope.js';
5
+ import { execArgvError } from './worker-runtime.js';
5
6
  // Symlink-stable spelling of a directory (e.g. macOS /tmp -> /private/tmp),
6
7
  // so the project identity matches the plugin's symlink-resolved process.cwd().
7
8
  function realpathOrSelf(dir) {
@@ -226,9 +227,11 @@ const EDITABLE_CONFIG_KEYS = {
226
227
  packageArgs: { kind: 'packageArgs' },
227
228
  packageEnv: { kind: 'envRecord' },
228
229
  packageTimeoutMs: { kind: 'positiveInt' },
230
+ packageCwd: { kind: 'projectRelativePath' },
229
231
  },
230
232
  },
231
233
  packageTimeoutMs: { kind: 'positiveInt' },
234
+ scanIgnore: { kind: 'stringArray' },
232
235
  lambdaRuntime: {
233
236
  kind: 'object',
234
237
  subKeys: {
@@ -250,6 +253,16 @@ const EDITABLE_CONFIG_KEYS = {
250
253
  invokePort: { kind: 'port' },
251
254
  execution: { kind: 'string', enum: ['auto', 'artifact', 'source'] },
252
255
  watch: { kind: 'boolean' },
256
+ // Same fence as packageEnv: the map lands in a forked worker's
257
+ // environment, where NODE_OPTIONS & co. load code of the caller's choosing.
258
+ env: { kind: 'envRecord' },
259
+ execArgv: { kind: 'execArgv' },
260
+ functions: {
261
+ kind: 'object',
262
+ entrySubKeys: {
263
+ env: { kind: 'envRecord' },
264
+ },
265
+ },
253
266
  },
254
267
  },
255
268
  selfEngine: {
@@ -262,6 +275,7 @@ const EDITABLE_CONFIG_KEYS = {
262
275
  memoryBudgetMb: { kind: 'positiveInt' },
263
276
  fsync: { kind: 'boolean' },
264
277
  fallbackEndpoint: { kind: 'string' },
278
+ fallbackEndpoints: { kind: 'stringRecord' },
265
279
  },
266
280
  },
267
281
  branding: {
@@ -303,7 +317,7 @@ const CONFIG_ENV_OVERRIDES = {
303
317
  packageCommand: ['LSS_PACKAGE_COMMAND'],
304
318
  packageTimeoutMs: ['LSS_PACKAGE_TIMEOUT_MS'],
305
319
  lambdaRuntime: ['LSS_LAMBDA_RUNTIME', 'LSS_LAMBDA_EXECUTION', 'LSS_LAMBDA_WATCH', 'LSS_INVOKE_HOST'],
306
- selfEngine: ['LSS_ENGINE_PORT', 'LSS_ENGINE_DATA_DIR'],
320
+ selfEngine: ['LSS_ENGINE_PORT', 'LSS_ENGINE_DATA_DIR', 'LSS_FALLBACK_ENDPOINT'],
307
321
  };
308
322
  function isPlainObject(value) {
309
323
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -438,20 +452,53 @@ function validateValue(key, value, spec) {
438
452
  const index = value.findIndex(isBlockedPackageFlag);
439
453
  return index === -1 ? null : blockedFlagError(`${key}[${index}]`, value[index]);
440
454
  }
455
+ // A plain list of match patterns (scanIgnore) — never spawned, so the
456
+ // package-args screen above does not apply; it just has to be a list of
457
+ // non-empty strings so the matcher cannot be fed garbage.
458
+ case 'stringArray':
459
+ return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length > 0)
460
+ ? null
461
+ : `"${key}" must be an array of non-empty strings`;
462
+ // A directory INSIDE the project, written the way the config file spells
463
+ // every other path: relative, forward slashes. Rejected lexically here
464
+ // (absolute, or any `..` segment) and again by realpath when it is resolved
465
+ // — this one becomes the cwd of a spawned child, so "inside the project"
466
+ // has to hold for the path that is actually used, symlinks included.
467
+ case 'projectRelativePath': {
468
+ if (typeof value !== 'string' || value.length === 0) {
469
+ return `"${key}" must be a non-empty string`;
470
+ }
471
+ if (path.isAbsolute(value) || value.split(/[\\/]/).includes('..')) {
472
+ return `"${key}" must be a path inside the project root, written relative to it (no leading "/" and no "..")`;
473
+ }
474
+ return null;
475
+ }
476
+ // Handed to fork() as the worker's execArgv — an allowlist, because a
477
+ // single `--import`/`--require` is code execution. See execArgvError().
478
+ case 'execArgv':
479
+ return execArgvError(key, value);
441
480
  case 'stringRecord':
481
+ return isPlainObject(value) && Object.values(value).every(v => typeof v === 'string')
482
+ ? null
483
+ : `"${key}" must be an object of string values`;
484
+ // Written as a PATCH, one variable at a time (mergeEnvRecord), so `null` is
485
+ // a value with a meaning here — "remove this variable" — and not just the
486
+ // top-level "delete the block" spelling. Anything else that is not a string
487
+ // is still refused: the map ends up in a child process's environment.
442
488
  case 'envRecord': {
443
- if (!isPlainObject(value) || !Object.values(value).every(v => typeof v === 'string')) {
444
- return `"${key}" must be an object of string values`;
489
+ if (!isPlainObject(value) || !Object.values(value).every(v => typeof v === 'string' || v === null)) {
490
+ return `"${key}" must be an object of string values (null removes a key)`;
445
491
  }
446
492
  // An env map is merged into the packaging child's environment, where a
447
493
  // handful of names are loader/interpreter hooks: they run code in the
448
494
  // child whatever program the command grammar let through, which would
449
- // make that grammar decorative.
450
- if (spec.kind === 'envRecord') {
451
- for (const envKey of Object.keys(value)) {
452
- if (BLOCKED_PACKAGE_ENV_KEYS.has(envKey.toLowerCase())) {
453
- return `"${key}.${envKey}" cannot be set — it injects code into the packaging process`;
454
- }
495
+ // make that grammar decorative. Screened per variable being SET —
496
+ // REMOVING one of those names only ever takes capability away, and is the
497
+ // one way the dashboard can undo a hand-edited config file that carries
498
+ // it (the fence guards the API, not the read path).
499
+ for (const [envKey, envValue] of Object.entries(value)) {
500
+ if (envValue !== null && BLOCKED_PACKAGE_ENV_KEYS.has(envKey.toLowerCase())) {
501
+ return `"${key}.${envKey}" cannot be set — it injects code into the packaging process`;
455
502
  }
456
503
  }
457
504
  return null;
@@ -502,6 +549,51 @@ function validateValue(key, value, spec) {
502
549
  }
503
550
  }
504
551
  }
552
+ /**
553
+ * Merge an `envRecord` patch over the map already in the file: a string sets or
554
+ * replaces one variable, `null` removes one, and every variable the patch does
555
+ * not name is left exactly as it was.
556
+ *
557
+ * Replacing the whole map — what every other non-`object` key does on write —
558
+ * cannot work for `packageEnv`, because its values are **write-only**:
559
+ * `GET /api/config` reports `packageEnvKeys`, the NAMES alone (see
560
+ * routes/config.ts), so the values never leave this process and a client
561
+ * editing one variable has nothing to resend for its siblings. Under a
562
+ * wholesale replace, saving `{"A":"v"}` would silently delete `B` and `C` —
563
+ * secrets the editor was never shown and cannot recover. So the write is a
564
+ * patch, for the same reason the one-level block merge above exists (a partial
565
+ * edit must not drop what it could not see), one level deeper.
566
+ *
567
+ * A file value that is not an object is replaced rather than merged into,
568
+ * mirroring how a malformed `object` block is treated.
569
+ */
570
+ function mergeEnvRecord(existing, patch) {
571
+ const merged = isPlainObject(existing) ? { ...existing } : {};
572
+ for (const [envKey, envValue] of Object.entries(patch)) {
573
+ if (envValue === null) {
574
+ delete merged[envKey];
575
+ }
576
+ else {
577
+ merged[envKey] = envValue;
578
+ }
579
+ }
580
+ return merged;
581
+ }
582
+ /**
583
+ * Write a merged block back, or drop the key when the merge emptied it — an
584
+ * empty block carries no setting and would only add noise to the file the human
585
+ * reviews. It is load-bearing for a map entry: an empty object is truthy, so a
586
+ * leftover `{}` would shadow a basename-keyed entry for the same service and
587
+ * silently disable that override.
588
+ */
589
+ function setOrDeleteBlock(target, key, merged) {
590
+ if (Object.keys(merged).length === 0) {
591
+ delete target[key];
592
+ }
593
+ else {
594
+ target[key] = merged;
595
+ }
596
+ }
505
597
  // Thrown by updateConfig so the route can answer 400 with every problem at
506
598
  // once instead of failing on the first.
507
599
  export class ConfigValidationError extends Error {
@@ -634,6 +726,14 @@ export class ConfigManager {
634
726
  dataDir: process.env.LSS_ENGINE_DATA_DIR,
635
727
  };
636
728
  }
729
+ // Point unsupported services at another emulator per run (a CI job that
730
+ // starts one next to LSS) without writing it into the shared config file.
731
+ if (process.env.LSS_FALLBACK_ENDPOINT) {
732
+ this.config.selfEngine = {
733
+ ...this.config.selfEngine,
734
+ fallbackEndpoint: process.env.LSS_FALLBACK_ENDPOINT,
735
+ };
736
+ }
637
737
  }
638
738
  getConfig() {
639
739
  return this.config;
@@ -711,8 +811,51 @@ export class ConfigManager {
711
811
  args: [...(this.config.packageArgs ?? []), ...(perService.packageArgs ?? [])],
712
812
  env: { ...(this.config.packageEnv ?? {}), ...(perService.packageEnv ?? {}) },
713
813
  timeoutMs: perService.packageTimeoutMs ?? this.getPackageTimeoutMs(),
814
+ cwd: this.resolvePackageCwd(perService.packageCwd, servicePath),
714
815
  };
715
816
  }
817
+ /**
818
+ * Where a package command runs. `packageCwd` is resolved against the PROJECT
819
+ * ROOT (not the service dir — the point of the key is to reach a directory
820
+ * above the service) and fenced to it with the same realpath comparison the
821
+ * /install and /package endpoints apply to `servicePath`: this value is the
822
+ * cwd of a spawned child, so an unfenced `../..` would walk the packaging
823
+ * command out of the project entirely. A rejected value falls back to the
824
+ * service directory and says so, rather than silently packaging elsewhere.
825
+ */
826
+ resolvePackageCwd(packageCwd, servicePath) {
827
+ const serviceDir = path.resolve(servicePath);
828
+ if (!packageCwd)
829
+ return serviceDir;
830
+ const projectRoot = this.getProjectRoot();
831
+ const resolved = path.resolve(projectRoot, packageCwd);
832
+ const realRoot = realpathOrSelf(projectRoot);
833
+ const realTarget = realpathOrSelf(resolved);
834
+ const relative = path.relative(realRoot, realTarget);
835
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
836
+ console.warn(`⚠️ Ignoring packageCwd "${packageCwd}" for ${serviceDir}: it resolves outside the project root (${projectRoot}).`);
837
+ return serviceDir;
838
+ }
839
+ return resolved;
840
+ }
841
+ /**
842
+ * Stacks the scan must not offer. A monorepo always holds a few that are
843
+ * never local by decision — a bootstrap stack deployed once per AWS account,
844
+ * a us-east-1 DNS/certificate stack the engine does not emulate — and they
845
+ * came back as pending work, with warnings, on every single scan. Permanent
846
+ * noise is how a real warning goes unread.
847
+ *
848
+ * Matched with the same key spellings as `servicePackaging`/`serviceRuntime`
849
+ * (path relative to the config file's directory or to the project root, or
850
+ * the directory basename), so one convention covers all three.
851
+ */
852
+ isScanIgnored(servicePath) {
853
+ const ignore = this.config.scanIgnore;
854
+ if (!ignore || ignore.length === 0)
855
+ return false;
856
+ const patterns = new Set(ignore.map(entry => entry.replace(/^\.\//, '').replace(/\/+$/, '')));
857
+ return this.serviceEntryKeys(servicePath).some(key => patterns.has(key));
858
+ }
716
859
  /**
717
860
  * The keys a `servicePackaging`/`serviceRuntime` entry may be written under,
718
861
  * most specific first: the path relative to the config file's directory, the
@@ -804,19 +947,34 @@ export class ConfigManager {
804
947
  invokePort: perService.invokePort,
805
948
  };
806
949
  }
950
+ /**
951
+ * The worker-process overrides for one service: `serviceRuntime.<svc>.env`,
952
+ * `.execArgv` and `.functions`. Looked up with the same key spellings as the
953
+ * rest of the entry. Never throws on a malformed hand-edited value — a
954
+ * non-object env or non-array execArgv reads as absent.
955
+ */
956
+ getWorkerOverridesForService(servicePath) {
957
+ const perService = this.lookupServiceEntry(this.config.serviceRuntime, servicePath);
958
+ return {
959
+ env: isPlainObject(perService.env) ? { ...perService.env } : {},
960
+ execArgv: Array.isArray(perService.execArgv) ? [...perService.execArgv] : [],
961
+ functions: isPlainObject(perService.functions) ? { ...perService.functions } : {},
962
+ };
963
+ }
807
964
  getSelfEngineConfig() {
808
965
  const selfEngine = this.config.selfEngine ?? {};
809
966
  const stateDir = this.getStateDir();
810
967
  const rawDataDir = selfEngine.dataDir
811
968
  ?? (stateDir ? path.join(stateDir, 'engine') : this.homeStateDir('engine'));
812
969
  return {
813
- port: selfEngine.port ?? DEFAULT_PORT,
970
+ port: this.getSelfEnginePort(),
814
971
  dataDir: path.isAbsolute(rawDataDir) ? rawDataDir : path.resolve(process.cwd(), rawDataDir),
815
972
  account: selfEngine.account ?? '000000000000',
816
973
  idleUnloadMs: selfEngine.idleUnloadMs ?? 300000,
817
974
  memoryBudgetMb: selfEngine.memoryBudgetMb ?? 128,
818
975
  fsync: selfEngine.fsync ?? false,
819
976
  fallbackEndpoint: selfEngine.fallbackEndpoint ?? null,
977
+ fallbackEndpoints: selfEngine.fallbackEndpoints ?? {},
820
978
  persistence: this.isPersistence(),
821
979
  region: this.getRegion(),
822
980
  };
@@ -833,15 +991,23 @@ export class ConfigManager {
833
991
  // which would also resolve the data dir — needless work for a URL, and it
834
992
  // drags the project-root lookup into every caller that only wants an endpoint.
835
993
  getEngineEndpoint() {
836
- return `http://localhost:${this.config.selfEngine?.port ?? DEFAULT_PORT}`;
994
+ return `http://localhost:${this.getSelfEnginePort()}`;
995
+ }
996
+ // The engine port follows serverPort unless set explicitly. It used to
997
+ // default to 14566 on its own, so `LSS_DASHBOARD_PORT=3100` alone (or a
998
+ // config with only `serverPort`) silently split the stack: the dashboard
999
+ // moved to 3100 and a second listener still opened 14566 — colliding with
1000
+ // the instance that a second stack was meant to run beside.
1001
+ getSelfEnginePort() {
1002
+ return this.config.selfEngine?.port ?? this.getServerPort();
837
1003
  }
838
1004
  /**
839
1005
  * True when the orchestrator and the engine share one listener — the default,
840
- * since both ports default to the same value. Give them different values to
841
- * go back to two listeners.
1006
+ * since the engine port follows serverPort. Give `selfEngine.port` a
1007
+ * different value to go back to two listeners.
842
1008
  */
843
1009
  isSingleListener() {
844
- return this.getServerPort() === (this.config.selfEngine?.port ?? DEFAULT_PORT);
1010
+ return this.getServerPort() === this.getSelfEnginePort();
845
1011
  }
846
1012
  getConfigPath() {
847
1013
  return this.configPath;
@@ -931,13 +1097,16 @@ export class ConfigManager {
931
1097
  // Only the block's OWN settings: region/persistence are tracked under
932
1098
  // their own keys above, so one change never flags two entries.
933
1099
  selfEngine: {
934
- port: selfEngine.port,
1100
+ // The explicit value, not the resolved one: an unset engine port
1101
+ // follows serverPort, and that change is already tracked there.
1102
+ port: this.config.selfEngine?.port,
935
1103
  dataDir: selfEngine.dataDir,
936
1104
  account: selfEngine.account,
937
1105
  idleUnloadMs: selfEngine.idleUnloadMs,
938
1106
  memoryBudgetMb: selfEngine.memoryBudgetMb,
939
1107
  fsync: selfEngine.fsync,
940
1108
  fallbackEndpoint: selfEngine.fallbackEndpoint,
1109
+ fallbackEndpoints: selfEngine.fallbackEndpoints,
941
1110
  },
942
1111
  };
943
1112
  }
@@ -1028,16 +1197,23 @@ export class ConfigManager {
1028
1197
  }
1029
1198
  }
1030
1199
  for (const [key, value] of Object.entries(patch)) {
1200
+ const spec = EDITABLE_CONFIG_KEYS[key];
1031
1201
  if (value === null) {
1032
1202
  delete fileConfig[key];
1033
1203
  }
1034
- else if (EDITABLE_CONFIG_KEYS[key].kind === 'object') {
1204
+ else if (spec.kind === 'envRecord') {
1205
+ // Per-variable patch instead of a replace: these values are write-only,
1206
+ // so the client cannot resend the siblings it was never shown and a
1207
+ // replace would delete them. See mergeEnvRecord.
1208
+ setOrDeleteBlock(fileConfig, key, mergeEnvRecord(fileConfig[key], value));
1209
+ }
1210
+ else if (spec.kind === 'object') {
1035
1211
  // One-level merge so a partial block edit (e.g. branding.title) never
1036
1212
  // drops sibling settings the UI does not round-trip (branding.logo).
1037
1213
  // Map-shaped blocks (serviceRuntime/servicePackaging) merge one level
1038
1214
  // deeper for the same reason: onboarding sending { apiPort } for one
1039
1215
  // service must not drop that entry's execution/packageEnv siblings.
1040
- const perEntry = Boolean(EDITABLE_CONFIG_KEYS[key].entrySubKeys);
1216
+ const entrySubKeys = spec.entrySubKeys;
1041
1217
  const existing = isPlainObject(fileConfig[key])
1042
1218
  ? fileConfig[key]
1043
1219
  : {};
@@ -1046,7 +1222,7 @@ export class ConfigManager {
1046
1222
  if (subValue === null) {
1047
1223
  delete merged[subKey];
1048
1224
  }
1049
- else if (perEntry && isPlainObject(subValue)) {
1225
+ else if (entrySubKeys && isPlainObject(subValue)) {
1050
1226
  const entryExisting = isPlainObject(merged[subKey])
1051
1227
  ? merged[subKey]
1052
1228
  : {};
@@ -1055,6 +1231,14 @@ export class ConfigManager {
1055
1231
  if (entryValue === null) {
1056
1232
  delete entryMerged[entryKey];
1057
1233
  }
1234
+ else if (entrySubKeys[entryKey].kind === 'envRecord') {
1235
+ // servicePackaging[*].packageEnv is as write-only as the global
1236
+ // map (redactPackaging() in routes/config.ts collapses it the
1237
+ // same way), so it patches per variable too. Validation already
1238
+ // rejected any subkey that is not in entrySubKeys, so the spec
1239
+ // lookup here always lands on a declared one.
1240
+ setOrDeleteBlock(entryMerged, entryKey, mergeEnvRecord(entryMerged[entryKey], entryValue));
1241
+ }
1058
1242
  else {
1059
1243
  entryMerged[entryKey] = entryValue;
1060
1244
  }
@@ -1062,12 +1246,7 @@ export class ConfigManager {
1062
1246
  // An entry left with no settings carries no meaning and would
1063
1247
  // shadow a basename-keyed entry for the same service (an empty
1064
1248
  // object is truthy), so it is dropped rather than written.
1065
- if (Object.keys(entryMerged).length === 0) {
1066
- delete merged[subKey];
1067
- }
1068
- else {
1069
- merged[subKey] = entryMerged;
1070
- }
1249
+ setOrDeleteBlock(merged, subKey, entryMerged);
1071
1250
  }
1072
1251
  else {
1073
1252
  merged[subKey] = subValue;
@@ -1100,6 +1279,9 @@ export class ConfigManager {
1100
1279
  const selfEngine = this.getSelfEngineConfig();
1101
1280
  console.log(` Self Engine Port: ${selfEngine.port} (http://localhost:${selfEngine.port})`);
1102
1281
  console.log(` Self Engine Data Dir: ${selfEngine.dataDir}`);
1282
+ for (const [service, endpoint] of Object.entries(selfEngine.fallbackEndpoints)) {
1283
+ console.log(` Self Engine Fallback (${service}): ${endpoint}`);
1284
+ }
1103
1285
  if (selfEngine.fallbackEndpoint) {
1104
1286
  console.log(` Self Engine Fallback: ${selfEngine.fallbackEndpoint}`);
1105
1287
  }