@empire-builder-kit/nx 1.0.0-rc.40 → 1.0.0-rc.41

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 (56) hide show
  1. package/README.md +26 -10
  2. package/package.json +3 -3
  3. package/src/blueprints/external-sandbox.d.ts +1 -1
  4. package/src/cli/deploy-runtime.d.ts +25 -1
  5. package/src/cli/deploy-runtime.js +120 -4
  6. package/src/cli/dev-command.d.ts +2 -0
  7. package/src/cli/dev-command.js +8 -0
  8. package/src/cli/main.js +20 -6
  9. package/src/cli/personal-secret-runtime.d.ts +12 -0
  10. package/src/cli/personal-secret-runtime.js +18 -3
  11. package/src/cli/secret-terminal.js +1 -1
  12. package/src/container-images.d.ts +1 -1
  13. package/src/container-images.js +1 -1
  14. package/src/developer-agent/operation-registry.d.ts +10 -0
  15. package/src/developer-agent/operation-registry.js +60 -13
  16. package/src/developer-agent/planning.d.ts +8 -1
  17. package/src/developer-agent/planning.js +23 -3
  18. package/src/executors/local-deploy/local-approval.js +1 -1
  19. package/src/executors/sst-lifecycle/governed-native-operation.js +3 -2
  20. package/src/executors/sst-lifecycle/run-sst-command.d.ts +6 -0
  21. package/src/executors/sst-lifecycle/run-sst-command.js +3 -33
  22. package/src/foundation/cost-estimate.d.ts +170 -0
  23. package/src/foundation/cost-estimate.js +683 -0
  24. package/src/generators/preset/tool-versions.d.ts +0 -2
  25. package/src/generators/preset/tool-versions.js +1 -3
  26. package/src/generators/preset/workspace-files-adopter-skills.js +39 -18
  27. package/src/generators/preset/workspace-files-developer-agent.js +83 -27
  28. package/src/generators/preset/workspace-files-shared-operation-claims.d.ts +10 -0
  29. package/src/generators/preset/workspace-files-shared-operation-claims.js +59 -0
  30. package/src/generators/preset/workspace-files-stage-authority.js +13 -2
  31. package/src/generators/preset/workspace-files-thin-deploy.d.ts +10 -0
  32. package/src/generators/preset/workspace-files-thin-deploy.js +54 -20
  33. package/src/generators/preset/workspace-files-thin-docs.js +32 -14
  34. package/src/generators/preset/workspace-files.js +28 -8
  35. package/src/generators/slice/built-in-plan-api-contract-files.js +7 -4
  36. package/src/generators/slice/built-in-plan-authorization-files.js +10 -5
  37. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +933 -249
  38. package/src/generators/slice/built-in-plan-data-files.js +17 -8
  39. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +5 -1
  40. package/src/generators/slice/built-in-plan-frontend-files.js +7 -0
  41. package/src/generators/slice/built-in-plan-infrastructure-files.js +0 -1
  42. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +8 -3
  43. package/src/generators/slice/built-in-plan-metadata-files.js +2 -1
  44. package/src/generators/slice/built-in-plan-reliability-files.js +4 -1
  45. package/src/generators/slice/built-in-plan-testing-persistence-event-files.d.ts +7 -0
  46. package/src/generators/slice/built-in-plan-testing-persistence-event-files.js +487 -0
  47. package/src/generators/slice/built-in-plan-testing-persistence-files.js +2 -0
  48. package/src/generators/slice/built-in-plan.js +3 -1
  49. package/src/generators/workload/frozen-container.d.ts +1 -1
  50. package/src/git-policy/github-bootstrap.d.ts +9 -0
  51. package/src/git-policy/github-bootstrap.js +16 -2
  52. package/src/local-dev/fleet-inventory.js +18 -10
  53. package/src/local-dev/fleet-launcher.js +2 -2
  54. package/src/secrets/personal-command.d.ts +18 -2
  55. package/src/secrets/personal-command.js +66 -12
  56. package/src/secrets/verification.js +6 -1
package/README.md CHANGED
@@ -39,7 +39,7 @@ Consumers should never invoke SST lifecycle commands directly.
39
39
 
40
40
  The workspace-pinned `ebk` binary is the single framework CLI. It owns local
41
41
  development, bounded Developer-Agent inspection/planning, external Blueprint
42
- receipt review, and the human-only personal secret ceremony. Nx remains the
42
+ receipt review, and the personal secret commands. Nx remains the
43
43
  workspace graph and owning-target authority behind generated work.
44
44
 
45
45
  `ebk agent context --json` is the sole full structured context transport. The
@@ -77,7 +77,10 @@ recurring target only after retention and after recomputing its portable
77
77
  package authority from the protected target, manifests, and lockfile. Exact
78
78
  Node declarations and the full lockfile digest are receipt-bound as well. A
79
79
  human may invoke the structured Nx target without a receipt. Neither path is
80
- cryptographic or hosted attestation.
80
+ cryptographic or hosted attestation. Until the receipt is retained, an agent
81
+ prepares the change, asks the human to run `ebk blueprint verification-review`
82
+ on the protected tip and land the retaining commit, and verifies its own edits
83
+ through built-in targets.
81
84
 
82
85
  The external manifest's sole recurring root is named exactly `verify`.
83
86
  Generated project configurations are whole-file managed, canonically rooted
@@ -86,8 +89,11 @@ result. Agent inspection classifies every graph project beneath a current or
86
89
  historically external Slice ownership root as external without trusting tags
87
90
  or that publisher-authored inventory; historical roots and identities remain
88
91
  external after deletion or relabeling. Only the protected-reviewed chain is
89
- exposed. Read-only fleet status and logs remain available, but agent planning
90
- withholds local lifecycle operations for external Slices in v1.
92
+ exposed. Read-only fleet status and logs remain available. In v1, agent
93
+ planning withholds `ebk dev up`, `restart`, and `down` for external Slices
94
+ because their code is not bound by a protected receipt; an agent asks the human
95
+ to run the lifecycle command directly, then continues with status, logs, and
96
+ the reviewed verification chain.
91
97
 
92
98
  Independently of Blueprint provenance, Developer-Agent inspection performs a
93
99
  bounded repository-wide Nx-configuration preflight before graph loading. Each
@@ -109,8 +115,11 @@ external ownership appears currently or anywhere in the bounded first-parent
109
115
  histories of HEAD, protected develop, or protected main; mutable
110
116
  `nx.json.defaultBase` cannot redirect the inventory. A missing protected ref
111
117
  makes the inventory incomplete and activates the same strict boundary; a
112
- shallow clone does likewise because hidden history cannot prove absence. While
113
- that inventory is incomplete, every graph project under `packages/` except the
118
+ shallow clone does likewise because hidden history cannot prove absence. This
119
+ is a recoverable state: complete the history with `git fetch --unshallow origin`
120
+ (or a full-depth `git fetch origin develop main`) so that
121
+ `refs/remotes/origin/develop` and `refs/remotes/origin/main` exist, then rerun
122
+ inspection. While that inventory is incomplete, every graph project under `packages/` except the
114
123
  sole canonical `foundation` project at `packages/foundation` is treated as
115
124
  externally owned. The Foundation tag cannot create that exception: its project
116
125
  name, declared root, and canonical directory are checked together. Read-only
@@ -124,16 +133,23 @@ outside the local-evidence claim. That includes mutation between the
124
133
  repository-wide configuration inventory and Nx graph loading; the preflight is
125
134
  not an immutable repository snapshot.
126
135
 
127
- The human-only personal secret commands are:
136
+ The personal secret commands are:
128
137
 
129
138
  ```bash
130
139
  ebk secret set <slice> <logical-name>
131
140
  ebk secret remove <slice> <logical-name>
132
141
  ```
133
142
 
134
- These commands derive the personal stage, use the profile and Region saved by
135
- `ebk dev configure`, verify the development Foundation account, and accept
136
- values only from a hidden local TTY. Agents and CI must never invoke them.
143
+ Both commands derive the personal stage, use the profile and Region saved by
144
+ `ebk dev configure`, verify the development Foundation account, and refuse
145
+ canonical stages. `set` accepts the value only from a hidden local TTY, never
146
+ from argv, environment, or files, and refuses agent and CI environments,
147
+ because a hidden prompt does not hide the value from the process that owns the
148
+ terminal: a human runs it in a terminal no agent spawned, and the agent
149
+ verifies afterwards with the value-blind `secret-metadata` and status targets.
150
+ `remove` carries no value. An agent may start it; it still needs a local
151
+ interactive terminal where the human types the hidden `REMOVE <logical-name>`
152
+ confirmation.
137
153
 
138
154
  Generated workspaces expose `work-markers` as both a direct target and part of
139
155
  workspace health. The generated `AGENTS.md`, `CLAUDE.md`, and runbook define how
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empire-builder-kit/nx",
3
- "version": "1.0.0-rc.40",
3
+ "version": "1.0.0-rc.41",
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.40",
148
+ "@empire-builder-kit/runtime": "1.0.0-rc.41",
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": "56a2e45b04b8ce8849990baba025cecb5909f8c7"
181
+ "gitHead": "6c8e932ac3acf0a1e124f7687cd7b8582b864c49"
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:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2";
2
+ export declare const EXTERNAL_BLUEPRINT_SANDBOX_IMAGE: "node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402";
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';
@@ -1,6 +1,30 @@
1
1
  import { runGovernedChildProcess } from '../executors/sst-lifecycle/run-sst-command.js';
2
2
  import type { DeployCliCommand } from './dev-command.js';
3
+ export declare const DEPLOY_RESULT_KIND = "ebk-deploy-result";
4
+ export declare const DEPLOY_RECEIPT_PATH = ".ebk/evidence/deployment/latest.json";
5
+ export interface DeployResultProject {
6
+ outcome: string;
7
+ project: string;
8
+ target: string;
9
+ }
10
+ /** The development-only `--json` result: the executor's own written receipt
11
+ * (already secret-free evidence) and per-project status. It is printed after
12
+ * the run and carries no authority; protected stages never produce it. */
13
+ export interface DeployJsonResult {
14
+ exitCode: number;
15
+ kind: typeof DEPLOY_RESULT_KIND;
16
+ outcome: 'failed' | 'receipt-unavailable' | 'review-required' | 'succeeded';
17
+ projects: DeployResultProject[];
18
+ receipt: Record<string, unknown> | null;
19
+ receiptPath: typeof DEPLOY_RECEIPT_PATH;
20
+ schemaVersion: 1;
21
+ stage: 'development';
22
+ }
23
+ export interface DeployCommandOutput {
24
+ now?: () => number;
25
+ stdout: (value: string) => void;
26
+ }
3
27
  /** The CLI selects the existing workspace lifecycle, never an individual SST
4
28
  * child. All policy, identity, TTY, migration, smoke and receipt decisions
5
29
  * remain in the generated Nx target and its executor. */
6
- export declare function executeDeployCommand(command: DeployCliCommand, workspaceRoot: string, environment: NodeJS.ProcessEnv, run?: typeof runGovernedChildProcess): Promise<number>;
30
+ export declare function executeDeployCommand(command: DeployCliCommand, workspaceRoot: string, environment: NodeJS.ProcessEnv, run?: typeof runGovernedChildProcess, output?: DeployCommandOutput): Promise<number>;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEPLOY_RECEIPT_PATH = exports.DEPLOY_RESULT_KIND = void 0;
3
4
  exports.executeDeployCommand = executeDeployCommand;
4
5
  const node_fs_1 = require("node:fs");
5
6
  const node_os_1 = require("node:os");
@@ -7,10 +8,104 @@ const node_path_1 = require("node:path");
7
8
  const run_sst_command_js_1 = require("../executors/sst-lifecycle/run-sst-command.js");
8
9
  const errors_js_1 = require("../local-dev/errors.js");
9
10
  const nx_workspace_js_1 = require("../local-dev/nx-workspace.js");
11
+ exports.DEPLOY_RESULT_KIND = 'ebk-deploy-result';
12
+ exports.DEPLOY_RECEIPT_PATH = '.ebk/evidence/deployment/latest.json';
13
+ const MAX_DEPLOY_RECEIPT_BYTES = 1024 * 1024;
14
+ const MAX_DEPLOY_RECEIPT_CHILDREN = 4096;
15
+ function isRecord(value) {
16
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
17
+ }
18
+ function boundedText(value) {
19
+ return typeof value === 'string' && value.length > 0 && value.length <= 256;
20
+ }
21
+ /** Reads only a fresh, regular, bounded development receipt written by this
22
+ * run. A stale receipt left by an earlier run (for example when the
23
+ * workspace-health dependency fails before the executor invalidates it) is
24
+ * reported as unavailable, never as this run's result. */
25
+ function readFreshDevelopmentReceipt(workspaceRoot, startedAtMs) {
26
+ let descriptor;
27
+ let source;
28
+ try {
29
+ const receiptPath = (0, node_path_1.join)(workspaceRoot, exports.DEPLOY_RECEIPT_PATH);
30
+ // Windows has no O_NOFOLLOW, so the receipt file itself must not be a
31
+ // symbolic link, and the opened file must be the one lstat saw (exact
32
+ // 64-bit identity). A filesystem whose fstat identity differs from lstat
33
+ // reports the receipt as unavailable.
34
+ const entry = (0, node_fs_1.lstatSync)(receiptPath, { bigint: true });
35
+ if (entry.isSymbolicLink() || !entry.isFile())
36
+ return undefined;
37
+ descriptor = (0, node_fs_1.openSync)(receiptPath,
38
+ // O_NOFOLLOW refuses a symbolic link at the receipt path itself.
39
+ node_fs_1.constants.O_RDONLY | (node_fs_1.constants.O_NOFOLLOW ?? 0));
40
+ const stat = (0, node_fs_1.fstatSync)(descriptor, { bigint: true });
41
+ if (!stat.isFile() ||
42
+ stat.dev !== entry.dev ||
43
+ stat.ino !== entry.ino ||
44
+ stat.size > BigInt(MAX_DEPLOY_RECEIPT_BYTES)) {
45
+ return undefined;
46
+ }
47
+ const size = Number(stat.size);
48
+ const buffer = Buffer.alloc(size);
49
+ let offset = 0;
50
+ while (offset < size) {
51
+ const read = (0, node_fs_1.readSync)(descriptor, buffer, offset, size - offset, null);
52
+ if (read === 0)
53
+ break;
54
+ offset += read;
55
+ }
56
+ source = buffer.subarray(0, offset).toString('utf8');
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ finally {
62
+ if (descriptor !== undefined)
63
+ (0, node_fs_1.closeSync)(descriptor);
64
+ }
65
+ let receipt;
66
+ try {
67
+ receipt = JSON.parse(source);
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ if (!isRecord(receipt) ||
73
+ receipt.kind !== 'ebk-deployment-receipt' ||
74
+ receipt.schemaVersion !== 1 ||
75
+ receipt.stage !== 'development' ||
76
+ typeof receipt.startedAt !== 'string' ||
77
+ !Number.isFinite(Date.parse(receipt.startedAt)) ||
78
+ Date.parse(receipt.startedAt) < startedAtMs ||
79
+ !['failed', 'review-required', 'succeeded'].includes(String(receipt.outcome)) ||
80
+ !Array.isArray(receipt.children) ||
81
+ receipt.children.length > MAX_DEPLOY_RECEIPT_CHILDREN) {
82
+ return undefined;
83
+ }
84
+ const projects = [];
85
+ for (const child of receipt.children) {
86
+ if (!isRecord(child) ||
87
+ !boundedText(child.project) ||
88
+ !boundedText(child.target) ||
89
+ !boundedText(child.outcome)) {
90
+ return undefined;
91
+ }
92
+ projects.push({
93
+ outcome: child.outcome,
94
+ project: child.project,
95
+ target: child.target,
96
+ });
97
+ }
98
+ return { projects, receipt };
99
+ }
10
100
  /** The CLI selects the existing workspace lifecycle, never an individual SST
11
101
  * child. All policy, identity, TTY, migration, smoke and receipt decisions
12
102
  * remain in the generated Nx target and its executor. */
13
- async function executeDeployCommand(command, workspaceRoot, environment, run = run_sst_command_js_1.runGovernedChildProcess) {
103
+ async function executeDeployCommand(command, workspaceRoot, environment, run = run_sst_command_js_1.runGovernedChildProcess, output) {
104
+ const structured = command.json === true;
105
+ if (structured && (command.stage !== 'development' || !output)) {
106
+ // Parsing already refuses this; fail closed if a caller bypasses it.
107
+ throw new errors_js_1.LocalDevContractError('invalid-input', 'ebk deploy --json is available only for development; staging and production keep native output and the interactive acknowledgement.');
108
+ }
14
109
  let project;
15
110
  try {
16
111
  project = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(workspaceRoot, 'project.json'), 'utf8'));
@@ -28,6 +123,7 @@ async function executeDeployCommand(command, workspaceRoot, environment, run = r
28
123
  throw new errors_js_1.LocalDevContractError('prerequisite-unavailable', 'ebk deploy requires the generated, non-cacheable workspace deploy-local target. Run the supported workspace upgrade and workspace-health check.');
29
124
  }
30
125
  const nx = (0, nx_workspace_js_1.resolveWorkspaceNxRuntime)(workspaceRoot);
126
+ const startedAtMs = (output?.now ?? Date.now)();
31
127
  const result = await run({
32
128
  executable: process.execPath,
33
129
  args: [
@@ -48,11 +144,31 @@ async function executeDeployCommand(command, workspaceRoot, environment, run = r
48
144
  NX_TUI: 'false',
49
145
  NX_TASKS_RUNNER_DYNAMIC_OUTPUT: 'false',
50
146
  },
147
+ // With --json, native output moves to stderr so stdout carries only the
148
+ // structured result; stdin stays inherited.
149
+ ...(structured ? { stdio: ['inherit', 2, 'inherit'] } : {}),
51
150
  });
52
151
  if (result.error)
53
152
  throw result.error;
54
- if (result.signal)
55
- return 128 + (node_os_1.constants.signals[result.signal] ?? 1);
56
- return result.status ?? 1;
153
+ const exitCode = result.signal
154
+ ? 128 + (node_os_1.constants.signals[result.signal] ?? 1)
155
+ : (result.status ?? 1);
156
+ if (structured && output) {
157
+ const fresh = readFreshDevelopmentReceipt(workspaceRoot, startedAtMs);
158
+ const payload = {
159
+ exitCode,
160
+ kind: exports.DEPLOY_RESULT_KIND,
161
+ outcome: fresh
162
+ ? fresh.receipt.outcome
163
+ : 'receipt-unavailable',
164
+ projects: fresh?.projects ?? [],
165
+ receipt: fresh?.receipt ?? null,
166
+ receiptPath: exports.DEPLOY_RECEIPT_PATH,
167
+ schemaVersion: 1,
168
+ stage: 'development',
169
+ };
170
+ output.stdout(`${JSON.stringify(payload, null, 2)}\n`);
171
+ }
172
+ return exitCode;
57
173
  }
58
174
  //# sourceMappingURL=deploy-runtime.js.map
@@ -97,6 +97,8 @@ export interface DeployCliCommand {
97
97
  stage: 'development' | 'staging' | 'production';
98
98
  profile?: string;
99
99
  expectedRoleArn: string;
100
+ /** Development only: print one bounded receipt result on stdout. */
101
+ json?: true;
100
102
  slices?: string[];
101
103
  }
102
104
  export type ParsedEbkCommand = AgentCliCommand | CliHelpCommand | CliVersionCommand | DeployCliCommand | DevCliCommand | SecretCliCommand;
@@ -501,6 +501,7 @@ function parseEbkArguments(arguments_) {
501
501
  },
502
502
  profile: { kind: 'string' },
503
503
  expectedRoleArn: { kind: 'string' },
504
+ json: { kind: 'flag' },
504
505
  });
505
506
  const stages = ['development', 'staging', 'production'];
506
507
  const positionalStage = stages.includes(tokens.positionals[0])
@@ -515,6 +516,12 @@ function parseEbkArguments(arguments_) {
515
516
  stage !== 'production') {
516
517
  return usageError('ebk deploy stage must be development, staging, or production.');
517
518
  }
519
+ const json = booleanOption(tokens, 'json');
520
+ if (json && stage !== 'development') {
521
+ // Protected stages keep native output and the human-typed TTY
522
+ // acknowledgement; a structured mode must never stand in for either.
523
+ return usageError('ebk deploy --json is available only for development; staging and production keep native output and the interactive acknowledgement.');
524
+ }
518
525
  const expectedRoleArn = stringOption(tokens, 'expectedRoleArn');
519
526
  if (!expectedRoleArn ||
520
527
  !/^arn:(aws|aws-us-gov|aws-cn):iam::[0-9]{12}:role\/[A-Za-z0-9+=,.@_/-]{1,512}$/.test(expectedRoleArn)) {
@@ -535,6 +542,7 @@ function parseEbkArguments(arguments_) {
535
542
  ...(profile === undefined
536
543
  ? {}
537
544
  : { profile: (0, local_context_js_1.normalizeLocalDevProfile)(profile) }),
545
+ ...(json ? { json: true } : {}),
538
546
  ...(slices.length === 0 ? {} : { slices }),
539
547
  };
540
548
  }
package/src/cli/main.js CHANGED
@@ -57,6 +57,7 @@ const DEPLOY_HELP = `Empire Builder Kit local deployment
57
57
 
58
58
  Usage:
59
59
  ebk deploy [development|staging|production] [slice...] --expectedRoleArn=<role-arn> [--profile=<aws-profile>]
60
+ ebk deploy development [slice...] --expectedRoleArn=<role-arn> [--profile=<aws-profile>] --json
60
61
 
61
62
  The stage defaults to development; --stage=<stage> is also supported instead
62
63
  of a positional stage. Account and Region come from committed repository policy.
@@ -65,9 +66,15 @@ Slices, declared migrations and deployed smoke. Supply billing to deploy only
65
66
  that Slice after Foundation; omit Slice names for the complete stage. Other
66
67
  Slices are not redeployed and must already exist if the feature needs them.
67
68
  Native output and terminal
68
- interaction are inherited; --json and arbitrary Nx/SST options are not accepted.
69
+ interaction are inherited; arbitrary Nx/SST options are not accepted.
69
70
  The existing deployment receipt is the structured result, not a CLI success seal.
70
71
 
72
+ --json is accepted for development only. Native output then moves to stderr,
73
+ and stdout carries one bounded, secret-free result after the run: the
74
+ deployment receipt this run wrote and each project's target outcome (receipt
75
+ null when none was written). The exit code is the lifecycle's own. --json is
76
+ refused for staging and production and never replaces their acknowledgement.
77
+
71
78
  Development allows dirty source without a prompt. Local staging requires a
72
79
  clean commit and its TTY acknowledgement. Starter production requires a clean
73
80
  commit and the exact deploy:production:<account>:<region>:<source-commit> TTY
@@ -132,9 +139,12 @@ Usage:
132
139
  ebk secret set <slice> <logical-name>
133
140
  ebk secret remove <slice> <logical-name>
134
141
 
135
- These human-only commands derive the personal stage and read the saved
136
- development profile and Region. Secret values are accepted only through a
137
- hidden local TTY and are never accepted through argv, environment, or files.`;
142
+ Both commands derive the personal stage and read the saved development
143
+ profile and Region. set accepts the value only through a hidden local TTY,
144
+ never through argv, environment, or files, and refuses agent and CI
145
+ environments: a human runs it in a terminal no agent spawned. remove carries
146
+ no value; an agent may start it, and the human types the hidden
147
+ REMOVE <logical-name> confirmation in that interactive terminal.`;
138
148
  function inspectWorkspaceFilesystemIdentity(path) {
139
149
  const status = (0, node_fs_1.statSync)(path, { bigint: true });
140
150
  return {
@@ -450,7 +460,7 @@ function secretCommandFailure(error) {
450
460
  async function executeSecretCommand(command, dispatch) {
451
461
  try {
452
462
  const ceremony = dispatch.dependencies.secretCeremony(dispatch.environment);
453
- (0, personal_command_js_1.assertHumanSecretCeremony)(ceremony);
463
+ (0, personal_command_js_1.assertHumanSecretCeremony)(ceremony, command.command);
454
464
  const common = dispatch.dependencies.gitCommonDirectory(dispatch.workspaceRoot);
455
465
  const saved = dispatch.dependencies.readContext(common);
456
466
  if (saved.kind !== 'ready') {
@@ -1159,6 +1169,8 @@ function processAbortSignal() {
1159
1169
  };
1160
1170
  }
1161
1171
  function commandJson(command) {
1172
+ if (command?.kind === 'deploy')
1173
+ return command.json === true;
1162
1174
  return (command?.kind === 'agent' || command?.kind === 'dev') && command.json;
1163
1175
  }
1164
1176
  function commandName(command) {
@@ -1280,7 +1292,9 @@ async function runEbkCli(arguments_, io = {}, overrides = {}) {
1280
1292
  const dependencies = defaultDependencies(overrides);
1281
1293
  const root = dependencies.resolveWorkspaceRoot(io.cwd ?? process.cwd());
1282
1294
  if (parsed.kind === 'deploy') {
1283
- return await dependencies.executeDeploy(parsed, root, io.environment ?? process.env);
1295
+ return parsed.json
1296
+ ? await dependencies.executeDeploy(parsed, root, io.environment ?? process.env, undefined, { stdout: output.stdout })
1297
+ : await dependencies.executeDeploy(parsed, root, io.environment ?? process.env);
1284
1298
  }
1285
1299
  dependencies.changeDirectory(root);
1286
1300
  if (!io.abortSignal)
@@ -29,6 +29,15 @@ export interface WorkspaceSstSecretInvocation {
29
29
  operation: 'remove' | 'set';
30
30
  }
31
31
  export type PersonalSecretProcessSpawner = typeof spawn;
32
+ /**
33
+ * Markers that identify an agent- or CI-owned process. `ebk secret set`
34
+ * refuses them because a hidden TTY prompt does not hide the value from the
35
+ * process that owns the terminal; the human runs set in a terminal no agent
36
+ * spawned. `ebk secret remove` carries no value and is not gated by this
37
+ * list; it still requires an interactive terminal and the hidden typed
38
+ * confirmation. Only the variable name is ever reported, never its value.
39
+ */
40
+ declare const agentEnvironmentNames: readonly ["BUILDKITE", "CI", "CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT", "CODEBUILD_BUILD_ID", "CODEX_SANDBOX", "CODEX_THREAD_ID", "CURSOR_AGENT", "EBK_AGENT_DELEGATED", "GITHUB_ACTIONS", "JENKINS_URL", "TF_BUILD"];
32
41
  /**
33
42
  * Binds the stored SST value to one personal stage, declaration, and value
34
43
  * shape. Raw SST values and values inherited from another stage cannot satisfy
@@ -40,6 +49,8 @@ export declare function encodePersonalSstSecretEnvelope(input: {
40
49
  stage: string;
41
50
  valueKind: SecretMetadataDeclaration['valueKind'];
42
51
  }): Uint8Array;
52
+ /** Returns the first detected marker variable name, never its value. */
53
+ export declare function detectedAgentOrCiMarker(environment: NodeJS.ProcessEnv): (typeof agentEnvironmentNames)[number] | undefined;
43
54
  export declare function detectDelegatedAgentOrCi(environment: NodeJS.ProcessEnv): boolean;
44
55
  export declare function createHumanSecretCeremony(environment: NodeJS.ProcessEnv, stdinIsTty: boolean, outputIsTty: boolean): HumanSecretCeremony;
45
56
  export declare function loadPersonalOperatorSecret(input: {
@@ -79,3 +90,4 @@ export declare function createWorkspacePinnedSstSecretStore(input: {
79
90
  signal?: AbortSignal;
80
91
  spawnProcess?: PersonalSecretProcessSpawner;
81
92
  }): PersonalSstSecretStore;
93
+ export {};
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PersonalSecretRuntimeError = void 0;
4
4
  exports.encodePersonalSstSecretEnvelope = encodePersonalSstSecretEnvelope;
5
+ exports.detectedAgentOrCiMarker = detectedAgentOrCiMarker;
5
6
  exports.detectDelegatedAgentOrCi = detectDelegatedAgentOrCi;
6
7
  exports.createHumanSecretCeremony = createHumanSecretCeremony;
7
8
  exports.loadPersonalOperatorSecret = loadPersonalOperatorSecret;
@@ -44,6 +45,14 @@ class PersonalSecretRuntimeError extends Error {
44
45
  }
45
46
  }
46
47
  exports.PersonalSecretRuntimeError = PersonalSecretRuntimeError;
48
+ /**
49
+ * Markers that identify an agent- or CI-owned process. `ebk secret set`
50
+ * refuses them because a hidden TTY prompt does not hide the value from the
51
+ * process that owns the terminal; the human runs set in a terminal no agent
52
+ * spawned. `ebk secret remove` carries no value and is not gated by this
53
+ * list; it still requires an interactive terminal and the hidden typed
54
+ * confirmation. Only the variable name is ever reported, never its value.
55
+ */
47
56
  const agentEnvironmentNames = [
48
57
  'BUILDKITE',
49
58
  'CI',
@@ -124,13 +133,19 @@ function encodePersonalSstSecretEnvelope(input) {
124
133
  envelope.fill(0);
125
134
  }
126
135
  }
127
- function detectDelegatedAgentOrCi(environment) {
128
- return agentEnvironmentNames.some((name) => environment[name] !== undefined &&
136
+ /** Returns the first detected marker variable name, never its value. */
137
+ function detectedAgentOrCiMarker(environment) {
138
+ return agentEnvironmentNames.find((name) => environment[name] !== undefined &&
129
139
  (name !== 'CI' || enabledEnvironmentFlag(environment[name])));
130
140
  }
141
+ function detectDelegatedAgentOrCi(environment) {
142
+ return detectedAgentOrCiMarker(environment) !== undefined;
143
+ }
131
144
  function createHumanSecretCeremony(environment, stdinIsTty, outputIsTty) {
145
+ const detectedMarker = detectedAgentOrCiMarker(environment);
132
146
  return (0, personal_command_js_1.issueHumanSecretCeremony)({
133
- agentDelegated: detectDelegatedAgentOrCi(environment),
147
+ agentDelegated: detectedMarker !== undefined,
148
+ ...(detectedMarker === undefined ? {} : { detectedMarker }),
134
149
  interactive: stdinIsTty && outputIsTty,
135
150
  stdinIsTty,
136
151
  });
@@ -6,7 +6,7 @@ exports.createTtySecretConfirmation = createTtySecretConfirmation;
6
6
  const MAX_HIDDEN_INPUT_BYTES = 256 * 1024;
7
7
  class SecretTerminalError extends Error {
8
8
  constructor() {
9
- super('The personal secret ceremony requires a local interactive TTY.');
9
+ super('The personal secret prompt needs a local interactive TTY that a human types into, and stops on Ctrl-C or end of input. Run ebk secret set in a terminal no agent spawned; an agent may start ebk secret remove only where the human can type the hidden confirmation.');
10
10
  this.name = 'SecretTerminalError';
11
11
  }
12
12
  }
@@ -1 +1 @@
1
- export declare const EBK_NODE_22_ALPINE_IMAGE: "node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2";
1
+ export declare const EBK_NODE_22_ALPINE_IMAGE: "node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402";
@@ -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:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2';
4
+ exports.EBK_NODE_22_ALPINE_IMAGE = 'node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402';
5
5
  //# sourceMappingURL=container-images.js.map
@@ -34,6 +34,16 @@ export declare class DeveloperAgentOperationRegistryError extends Error {
34
34
  readonly classification: "unsupported-operation";
35
35
  constructor();
36
36
  }
37
+ /**
38
+ * Non-plannable local targets name their real owner. Each string states only
39
+ * behaviour that exists: none of them promises a Developer-Agent plan entry.
40
+ */
41
+ export declare const DEPLOY_LOCAL_OWNER_RATIONALE = "Development deployment is owned by ebk deploy development [slice]: run that command directly with the saved development --profile and the policy-derived --expectedRoleArn. This root target is its Nx implementation, not a separate entry point.";
42
+ export declare const DOCKER_BUILD_OWNER_RATIONALE = "Local workload image builds use the host Docker daemon outside any bounded owner. Verify workload source with its build, test and typecheck targets; ebk deploy development <slice> builds the deployable image.";
43
+ export declare const WORKLOAD_DEV_OWNER_RATIONALE = "A hot workload process started outside the ebk dev Fleet has no owned port, lifecycle or cleanup. Verify Service and Job source with its test, typecheck and build targets and exercise it through ebk deploy development <slice>.";
44
+ export declare const FORMAT_WRITE_OWNER_RATIONALE = "Formatting rewrites source files only: run pnpm nx format:write directly as part of a source edit, then rerun the required verification.";
45
+ export declare const GRAPH_INTERACTIVE_RATIONALE = "nx graph starts an interactive browser server and is not a bounded operation; use ebk agent context for the read-only project inventory.";
46
+ export declare const ROUTER_WAF_REHEARSAL_OWNER_RATIONALE = "The Router WAF rehearsal requires an exact --probeUrl that planning cannot derive: run it directly through its Nx target with that option after a development deployment.";
37
47
  /**
38
48
  * Authority classes are not a severity list. In particular, development
39
49
  * mutation plus protected proposal crosses authority domains and therefore