@penvhq/cli 0.4.0 → 0.6.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.
package/dist/index.d.cts CHANGED
@@ -1,14 +1,15 @@
1
1
  import * as citty from 'citty';
2
- import { Sink, Provider, ParameterRef, Scope, RotationMechanism, RotationState } from '@penvhq/core';
2
+ import { ProjectionProvider, Provider, ParameterRef, Scope, AnyProvider, RotationMechanism, RotationState } from '@penvhq/core';
3
3
 
4
4
  /**
5
5
  * A check reports one of four verdicts. `unknown` — a check that ran but could
6
6
  * not reach a verdict — is never rendered as a pass: "I looked and found nothing
7
7
  * wrong" and "I could not look" are opposite situations with opposite remedies,
8
- * and a write-only sink makes most of what doctor can say the second kind.
8
+ * and a value-withholding destination makes most of what doctor can say the
9
+ * second kind.
9
10
  */
10
11
  type DoctorSeverity = "pass" | "warning" | "failure" | "unknown";
11
- type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "rotation-overdue" | "rotation-stuck" | "provider-value-drift" | "provider" | "sink-unreachable" | "sink-name-drift" | "sink-manual-edit" | "sink-value-drift";
12
+ type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "rotation-overdue" | "rotation-stuck" | "provider-value-drift" | "provider" | "projection-unreachable" | "projection-name-drift" | "projection-manual-edit" | "projection-value-drift" | "environment-flag-shadow";
12
13
  interface DoctorFinding {
13
14
  readonly check: DoctorCheck;
14
15
  readonly severity: DoctorSeverity;
@@ -27,13 +28,15 @@ interface DoctorReport {
27
28
  interface DoctorOptions {
28
29
  readonly cwd: string;
29
30
  readonly environment?: string;
30
- /** Injected in tests: the sink to check against. Defaults to the one the config declares. */
31
- readonly sink?: Sink;
31
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
32
+ readonly envFlags?: readonly string[];
33
+ /** Injected in tests: the projection-holding destination to check against. Defaults to the one the config declares. */
34
+ readonly projection?: ProjectionProvider;
32
35
  /**
33
36
  * Injected in tests: the source-of-truth provider to compare the local tree
34
37
  * against. Defaults to the one the config declares (`sourceProviderFor`).
35
- * Mirrors `sink`, for the same reason — the drift checks stay driveable without
36
- * a live backend.
38
+ * Mirrors `projection`, for the same reason — the drift checks stay driveable
39
+ * without a live backend.
37
40
  */
38
41
  readonly source?: Provider;
39
42
  /** Injected in tests: the wall-clock reading the rotation clocks are read against. Defaults to now. */
@@ -127,6 +130,8 @@ interface FillPrompt {
127
130
  interface FillOptions {
128
131
  readonly cwd: string;
129
132
  readonly environment?: string;
133
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
134
+ readonly envFlags?: readonly string[];
130
135
  /**
131
136
  * How a value is obtained for one prompt. `undefined` or an empty answer skips
132
137
  * the parameter — the readline half lives only in the wrapper, so `runFill`
@@ -176,6 +181,8 @@ declare function renderFill(result: FillResult): string[];
176
181
  interface GenerateOptions {
177
182
  readonly cwd: string;
178
183
  readonly environment?: string;
184
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
185
+ readonly envFlags?: readonly string[];
179
186
  /** Where to write, absolute or relative to `cwd`. Defaults to `.env` at the project root. */
180
187
  readonly out?: string;
181
188
  /** Permits sealed values to be written into the artifact as plaintext. */
@@ -243,14 +250,18 @@ declare function runGet(options: GetOptions): Promise<string>;
243
250
  declare function runExplain(options: GetOptions): Promise<GetExplanation>;
244
251
 
245
252
  /** What init touched, so a caller can report it and a test can assert it. */
246
- type InitTarget = "penv-dir" | "schema" | "config" | "tsconfig" | "gitignore";
253
+ type InitTarget = "penv-dir" | "schema" | "config" | "tsconfig" | "gitignore" | "seam";
247
254
  /**
248
255
  * `conflicted` is the one that is not a success. penv wanted to write something,
249
256
  * found the user's file already saying something else about the same thing, and
250
257
  * left it alone — so the step is reported with a warning rather than a ✓, and the
251
258
  * text says what will not work until the user decides.
259
+ *
260
+ * `info` is a step penv did not perform automatically — a manual instruction (the
261
+ * injection seam for a framework penv cannot scaffold), reported so the user
262
+ * knows the one thing left to do.
252
263
  */
253
- type InitAction = "created" | "kept" | "updated" | "conflicted";
264
+ type InitAction = "created" | "kept" | "updated" | "conflicted" | "info";
254
265
  interface InitStep {
255
266
  readonly target: InitTarget;
256
267
  readonly action: InitAction;
@@ -282,6 +293,13 @@ interface InitDecisions {
282
293
  * offers that.
283
294
  */
284
295
  readonly alias: string;
296
+ /**
297
+ * Whether to inject the validated config into `process.env` for libraries that
298
+ * read it directly, so `env.ts` loads with `{ inject: true }` and penv places
299
+ * the framework's pre-app seam. Off by default and only ever turned on by an
300
+ * explicit yes — a project that reads config only through `@env` gets none.
301
+ */
302
+ readonly inject: boolean;
285
303
  }
286
304
  interface InitResult {
287
305
  readonly root: string;
@@ -410,6 +428,8 @@ interface ValidateResult {
410
428
  interface ValidateOptions {
411
429
  readonly cwd: string;
412
430
  readonly environment?: string;
431
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
432
+ readonly envFlags?: readonly string[];
413
433
  }
414
434
  declare function runValidate(options: ValidateOptions): Promise<ValidateResult>;
415
435
 
@@ -556,26 +576,13 @@ interface MoveResult {
556
576
  declare function runMove(options: MoveOptions): Promise<MoveResult>;
557
577
  declare function renderMove(result: MoveResult): string[];
558
578
 
559
- /**
560
- * `penv pull` — materialise the local `.penv` tree from an environment's
561
- * source-of-truth provider. It is the inverse of the deploy-time injection most
562
- * stacks already have: instead of reading the tree to feed a backend, it reads
563
- * the backend to feed the tree.
564
- *
565
- * It only means anything when the environment declares a real backend
566
- * (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
567
- * and pulling copies it down so every other command — which reads the local tree
568
- * — sees it. An environment with no separate `providers` entry has the local
569
- * tree *as* its source of truth, so a pull would be the tree copying onto
570
- * itself; that degenerate case is reported as nothing to do, never a self-copy.
571
- *
572
- * Values cross verbatim. They are opaque envelope strings the source holds and
573
- * penv does not open here — a sealed value stays sealed, byte-for-byte, so the
574
- * key that opens it never has to be present to pull it.
575
- */
576
579
  interface PullOptions {
577
580
  readonly cwd: string;
578
581
  readonly environment?: string;
582
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
583
+ readonly envFlags?: readonly string[];
584
+ /** Injected in tests: the source provider. Defaults to the one the config declares. */
585
+ readonly source?: AnyProvider;
579
586
  }
580
587
  interface PullResult {
581
588
  readonly environment: string;
@@ -593,6 +600,12 @@ interface PullResult {
593
600
  readonly meta: number;
594
601
  /** Distinct parameters the pull touched, at any scope. */
595
602
  readonly refs: number;
603
+ /**
604
+ * True when the source declares `readsValues: false`: names and meta came
605
+ * down, values stayed absent — the destination never returns one, and the
606
+ * pull says so rather than dressing emptiness as freshness.
607
+ */
608
+ readonly valuesUnreadable?: boolean;
596
609
  }
597
610
  declare function runPull(options: PullOptions): Promise<PullResult>;
598
611
  declare function renderPull(result: PullResult): string[];
@@ -602,22 +615,41 @@ declare const LAST_PUSHED_KEY = "lastPushedAt";
602
615
  interface PushOptions {
603
616
  readonly cwd: string;
604
617
  readonly environment?: string;
618
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
619
+ readonly envFlags?: readonly string[];
605
620
  /** Permits sealed values to be decrypted locally and pushed as plaintext for the destination to re-seal. */
606
621
  readonly allowDecrypt?: boolean;
607
- /** Injected in tests: the sink to push to. Defaults to the one the config declares. */
608
- readonly sink?: Sink;
622
+ /** One-shot destination override: a provider package name. Nothing is persisted. */
623
+ readonly destination?: string;
624
+ /** The destination-side place, when `destination` needs one — `--location`. */
625
+ readonly location?: string;
626
+ /** Pre-approves creating a missing destination-side target (`--yes`). */
627
+ readonly yes?: boolean;
628
+ /** Injected in tests: the destination provider. Defaults to the one the config (or `--destination`) declares. */
629
+ readonly provider?: AnyProvider;
630
+ /** Injected in tests: answers the create-target question. Defaults to a terminal prompt. */
631
+ readonly confirm?: (question: string) => Promise<boolean>;
609
632
  /** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
610
633
  readonly now?: string;
611
634
  }
612
635
  interface PushResult {
613
636
  readonly environment: string;
614
- /** The `owner/repo` targeted, when the config named one. */
615
- readonly repo: string | undefined;
637
+ /** The destination provider's type — its package name. */
638
+ readonly destination: string;
639
+ /** What the destination holds, which decided what crossed. */
640
+ readonly mode: "records" | "projection";
641
+ /** The `location` targeted, when one was declared. */
642
+ readonly location: string | undefined;
643
+ /** Values sent — resolved secrets for a projection, value files for records. */
616
644
  readonly pushed: number;
645
+ /** Meta records mirrored. Records mode only. */
646
+ readonly meta: number;
617
647
  readonly repositorySecrets: number;
618
648
  readonly environmentSecrets: number;
619
649
  /** How many were sealed and crossed as plaintext for the destination to re-seal. */
620
650
  readonly decrypted: number;
651
+ /** True when the destination-side target was created by this push, on approval. */
652
+ readonly createdTarget: boolean;
621
653
  }
622
654
  declare function runPush(options: PushOptions): Promise<PushResult>;
623
655
  declare function renderPush(result: PushResult): string[];
package/dist/index.d.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import * as citty from 'citty';
2
- import { Sink, Provider, ParameterRef, Scope, RotationMechanism, RotationState } from '@penvhq/core';
2
+ import { ProjectionProvider, Provider, ParameterRef, Scope, AnyProvider, RotationMechanism, RotationState } from '@penvhq/core';
3
3
 
4
4
  /**
5
5
  * A check reports one of four verdicts. `unknown` — a check that ran but could
6
6
  * not reach a verdict — is never rendered as a pass: "I looked and found nothing
7
7
  * wrong" and "I could not look" are opposite situations with opposite remedies,
8
- * and a write-only sink makes most of what doctor can say the second kind.
8
+ * and a value-withholding destination makes most of what doctor can say the
9
+ * second kind.
9
10
  */
10
11
  type DoctorSeverity = "pass" | "warning" | "failure" | "unknown";
11
- type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "rotation-overdue" | "rotation-stuck" | "provider-value-drift" | "provider" | "sink-unreachable" | "sink-name-drift" | "sink-manual-edit" | "sink-value-drift";
12
+ type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "rotation-overdue" | "rotation-stuck" | "provider-value-drift" | "provider" | "projection-unreachable" | "projection-name-drift" | "projection-manual-edit" | "projection-value-drift" | "environment-flag-shadow";
12
13
  interface DoctorFinding {
13
14
  readonly check: DoctorCheck;
14
15
  readonly severity: DoctorSeverity;
@@ -27,13 +28,15 @@ interface DoctorReport {
27
28
  interface DoctorOptions {
28
29
  readonly cwd: string;
29
30
  readonly environment?: string;
30
- /** Injected in tests: the sink to check against. Defaults to the one the config declares. */
31
- readonly sink?: Sink;
31
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
32
+ readonly envFlags?: readonly string[];
33
+ /** Injected in tests: the projection-holding destination to check against. Defaults to the one the config declares. */
34
+ readonly projection?: ProjectionProvider;
32
35
  /**
33
36
  * Injected in tests: the source-of-truth provider to compare the local tree
34
37
  * against. Defaults to the one the config declares (`sourceProviderFor`).
35
- * Mirrors `sink`, for the same reason — the drift checks stay driveable without
36
- * a live backend.
38
+ * Mirrors `projection`, for the same reason — the drift checks stay driveable
39
+ * without a live backend.
37
40
  */
38
41
  readonly source?: Provider;
39
42
  /** Injected in tests: the wall-clock reading the rotation clocks are read against. Defaults to now. */
@@ -127,6 +130,8 @@ interface FillPrompt {
127
130
  interface FillOptions {
128
131
  readonly cwd: string;
129
132
  readonly environment?: string;
133
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
134
+ readonly envFlags?: readonly string[];
130
135
  /**
131
136
  * How a value is obtained for one prompt. `undefined` or an empty answer skips
132
137
  * the parameter — the readline half lives only in the wrapper, so `runFill`
@@ -176,6 +181,8 @@ declare function renderFill(result: FillResult): string[];
176
181
  interface GenerateOptions {
177
182
  readonly cwd: string;
178
183
  readonly environment?: string;
184
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
185
+ readonly envFlags?: readonly string[];
179
186
  /** Where to write, absolute or relative to `cwd`. Defaults to `.env` at the project root. */
180
187
  readonly out?: string;
181
188
  /** Permits sealed values to be written into the artifact as plaintext. */
@@ -243,14 +250,18 @@ declare function runGet(options: GetOptions): Promise<string>;
243
250
  declare function runExplain(options: GetOptions): Promise<GetExplanation>;
244
251
 
245
252
  /** What init touched, so a caller can report it and a test can assert it. */
246
- type InitTarget = "penv-dir" | "schema" | "config" | "tsconfig" | "gitignore";
253
+ type InitTarget = "penv-dir" | "schema" | "config" | "tsconfig" | "gitignore" | "seam";
247
254
  /**
248
255
  * `conflicted` is the one that is not a success. penv wanted to write something,
249
256
  * found the user's file already saying something else about the same thing, and
250
257
  * left it alone — so the step is reported with a warning rather than a ✓, and the
251
258
  * text says what will not work until the user decides.
259
+ *
260
+ * `info` is a step penv did not perform automatically — a manual instruction (the
261
+ * injection seam for a framework penv cannot scaffold), reported so the user
262
+ * knows the one thing left to do.
252
263
  */
253
- type InitAction = "created" | "kept" | "updated" | "conflicted";
264
+ type InitAction = "created" | "kept" | "updated" | "conflicted" | "info";
254
265
  interface InitStep {
255
266
  readonly target: InitTarget;
256
267
  readonly action: InitAction;
@@ -282,6 +293,13 @@ interface InitDecisions {
282
293
  * offers that.
283
294
  */
284
295
  readonly alias: string;
296
+ /**
297
+ * Whether to inject the validated config into `process.env` for libraries that
298
+ * read it directly, so `env.ts` loads with `{ inject: true }` and penv places
299
+ * the framework's pre-app seam. Off by default and only ever turned on by an
300
+ * explicit yes — a project that reads config only through `@env` gets none.
301
+ */
302
+ readonly inject: boolean;
285
303
  }
286
304
  interface InitResult {
287
305
  readonly root: string;
@@ -410,6 +428,8 @@ interface ValidateResult {
410
428
  interface ValidateOptions {
411
429
  readonly cwd: string;
412
430
  readonly environment?: string;
431
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
432
+ readonly envFlags?: readonly string[];
413
433
  }
414
434
  declare function runValidate(options: ValidateOptions): Promise<ValidateResult>;
415
435
 
@@ -556,26 +576,13 @@ interface MoveResult {
556
576
  declare function runMove(options: MoveOptions): Promise<MoveResult>;
557
577
  declare function renderMove(result: MoveResult): string[];
558
578
 
559
- /**
560
- * `penv pull` — materialise the local `.penv` tree from an environment's
561
- * source-of-truth provider. It is the inverse of the deploy-time injection most
562
- * stacks already have: instead of reading the tree to feed a backend, it reads
563
- * the backend to feed the tree.
564
- *
565
- * It only means anything when the environment declares a real backend
566
- * (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
567
- * and pulling copies it down so every other command — which reads the local tree
568
- * — sees it. An environment with no separate `providers` entry has the local
569
- * tree *as* its source of truth, so a pull would be the tree copying onto
570
- * itself; that degenerate case is reported as nothing to do, never a self-copy.
571
- *
572
- * Values cross verbatim. They are opaque envelope strings the source holds and
573
- * penv does not open here — a sealed value stays sealed, byte-for-byte, so the
574
- * key that opens it never has to be present to pull it.
575
- */
576
579
  interface PullOptions {
577
580
  readonly cwd: string;
578
581
  readonly environment?: string;
582
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
583
+ readonly envFlags?: readonly string[];
584
+ /** Injected in tests: the source provider. Defaults to the one the config declares. */
585
+ readonly source?: AnyProvider;
579
586
  }
580
587
  interface PullResult {
581
588
  readonly environment: string;
@@ -593,6 +600,12 @@ interface PullResult {
593
600
  readonly meta: number;
594
601
  /** Distinct parameters the pull touched, at any scope. */
595
602
  readonly refs: number;
603
+ /**
604
+ * True when the source declares `readsValues: false`: names and meta came
605
+ * down, values stayed absent — the destination never returns one, and the
606
+ * pull says so rather than dressing emptiness as freshness.
607
+ */
608
+ readonly valuesUnreadable?: boolean;
596
609
  }
597
610
  declare function runPull(options: PullOptions): Promise<PullResult>;
598
611
  declare function renderPull(result: PullResult): string[];
@@ -602,22 +615,41 @@ declare const LAST_PUSHED_KEY = "lastPushedAt";
602
615
  interface PushOptions {
603
616
  readonly cwd: string;
604
617
  readonly environment?: string;
618
+ /** Bare flags the command did not declare — environment shorthands, judged against the whitelist. */
619
+ readonly envFlags?: readonly string[];
605
620
  /** Permits sealed values to be decrypted locally and pushed as plaintext for the destination to re-seal. */
606
621
  readonly allowDecrypt?: boolean;
607
- /** Injected in tests: the sink to push to. Defaults to the one the config declares. */
608
- readonly sink?: Sink;
622
+ /** One-shot destination override: a provider package name. Nothing is persisted. */
623
+ readonly destination?: string;
624
+ /** The destination-side place, when `destination` needs one — `--location`. */
625
+ readonly location?: string;
626
+ /** Pre-approves creating a missing destination-side target (`--yes`). */
627
+ readonly yes?: boolean;
628
+ /** Injected in tests: the destination provider. Defaults to the one the config (or `--destination`) declares. */
629
+ readonly provider?: AnyProvider;
630
+ /** Injected in tests: answers the create-target question. Defaults to a terminal prompt. */
631
+ readonly confirm?: (question: string) => Promise<boolean>;
609
632
  /** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
610
633
  readonly now?: string;
611
634
  }
612
635
  interface PushResult {
613
636
  readonly environment: string;
614
- /** The `owner/repo` targeted, when the config named one. */
615
- readonly repo: string | undefined;
637
+ /** The destination provider's type — its package name. */
638
+ readonly destination: string;
639
+ /** What the destination holds, which decided what crossed. */
640
+ readonly mode: "records" | "projection";
641
+ /** The `location` targeted, when one was declared. */
642
+ readonly location: string | undefined;
643
+ /** Values sent — resolved secrets for a projection, value files for records. */
616
644
  readonly pushed: number;
645
+ /** Meta records mirrored. Records mode only. */
646
+ readonly meta: number;
617
647
  readonly repositorySecrets: number;
618
648
  readonly environmentSecrets: number;
619
649
  /** How many were sealed and crossed as plaintext for the destination to re-seal. */
620
650
  readonly decrypted: number;
651
+ /** True when the destination-side target was created by this push, on approval. */
652
+ readonly createdTarget: boolean;
621
653
  }
622
654
  declare function runPush(options: PushOptions): Promise<PushResult>;
623
655
  declare function renderPush(result: PushResult): string[];