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
package/bin/cli.js CHANGED
@@ -51,19 +51,25 @@ function runtimePaths() {
51
51
  /**
52
52
  * Load configuration. An explicit `--config <path>` (or LSS_CONFIG env) wins over
53
53
  * the cwd/home search so an isolated instance can point at its own config file.
54
- * A missing or unparseable explicit file warns and falls back to the search,
55
- * rather than hard-exiting, so stop/status/logs never orphan a running instance.
54
+ *
55
+ * A missing or unparseable explicit file is a hard error (exit 1), for every
56
+ * command. It used to warn and fall back to the cwd/home search — and that
57
+ * search resolves to whatever instance the default config describes, so a typo
58
+ * in `lss stop --config ./wrong.json` stopped a DIFFERENT running stack. Naming
59
+ * a file is a statement about which instance you mean; guessing another one is
60
+ * never the safe default.
56
61
  */
57
62
  function loadConfig() {
58
63
  if (EXPLICIT_CONFIG) {
59
- if (fs.existsSync(EXPLICIT_CONFIG)) {
60
- try {
61
- return JSON.parse(fs.readFileSync(EXPLICIT_CONFIG, 'utf-8'));
62
- } catch (error) {
63
- console.warn(t('config.parseFailed', { path: EXPLICIT_CONFIG }));
64
- }
65
- } else {
66
- console.warn(t('config.notFound', { path: EXPLICIT_CONFIG }));
64
+ if (!fs.existsSync(EXPLICIT_CONFIG)) {
65
+ console.error(t('config.explicitNotFound', { path: EXPLICIT_CONFIG }));
66
+ process.exit(1);
67
+ }
68
+ try {
69
+ return JSON.parse(fs.readFileSync(EXPLICIT_CONFIG, 'utf-8'));
70
+ } catch (error) {
71
+ console.error(t('config.explicitParseFailed', { path: EXPLICIT_CONFIG, error: formatError(error) }));
72
+ process.exit(1);
67
73
  }
68
74
  }
69
75
 
@@ -106,8 +112,9 @@ function envPort(name) {
106
112
  * documented workflow (docs/CONFIGURATION.md), so it has to agree end to end.
107
113
  */
108
114
  function getConfig(config) {
115
+ const serverPort = envPort('LSS_DASHBOARD_PORT') || envPort('PORT') || config.serverPort || DEFAULT_PORT;
109
116
  return {
110
- serverPort: envPort('LSS_DASHBOARD_PORT') || envPort('PORT') || config.serverPort || DEFAULT_PORT,
117
+ serverPort,
111
118
  enableDynamoProxy: process.env.LSS_ENABLE_DYNAMO_PROXY
112
119
  ? process.env.LSS_ENABLE_DYNAMO_PROXY === 'true' || process.env.LSS_ENABLE_DYNAMO_PROXY === '1'
113
120
  : config.enableDynamoProxy || false,
@@ -115,7 +122,9 @@ function getConfig(config) {
115
122
  mode: config.mode || 'managed',
116
123
  stateDir: config.stateDir,
117
124
  engine: config.engine,
118
- selfEnginePort: envPort('LSS_ENGINE_PORT') || (config.selfEngine && config.selfEngine.port) || DEFAULT_PORT,
125
+ // Follows serverPort unless set explicitly, exactly like the server
126
+ // (ConfigManager.getSelfEnginePort): one port for the whole stack.
127
+ selfEnginePort: envPort('LSS_ENGINE_PORT') || (config.selfEngine && config.selfEngine.port) || serverPort,
119
128
  };
120
129
  }
121
130
 
@@ -159,7 +168,47 @@ function getOrchestratorPath() {
159
168
  return null;
160
169
  }
161
170
 
162
- function startOrchestrator() {
171
+ /**
172
+ * Who is answering on our serverPort, if anyone: the `projectRoot` reported by
173
+ * `GET /api/config`. Under `--net=host` the port belongs to the whole machine,
174
+ * so "someone answers but it is not us" is a routine outcome — and the one the
175
+ * old `NOT RUNNING` turned into a wild goose chase. Returns null when nothing
176
+ * answers (or answers something else), because this only ever adds detail to a
177
+ * message; it must never become a reason to fail.
178
+ */
179
+ async function probePortOwner() {
180
+ try {
181
+ const config = await getJson('/api/config');
182
+ return typeof config?.projectRoot === 'string' ? config.projectRoot : null;
183
+ } catch {
184
+ return null;
185
+ }
186
+ }
187
+
188
+ // The last `lines` lines of the orchestrator log — where the reason a boot died
189
+ // (EADDRINUSE, a bad config) is written, seconds before the CLI notices.
190
+ function tailLog(logFile, lines = 15) {
191
+ try {
192
+ const content = fs.readFileSync(logFile, 'utf8');
193
+ return content.split('\n').filter(Boolean).slice(-lines).join('\n');
194
+ } catch {
195
+ return '';
196
+ }
197
+ }
198
+
199
+ // Report a port held by a different project's orchestrator. Returns true when
200
+ // it printed something, so callers can skip their own vaguer message.
201
+ function reportPortOwner(owner, port, log = console.log) {
202
+ if (!owner) return false;
203
+ const here = process.cwd();
204
+ const mine = owner === here || here.startsWith(`${owner}${path.sep}`);
205
+ log(mine
206
+ ? t('status.portAnsweringHere', { port })
207
+ : t('status.portOwnedByOther', { port, root: owner }));
208
+ return true;
209
+ }
210
+
211
+ async function startOrchestrator() {
163
212
  // Before anything else — including the already-running short-circuit — so a
164
213
  // stale 0.x flag or config value always fails loudly instead of looking like
165
214
  // a successful no-op.
@@ -243,17 +292,88 @@ function startOrchestrator() {
243
292
  }
244
293
  console.log(t('start.logs', { path: logFile }));
245
294
 
246
- setTimeout(() => {
295
+ // `--wait` blocks until the orchestrator actually answers, which is what a
296
+ // sequential chain (a VS Code task, a CI step, a boot script) needs: without
297
+ // it the next step's `register` fails fifteen times for a reason that is not
298
+ // the cause — the cause is in the first line of the log, minutes earlier.
299
+ if (process.argv.includes('--wait') || getArgValue('--wait') !== undefined) {
300
+ const seconds = Number.parseInt(getArgValue('--wait') || '', 10);
301
+ return waitForHealthy(child.pid, pidFile, logFile, Number.isInteger(seconds) && seconds > 0 ? seconds : 30);
302
+ }
303
+
304
+ // Without --wait the CLI still has to answer honestly. Daemonizing means the
305
+ // child discovers a busy port AFTER this process has printed a PID, so the
306
+ // liveness check below is the only place a non-zero exit can come from — and
307
+ // it used to print the failure and exit 0 anyway, making a dead orchestrator
308
+ // count as a successful step.
309
+ return scheduleLivenessCheck(child.pid, pidFile, logFile);
310
+ }
311
+
312
+ // Two seconds later: is the child still there? Resolves when it is, exits
313
+ // non-zero when it is not. Returned as a promise so the caller (and the tests)
314
+ // can observe the outcome instead of it happening in a detached timer.
315
+ function scheduleLivenessCheck(pid, pidFile, logFile) {
316
+ return new Promise((resolve, reject) => {
317
+ setTimeout(() => {
318
+ try {
319
+ process.kill(pid, 0);
320
+ } catch (e) {
321
+ // process.exit never returns in production; under test it throws, and
322
+ // the rejection is how a test asserts the exit code.
323
+ reportStartFailure(pidFile, logFile).then(() => process.exit(1)).then(resolve, reject);
324
+ return;
325
+ }
326
+ console.log(t('start.running'));
327
+ resolve();
328
+ }, 2000);
329
+ });
330
+ }
331
+
332
+ // Everything known about a boot that did not survive: the log tail, and whether
333
+ // the port it wanted is held by another project's orchestrator.
334
+ async function reportStartFailure(pidFile, logFile) {
335
+ console.error(t('start.failed', { path: logFile }));
336
+ if (fs.existsSync(pidFile)) {
337
+ fs.unlinkSync(pidFile);
338
+ }
339
+ const tail = tailLog(logFile);
340
+ if (tail) {
341
+ console.error(t('start.logTail'));
342
+ console.error(tail);
343
+ }
344
+ reportPortOwner(await probePortOwner(), getServerPort(), console.error);
345
+ }
346
+
347
+ /**
348
+ * Poll `GET /api/health` until the orchestrator answers, the child dies, or the
349
+ * deadline passes. Resolves on success; exits non-zero (after printing the log
350
+ * tail) on either failure — a `lss start --wait` that returns 0 means the stack
351
+ * is up and can be registered against.
352
+ */
353
+ async function waitForHealthy(pid, pidFile, logFile, timeoutSeconds) {
354
+ const deadline = Date.now() + timeoutSeconds * 1000;
355
+ console.log(t('start.waiting', { seconds: timeoutSeconds }));
356
+ for (;;) {
357
+ try {
358
+ process.kill(pid, 0);
359
+ } catch {
360
+ await reportStartFailure(pidFile, logFile);
361
+ process.exit(1);
362
+ }
247
363
  try {
248
- process.kill(child.pid, 0);
364
+ await getJson('/api/health');
249
365
  console.log(t('start.running'));
250
- } catch (e) {
251
- console.error(t('start.failed', { path: logFile }));
252
- if (fs.existsSync(pidFile)) {
253
- fs.unlinkSync(pidFile);
254
- }
366
+ return;
367
+ } catch {
368
+ // Not up yet — the connection is refused until the listener binds.
369
+ }
370
+ if (Date.now() >= deadline) {
371
+ console.error(t('start.waitTimeout', { seconds: timeoutSeconds }));
372
+ await reportStartFailure(pidFile, logFile);
373
+ process.exit(1);
255
374
  }
256
- }, 2000);
375
+ await new Promise(resolve => setTimeout(resolve, 250));
376
+ }
257
377
  }
258
378
 
259
379
  // Poll kill(pid, 0) until the process is gone. SIGTERM alone isn't enough to
@@ -317,10 +437,23 @@ async function stopOrchestrator() {
317
437
  }
318
438
  }
319
439
 
320
- function showStatus() {
440
+ /**
441
+ * `lss status` — is THIS project's orchestrator running?
442
+ *
443
+ * The pid file answers that, and it used to answer nothing else: with
444
+ * `--net=host` the port belongs to the whole machine, so another project's
445
+ * orchestrator holding it produced `NOT RUNNING` from a port that answers 200 —
446
+ * a status that is not merely unhelpful but points the operator at the wrong
447
+ * place. Every "no" is now followed by asking WHO is on the port, which
448
+ * `GET /api/config` answers exactly (`projectRoot`).
449
+ */
450
+ async function showStatus() {
321
451
  const { pidFile, logFile } = runtimePaths();
452
+ const cfg = getConfig(loadConfig());
322
453
  if (!fs.existsSync(pidFile)) {
323
- console.log(t('status.notRunning'));
454
+ if (!reportPortOwner(await probePortOwner(), cfg.serverPort)) {
455
+ console.log(t('status.notRunning'));
456
+ }
324
457
  return;
325
458
  }
326
459
 
@@ -328,8 +461,6 @@ function showStatus() {
328
461
 
329
462
  try {
330
463
  process.kill(pid, 0);
331
- const config = loadConfig();
332
- const cfg = getConfig(config);
333
464
  console.log(t('status.running', { pid }));
334
465
  console.log(t('status.server', { url: `http://localhost:${cfg.serverPort}` }));
335
466
  console.log(t('status.engine', { url: `http://localhost:${cfg.selfEnginePort}` }));
@@ -340,6 +471,7 @@ function showStatus() {
340
471
  } catch (e) {
341
472
  console.log(t('status.stale'));
342
473
  fs.unlinkSync(pidFile);
474
+ reportPortOwner(await probePortOwner(), cfg.serverPort);
343
475
  }
344
476
  }
345
477
 
@@ -374,19 +506,23 @@ function buildHttpError(res, data) {
374
506
  return new Error(snippet ? `HTTP ${statusText}: ${snippet}` : t('error.httpNoBody', { status: statusText }));
375
507
  }
376
508
 
377
- function postJson(path, body) {
509
+ // `options.port` / `options.headers` address the engine's own listener when it
510
+ // is split from the orchestrator (`lss reset`); everything else talks to the
511
+ // orchestrator API on serverPort.
512
+ function postJson(path, body, options = {}) {
378
513
  return new Promise((resolve, reject) => {
379
514
  const http = require('http');
380
515
  const payload = JSON.stringify(body || {});
381
516
  const req = http.request(
382
517
  {
383
518
  hostname: 'localhost',
384
- port: getServerPort(),
519
+ port: options.port || getServerPort(),
385
520
  path,
386
521
  method: 'POST',
387
522
  headers: {
388
523
  'Content-Type': 'application/json',
389
524
  'Content-Length': Buffer.byteLength(payload),
525
+ ...options.headers,
390
526
  },
391
527
  },
392
528
  res => {
@@ -617,6 +753,47 @@ async function clearSeed(tableName) {
617
753
  }
618
754
  }
619
755
 
756
+ /**
757
+ * `lss reset` — wipe every emulator's state (tables, queues, buckets, topics,
758
+ * rules, secrets, collections…) in the running engine, without restarting it.
759
+ *
760
+ * Talks to the engine's own `POST /_lss/reset`, on selfEnginePort: that is the
761
+ * shared port by default, and the engine's separate listener when the two are
762
+ * split. Registered services stay registered — `lss register` re-provisions
763
+ * their resources. Asks for the same `confirmar` token as seed:clear.
764
+ */
765
+ async function resetEngine() {
766
+ ensureRunningOrExit();
767
+ const cfg = getConfig(loadConfig());
768
+ const engineUrl = `http://localhost:${cfg.selfEnginePort}`;
769
+ console.log(t('clear.warning'));
770
+ console.log(t('reset.target'));
771
+ console.log(t('clear.engine', { url: engineUrl }));
772
+ console.log(t('clear.noAws'));
773
+ console.log('');
774
+
775
+ if (process.argv.includes('--yes') || process.argv.includes('-y')) {
776
+ console.log(t('clear.skipPrompt'));
777
+ } else {
778
+ const ok = await promptConfirmation('confirmar');
779
+ if (!ok) {
780
+ console.log(t('seed.aborted'));
781
+ return;
782
+ }
783
+ }
784
+
785
+ try {
786
+ const res = await postJson('/_lss/reset', {}, {
787
+ port: cfg.selfEnginePort,
788
+ headers: { 'x-lss-confirm-reset': 'true' },
789
+ });
790
+ console.log(t(res.persistence ? 'reset.donePersistent' : 'reset.done'));
791
+ } catch (e) {
792
+ console.error(t('reset.failed'), formatError(e));
793
+ process.exit(1);
794
+ }
795
+ }
796
+
620
797
  /**
621
798
  * `lss mcp` — run the Model Context Protocol server on stdio.
622
799
  *
@@ -636,6 +813,10 @@ async function clearSeed(tableName) {
636
813
  async function registerServices(paths) {
637
814
  ensureRunningOrExit();
638
815
  const targets = paths.length > 0 ? paths : ['.'];
816
+ // `autoPackage` only packages when the template is MISSING, so on a service
817
+ // already packaged once `register` faithfully re-registers whatever is on
818
+ // disk. `--repackage` is how the operator says "build first, then register".
819
+ const repackage = process.argv.includes('--repackage');
639
820
  let failed = 0;
640
821
  for (const target of targets) {
641
822
  const servicePath = path.resolve(target);
@@ -645,7 +826,7 @@ async function registerServices(paths) {
645
826
  continue;
646
827
  }
647
828
  try {
648
- const result = await postJson('/api/services/register', { servicePath });
829
+ const result = await postJson('/api/services/register', { servicePath, repackage });
649
830
  console.log(t('register.ok', {
650
831
  name: result.serviceName,
651
832
  resources: result.resourcesCount,
@@ -804,18 +985,22 @@ function showHelp() {
804
985
  cmd('scan', 'help.cmd.scan'),
805
986
  cmd('register [path...]', 'help.cmd.register'),
806
987
  cmd('seed:clear [table]', 'help.cmd.seedClear'),
988
+ cmd('reset', 'help.cmd.reset'),
807
989
  cmd('mcp', 'help.cmd.mcp'),
808
990
  cmd('help', 'help.cmd.help'),
809
991
  '',
810
992
  t('help.options'),
811
993
  opt('--config <path>', 'help.opt.config'),
812
994
  opt('--enable-dynamo-proxy', 'help.opt.dynamoProxy'),
995
+ opt('--repackage', 'help.opt.repackage'),
996
+ opt('--wait[=<seconds>]', 'help.opt.wait'),
813
997
  opt('--yes, -y', 'help.opt.yes'),
814
998
  '',
815
999
  t('help.environment'),
816
1000
  opt('LSS_DASHBOARD_PORT', 'help.env.dashboardPort'),
817
1001
  opt('LSS_ENGINE_PORT', 'help.env.enginePort'),
818
1002
  opt('LSS_ENGINE_DATA_DIR', 'help.env.engineDataDir'),
1003
+ opt('LSS_FALLBACK_ENDPOINT', 'help.env.fallbackEndpoint'),
819
1004
  opt('AWS_REGION', 'help.env.awsRegion'),
820
1005
  opt('LSS_LANG', 'help.env.lang'),
821
1006
  '',
@@ -849,6 +1034,7 @@ function showHelp() {
849
1034
  helpExample('npx lss seed users', t('help.ex.seedTable')),
850
1035
  helpExample('npx lss seed:clear users', t('help.ex.seedClear')),
851
1036
  helpExample('npx lss seed:clear users --yes', t('help.ex.seedClearYes')),
1037
+ helpExample('npx lss reset --yes', t('help.ex.reset')),
852
1038
  '',
853
1039
  ].join('\n'));
854
1040
  }
@@ -925,6 +1111,7 @@ module.exports = {
925
1111
  showLogs,
926
1112
  runSeed,
927
1113
  clearSeed,
1114
+ resetEngine,
928
1115
  showHelp,
929
1116
  };
930
1117
 
@@ -950,6 +1137,9 @@ if (require.main === module) {
950
1137
  case 'seed:clear':
951
1138
  clearSeed(firstPositional());
952
1139
  break;
1140
+ case 'reset':
1141
+ resetEngine();
1142
+ break;
953
1143
  case 'register':
954
1144
  registerServices(allPositionals());
955
1145
  break;
package/bin/i18n.js CHANGED
@@ -43,13 +43,18 @@ function detectLocale(env = process.env) {
43
43
  const MESSAGES = {
44
44
  en: {
45
45
  'scan.warning.not-installed': 'dependencies not installed — packaging needs an install first',
46
+ // Two codes for one fact, because the fact depends on `autoPackage`: the
47
+ // single string that promised packaging was read as a promise by operators
48
+ // whose config had it off, and registering answered 400 instead.
46
49
  'scan.warning.not-packaged': 'not packaged yet — registering packages it for you (autoPackage)',
50
+ 'scan.warning.not-packaged-manual': 'not packaged yet — autoPackage is off, so run `serverless package` before registering',
47
51
  'scan.warning.ts-config': 'TypeScript service config — name, region and ports resolve at packaging time',
48
52
  'scan.warning.unreadable-config': 'could not read {file}',
49
53
  'scan.warning.invalid-json': 'serverless.json is not valid JSON',
50
54
  // --- config discovery --------------------------------------------------
51
55
  'config.parseFailed': '⚠️ Failed to parse config file {path}',
52
- 'config.notFound': '⚠️ Config file not found: {path}',
56
+ 'config.explicitNotFound': '❌ Config file not found: {path} (given by --config / LSS_CONFIG). Not falling back to the default config search — it could address a different instance.',
57
+ 'config.explicitParseFailed': '❌ Could not parse config file {path} (given by --config / LSS_CONFIG): {error}',
53
58
 
54
59
  // --- lifecycle ---------------------------------------------------------
55
60
  'start.already': '⚠️ LSS Orchestrator is already running (PID: {pid})',
@@ -61,6 +66,9 @@ const MESSAGES = {
61
66
  'start.logs': '📝 Logs: {path}',
62
67
  'start.running': '✅ Service is running',
63
68
  'start.failed': '❌ Service failed to start. Check logs: {path}',
69
+ 'start.waiting': '⏳ Waiting for the orchestrator to answer (up to {seconds}s)...',
70
+ 'start.waitTimeout': '❌ The orchestrator did not answer within {seconds}s',
71
+ 'start.logTail': '--- last lines of the log ---',
64
72
  'start.notBuilt': '❌ Orchestrator not found or not built.',
65
73
  'start.notBuiltDev': 'If you are developing LSS, run:',
66
74
  'start.notBuiltBug': 'If you installed via npm, please report this as a bug.',
@@ -75,6 +83,11 @@ const MESSAGES = {
75
83
  'status.engine': ' Self Engine: {url}',
76
84
  'status.dynamoProxy': ' DynamoDB Proxy: {url}',
77
85
  'status.logs': ' Logs: {path}',
86
+ // A port answering from another checkout is the routine outcome under
87
+ // `--net=host`, and reporting it as NOT RUNNING sent people looking in the
88
+ // wrong place. `projectRoot` comes from GET /api/config on that port.
89
+ 'status.portOwnedByOther': '🔶 Port {port} is in use by the orchestrator of {root}',
90
+ 'status.portAnsweringHere': '🔶 Port {port} answers for this project, but no PID file is registered here (started from another shell?)',
78
91
  'logs.missing': '⚠️ No log file at {path}',
79
92
  'logs.from': '📝 Logs from: {path}',
80
93
 
@@ -142,6 +155,10 @@ const MESSAGES = {
142
155
  'clear.skipPrompt': '↳ --yes given, skipping the interactive confirmation.',
143
156
  'clear.listFailed': '❌ Could not list the tables before clearing:',
144
157
  'clear.failed': '❌ Clear failed:',
158
+ 'reset.target': ' Target: ALL engine state — tables, queues, buckets, topics, rules, secrets, collections',
159
+ 'reset.done': '✅ Engine reset: every emulator is empty. Run `lss register` to re-provision your services\' resources.',
160
+ 'reset.donePersistent': '✅ Engine reset: every emulator is empty and its data directory was wiped. Run `lss register` to re-provision your services\' resources.',
161
+ 'reset.failed': '❌ Reset failed:',
145
162
 
146
163
  // --- mcp ---------------------------------------------------------------
147
164
  'mcp.missing': '❌ MCP server build not found. Run `npm run build` first.',
@@ -165,14 +182,18 @@ const MESSAGES = {
165
182
  'help.cmd.scan': 'List every Serverless/osls service found under the project root (registered/installed/packaged flags, ports and package command)',
166
183
  'help.cmd.register': 'Register services with the running orchestrator (defaults to the current directory; packaging runs on demand via autoPackage)',
167
184
  'help.cmd.seedClear': 'Delete every item from the given table (or from every table that has a seed file). Asks for interactive confirmation (type "confirmar") before any write.',
185
+ 'help.cmd.reset': 'Wipe ALL engine state (every table, queue, bucket, topic, rule, secret and collection) without restarting. Asks for interactive confirmation (type "confirmar").',
168
186
  'help.cmd.mcp': 'Run the Model Context Protocol server on stdio, so an MCP client can drive this stack with tools. Requires a running orchestrator; see docs/MCP.md.',
169
187
  'help.cmd.help': 'Show this help message',
170
188
  'help.opt.config': 'Load config from this file (precedes the cwd/home search). Applies to start/stop/status/logs, so an isolated instance can be addressed without cd-ing into its folder.',
171
189
  'help.opt.dynamoProxy': 'Enable the DynamoDB proxy on port 8000 (for the start command)',
190
+ 'help.opt.repackage': 'On register, run the service packaging command before reading the artifacts, even when they already exist. autoPackage only covers the missing-template case, so this is what loads code you just edited. Without it, register warns when the package predates the sources but still registers what is on disk.',
191
+ 'help.opt.wait': 'On start, block until the orchestrator answers GET /api/health (default 30s) and exit non-zero — with the log tail — if it never does. What a sequential chain needs so the next step is not run against a stack that is not up.',
172
192
  'help.opt.yes': 'Skip the interactive confirmation on seed:clear (use only in CI)',
173
193
  'help.env.dashboardPort': 'Orchestrator port (overrides serverPort)',
174
194
  'help.env.enginePort': 'Self engine port (overrides selfEngine.port)',
175
195
  'help.env.engineDataDir': 'Self engine state directory (overrides selfEngine.dataDir)',
196
+ 'help.env.fallbackEndpoint': 'Forward AWS services the engine does not emulate to this endpoint (overrides selfEngine.fallbackEndpoint)',
176
197
  'help.env.awsRegion': 'Default region for provisioning and the explorers',
177
198
  'help.env.lang': 'CLI language: en, pt-BR or es (falls back to LC_ALL/LC_MESSAGES/LANG)',
178
199
  'help.config.intro': 'Create a lss.config.json or .lssrc file in your project root to customize:',
@@ -190,16 +211,19 @@ const MESSAGES = {
190
211
  'help.ex.seedTable': 'Seed only the "users" table',
191
212
  'help.ex.seedClear': 'Delete every item from "users" (with confirmation)',
192
213
  'help.ex.seedClearYes': 'Same thing, without the prompt (CI)',
214
+ 'help.ex.reset': 'Empty every emulator, without the prompt (CI)',
193
215
  },
194
216
 
195
217
  'pt-BR': {
196
218
  'scan.warning.not-installed': 'dependências não instaladas — empacotar exige instalar antes',
197
219
  'scan.warning.not-packaged': 'ainda não empacotado — registrar empacota para você (autoPackage)',
220
+ 'scan.warning.not-packaged-manual': 'ainda não empacotado — autoPackage está desligado, rode `serverless package` antes de registrar',
198
221
  'scan.warning.ts-config': 'config em TypeScript — nome, região e portas só resolvem no empacotamento',
199
222
  'scan.warning.unreadable-config': 'não consegui ler {file}',
200
223
  'scan.warning.invalid-json': 'serverless.json não é um JSON válido',
201
224
  'config.parseFailed': '⚠️ Não consegui ler o arquivo de config {path}',
202
- 'config.notFound': '⚠️ Arquivo de config não encontrado: {path}',
225
+ 'config.explicitNotFound': '❌ Arquivo de config não encontrado: {path} (informado por --config / LSS_CONFIG). Sem fallback para a busca padrão de config — ela poderia apontar para outra instância.',
226
+ 'config.explicitParseFailed': '❌ Não consegui ler o arquivo de config {path} (informado por --config / LSS_CONFIG): {error}',
203
227
 
204
228
  'start.already': '⚠️ O orquestrador LSS já está rodando (PID: {pid})',
205
229
  'start.starting': '🚀 Iniciando o orquestrador LSS...',
@@ -210,6 +234,9 @@ const MESSAGES = {
210
234
  'start.logs': '📝 Logs: {path}',
211
235
  'start.running': '✅ Serviço no ar',
212
236
  'start.failed': '❌ O serviço não subiu. Veja os logs: {path}',
237
+ 'start.waiting': '⏳ Esperando o orquestrador responder (até {seconds}s)...',
238
+ 'start.waitTimeout': '❌ O orquestrador não respondeu em {seconds}s',
239
+ 'start.logTail': '--- últimas linhas do log ---',
213
240
  'start.notBuilt': '❌ Orquestrador não encontrado ou sem build.',
214
241
  'start.notBuiltDev': 'Se você está desenvolvendo o LSS, rode:',
215
242
  'start.notBuiltBug': 'Se você instalou via npm, por favor abra um bug.',
@@ -224,6 +251,8 @@ const MESSAGES = {
224
251
  'status.engine': ' Self Engine: {url}',
225
252
  'status.dynamoProxy': ' Proxy DynamoDB: {url}',
226
253
  'status.logs': ' Logs: {path}',
254
+ 'status.portOwnedByOther': '🔶 A porta {port} está em uso pelo orquestrador de {root}',
255
+ 'status.portAnsweringHere': '🔶 A porta {port} responde por este projeto, mas não há arquivo de PID registrado aqui (subiu de outro shell?)',
227
256
  'logs.missing': '⚠️ Nenhum arquivo de log em {path}',
228
257
  'logs.from': '📝 Logs de: {path}',
229
258
 
@@ -285,6 +314,10 @@ const MESSAGES = {
285
314
  'clear.skipPrompt': '↳ --yes informado, pulando a confirmação interativa.',
286
315
  'clear.listFailed': '❌ Não consegui listar as tabelas antes de limpar:',
287
316
  'clear.failed': '❌ A limpeza falhou:',
317
+ 'reset.target': ' Alvo: TODO o estado do engine — tabelas, filas, buckets, tópicos, regras, secrets, coleções',
318
+ 'reset.done': '✅ Engine resetado: todos os emuladores estão vazios. Rode `lss register` para reprovisionar os recursos dos seus serviços.',
319
+ 'reset.donePersistent': '✅ Engine resetado: todos os emuladores estão vazios e o diretório de dados foi apagado. Rode `lss register` para reprovisionar os recursos dos seus serviços.',
320
+ 'reset.failed': '❌ O reset falhou:',
288
321
 
289
322
  'mcp.missing': '❌ Build do servidor MCP não encontrado. Rode `npm run build` antes.',
290
323
  'mcp.startFailed': '❌ Não consegui subir o servidor MCP:',
@@ -304,14 +337,18 @@ const MESSAGES = {
304
337
  'help.cmd.scan': 'Lista todo serviço Serverless/osls encontrado sob a raiz do projeto (flags registrado/instalado/empacotado, portas e comando de package)',
305
338
  'help.cmd.register': 'Registra serviços no orquestrador que está rodando (usa o diretório atual por padrão; o empacotamento roda sob demanda via autoPackage)',
306
339
  'help.cmd.seedClear': 'Apaga todos os itens da tabela informada (ou de todas as tabelas com arquivo de seed). Pede confirmação interativa (digitar "confirmar") antes de qualquer escrita.',
340
+ 'help.cmd.reset': 'Apaga TODO o estado do engine (todas as tabelas, filas, buckets, tópicos, regras, secrets e coleções) sem reiniciar. Pede confirmação interativa (digitar "confirmar").',
307
341
  'help.cmd.mcp': 'Roda o servidor Model Context Protocol no stdio, para um cliente MCP dirigir esta stack com tools. Precisa do orquestrador rodando; veja docs/MCP.md.',
308
342
  'help.cmd.help': 'Mostra esta ajuda',
309
343
  'help.opt.config': 'Carrega a config deste arquivo (tem precedência sobre a busca em cwd/home). Vale para start/stop/status/logs, então dá para endereçar uma instância isolada sem entrar na pasta dela.',
310
344
  'help.opt.dynamoProxy': 'Liga o proxy DynamoDB na porta 8000 (para o comando start)',
345
+ 'help.opt.repackage': 'No register, roda o comando de empacotamento do serviço antes de ler os artefatos, mesmo que eles já existam. O autoPackage só cobre o caso de template ausente, então é isto que carrega o código que você acabou de editar. Sem a flag, o register avisa quando o pacote é anterior às fontes, mas registra o que está no disco.',
346
+ 'help.opt.wait': 'No start, bloqueia até o orquestrador responder GET /api/health (padrão 30s) e sai com código diferente de zero — mostrando o fim do log — se ele não responder. É o que uma cadeia sequencial precisa para não rodar o passo seguinte contra um stack que não subiu.',
311
347
  'help.opt.yes': 'Pula a confirmação interativa do seed:clear (use só em CI)',
312
348
  'help.env.dashboardPort': 'Porta do orquestrador (sobrepõe serverPort)',
313
349
  'help.env.enginePort': 'Porta do self engine (sobrepõe selfEngine.port)',
314
350
  'help.env.engineDataDir': 'Diretório de estado do self engine (sobrepõe selfEngine.dataDir)',
351
+ 'help.env.fallbackEndpoint': 'Encaminha para este endpoint os serviços AWS que o engine não emula (sobrepõe selfEngine.fallbackEndpoint)',
315
352
  'help.env.awsRegion': 'Região padrão para o provisionamento e para os explorers',
316
353
  'help.env.lang': 'Idioma da CLI: en, pt-BR ou es (cai para LC_ALL/LC_MESSAGES/LANG)',
317
354
  'help.config.intro': 'Crie um lss.config.json ou .lssrc na raiz do projeto para customizar:',
@@ -329,16 +366,19 @@ const MESSAGES = {
329
366
  'help.ex.seedTable': 'Semeia só a tabela "users"',
330
367
  'help.ex.seedClear': 'Apaga todos os itens de "users" (com confirmação)',
331
368
  'help.ex.seedClearYes': 'Mesma coisa, sem prompt (CI)',
369
+ 'help.ex.reset': 'Esvazia todos os emuladores, sem prompt (CI)',
332
370
  },
333
371
 
334
372
  es: {
335
373
  'scan.warning.not-installed': 'dependencias sin instalar — empaquetar requiere instalarlas antes',
336
374
  'scan.warning.not-packaged': 'aún sin empaquetar — registrar lo empaqueta por ti (autoPackage)',
375
+ 'scan.warning.not-packaged-manual': 'aún sin empaquetar — autoPackage está desactivado, ejecuta `serverless package` antes de registrar',
337
376
  'scan.warning.ts-config': 'config en TypeScript — nombre, región y puertos se resuelven al empaquetar',
338
377
  'scan.warning.unreadable-config': 'no se pudo leer {file}',
339
378
  'scan.warning.invalid-json': 'serverless.json no es un JSON válido',
340
379
  'config.parseFailed': '⚠️ No se pudo leer el archivo de configuración {path}',
341
- 'config.notFound': '⚠️ Archivo de configuración no encontrado: {path}',
380
+ 'config.explicitNotFound': '❌ Archivo de configuración no encontrado: {path} (indicado por --config / LSS_CONFIG). Sin recurrir a la búsqueda de configuración por defecto — podría apuntar a otra instancia.',
381
+ 'config.explicitParseFailed': '❌ No se pudo leer el archivo de configuración {path} (indicado por --config / LSS_CONFIG): {error}',
342
382
 
343
383
  'start.already': '⚠️ El orquestador LSS ya está en marcha (PID: {pid})',
344
384
  'start.starting': '🚀 Iniciando el orquestador LSS...',
@@ -349,6 +389,9 @@ const MESSAGES = {
349
389
  'start.logs': '📝 Registros: {path}',
350
390
  'start.running': '✅ El servicio está en marcha',
351
391
  'start.failed': '❌ El servicio no arrancó. Revisa los registros: {path}',
392
+ 'start.waiting': '⏳ Esperando a que el orquestador responda (hasta {seconds}s)...',
393
+ 'start.waitTimeout': '❌ El orquestador no respondió en {seconds}s',
394
+ 'start.logTail': '--- últimas líneas del registro ---',
352
395
  'start.notBuilt': '❌ Orquestador no encontrado o sin compilar.',
353
396
  'start.notBuiltDev': 'Si estás desarrollando LSS, ejecuta:',
354
397
  'start.notBuiltBug': 'Si lo instalaste con npm, por favor repórtalo como un bug.',
@@ -363,6 +406,8 @@ const MESSAGES = {
363
406
  'status.engine': ' Self Engine: {url}',
364
407
  'status.dynamoProxy': ' Proxy de DynamoDB: {url}',
365
408
  'status.logs': ' Registros: {path}',
409
+ 'status.portOwnedByOther': '🔶 El puerto {port} lo ocupa el orquestador de {root}',
410
+ 'status.portAnsweringHere': '🔶 El puerto {port} responde por este proyecto, pero no hay archivo PID registrado aquí (¿arrancó desde otra terminal?)',
366
411
  'logs.missing': '⚠️ No hay archivo de registro en {path}',
367
412
  'logs.from': '📝 Registros de: {path}',
368
413
 
@@ -424,6 +469,10 @@ const MESSAGES = {
424
469
  'clear.skipPrompt': '↳ --yes indicado, saltando la confirmación interactiva.',
425
470
  'clear.listFailed': '❌ No se pudieron listar las tablas antes de vaciar:',
426
471
  'clear.failed': '❌ El vaciado falló:',
472
+ 'reset.target': ' Objetivo: TODO el estado del engine — tablas, colas, buckets, tópicos, reglas, secrets, colecciones',
473
+ 'reset.done': '✅ Engine reiniciado: todos los emuladores están vacíos. Ejecuta `lss register` para reaprovisionar los recursos de tus servicios.',
474
+ 'reset.donePersistent': '✅ Engine reiniciado: todos los emuladores están vacíos y su directorio de datos se borró. Ejecuta `lss register` para reaprovisionar los recursos de tus servicios.',
475
+ 'reset.failed': '❌ El reinicio falló:',
427
476
 
428
477
  'mcp.missing': '❌ No se encontró el build del servidor MCP. Ejecuta `npm run build` primero.',
429
478
  'mcp.startFailed': '❌ No se pudo iniciar el servidor MCP:',
@@ -443,14 +492,18 @@ const MESSAGES = {
443
492
  'help.cmd.scan': 'Lista cada servicio Serverless/osls encontrado bajo la raíz del proyecto (banderas registrado/instalado/empaquetado, puertos y comando de package)',
444
493
  'help.cmd.register': 'Registra servicios en el orquestador en marcha (usa el directorio actual por defecto; el empaquetado corre a demanda con autoPackage)',
445
494
  'help.cmd.seedClear': 'Elimina todos los ítems de la tabla indicada (o de todas las tablas con archivo de seed). Pide confirmación interactiva (escribir "confirmar") antes de cualquier escritura.',
495
+ 'help.cmd.reset': 'Borra TODO el estado del engine (todas las tablas, colas, buckets, tópicos, reglas, secrets y colecciones) sin reiniciar. Pide confirmación interactiva (escribir "confirmar").',
446
496
  'help.cmd.mcp': 'Ejecuta el servidor Model Context Protocol en stdio, para que un cliente MCP maneje este stack con tools. Requiere el orquestador en marcha; mira docs/MCP.md.',
447
497
  'help.cmd.help': 'Muestra esta ayuda',
448
498
  'help.opt.config': 'Carga la configuración de este archivo (tiene precedencia sobre la búsqueda en cwd/home). Aplica a start/stop/status/logs, así una instancia aislada se puede direccionar sin entrar a su carpeta.',
449
499
  'help.opt.dynamoProxy': 'Habilita el proxy de DynamoDB en el puerto 8000 (para el comando start)',
500
+ 'help.opt.repackage': 'En register, ejecuta el comando de empaquetado del servicio antes de leer los artefactos, aunque ya existan. autoPackage sólo cubre el caso de plantilla ausente, así que esto es lo que carga el código que acabas de editar. Sin la bandera, register avisa cuando el paquete es anterior a las fuentes, pero registra lo que está en el disco.',
501
+ 'help.opt.wait': 'En start, bloquea hasta que el orquestador responda GET /api/health (30s por defecto) y sale con código distinto de cero — mostrando el final del registro — si no responde. Es lo que necesita una cadena secuencial para no ejecutar el paso siguiente contra un stack que no arrancó.',
450
502
  'help.opt.yes': 'Salta la confirmación interactiva de seed:clear (úsalo solo en CI)',
451
503
  'help.env.dashboardPort': 'Puerto del orquestador (sobrescribe serverPort)',
452
504
  'help.env.enginePort': 'Puerto del self engine (sobrescribe selfEngine.port)',
453
505
  'help.env.engineDataDir': 'Directorio de estado del self engine (sobrescribe selfEngine.dataDir)',
506
+ 'help.env.fallbackEndpoint': 'Redirige a este endpoint los servicios AWS que el engine no emula (sobrescribe selfEngine.fallbackEndpoint)',
454
507
  'help.env.awsRegion': 'Región por defecto para el aprovisionamiento y los exploradores',
455
508
  'help.env.lang': 'Idioma de la CLI: en, pt-BR o es (cae a LC_ALL/LC_MESSAGES/LANG)',
456
509
  'help.config.intro': 'Crea un lss.config.json o .lssrc en la raíz del proyecto para personalizar:',
@@ -468,6 +521,7 @@ const MESSAGES = {
468
521
  'help.ex.seedTable': 'Siembra solo la tabla "users"',
469
522
  'help.ex.seedClear': 'Elimina todos los ítems de "users" (con confirmación)',
470
523
  'help.ex.seedClearYes': 'Lo mismo, sin prompt (CI)',
524
+ 'help.ex.reset': 'Vacía todos los emuladores, sin prompt (CI)',
471
525
  },
472
526
  };
473
527
 
@@ -41,7 +41,7 @@ export type { ConfigApi } from './namespaces/config';
41
41
  export type { HealthApi } from './namespaces/health';
42
42
  export type { LambdasApi, LambdaSummary, LambdaDetail, InvokeLambdaInput, InvokeLambdaResult, LambdaInvocationRecord, } from './namespaces/lambdas';
43
43
  export type { ApisApi, ServiceApiInfo, ApiRouteInfo, ApiAuthorizerInfo, GatewayListenerStatus, } from './namespaces/apis';
44
- export type { ServiceRuntimeStatus } from './namespaces/services';
44
+ export type { ServiceRuntimeStatus, ServiceGraph, ServiceGraphNode, ServiceGraphEdge, ServiceGraphNodeKind, ServiceGraphEdgeKind, } from './namespaces/services';
45
45
  export type { LifecycleApi, StartOptions, StartResult, StopResult, StatusResult, LogsResult, WaitUntilReadyOptions, } from './lifecycle';
46
46
  export type * from './types';
47
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}