@penvhq/cli 0.1.0 → 0.3.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.cjs +1042 -261
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -7
- package/dist/index.d.ts +165 -7
- package/dist/index.js +986 -197
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as citty from 'citty';
|
|
2
|
-
import { Sink, ParameterRef, Scope } from '@penvhq/core';
|
|
2
|
+
import { Sink, Provider, ParameterRef, Scope, 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
|
|
@@ -8,7 +8,7 @@ import { Sink, ParameterRef, Scope } from '@penvhq/core';
|
|
|
8
8
|
* and a write-only sink makes most of what doctor can say the second kind.
|
|
9
9
|
*/
|
|
10
10
|
type DoctorSeverity = "pass" | "warning" | "failure" | "unknown";
|
|
11
|
-
type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "provider" | "sink-unreachable" | "sink-name-drift" | "sink-manual-edit" | "sink-value-drift";
|
|
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
12
|
interface DoctorFinding {
|
|
13
13
|
readonly check: DoctorCheck;
|
|
14
14
|
readonly severity: DoctorSeverity;
|
|
@@ -29,6 +29,17 @@ interface DoctorOptions {
|
|
|
29
29
|
readonly environment?: string;
|
|
30
30
|
/** Injected in tests: the sink to check against. Defaults to the one the config declares. */
|
|
31
31
|
readonly sink?: Sink;
|
|
32
|
+
/**
|
|
33
|
+
* Injected in tests: the source-of-truth provider to compare the local tree
|
|
34
|
+
* 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.
|
|
37
|
+
*/
|
|
38
|
+
readonly source?: Provider;
|
|
39
|
+
/** Injected in tests: the wall-clock reading the rotation clocks are read against. Defaults to now. */
|
|
40
|
+
readonly now?: string;
|
|
41
|
+
/** Injected in tests: how long a `dual-valid` window may stay open before it reads as stuck. Defaults to 24h. */
|
|
42
|
+
readonly stuckThresholdMs?: number;
|
|
32
43
|
}
|
|
33
44
|
declare function runDoctor(options: DoctorOptions): Promise<DoctorReport>;
|
|
34
45
|
declare function renderDoctor(report: DoctorReport): string[];
|
|
@@ -53,10 +64,8 @@ interface SetResult {
|
|
|
53
64
|
/**
|
|
54
65
|
* Writes one value file, sealing it when meta says the parameter is a secret.
|
|
55
66
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* is validated *against* the policy, so a marker chosen at the keyboard would
|
|
59
|
-
* invert the direction the check runs in. The policy decides; `set` obeys.
|
|
67
|
+
* The scope is chosen from the flags, then the seal-and-twin write is the shared
|
|
68
|
+
* {@link sealAwareWrite}, against the local tree — the store `set` always edits.
|
|
60
69
|
*/
|
|
61
70
|
declare function runSet(options: SetOptions): Promise<SetResult>;
|
|
62
71
|
|
|
@@ -74,6 +83,76 @@ interface ResealResult {
|
|
|
74
83
|
declare function runEncrypt(options: ResealOptions): Promise<ResealResult>;
|
|
75
84
|
declare function runDecrypt(options: ResealOptions): Promise<ResealResult>;
|
|
76
85
|
|
|
86
|
+
/**
|
|
87
|
+
* `penv fill` — walk the schema's required-but-missing parameters and ask for
|
|
88
|
+
* each one, deriving the value file's name so the user never has to.
|
|
89
|
+
*
|
|
90
|
+
* The schema-first flow writes `.penv/env.ts` before any value exists, and there
|
|
91
|
+
* the user hits a translation they should not have to make: `databaseUrl` in the
|
|
92
|
+
* schema is `database-url` on disk, and typing the wrong one writes a file the
|
|
93
|
+
* schema still cannot see. `fill` reads the same declared drift `validate`
|
|
94
|
+
* computes, and for each missing parameter asks for a value and writes it through
|
|
95
|
+
* the one writer — `runSet` — deriving the kebab filename from the schema key.
|
|
96
|
+
*
|
|
97
|
+
* A value is never invented: a blank answer skips the parameter, because the
|
|
98
|
+
* silent value reaching runtime is the failure penv exists to delete, and a
|
|
99
|
+
* placeholder written here is exactly that value by a friendlier route.
|
|
100
|
+
*/
|
|
101
|
+
/** One question `fill` puts to the user: which parameter, in which environment. */
|
|
102
|
+
interface FillPrompt {
|
|
103
|
+
/** The value file's key, kebab and slash-separated — the name the user need never derive. */
|
|
104
|
+
readonly parameter: string;
|
|
105
|
+
readonly environment: string;
|
|
106
|
+
/**
|
|
107
|
+
* Whether meta says this is a secret. Carried so a wrapper can mute the echo;
|
|
108
|
+
* v1 does not, and the drift carries no meta, so this is `false` today.
|
|
109
|
+
*/
|
|
110
|
+
readonly secret: boolean;
|
|
111
|
+
readonly description?: string;
|
|
112
|
+
}
|
|
113
|
+
interface FillOptions {
|
|
114
|
+
readonly cwd: string;
|
|
115
|
+
readonly environment?: string;
|
|
116
|
+
/**
|
|
117
|
+
* How a value is obtained for one prompt. `undefined` or an empty answer skips
|
|
118
|
+
* the parameter — the readline half lives only in the wrapper, so `runFill`
|
|
119
|
+
* stays pure and unit-testable.
|
|
120
|
+
*/
|
|
121
|
+
readonly ask: (prompt: FillPrompt) => Promise<string | undefined>;
|
|
122
|
+
}
|
|
123
|
+
interface FillResult {
|
|
124
|
+
readonly environment: string;
|
|
125
|
+
/** The value files written, one per answered prompt. */
|
|
126
|
+
readonly written: ReadonlyArray<{
|
|
127
|
+
readonly parameter: string;
|
|
128
|
+
/** The value file written, relative to `.penv/`. */
|
|
129
|
+
readonly location: string;
|
|
130
|
+
readonly encrypted: boolean;
|
|
131
|
+
}>;
|
|
132
|
+
/** The parameters a blank answer left for later — never written as an empty value. */
|
|
133
|
+
readonly skipped: readonly string[];
|
|
134
|
+
/**
|
|
135
|
+
* The declared keys no filename reaches (`apiURL`, a reserved token). `fill`
|
|
136
|
+
* cannot ask for a value it could never write, so it carries the rename remedy
|
|
137
|
+
* out rather than prompting for a file that would error.
|
|
138
|
+
*/
|
|
139
|
+
readonly unreachable: ReadonlyArray<{
|
|
140
|
+
readonly subject: string;
|
|
141
|
+
readonly remedy: string;
|
|
142
|
+
}>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Asks for every declared-but-missing parameter, and writes the ones answered.
|
|
146
|
+
*
|
|
147
|
+
* The drift is `validate`'s, not a second reading of the schema: `runValidate`
|
|
148
|
+
* already computes exactly the required-but-absent set, so `fill` and `validate`
|
|
149
|
+
* can never disagree about what is missing. The writing is `runSet`'s, so a
|
|
150
|
+
* filled secret is sealed exactly as a `set` one is — `fill` owns neither the
|
|
151
|
+
* resolution nor the write, only the prompting between them.
|
|
152
|
+
*/
|
|
153
|
+
declare function runFill(options: FillOptions): Promise<FillResult>;
|
|
154
|
+
declare function renderFill(result: FillResult): string[];
|
|
155
|
+
|
|
77
156
|
interface GenerateOptions {
|
|
78
157
|
readonly cwd: string;
|
|
79
158
|
readonly environment?: string;
|
|
@@ -437,6 +516,47 @@ interface MoveResult {
|
|
|
437
516
|
declare function runMove(options: MoveOptions): Promise<MoveResult>;
|
|
438
517
|
declare function renderMove(result: MoveResult): string[];
|
|
439
518
|
|
|
519
|
+
/**
|
|
520
|
+
* `penv pull` — materialise the local `.penv` tree from an environment's
|
|
521
|
+
* source-of-truth provider. It is the inverse of the deploy-time injection most
|
|
522
|
+
* stacks already have: instead of reading the tree to feed a backend, it reads
|
|
523
|
+
* the backend to feed the tree.
|
|
524
|
+
*
|
|
525
|
+
* It only means anything when the environment declares a real backend
|
|
526
|
+
* (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
|
|
527
|
+
* and pulling copies it down so every other command — which reads the local tree
|
|
528
|
+
* — sees it. An environment with no separate `providers` entry has the local
|
|
529
|
+
* tree *as* its source of truth, so a pull would be the tree copying onto
|
|
530
|
+
* itself; that degenerate case is reported as nothing to do, never a self-copy.
|
|
531
|
+
*
|
|
532
|
+
* Values cross verbatim. They are opaque envelope strings the source holds and
|
|
533
|
+
* penv does not open here — a sealed value stays sealed, byte-for-byte, so the
|
|
534
|
+
* key that opens it never has to be present to pull it.
|
|
535
|
+
*/
|
|
536
|
+
interface PullOptions {
|
|
537
|
+
readonly cwd: string;
|
|
538
|
+
readonly environment?: string;
|
|
539
|
+
}
|
|
540
|
+
interface PullResult {
|
|
541
|
+
readonly environment: string;
|
|
542
|
+
/** The source provider's type — `filesystem` when the environment declares no separate backend. */
|
|
543
|
+
readonly source: string;
|
|
544
|
+
/**
|
|
545
|
+
* True when the source *is* the local tree, so there was nothing to pull. The
|
|
546
|
+
* caller distinguishes "pulled nothing because the backend was empty" from
|
|
547
|
+
* "there is no backend to pull from" — opposite situations.
|
|
548
|
+
*/
|
|
549
|
+
readonly localSource: boolean;
|
|
550
|
+
/** Value files written into the local tree. */
|
|
551
|
+
readonly values: number;
|
|
552
|
+
/** Meta files written into the local tree. */
|
|
553
|
+
readonly meta: number;
|
|
554
|
+
/** Distinct parameters the pull touched, at any scope. */
|
|
555
|
+
readonly refs: number;
|
|
556
|
+
}
|
|
557
|
+
declare function runPull(options: PullOptions): Promise<PullResult>;
|
|
558
|
+
declare function renderPull(result: PullResult): string[];
|
|
559
|
+
|
|
440
560
|
/** The per-environment meta field recording penv's last push, compared against the destination's `updatedAt`. */
|
|
441
561
|
declare const LAST_PUSHED_KEY = "lastPushedAt";
|
|
442
562
|
interface PushOptions {
|
|
@@ -475,6 +595,44 @@ interface RemoveResult {
|
|
|
475
595
|
}
|
|
476
596
|
declare function runRemove(options: RemoveOptions): Promise<RemoveResult>;
|
|
477
597
|
|
|
598
|
+
interface RotateOptions {
|
|
599
|
+
readonly cwd: string;
|
|
600
|
+
readonly key: string;
|
|
601
|
+
readonly environment?: string;
|
|
602
|
+
/** Open a `dual-valid` window: write the new value while the old is still retained. */
|
|
603
|
+
readonly begin?: boolean;
|
|
604
|
+
/** Close a `dual-valid` window: return to `active`, stamp the completion. */
|
|
605
|
+
readonly complete?: boolean;
|
|
606
|
+
/**
|
|
607
|
+
* The new value. A `begin` and an `atomic-cutover` flip write it; a `complete`
|
|
608
|
+
* does not touch the value at all, so it needs none. Injected in tests; on the
|
|
609
|
+
* CLI it is the positional argument or stdin, the same source `set` reads.
|
|
610
|
+
*/
|
|
611
|
+
readonly value?: string;
|
|
612
|
+
/** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
|
|
613
|
+
readonly now?: string;
|
|
614
|
+
}
|
|
615
|
+
/** The single step a run performed — the three the two mechanisms decompose into. */
|
|
616
|
+
type RotatePhase = "begin" | "complete" | "cutover";
|
|
617
|
+
interface RotateResult {
|
|
618
|
+
readonly parameter: string;
|
|
619
|
+
readonly environment: string;
|
|
620
|
+
readonly mechanism: RotationMechanism;
|
|
621
|
+
readonly phase: RotatePhase;
|
|
622
|
+
/** The source provider's type — where the value and its meta were written. */
|
|
623
|
+
readonly source: string;
|
|
624
|
+
/** True when this run wrote a new value. `begin` and `cutover` do; `complete` does not. */
|
|
625
|
+
readonly wroteValue: boolean;
|
|
626
|
+
/** The rotation state after this run — `rotating` after a begin, `active` otherwise. */
|
|
627
|
+
readonly state: RotationState;
|
|
628
|
+
/** When the current window opened, ISO. Set only after a `begin`, else `null`. */
|
|
629
|
+
readonly rotatingSince: string | null;
|
|
630
|
+
/** When a rotation last completed, ISO. Set after a `complete` or a `cutover`. */
|
|
631
|
+
readonly lastRotated: string | null;
|
|
632
|
+
}
|
|
633
|
+
declare function runRotate(options: RotateOptions): Promise<RotateResult>;
|
|
634
|
+
declare function renderRotate(result: RotateResult): string[];
|
|
635
|
+
|
|
478
636
|
interface WatchOptions {
|
|
479
637
|
readonly cwd: string;
|
|
480
638
|
readonly environment?: string;
|
|
@@ -523,4 +681,4 @@ declare function renderWatch(result: ValidateResult): string[];
|
|
|
523
681
|
declare const main: citty.CommandDef<citty.ArgsDef>;
|
|
524
682
|
declare function runMain(): Promise<void>;
|
|
525
683
|
|
|
526
|
-
export { type DoctorCheck, type DoctorFinding, type DoctorReport, type DoctorSeverity, type GenerateResult, type GetExplanation, type ImportReport, type InitResult, type InitStep, LAST_PUSHED_KEY, type ListResult, type MoveResult, type PushOptions, type PushResult, type RemoveResult, type ResealResult, type SetResult, type ValidateIssue, type ValidateResult, type WatchHandle, type WatchOptions, generateDotenv, importDotenv, insertEnvAlias, main, renderDoctor, renderMove, renderPush, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, runGenerate, runGet, runInit, runList, runMain, runMove, runPush, runRemove, runSet, runValidate, runWatch };
|
|
684
|
+
export { type DoctorCheck, type DoctorFinding, type DoctorReport, type DoctorSeverity, type FillOptions, type FillPrompt, type FillResult, type GenerateResult, type GetExplanation, type ImportReport, type InitResult, type InitStep, LAST_PUSHED_KEY, type ListResult, type MoveResult, type PullOptions, type PullResult, type PushOptions, type PushResult, type RemoveResult, type ResealResult, type RotateOptions, type RotatePhase, type RotateResult, type SetResult, type ValidateIssue, type ValidateResult, type WatchHandle, type WatchOptions, generateDotenv, importDotenv, insertEnvAlias, main, renderDoctor, renderFill, renderMove, renderPull, renderPush, renderRotate, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, runFill, runGenerate, runGet, runInit, runList, runMain, runMove, runPull, runPush, runRemove, runRotate, runSet, runValidate, runWatch };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as citty from 'citty';
|
|
2
|
-
import { Sink, ParameterRef, Scope } from '@penvhq/core';
|
|
2
|
+
import { Sink, Provider, ParameterRef, Scope, 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
|
|
@@ -8,7 +8,7 @@ import { Sink, ParameterRef, Scope } from '@penvhq/core';
|
|
|
8
8
|
* and a write-only sink makes most of what doctor can say the second kind.
|
|
9
9
|
*/
|
|
10
10
|
type DoctorSeverity = "pass" | "warning" | "failure" | "unknown";
|
|
11
|
-
type DoctorCheck = "schema" | "missing" | "declared" | "weak" | "unused" | "unscoped-fallback" | "plaintext-secret" | "public-secret" | "encryption" | "provider" | "sink-unreachable" | "sink-name-drift" | "sink-manual-edit" | "sink-value-drift";
|
|
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
12
|
interface DoctorFinding {
|
|
13
13
|
readonly check: DoctorCheck;
|
|
14
14
|
readonly severity: DoctorSeverity;
|
|
@@ -29,6 +29,17 @@ interface DoctorOptions {
|
|
|
29
29
|
readonly environment?: string;
|
|
30
30
|
/** Injected in tests: the sink to check against. Defaults to the one the config declares. */
|
|
31
31
|
readonly sink?: Sink;
|
|
32
|
+
/**
|
|
33
|
+
* Injected in tests: the source-of-truth provider to compare the local tree
|
|
34
|
+
* 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.
|
|
37
|
+
*/
|
|
38
|
+
readonly source?: Provider;
|
|
39
|
+
/** Injected in tests: the wall-clock reading the rotation clocks are read against. Defaults to now. */
|
|
40
|
+
readonly now?: string;
|
|
41
|
+
/** Injected in tests: how long a `dual-valid` window may stay open before it reads as stuck. Defaults to 24h. */
|
|
42
|
+
readonly stuckThresholdMs?: number;
|
|
32
43
|
}
|
|
33
44
|
declare function runDoctor(options: DoctorOptions): Promise<DoctorReport>;
|
|
34
45
|
declare function renderDoctor(report: DoctorReport): string[];
|
|
@@ -53,10 +64,8 @@ interface SetResult {
|
|
|
53
64
|
/**
|
|
54
65
|
* Writes one value file, sealing it when meta says the parameter is a secret.
|
|
55
66
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* is validated *against* the policy, so a marker chosen at the keyboard would
|
|
59
|
-
* invert the direction the check runs in. The policy decides; `set` obeys.
|
|
67
|
+
* The scope is chosen from the flags, then the seal-and-twin write is the shared
|
|
68
|
+
* {@link sealAwareWrite}, against the local tree — the store `set` always edits.
|
|
60
69
|
*/
|
|
61
70
|
declare function runSet(options: SetOptions): Promise<SetResult>;
|
|
62
71
|
|
|
@@ -74,6 +83,76 @@ interface ResealResult {
|
|
|
74
83
|
declare function runEncrypt(options: ResealOptions): Promise<ResealResult>;
|
|
75
84
|
declare function runDecrypt(options: ResealOptions): Promise<ResealResult>;
|
|
76
85
|
|
|
86
|
+
/**
|
|
87
|
+
* `penv fill` — walk the schema's required-but-missing parameters and ask for
|
|
88
|
+
* each one, deriving the value file's name so the user never has to.
|
|
89
|
+
*
|
|
90
|
+
* The schema-first flow writes `.penv/env.ts` before any value exists, and there
|
|
91
|
+
* the user hits a translation they should not have to make: `databaseUrl` in the
|
|
92
|
+
* schema is `database-url` on disk, and typing the wrong one writes a file the
|
|
93
|
+
* schema still cannot see. `fill` reads the same declared drift `validate`
|
|
94
|
+
* computes, and for each missing parameter asks for a value and writes it through
|
|
95
|
+
* the one writer — `runSet` — deriving the kebab filename from the schema key.
|
|
96
|
+
*
|
|
97
|
+
* A value is never invented: a blank answer skips the parameter, because the
|
|
98
|
+
* silent value reaching runtime is the failure penv exists to delete, and a
|
|
99
|
+
* placeholder written here is exactly that value by a friendlier route.
|
|
100
|
+
*/
|
|
101
|
+
/** One question `fill` puts to the user: which parameter, in which environment. */
|
|
102
|
+
interface FillPrompt {
|
|
103
|
+
/** The value file's key, kebab and slash-separated — the name the user need never derive. */
|
|
104
|
+
readonly parameter: string;
|
|
105
|
+
readonly environment: string;
|
|
106
|
+
/**
|
|
107
|
+
* Whether meta says this is a secret. Carried so a wrapper can mute the echo;
|
|
108
|
+
* v1 does not, and the drift carries no meta, so this is `false` today.
|
|
109
|
+
*/
|
|
110
|
+
readonly secret: boolean;
|
|
111
|
+
readonly description?: string;
|
|
112
|
+
}
|
|
113
|
+
interface FillOptions {
|
|
114
|
+
readonly cwd: string;
|
|
115
|
+
readonly environment?: string;
|
|
116
|
+
/**
|
|
117
|
+
* How a value is obtained for one prompt. `undefined` or an empty answer skips
|
|
118
|
+
* the parameter — the readline half lives only in the wrapper, so `runFill`
|
|
119
|
+
* stays pure and unit-testable.
|
|
120
|
+
*/
|
|
121
|
+
readonly ask: (prompt: FillPrompt) => Promise<string | undefined>;
|
|
122
|
+
}
|
|
123
|
+
interface FillResult {
|
|
124
|
+
readonly environment: string;
|
|
125
|
+
/** The value files written, one per answered prompt. */
|
|
126
|
+
readonly written: ReadonlyArray<{
|
|
127
|
+
readonly parameter: string;
|
|
128
|
+
/** The value file written, relative to `.penv/`. */
|
|
129
|
+
readonly location: string;
|
|
130
|
+
readonly encrypted: boolean;
|
|
131
|
+
}>;
|
|
132
|
+
/** The parameters a blank answer left for later — never written as an empty value. */
|
|
133
|
+
readonly skipped: readonly string[];
|
|
134
|
+
/**
|
|
135
|
+
* The declared keys no filename reaches (`apiURL`, a reserved token). `fill`
|
|
136
|
+
* cannot ask for a value it could never write, so it carries the rename remedy
|
|
137
|
+
* out rather than prompting for a file that would error.
|
|
138
|
+
*/
|
|
139
|
+
readonly unreachable: ReadonlyArray<{
|
|
140
|
+
readonly subject: string;
|
|
141
|
+
readonly remedy: string;
|
|
142
|
+
}>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Asks for every declared-but-missing parameter, and writes the ones answered.
|
|
146
|
+
*
|
|
147
|
+
* The drift is `validate`'s, not a second reading of the schema: `runValidate`
|
|
148
|
+
* already computes exactly the required-but-absent set, so `fill` and `validate`
|
|
149
|
+
* can never disagree about what is missing. The writing is `runSet`'s, so a
|
|
150
|
+
* filled secret is sealed exactly as a `set` one is — `fill` owns neither the
|
|
151
|
+
* resolution nor the write, only the prompting between them.
|
|
152
|
+
*/
|
|
153
|
+
declare function runFill(options: FillOptions): Promise<FillResult>;
|
|
154
|
+
declare function renderFill(result: FillResult): string[];
|
|
155
|
+
|
|
77
156
|
interface GenerateOptions {
|
|
78
157
|
readonly cwd: string;
|
|
79
158
|
readonly environment?: string;
|
|
@@ -437,6 +516,47 @@ interface MoveResult {
|
|
|
437
516
|
declare function runMove(options: MoveOptions): Promise<MoveResult>;
|
|
438
517
|
declare function renderMove(result: MoveResult): string[];
|
|
439
518
|
|
|
519
|
+
/**
|
|
520
|
+
* `penv pull` — materialise the local `.penv` tree from an environment's
|
|
521
|
+
* source-of-truth provider. It is the inverse of the deploy-time injection most
|
|
522
|
+
* stacks already have: instead of reading the tree to feed a backend, it reads
|
|
523
|
+
* the backend to feed the tree.
|
|
524
|
+
*
|
|
525
|
+
* It only means anything when the environment declares a real backend
|
|
526
|
+
* (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
|
|
527
|
+
* and pulling copies it down so every other command — which reads the local tree
|
|
528
|
+
* — sees it. An environment with no separate `providers` entry has the local
|
|
529
|
+
* tree *as* its source of truth, so a pull would be the tree copying onto
|
|
530
|
+
* itself; that degenerate case is reported as nothing to do, never a self-copy.
|
|
531
|
+
*
|
|
532
|
+
* Values cross verbatim. They are opaque envelope strings the source holds and
|
|
533
|
+
* penv does not open here — a sealed value stays sealed, byte-for-byte, so the
|
|
534
|
+
* key that opens it never has to be present to pull it.
|
|
535
|
+
*/
|
|
536
|
+
interface PullOptions {
|
|
537
|
+
readonly cwd: string;
|
|
538
|
+
readonly environment?: string;
|
|
539
|
+
}
|
|
540
|
+
interface PullResult {
|
|
541
|
+
readonly environment: string;
|
|
542
|
+
/** The source provider's type — `filesystem` when the environment declares no separate backend. */
|
|
543
|
+
readonly source: string;
|
|
544
|
+
/**
|
|
545
|
+
* True when the source *is* the local tree, so there was nothing to pull. The
|
|
546
|
+
* caller distinguishes "pulled nothing because the backend was empty" from
|
|
547
|
+
* "there is no backend to pull from" — opposite situations.
|
|
548
|
+
*/
|
|
549
|
+
readonly localSource: boolean;
|
|
550
|
+
/** Value files written into the local tree. */
|
|
551
|
+
readonly values: number;
|
|
552
|
+
/** Meta files written into the local tree. */
|
|
553
|
+
readonly meta: number;
|
|
554
|
+
/** Distinct parameters the pull touched, at any scope. */
|
|
555
|
+
readonly refs: number;
|
|
556
|
+
}
|
|
557
|
+
declare function runPull(options: PullOptions): Promise<PullResult>;
|
|
558
|
+
declare function renderPull(result: PullResult): string[];
|
|
559
|
+
|
|
440
560
|
/** The per-environment meta field recording penv's last push, compared against the destination's `updatedAt`. */
|
|
441
561
|
declare const LAST_PUSHED_KEY = "lastPushedAt";
|
|
442
562
|
interface PushOptions {
|
|
@@ -475,6 +595,44 @@ interface RemoveResult {
|
|
|
475
595
|
}
|
|
476
596
|
declare function runRemove(options: RemoveOptions): Promise<RemoveResult>;
|
|
477
597
|
|
|
598
|
+
interface RotateOptions {
|
|
599
|
+
readonly cwd: string;
|
|
600
|
+
readonly key: string;
|
|
601
|
+
readonly environment?: string;
|
|
602
|
+
/** Open a `dual-valid` window: write the new value while the old is still retained. */
|
|
603
|
+
readonly begin?: boolean;
|
|
604
|
+
/** Close a `dual-valid` window: return to `active`, stamp the completion. */
|
|
605
|
+
readonly complete?: boolean;
|
|
606
|
+
/**
|
|
607
|
+
* The new value. A `begin` and an `atomic-cutover` flip write it; a `complete`
|
|
608
|
+
* does not touch the value at all, so it needs none. Injected in tests; on the
|
|
609
|
+
* CLI it is the positional argument or stdin, the same source `set` reads.
|
|
610
|
+
*/
|
|
611
|
+
readonly value?: string;
|
|
612
|
+
/** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
|
|
613
|
+
readonly now?: string;
|
|
614
|
+
}
|
|
615
|
+
/** The single step a run performed — the three the two mechanisms decompose into. */
|
|
616
|
+
type RotatePhase = "begin" | "complete" | "cutover";
|
|
617
|
+
interface RotateResult {
|
|
618
|
+
readonly parameter: string;
|
|
619
|
+
readonly environment: string;
|
|
620
|
+
readonly mechanism: RotationMechanism;
|
|
621
|
+
readonly phase: RotatePhase;
|
|
622
|
+
/** The source provider's type — where the value and its meta were written. */
|
|
623
|
+
readonly source: string;
|
|
624
|
+
/** True when this run wrote a new value. `begin` and `cutover` do; `complete` does not. */
|
|
625
|
+
readonly wroteValue: boolean;
|
|
626
|
+
/** The rotation state after this run — `rotating` after a begin, `active` otherwise. */
|
|
627
|
+
readonly state: RotationState;
|
|
628
|
+
/** When the current window opened, ISO. Set only after a `begin`, else `null`. */
|
|
629
|
+
readonly rotatingSince: string | null;
|
|
630
|
+
/** When a rotation last completed, ISO. Set after a `complete` or a `cutover`. */
|
|
631
|
+
readonly lastRotated: string | null;
|
|
632
|
+
}
|
|
633
|
+
declare function runRotate(options: RotateOptions): Promise<RotateResult>;
|
|
634
|
+
declare function renderRotate(result: RotateResult): string[];
|
|
635
|
+
|
|
478
636
|
interface WatchOptions {
|
|
479
637
|
readonly cwd: string;
|
|
480
638
|
readonly environment?: string;
|
|
@@ -523,4 +681,4 @@ declare function renderWatch(result: ValidateResult): string[];
|
|
|
523
681
|
declare const main: citty.CommandDef<citty.ArgsDef>;
|
|
524
682
|
declare function runMain(): Promise<void>;
|
|
525
683
|
|
|
526
|
-
export { type DoctorCheck, type DoctorFinding, type DoctorReport, type DoctorSeverity, type GenerateResult, type GetExplanation, type ImportReport, type InitResult, type InitStep, LAST_PUSHED_KEY, type ListResult, type MoveResult, type PushOptions, type PushResult, type RemoveResult, type ResealResult, type SetResult, type ValidateIssue, type ValidateResult, type WatchHandle, type WatchOptions, generateDotenv, importDotenv, insertEnvAlias, main, renderDoctor, renderMove, renderPush, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, runGenerate, runGet, runInit, runList, runMain, runMove, runPush, runRemove, runSet, runValidate, runWatch };
|
|
684
|
+
export { type DoctorCheck, type DoctorFinding, type DoctorReport, type DoctorSeverity, type FillOptions, type FillPrompt, type FillResult, type GenerateResult, type GetExplanation, type ImportReport, type InitResult, type InitStep, LAST_PUSHED_KEY, type ListResult, type MoveResult, type PullOptions, type PullResult, type PushOptions, type PushResult, type RemoveResult, type ResealResult, type RotateOptions, type RotatePhase, type RotateResult, type SetResult, type ValidateIssue, type ValidateResult, type WatchHandle, type WatchOptions, generateDotenv, importDotenv, insertEnvAlias, main, renderDoctor, renderFill, renderMove, renderPull, renderPush, renderRotate, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, runFill, runGenerate, runGet, runInit, runList, runMain, runMove, runPull, runPush, runRemove, runRotate, runSet, runValidate, runWatch };
|