@penvhq/cli 0.1.0 → 0.2.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 +785 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -7
- package/dist/index.d.ts +95 -7
- package/dist/index.js +796 -154
- package/dist/index.js.map +1 -1
- package/package.json +7 -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
|
|
|
@@ -437,6 +446,47 @@ interface MoveResult {
|
|
|
437
446
|
declare function runMove(options: MoveOptions): Promise<MoveResult>;
|
|
438
447
|
declare function renderMove(result: MoveResult): string[];
|
|
439
448
|
|
|
449
|
+
/**
|
|
450
|
+
* `penv pull` — materialise the local `.penv` tree from an environment's
|
|
451
|
+
* source-of-truth provider. It is the inverse of the deploy-time injection most
|
|
452
|
+
* stacks already have: instead of reading the tree to feed a backend, it reads
|
|
453
|
+
* the backend to feed the tree.
|
|
454
|
+
*
|
|
455
|
+
* It only means anything when the environment declares a real backend
|
|
456
|
+
* (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
|
|
457
|
+
* and pulling copies it down so every other command — which reads the local tree
|
|
458
|
+
* — sees it. An environment with no separate `providers` entry has the local
|
|
459
|
+
* tree *as* its source of truth, so a pull would be the tree copying onto
|
|
460
|
+
* itself; that degenerate case is reported as nothing to do, never a self-copy.
|
|
461
|
+
*
|
|
462
|
+
* Values cross verbatim. They are opaque envelope strings the source holds and
|
|
463
|
+
* penv does not open here — a sealed value stays sealed, byte-for-byte, so the
|
|
464
|
+
* key that opens it never has to be present to pull it.
|
|
465
|
+
*/
|
|
466
|
+
interface PullOptions {
|
|
467
|
+
readonly cwd: string;
|
|
468
|
+
readonly environment?: string;
|
|
469
|
+
}
|
|
470
|
+
interface PullResult {
|
|
471
|
+
readonly environment: string;
|
|
472
|
+
/** The source provider's type — `filesystem` when the environment declares no separate backend. */
|
|
473
|
+
readonly source: string;
|
|
474
|
+
/**
|
|
475
|
+
* True when the source *is* the local tree, so there was nothing to pull. The
|
|
476
|
+
* caller distinguishes "pulled nothing because the backend was empty" from
|
|
477
|
+
* "there is no backend to pull from" — opposite situations.
|
|
478
|
+
*/
|
|
479
|
+
readonly localSource: boolean;
|
|
480
|
+
/** Value files written into the local tree. */
|
|
481
|
+
readonly values: number;
|
|
482
|
+
/** Meta files written into the local tree. */
|
|
483
|
+
readonly meta: number;
|
|
484
|
+
/** Distinct parameters the pull touched, at any scope. */
|
|
485
|
+
readonly refs: number;
|
|
486
|
+
}
|
|
487
|
+
declare function runPull(options: PullOptions): Promise<PullResult>;
|
|
488
|
+
declare function renderPull(result: PullResult): string[];
|
|
489
|
+
|
|
440
490
|
/** The per-environment meta field recording penv's last push, compared against the destination's `updatedAt`. */
|
|
441
491
|
declare const LAST_PUSHED_KEY = "lastPushedAt";
|
|
442
492
|
interface PushOptions {
|
|
@@ -475,6 +525,44 @@ interface RemoveResult {
|
|
|
475
525
|
}
|
|
476
526
|
declare function runRemove(options: RemoveOptions): Promise<RemoveResult>;
|
|
477
527
|
|
|
528
|
+
interface RotateOptions {
|
|
529
|
+
readonly cwd: string;
|
|
530
|
+
readonly key: string;
|
|
531
|
+
readonly environment?: string;
|
|
532
|
+
/** Open a `dual-valid` window: write the new value while the old is still retained. */
|
|
533
|
+
readonly begin?: boolean;
|
|
534
|
+
/** Close a `dual-valid` window: return to `active`, stamp the completion. */
|
|
535
|
+
readonly complete?: boolean;
|
|
536
|
+
/**
|
|
537
|
+
* The new value. A `begin` and an `atomic-cutover` flip write it; a `complete`
|
|
538
|
+
* does not touch the value at all, so it needs none. Injected in tests; on the
|
|
539
|
+
* CLI it is the positional argument or stdin, the same source `set` reads.
|
|
540
|
+
*/
|
|
541
|
+
readonly value?: string;
|
|
542
|
+
/** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
|
|
543
|
+
readonly now?: string;
|
|
544
|
+
}
|
|
545
|
+
/** The single step a run performed — the three the two mechanisms decompose into. */
|
|
546
|
+
type RotatePhase = "begin" | "complete" | "cutover";
|
|
547
|
+
interface RotateResult {
|
|
548
|
+
readonly parameter: string;
|
|
549
|
+
readonly environment: string;
|
|
550
|
+
readonly mechanism: RotationMechanism;
|
|
551
|
+
readonly phase: RotatePhase;
|
|
552
|
+
/** The source provider's type — where the value and its meta were written. */
|
|
553
|
+
readonly source: string;
|
|
554
|
+
/** True when this run wrote a new value. `begin` and `cutover` do; `complete` does not. */
|
|
555
|
+
readonly wroteValue: boolean;
|
|
556
|
+
/** The rotation state after this run — `rotating` after a begin, `active` otherwise. */
|
|
557
|
+
readonly state: RotationState;
|
|
558
|
+
/** When the current window opened, ISO. Set only after a `begin`, else `null`. */
|
|
559
|
+
readonly rotatingSince: string | null;
|
|
560
|
+
/** When a rotation last completed, ISO. Set after a `complete` or a `cutover`. */
|
|
561
|
+
readonly lastRotated: string | null;
|
|
562
|
+
}
|
|
563
|
+
declare function runRotate(options: RotateOptions): Promise<RotateResult>;
|
|
564
|
+
declare function renderRotate(result: RotateResult): string[];
|
|
565
|
+
|
|
478
566
|
interface WatchOptions {
|
|
479
567
|
readonly cwd: string;
|
|
480
568
|
readonly environment?: string;
|
|
@@ -523,4 +611,4 @@ declare function renderWatch(result: ValidateResult): string[];
|
|
|
523
611
|
declare const main: citty.CommandDef<citty.ArgsDef>;
|
|
524
612
|
declare function runMain(): Promise<void>;
|
|
525
613
|
|
|
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 };
|
|
614
|
+
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 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, renderMove, renderPull, renderPush, renderRotate, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, 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
|
|
|
@@ -437,6 +446,47 @@ interface MoveResult {
|
|
|
437
446
|
declare function runMove(options: MoveOptions): Promise<MoveResult>;
|
|
438
447
|
declare function renderMove(result: MoveResult): string[];
|
|
439
448
|
|
|
449
|
+
/**
|
|
450
|
+
* `penv pull` — materialise the local `.penv` tree from an environment's
|
|
451
|
+
* source-of-truth provider. It is the inverse of the deploy-time injection most
|
|
452
|
+
* stacks already have: instead of reading the tree to feed a backend, it reads
|
|
453
|
+
* the backend to feed the tree.
|
|
454
|
+
*
|
|
455
|
+
* It only means anything when the environment declares a real backend
|
|
456
|
+
* (`vault`, `mock`): those hold the truth somewhere penv does not edit in place,
|
|
457
|
+
* and pulling copies it down so every other command — which reads the local tree
|
|
458
|
+
* — sees it. An environment with no separate `providers` entry has the local
|
|
459
|
+
* tree *as* its source of truth, so a pull would be the tree copying onto
|
|
460
|
+
* itself; that degenerate case is reported as nothing to do, never a self-copy.
|
|
461
|
+
*
|
|
462
|
+
* Values cross verbatim. They are opaque envelope strings the source holds and
|
|
463
|
+
* penv does not open here — a sealed value stays sealed, byte-for-byte, so the
|
|
464
|
+
* key that opens it never has to be present to pull it.
|
|
465
|
+
*/
|
|
466
|
+
interface PullOptions {
|
|
467
|
+
readonly cwd: string;
|
|
468
|
+
readonly environment?: string;
|
|
469
|
+
}
|
|
470
|
+
interface PullResult {
|
|
471
|
+
readonly environment: string;
|
|
472
|
+
/** The source provider's type — `filesystem` when the environment declares no separate backend. */
|
|
473
|
+
readonly source: string;
|
|
474
|
+
/**
|
|
475
|
+
* True when the source *is* the local tree, so there was nothing to pull. The
|
|
476
|
+
* caller distinguishes "pulled nothing because the backend was empty" from
|
|
477
|
+
* "there is no backend to pull from" — opposite situations.
|
|
478
|
+
*/
|
|
479
|
+
readonly localSource: boolean;
|
|
480
|
+
/** Value files written into the local tree. */
|
|
481
|
+
readonly values: number;
|
|
482
|
+
/** Meta files written into the local tree. */
|
|
483
|
+
readonly meta: number;
|
|
484
|
+
/** Distinct parameters the pull touched, at any scope. */
|
|
485
|
+
readonly refs: number;
|
|
486
|
+
}
|
|
487
|
+
declare function runPull(options: PullOptions): Promise<PullResult>;
|
|
488
|
+
declare function renderPull(result: PullResult): string[];
|
|
489
|
+
|
|
440
490
|
/** The per-environment meta field recording penv's last push, compared against the destination's `updatedAt`. */
|
|
441
491
|
declare const LAST_PUSHED_KEY = "lastPushedAt";
|
|
442
492
|
interface PushOptions {
|
|
@@ -475,6 +525,44 @@ interface RemoveResult {
|
|
|
475
525
|
}
|
|
476
526
|
declare function runRemove(options: RemoveOptions): Promise<RemoveResult>;
|
|
477
527
|
|
|
528
|
+
interface RotateOptions {
|
|
529
|
+
readonly cwd: string;
|
|
530
|
+
readonly key: string;
|
|
531
|
+
readonly environment?: string;
|
|
532
|
+
/** Open a `dual-valid` window: write the new value while the old is still retained. */
|
|
533
|
+
readonly begin?: boolean;
|
|
534
|
+
/** Close a `dual-valid` window: return to `active`, stamp the completion. */
|
|
535
|
+
readonly complete?: boolean;
|
|
536
|
+
/**
|
|
537
|
+
* The new value. A `begin` and an `atomic-cutover` flip write it; a `complete`
|
|
538
|
+
* does not touch the value at all, so it needs none. Injected in tests; on the
|
|
539
|
+
* CLI it is the positional argument or stdin, the same source `set` reads.
|
|
540
|
+
*/
|
|
541
|
+
readonly value?: string;
|
|
542
|
+
/** Injected in tests: the wall-clock reading recorded in meta. Defaults to now. */
|
|
543
|
+
readonly now?: string;
|
|
544
|
+
}
|
|
545
|
+
/** The single step a run performed — the three the two mechanisms decompose into. */
|
|
546
|
+
type RotatePhase = "begin" | "complete" | "cutover";
|
|
547
|
+
interface RotateResult {
|
|
548
|
+
readonly parameter: string;
|
|
549
|
+
readonly environment: string;
|
|
550
|
+
readonly mechanism: RotationMechanism;
|
|
551
|
+
readonly phase: RotatePhase;
|
|
552
|
+
/** The source provider's type — where the value and its meta were written. */
|
|
553
|
+
readonly source: string;
|
|
554
|
+
/** True when this run wrote a new value. `begin` and `cutover` do; `complete` does not. */
|
|
555
|
+
readonly wroteValue: boolean;
|
|
556
|
+
/** The rotation state after this run — `rotating` after a begin, `active` otherwise. */
|
|
557
|
+
readonly state: RotationState;
|
|
558
|
+
/** When the current window opened, ISO. Set only after a `begin`, else `null`. */
|
|
559
|
+
readonly rotatingSince: string | null;
|
|
560
|
+
/** When a rotation last completed, ISO. Set after a `complete` or a `cutover`. */
|
|
561
|
+
readonly lastRotated: string | null;
|
|
562
|
+
}
|
|
563
|
+
declare function runRotate(options: RotateOptions): Promise<RotateResult>;
|
|
564
|
+
declare function renderRotate(result: RotateResult): string[];
|
|
565
|
+
|
|
478
566
|
interface WatchOptions {
|
|
479
567
|
readonly cwd: string;
|
|
480
568
|
readonly environment?: string;
|
|
@@ -523,4 +611,4 @@ declare function renderWatch(result: ValidateResult): string[];
|
|
|
523
611
|
declare const main: citty.CommandDef<citty.ArgsDef>;
|
|
524
612
|
declare function runMain(): Promise<void>;
|
|
525
613
|
|
|
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 };
|
|
614
|
+
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 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, renderMove, renderPull, renderPush, renderRotate, renderWatch, runDecrypt, runDoctor, runEncrypt, runExplain, runGenerate, runGet, runInit, runList, runMain, runMove, runPull, runPush, runRemove, runRotate, runSet, runValidate, runWatch };
|