@empire-builder-kit/nx 1.0.0-rc.44 → 1.0.0-rc.46

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 (100) hide show
  1. package/package.json +3 -3
  2. package/src/blueprints/external-sandbox.d.ts +1 -1
  3. package/src/cli/main.d.ts +11 -5
  4. package/src/cli/main.js +88 -29
  5. package/src/container-images.d.ts +1 -1
  6. package/src/container-images.js +1 -1
  7. package/src/developer-agent/generator-planning.js +10 -3
  8. package/src/developer-agent/operation-registry.js +1 -1
  9. package/src/executors/deployment-plan/deployment-plan.js +5 -1
  10. package/src/executors/deployment-plan/schema.json +1 -1
  11. package/src/executors/fleet/fleet.js +4 -0
  12. package/src/executors/fleet/schema.json +1 -1
  13. package/src/executors/github-bootstrap/github-bootstrap.d.ts +3 -0
  14. package/src/executors/github-bootstrap/github-bootstrap.js +28 -0
  15. package/src/executors/github-bootstrap/schema.json +14 -0
  16. package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
  17. package/src/executors/local-deploy/federated-credential-transport.js +15 -5
  18. package/src/foundation/cost-estimate.d.ts +8 -3
  19. package/src/foundation/cost-estimate.js +42 -19
  20. package/src/foundation/index.d.ts +1 -0
  21. package/src/foundation/index.js +1 -0
  22. package/src/foundation/local-private-smoke-access.js +6 -0
  23. package/src/foundation/logging.d.ts +3 -1
  24. package/src/foundation/logging.js +8 -1
  25. package/src/foundation/observability.d.ts +42 -0
  26. package/src/foundation/observability.js +181 -0
  27. package/src/foundation/preflight.js +1 -1
  28. package/src/generators/function/function.js +5 -2
  29. package/src/generators/observability-infrastructure.d.ts +2 -0
  30. package/src/generators/observability-infrastructure.js +31 -0
  31. package/src/generators/observability-policy.d.ts +4 -0
  32. package/src/generators/observability-policy.js +23 -0
  33. package/src/generators/preset/dependency-overrides.d.ts +17 -6
  34. package/src/generators/preset/dependency-overrides.js +17 -6
  35. package/src/generators/preset/tool-versions.d.ts +2 -2
  36. package/src/generators/preset/tool-versions.js +2 -2
  37. package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
  38. package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
  39. package/src/generators/preset/workspace-files-agent-development.js +42 -0
  40. package/src/generators/preset/workspace-files-thin-deploy.js +17 -4
  41. package/src/generators/preset/workspace-files-thin-docs.js +10 -22
  42. package/src/generators/preset/workspace-files.d.ts +1 -0
  43. package/src/generators/preset/workspace-files.js +120 -35
  44. package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
  45. package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
  46. package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
  47. package/src/generators/slice/built-in-plan-data-files.js +10 -3
  48. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
  49. package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
  50. package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
  51. package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
  52. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
  53. package/src/generators/slice/built-in-plan-metadata-files.js +37 -2
  54. package/src/generators/slice/built-in-plan-operations-files.js +106 -6
  55. package/src/generators/slice/built-in-plan-project-files.js +1 -0
  56. package/src/generators/slice/built-in-plan-release-files.js +2 -44
  57. package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
  58. package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
  59. package/src/generators/slice/built-in-plan-shared.js +29 -2
  60. package/src/generators/slice/built-in-plan-verification-files.js +0 -1
  61. package/src/generators/slice/built-in-plan-web-files.js +22 -6
  62. package/src/generators/slice/built-in-plan.js +2 -2
  63. package/src/generators/slice/slice.js +2 -0
  64. package/src/generators/upgrade/upgrade.d.ts +4 -3
  65. package/src/generators/upgrade/upgrade.js +139 -29
  66. package/src/generators/workload/frozen-container.d.ts +1 -1
  67. package/src/generators/workload/reliability-registration.d.ts +1 -1
  68. package/src/generators/workload/reliability-registration.js +9 -9
  69. package/src/generators/workload/workload.d.ts +1 -1
  70. package/src/generators/workload/workload.js +32 -10
  71. package/src/git-policy/github-bootstrap.d.ts +18 -0
  72. package/src/git-policy/github-bootstrap.js +99 -6
  73. package/src/local-dev/dev-command-core.d.ts +24 -3
  74. package/src/local-dev/dev-command-core.js +124 -12
  75. package/src/local-dev/errors.d.ts +19 -1
  76. package/src/local-dev/errors.js +18 -1
  77. package/src/local-dev/fleet-client.d.ts +64 -6
  78. package/src/local-dev/fleet-client.js +189 -15
  79. package/src/local-dev/fleet-process.d.ts +47 -2
  80. package/src/local-dev/fleet-process.js +248 -31
  81. package/src/local-dev/fleet-runtime.d.ts +37 -0
  82. package/src/local-dev/fleet-runtime.js +127 -69
  83. package/src/local-dev/fleet-session.d.ts +1 -0
  84. package/src/local-dev/fleet-session.js +83 -7
  85. package/src/local-dev/fleet-state.d.ts +5 -0
  86. package/src/local-dev/fleet-state.js +11 -2
  87. package/src/local-dev/fleet-supervisor-daemon.js +1 -5
  88. package/src/local-dev/fleet-supervisor.d.ts +7 -0
  89. package/src/local-dev/fleet-supervisor.js +83 -12
  90. package/src/local-dev/slice-runner.d.ts +19 -1
  91. package/src/local-dev/slice-runner.js +290 -91
  92. package/src/repository-policy/deployment-plan.d.ts +1 -1
  93. package/src/repository-policy/deployment-plan.js +16 -1
  94. package/src/secrets/generated-aws-control-plane.d.ts +11 -1
  95. package/src/secrets/generated-aws-control-plane.js +92 -29
  96. package/src/secrets/operator.d.ts +3 -2
  97. package/src/secrets/operator.js +27 -5
  98. package/src/supply-chain/blueprint-verification-authority.js +24 -3
  99. package/src/supply-chain/generated-files.js +9 -0
  100. package/src/supply-chain/sbom.js +74 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empire-builder-kit/nx",
3
- "version": "1.0.0-rc.44",
3
+ "version": "1.0.0-rc.46",
4
4
  "description": "Nx generators, executors, and Blueprint contracts for Empire Builder Kit",
5
5
  "keywords": [
6
6
  "aws",
@@ -145,7 +145,7 @@
145
145
  "provenance": false
146
146
  },
147
147
  "dependencies": {
148
- "@empire-builder-kit/runtime": "1.0.0-rc.44",
148
+ "@empire-builder-kit/runtime": "1.0.0-rc.46",
149
149
  "@aws-sdk/client-cloudfront": "^3.700.0",
150
150
  "@aws-sdk/client-cloudwatch": "^3.700.0",
151
151
  "@aws-sdk/client-cloudwatch-logs": "^3.700.0",
@@ -178,5 +178,5 @@
178
178
  "engines": {
179
179
  "node": ">=22.13.0"
180
180
  },
181
- "gitHead": "37e9dfcf2fcd9561ca389019600206d05975598a"
181
+ "gitHead": "2165c67769556e404a2d0e9d2f5bcdd23413fce8"
182
182
  }
@@ -1,5 +1,5 @@
1
1
  import { type ChildProcessWithoutNullStreams } from 'node:child_process';
2
- export declare const EXTERNAL_BLUEPRINT_SANDBOX_IMAGE: "node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402";
2
+ export declare const EXTERNAL_BLUEPRINT_SANDBOX_IMAGE: "node:22-alpine@sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914";
3
3
  export declare const EXTERNAL_BLUEPRINT_CONTAINER_AUTO_REMOVE: "--rm";
4
4
  export declare const EXTERNAL_BLUEPRINT_SANDBOX_FLAGS: readonly ["--network=none", "--read-only", "--cap-drop=ALL", "--security-opt=no-new-privileges", "--pids-limit=64", "--memory=256m", "--memory-swap=256m", "--cpus=1", "--ulimit=nofile=256:256", "--tmpfs=/tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777"];
5
5
  export type ExternalBlueprintSource = 'installed' | 'local';
package/src/cli/main.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { inspectLocalDevelopment } from '../executors/dev-doctor/dev-doctor.js';
3
3
  import { applyDevSetup } from '../executors/dev-setup/dev-setup.js';
4
4
  import { inspectDevelopmentEnvironment } from '../local-dev/environment-inspector.js';
5
- import { type FleetOperationSubmission } from '../local-dev/fleet-client.js';
5
+ import { type FleetOperationSubmission, type FleetOperationWaitOptions } from '../local-dev/fleet-client.js';
6
6
  import { inventoryFleets, inventorySliceClaims, type FleetInventoryRecord, pruneStaleFleets } from '../local-dev/fleet-inventory.js';
7
7
  import { type FleetLaunchInput } from '../local-dev/fleet-launcher.js';
8
8
  import { followFleetLogs, searchFleetLogs } from '../local-dev/fleet-logs.js';
@@ -35,10 +35,8 @@ export interface FleetClientPort {
35
35
  shutdown(idempotencyKey?: string): Promise<void>;
36
36
  status(): Promise<FleetStateRecord>;
37
37
  submit(command: 'down' | 'restart' | 'up', selector: DevSliceSelector, idempotencyKey?: string): Promise<FleetOperationSubmission>;
38
- waitForOperation(operationId: string, options?: {
39
- pollMs?: number;
40
- timeoutMs?: number;
41
- }): Promise<FleetOperationStateRecord>;
38
+ /** Resolves on the terminal record, `succeeded` or `failed`. */
39
+ waitForOperationOutcome(operationId: string, options?: FleetOperationWaitOptions): Promise<FleetOperationStateRecord>;
42
40
  }
43
41
  export interface CurrentFleetCoordinates {
44
42
  fleetId: string;
@@ -128,6 +126,14 @@ export interface EbkCliDependencies {
128
126
  }
129
127
  export declare function resolveCurrentFleetCoordinates(workspaceRoot: string, appData: string, dependencies: Pick<EbkCliDependencies, 'loadPlan' | 'personalStageResolver'>): Promise<CurrentFleetCoordinates>;
130
128
  export declare function resolveRecoveryFleetCoordinates(workspaceRoot: string, appData: string, dependencies: Pick<EbkCliDependencies, 'inventory' | 'loadPlan' | 'personalStageResolver'>, currentPlatform?: NodeJS.Platform): Promise<RecoveryFleetCoordinates>;
129
+ /** Process interrupts become an abort signal for the running command. With
130
+ * `onRepeatedInterrupt`, interrupts after the first are reported instead of
131
+ * reaching Node's default handler, which would kill the process. Exported for
132
+ * tests, which pass their own emitter in place of the process. */
133
+ export declare function processAbortSignal(onRepeatedInterrupt?: () => void, target?: Pick<NodeJS.Process, 'on' | 'once' | 'removeListener'>): {
134
+ cleanup: () => void;
135
+ signal: AbortSignal;
136
+ };
131
137
  export declare function runAgentCommandCore(command: AgentCliCommand, io?: EbkCliIo, overrides?: Partial<EbkCliDependencies>): Promise<0 | 1 | 2>;
132
138
  /**
133
139
  * Shared command core consumed by both the `ebk` parser shell and the Nx
package/src/cli/main.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.sameWorkspaceRealpath = sameWorkspaceRealpath;
5
5
  exports.resolveCurrentFleetCoordinates = resolveCurrentFleetCoordinates;
6
6
  exports.resolveRecoveryFleetCoordinates = resolveRecoveryFleetCoordinates;
7
+ exports.processAbortSignal = processAbortSignal;
7
8
  exports.runAgentCommandCore = runAgentCommandCore;
8
9
  exports.runDevCommandCore = runDevCommandCore;
9
10
  exports.runEbkCli = runEbkCli;
@@ -107,8 +108,8 @@ Usage:
107
108
  ebk dev configure [<context options>] [--clear-development-router-url] [--json]
108
109
  ebk dev setup [--trust] [--portless] [--upgrade] [--remove] [--json]
109
110
  ebk dev up <slice...>|--all [--foreground] [--sst-mode=mono|multiplexer]
110
- ebk dev attach <task-id> [--json]
111
111
  [--no-wait] [--timeout=<duration>] [<context options>] [--json]
112
+ ebk dev attach <task-id> [--json]
112
113
  ebk dev status [--json]
113
114
  ebk dev restart <slice...>|--all [--no-wait] [--timeout=<duration>] [--json]
114
115
  ebk dev down <slice...>|--all [--no-wait] [--timeout=<duration>] [--json]
@@ -130,6 +131,12 @@ Configure mutation requires at least one context option or
130
131
  --clear-development-router-url. The clear option may accompany other
131
132
  non-router context changes.
132
133
 
134
+ up, restart, and down wait for the supervised operation's outcome and print
135
+ progress to stderr; there is no default deadline. --timeout=<duration> caps only
136
+ a detached wait: the operation continues, so check ebk dev status and ebk dev
137
+ logs. A foreground up hosts its supervisor and watches until the outcome.
138
+ --no-wait returns once the operation is accepted.
139
+
133
140
  Use "ebk dev <command> --help" to return this bounded command reference.
134
141
  Fleet stage uses a persisted random task ID per worktree. Use attach explicitly
135
142
  to resume a verified inactive task; --stage overrides are not supported.`;
@@ -384,6 +391,18 @@ function failureFromError(namespace, command, error) {
384
391
  : (0, agent_runtime_js_1.classifyDeveloperAgentCliError)(error);
385
392
  return failure(namespace, command, classified.classification, classified.message);
386
393
  }
394
+ if (error instanceof errors_js_1.FleetObservationError) {
395
+ // The accepted operation may still be running: name it so a script can
396
+ // keep following it with ebk dev status.
397
+ return (0, output_js_1.createCliResult)({
398
+ command,
399
+ failureClassification: error.classification,
400
+ namespace,
401
+ operationId: error.detail.operationId,
402
+ outcome: 'failed',
403
+ payload: { ...error.detail, message: error.message },
404
+ });
405
+ }
387
406
  const classification = error instanceof errors_js_1.LocalDevContractError
388
407
  ? error.classification
389
408
  : 'internal-error';
@@ -711,27 +730,49 @@ async function resolveUpContext(command, dispatch) {
711
730
  dispatch.dependencies.writeContext(common, resolution.context);
712
731
  return { gitCommonDirectory: common, resolution };
713
732
  }
714
- function operationWaitOptions(command) {
715
- return command.timeoutMs === undefined
716
- ? {}
717
- : { timeoutMs: command.timeoutMs };
733
+ /** Observation of a submitted lifecycle operation: no default deadline,
734
+ * an explicit --timeout cap, interrupt ends observation only, and progress
735
+ * lines go to stderr so --json stdout stays one envelope. */
736
+ function operationWaitOptions(command, dispatch) {
737
+ return {
738
+ onProgress: (0, dev_command_core_js_1.fleetOperationProgressReporter)(command.command, (line) => dispatch.io.stderr(`${line}\n`)),
739
+ signal: dispatch.signal,
740
+ ...(command.timeoutMs === undefined
741
+ ? {}
742
+ : { timeoutMs: command.timeoutMs }),
743
+ };
718
744
  }
719
745
  async function executeUp(command, coordinates, dispatch) {
720
746
  const { resolution } = await resolveUpContext(command, dispatch);
721
- const executed = await (0, dev_command_core_js_1.executeFleetUpCore)(command, {
722
- appData: dispatch.appData,
723
- context: resolution.context,
724
- frameworkVersion: dispatch.dependencies.frameworkVersion(),
725
- identity: coordinates.identity,
726
- plan: coordinates.plan,
727
- signal: dispatch.signal,
728
- workspaceRoot: dispatch.workspaceRoot,
729
- }, {
730
- idempotencyKey: dispatch.dependencies.idempotencyKey,
731
- inspectEnvironment: dispatch.dependencies.inspectEnvironment,
732
- launchFleet: dispatch.dependencies.launchFleet,
733
- reportCleanupFailure: (message) => dispatch.io.stderr(`[cleanup-incomplete] ${message}\n`),
734
- });
747
+ let executed;
748
+ try {
749
+ executed = await (0, dev_command_core_js_1.executeFleetUpCore)(command, {
750
+ appData: dispatch.appData,
751
+ context: resolution.context,
752
+ frameworkVersion: dispatch.dependencies.frameworkVersion(),
753
+ identity: coordinates.identity,
754
+ plan: coordinates.plan,
755
+ signal: dispatch.signal,
756
+ workspaceRoot: dispatch.workspaceRoot,
757
+ }, {
758
+ idempotencyKey: dispatch.dependencies.idempotencyKey,
759
+ inspectEnvironment: dispatch.dependencies.inspectEnvironment,
760
+ launchFleet: dispatch.dependencies.launchFleet,
761
+ reportCleanupFailure: (message) => dispatch.io.stderr(`[cleanup-incomplete] ${message}\n`),
762
+ reportProgress: (line) => dispatch.io.stderr(`${line}\n`),
763
+ });
764
+ }
765
+ catch (error) {
766
+ const afterWrite = (0, dev_command_core_js_1.foregroundUpSettlement)(error);
767
+ if (!afterWrite)
768
+ throw error;
769
+ // The foreground supervisor outlives this failure in this process: report
770
+ // it now and keep interrupt handling until owned cleanup settles.
771
+ return {
772
+ afterWrite,
773
+ result: failureFromError('dev', command.command, error),
774
+ };
775
+ }
735
776
  return {
736
777
  ...(executed.afterWrite ? { afterWrite: executed.afterWrite } : {}),
737
778
  result: success(command.command, {
@@ -794,7 +835,7 @@ async function executeRestart(command, coordinates, dispatch) {
794
835
  result: success(command.command, { fleetId: coordinates.fleetId, operation: submission }, submission.operationId),
795
836
  };
796
837
  }
797
- const operation = await client.waitForOperation(submission.operationId, operationWaitOptions(command));
838
+ const operation = await client.waitForOperationOutcome(submission.operationId, operationWaitOptions(command, dispatch));
798
839
  const failed = lifecycleOperationFailure(command.command, coordinates.fleetId, operation);
799
840
  if (failed)
800
841
  return { result: failed };
@@ -841,7 +882,7 @@ async function executeDown(command, coordinates, dispatch) {
841
882
  result: success(command.command, { fleetId: coordinates.fleetId, operation: submission }, submission.operationId),
842
883
  };
843
884
  }
844
- const operation = await client.waitForOperation(submission.operationId, operationWaitOptions(command));
885
+ const operation = await client.waitForOperationOutcome(submission.operationId, operationWaitOptions(command, dispatch));
845
886
  const failed = lifecycleOperationFailure(command.command, coordinates.fleetId, operation);
846
887
  if (failed)
847
888
  return { result: failed };
@@ -867,7 +908,7 @@ function lifecycleOperationFailure(command, fleetId, operation) {
867
908
  outcome: 'failed',
868
909
  payload: {
869
910
  fleetId,
870
- message: `Fleet ${command} failed (${classification}).${retained}`,
911
+ message: `Fleet ${command} failed (${classification}).${retained}${(0, dev_command_core_js_1.fleetOperationFailureDetail)(operation)}`,
871
912
  operation,
872
913
  },
873
914
  });
@@ -1155,15 +1196,31 @@ async function dispatchDevCommand(command, dispatch) {
1155
1196
  return executeTail(command, coordinates, dispatch);
1156
1197
  }
1157
1198
  }
1158
- function processAbortSignal() {
1199
+ /** Process interrupts become an abort signal for the running command. With
1200
+ * `onRepeatedInterrupt`, interrupts after the first are reported instead of
1201
+ * reaching Node's default handler, which would kill the process. Exported for
1202
+ * tests, which pass their own emitter in place of the process. */
1203
+ function processAbortSignal(onRepeatedInterrupt, target = process) {
1159
1204
  const controller = new AbortController();
1160
- const abort = () => controller.abort();
1161
- process.once('SIGINT', abort);
1162
- process.once('SIGTERM', abort);
1205
+ const repeated = () => onRepeatedInterrupt?.();
1206
+ let guarded = false;
1207
+ const abort = () => {
1208
+ controller.abort();
1209
+ // A foreground Fleet hosts its supervisor in this process. After the
1210
+ // first interrupt starts the owned down, another Ctrl-C must not kill the
1211
+ // process and orphan native work; it only reports what is still running.
1212
+ if (onRepeatedInterrupt && !guarded) {
1213
+ guarded = true;
1214
+ target.on('SIGINT', repeated);
1215
+ }
1216
+ };
1217
+ target.once('SIGINT', abort);
1218
+ target.once('SIGTERM', abort);
1163
1219
  return {
1164
1220
  cleanup: () => {
1165
- process.removeListener('SIGINT', abort);
1166
- process.removeListener('SIGTERM', abort);
1221
+ target.removeListener('SIGINT', abort);
1222
+ target.removeListener('SIGTERM', abort);
1223
+ target.removeListener('SIGINT', repeated);
1167
1224
  },
1168
1225
  signal: controller.signal,
1169
1226
  };
@@ -1233,7 +1290,9 @@ async function runDevCommandCore(command, io = {}, overrides = {}) {
1233
1290
  const root = dependencies.resolveWorkspaceRoot(io.cwd ?? process.cwd());
1234
1291
  dependencies.changeDirectory(root);
1235
1292
  if (!io.abortSignal)
1236
- ownedSignal = processAbortSignal();
1293
+ ownedSignal = processAbortSignal(command.command === 'up' && command.foreground
1294
+ ? () => output.stderr('[ebk dev up] Fleet down is running in this process and exits when owned cleanup settles; another interrupt does not stop it, and killing the process orphans native work. Check ebk dev status from another terminal.\n')
1295
+ : undefined);
1237
1296
  const execution = await dispatchDevCommand(command, {
1238
1297
  appData: dependencies.appData(),
1239
1298
  dependencies,
@@ -1 +1 @@
1
- export declare const EBK_NODE_22_ALPINE_IMAGE: "node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402";
1
+ export declare const EBK_NODE_22_ALPINE_IMAGE: "node:22-alpine@sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914";
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EBK_NODE_22_ALPINE_IMAGE = void 0;
4
- exports.EBK_NODE_22_ALPINE_IMAGE = 'node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402';
4
+ exports.EBK_NODE_22_ALPINE_IMAGE = 'node:22-alpine@sha256:fe4f6d74e20370acb04ad889077d14d0f64a9c8ea6a42bc60cddf1e844699914';
5
5
  //# sourceMappingURL=container-images.js.map
@@ -11,7 +11,8 @@ exports.rebindDeveloperAgentGeneratorPlan = rebindDeveloperAgentGeneratorPlan;
11
11
  const node_util_1 = require("node:util");
12
12
  const node_fs_1 = require("node:fs");
13
13
  const node_path_1 = require("node:path");
14
- const index_js_1 = require("../blueprints/index.js");
14
+ const index_js_1 = require("../repository-policy/index.js");
15
+ const index_js_2 = require("../blueprints/index.js");
15
16
  const function_js_1 = require("../generators/function/function.js");
16
17
  const logging_registry_js_1 = require("../generators/logging-registry.js");
17
18
  const built_in_plan_js_1 = require("../generators/slice/built-in-plan.js");
@@ -313,7 +314,7 @@ function slicePlan(context, input, mode, rebindCompleted = false) {
313
314
  workspacePathExists(context, node_path_1.posix.join('packages', input.name))) {
314
315
  return unavailableGeneratorDestination(`Product Slice ${input.name} collides with an existing packages/${input.name} path.`);
315
316
  }
316
- const blueprint = (0, index_js_1.resolveBlueprint)({
317
+ const blueprint = (0, index_js_2.resolveBlueprint)({
317
318
  engineVersion: context.workspace.frameworkVersion,
318
319
  reference: 'standard',
319
320
  workspaceRoot: context.workspace.repositoryRoot,
@@ -420,7 +421,13 @@ function workloadPlan(context, input, mode, rebindCompleted = false) {
420
421
  const ownsProtectedLoadAdapter = kind === 'service'
421
422
  ? serviceShouldOwnProtectedLoadAdapter(context, input.slice)
422
423
  : false;
423
- const plan = (0, workload_js_1.createWorkloadFilePlan)(context.workspace.identity, input.slice, input.name, kind, context.workspace.frameworkVersion, input.feature, ownsProtectedLoadAdapter);
424
+ const plan = (0, workload_js_1.createWorkloadFilePlan)(context.workspace.identity, input.slice, input.name, kind, context.workspace.frameworkVersion, input.feature, ownsProtectedLoadAdapter, ownsProtectedLoadAdapter
425
+ ? [
426
+ (0, index_js_1.productionRehearsalStage)((0, index_js_1.readRepositoryOperatingPolicy)(context.workspace.repositoryRoot)
427
+ .deploymentProfile),
428
+ 'production',
429
+ ]
430
+ : []);
424
431
  if (!rebindCompleted) {
425
432
  requireAvailableProspectiveProjectIds(context, [plan.project], `${kind} ${input.name}`);
426
433
  }
@@ -409,7 +409,7 @@ const TARGET_SIGNATURES = [
409
409
  : undefined;
410
410
  }
411
411
  const kind = plural === 'services' ? 'service' : 'job';
412
- return (`docker build -f packages/${slice}/${plural}/${workload}/Dockerfile ` +
412
+ return ('docker build -f Dockerfile ' +
413
413
  `-t ${slice}-${kind}-${workload}:local ../../../..`);
414
414
  }, { nonPlannableRationale: exports.DOCKER_BUILD_OWNER_RATIONALE }),
415
415
  runScriptSignature('dev', (input) => input.project.root.includes('/services/')
@@ -36,7 +36,11 @@ const executor = async (options, context) => {
36
36
  if (options.action === 'deploy' || options.action === 'refresh') {
37
37
  const markerReport = (0, index_js_2.scanWorkMarkers)(context.root);
38
38
  devkit_1.logger.info((0, index_js_2.formatWorkMarkerReport)(markerReport));
39
- (0, index_js_2.assertWorkMarkerReport)(markerReport, { protectedPromotion: true });
39
+ // Like the SST lifecycle, EBK-SECURITY and EBK-BLOCKER block only
40
+ // staging and production; malformed markers still fail every stage.
41
+ (0, index_js_2.assertWorkMarkerReport)(markerReport, {
42
+ protectedPromotion: options.stage !== 'development',
43
+ });
40
44
  }
41
45
  const policy = (0, index_js_1.readRepositoryOperatingPolicy)(context.root);
42
46
  const plan = (0, index_js_1.createProtectedDeploymentPlan)(policy, {
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "stage": {
14
14
  "type": "string",
15
- "enum": ["staging", "production"]
15
+ "enum": ["development", "staging", "production"]
16
16
  },
17
17
  "project": {
18
18
  "type": "string",
@@ -126,6 +126,7 @@ const executor = async (options, context) => {
126
126
  workspaceRoot: context.root,
127
127
  }, {
128
128
  reportCleanupFailure: (message) => devkit_1.logger.error(message),
129
+ reportProgress: (line) => devkit_1.logger.info(line),
129
130
  });
130
131
  devkit_1.logger.info(JSON.stringify({
131
132
  attached: result.attached,
@@ -138,6 +139,9 @@ const executor = async (options, context) => {
138
139
  }
139
140
  catch (error) {
140
141
  devkit_1.logger.error(error instanceof Error ? error.message : String(error));
142
+ // A foreground supervisor can outlive the failure in this process; keep
143
+ // the interrupt handler until its owned cleanup settles.
144
+ await (0, dev_command_core_js_1.foregroundUpSettlement)(error);
141
145
  return { success: false };
142
146
  }
143
147
  finally {
@@ -67,7 +67,7 @@
67
67
  "default": "mono"
68
68
  },
69
69
  "timeout": {
70
- "description": "Bounded CLI operation timeout such as 30s.",
70
+ "description": "Optional cap on a detached wait (foreground: false), such as 30m. Without it the executor waits for the outcome; when the cap expires the operation continues. A foreground up watches until the outcome.",
71
71
  "type": "string",
72
72
  "pattern": "^[1-9][0-9]*(ms|s|m)$"
73
73
  }
@@ -3,6 +3,9 @@ interface GitHubBootstrapExecutorOptions {
3
3
  apply?: boolean;
4
4
  immutableOidcTrustAcknowledgement?: string;
5
5
  npmPrereleaseReviewerIds?: string;
6
+ npmStable?: boolean;
7
+ npmStableReviewerIds?: string;
8
+ npmStablePreventSelfReview?: boolean;
6
9
  productionReviewerIds?: string;
7
10
  repository?: string;
8
11
  }
@@ -10,6 +10,34 @@ function parseReviewerIds(value) {
10
10
  }
11
11
  const executor = async (options, context) => {
12
12
  try {
13
+ if (options.npmStable === true) {
14
+ if (options.npmStableReviewerIds === undefined ||
15
+ !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(options.npmStableReviewerIds) ||
16
+ typeof options.npmStablePreventSelfReview !== 'boolean') {
17
+ throw new Error('npm-stable requires explicit reviewer IDs and prevent-self-review choice.');
18
+ }
19
+ if (options.npmPrereleaseReviewerIds !== undefined ||
20
+ options.productionReviewerIds !== undefined ||
21
+ options.immutableOidcTrustAcknowledgement !== undefined) {
22
+ throw new Error('npm-stable does not accept adopter bootstrap options.');
23
+ }
24
+ const report = await (0, index_js_1.runGitHubBootstrap)({
25
+ npmStable: true,
26
+ apply: options.apply === true,
27
+ repository: options.repository?.trim() ?? '',
28
+ npmStableReviewerIds: parseReviewerIds(options.npmStableReviewerIds),
29
+ npmStablePreventSelfReview: options.npmStablePreventSelfReview,
30
+ token: process.env.GH_TOKEN?.trim() ||
31
+ process.env.GITHUB_TOKEN?.trim() ||
32
+ '',
33
+ });
34
+ devkit_1.logger.info(JSON.stringify(report, null, 2));
35
+ return { success: options.apply ? report.ready : true };
36
+ }
37
+ if (options.npmStableReviewerIds !== undefined ||
38
+ options.npmStablePreventSelfReview !== undefined) {
39
+ throw new Error('npm-stable settings require the explicit npmStable selector.');
40
+ }
13
41
  const policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
14
42
  const report = await (0, index_js_1.runGitHubBootstrap)({
15
43
  apply: options.apply === true,
@@ -31,6 +31,20 @@
31
31
  "type": "string",
32
32
  "pattern": "^[1-9][0-9]*(,[1-9][0-9]*)*$",
33
33
  "description": "Optional comma-separated GitHub user IDs when committed Repository Operating Policy selects environment-reviewer mode. Existing reviewers are preserved when omitted."
34
+ },
35
+ "npmStable": {
36
+ "type": "boolean",
37
+ "default": false,
38
+ "description": "Configure only the framework npm-stable environment; exact framework repository required."
39
+ },
40
+ "npmStableReviewerIds": {
41
+ "type": "string",
42
+ "pattern": "^[1-9][0-9]*(,[1-9][0-9]*)*$",
43
+ "description": "Explicit GitHub user IDs for the framework npm-stable required reviewers."
44
+ },
45
+ "npmStablePreventSelfReview": {
46
+ "type": "boolean",
47
+ "description": "Explicit prevent-self-review choice for the framework npm-stable environment."
34
48
  }
35
49
  }
36
50
  }
@@ -6,9 +6,10 @@ interface GovernedSignalCleanup {
6
6
  readonly priority: number;
7
7
  run(signal: GovernedSignal): Promise<void> | void;
8
8
  }
9
- /** Coordinate an exact child shutdown or private-state cleanup before the
10
- * current process preserves its normal SIGINT/SIGTERM behavior. Higher
11
- * priorities run first so mutation children stop before credentials vanish. */
9
+ /** Coordinate cancellation of exact owned resources before preserving normal
10
+ * SIGINT/SIGTERM behavior. Synchronous credential revocation runs before any
11
+ * awaited child teardown: an ancestor such as Nx can independently force-stop
12
+ * this process while that teardown is pending. */
12
13
  export declare function registerGovernedSignalCleanup(run: GovernedSignalCleanup['run'], priority?: number): () => void;
13
14
  export declare function governedSignalCleanupInProgress(): boolean;
14
15
  interface FederatedCredentialIdentity {
@@ -59,7 +59,9 @@ async function handleGovernedSignal(signal, handler) {
59
59
  let cleanupFailed = false;
60
60
  for (const cleanup of [...governedSignalCleanups].sort((left, right) => right.priority - left.priority)) {
61
61
  try {
62
- await cleanup.run(signal);
62
+ const pending = cleanup.run(signal);
63
+ if (pending !== undefined)
64
+ await pending;
63
65
  }
64
66
  catch {
65
67
  cleanupFailed = true;
@@ -90,9 +92,10 @@ function ensureGovernedSignalHandlers() {
90
92
  process.prependListener(signal, handler);
91
93
  }
92
94
  }
93
- /** Coordinate an exact child shutdown or private-state cleanup before the
94
- * current process preserves its normal SIGINT/SIGTERM behavior. Higher
95
- * priorities run first so mutation children stop before credentials vanish. */
95
+ /** Coordinate cancellation of exact owned resources before preserving normal
96
+ * SIGINT/SIGTERM behavior. Synchronous credential revocation runs before any
97
+ * awaited child teardown: an ancestor such as Nx can independently force-stop
98
+ * this process while that teardown is pending. */
96
99
  function registerGovernedSignalCleanup(run, priority = 0) {
97
100
  const cleanup = { priority, run };
98
101
  governedSignalCleanups.add(cleanup);
@@ -170,7 +173,7 @@ async function createLocalFederatedCredentialTransport(input, dependencies = {})
170
173
  unregisterSignalCleanup = registerGovernedSignalCleanup(() => {
171
174
  cleanupFiles(true);
172
175
  process.removeListener('exit', onExit);
173
- });
176
+ }, 200);
174
177
  process.once('exit', onExit);
175
178
  const resolve = dependencies.resolve ??
176
179
  (input.profile
@@ -186,7 +189,13 @@ async function createLocalFederatedCredentialTransport(input, dependencies = {})
186
189
  if (closed || disposed) {
187
190
  throw new Error('Local federated credential transport is already closed.');
188
191
  }
192
+ const assertOpen = () => {
193
+ if (closed || disposed) {
194
+ throw new Error('Local federated credential transport is already closed.');
195
+ }
196
+ };
189
197
  const identity = await resolve();
198
+ assertOpen();
190
199
  requireCredentialValue(identity.accessKeyId, 'access key');
191
200
  requireCredentialValue(identity.secretAccessKey, 'secret key');
192
201
  requireCredentialValue(identity.sessionToken, 'session token');
@@ -198,6 +207,7 @@ async function createLocalFederatedCredentialTransport(input, dependencies = {})
198
207
  throw new Error('Local deployment requires temporary federated credentials with at least 30 minutes remaining.');
199
208
  }
200
209
  const caller = await identify(identity, input.region);
210
+ assertOpen();
201
211
  const callerArn = caller.Arn?.trim() ?? '';
202
212
  if (caller.Account?.trim() !== input.account ||
203
213
  !(0, identity_js_1.matchesExpectedAssumedRoleArn)(input.account, callerArn, input.expectedRoleArn)) {
@@ -1,4 +1,5 @@
1
- import { type DeploymentProfile } from '../repository-policy/index.js';
1
+ import { type CanonicalDeploymentStage, type DeploymentProfile } from '../repository-policy/index.js';
2
+ import { type ObservabilityProfile } from './observability.js';
2
3
  /**
3
4
  * Dated AWS list-price snapshot for the generated cost estimate.
4
5
  *
@@ -98,8 +99,8 @@ export type ListPriceKey = keyof typeof FOUNDATION_LIST_PRICE_SNAPSHOT.prices;
98
99
  export declare const FOUNDATION_COST_INVENTORY: {
99
100
  /** One CloudFront Router Web ACL per stage. */
100
101
  readonly wafWebAcls: 1;
101
- /** One Foundation CloudWatch dashboard per stage. */
102
- readonly dashboards: 1;
102
+ /** Cost-optimized has no paid dashboard; richer profiles have one stage dashboard. */
103
+ readonly dashboards: 0;
103
104
  /** Declared network: `az: 2`, one Elastic IP and one NAT unit per public subnet. */
104
105
  readonly availabilityZones: 2;
105
106
  /** SST Vpc Cloud Map private DNS namespace (a Route 53 private hosted zone). */
@@ -154,6 +155,10 @@ export declare const FOUNDATION_COST_ESTIMATE_BEGIN_MARKER = "<!-- ebk:foundatio
154
155
  export declare const FOUNDATION_COST_ESTIMATE_END_MARKER = "<!-- ebk:foundation-cost-estimate:end -->";
155
156
  /** The exact label every rendered estimate carries. */
156
157
  export declare function foundationCostEstimateLabel(): string;
158
+ export declare function observabilityDashboardInventory(stage: CanonicalDeploymentStage, observabilityProfile?: ObservabilityProfile): {
159
+ foundation: number;
160
+ slice: number;
161
+ };
157
162
  /** Nonproduction Aurora scaling as costs.md prose, e.g. "0–4 ACUs, pause after 5 idle minutes". */
158
163
  export declare function nonproductionAuroraScalingLabel(stage: 'development' | 'staging'): string;
159
164
  export interface FoundationCostEstimateOptions {