@seekrit/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.js +59 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -315,7 +315,7 @@ async function unwrapDek(wrapped, privateKey) {
|
|
|
315
315
|
}
|
|
316
316
|
//#endregion
|
|
317
317
|
//#region package.json
|
|
318
|
-
var version = "0.
|
|
318
|
+
var version = "0.2.0";
|
|
319
319
|
const PROJECT_FILE = "seekrit.json";
|
|
320
320
|
function globalConfigPath() {
|
|
321
321
|
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "seekrit", "config.json");
|
|
@@ -549,7 +549,12 @@ async function readStdin() {
|
|
|
549
549
|
}
|
|
550
550
|
//#endregion
|
|
551
551
|
//#region src/context.ts
|
|
552
|
-
|
|
552
|
+
/**
|
|
553
|
+
* Build the client context from configured credentials, or return null when
|
|
554
|
+
* none are set. `seekrit run` uses this to degrade to a plain launcher instead
|
|
555
|
+
* of exiting; every other command goes through `buildContext`, which fails.
|
|
556
|
+
*/
|
|
557
|
+
function tryBuildContext() {
|
|
553
558
|
const config = readGlobalConfig();
|
|
554
559
|
const apiUrl = process.env.SEEKRIT_API_URL ?? config.apiUrl ?? "http://localhost:8787";
|
|
555
560
|
const token = process.env.SEEKRIT_TOKEN ?? config.token;
|
|
@@ -563,7 +568,7 @@ function buildContext() {
|
|
|
563
568
|
type: "dev",
|
|
564
569
|
email: devUser
|
|
565
570
|
};
|
|
566
|
-
else
|
|
571
|
+
else return null;
|
|
567
572
|
return {
|
|
568
573
|
client: new SeekritClient({
|
|
569
574
|
baseUrl: apiUrl,
|
|
@@ -572,6 +577,11 @@ function buildContext() {
|
|
|
572
577
|
auth
|
|
573
578
|
};
|
|
574
579
|
}
|
|
580
|
+
function buildContext() {
|
|
581
|
+
const ctx = tryBuildContext();
|
|
582
|
+
if (!ctx) fail("no credentials found — run `seekrit login --token skt_…`, or set SEEKRIT_TOKEN / SEEKRIT_DEV_USER");
|
|
583
|
+
return ctx;
|
|
584
|
+
}
|
|
575
585
|
function isTokenAuth(ctx) {
|
|
576
586
|
return ctx.auth.type === "bearer" && isServiceToken(ctx.auth.token);
|
|
577
587
|
}
|
|
@@ -682,21 +692,30 @@ async function materializeEnv(ctx, opts) {
|
|
|
682
692
|
provenance[secret.name] = label;
|
|
683
693
|
}
|
|
684
694
|
}
|
|
685
|
-
|
|
686
|
-
|
|
695
|
+
return {
|
|
696
|
+
values,
|
|
697
|
+
provenance,
|
|
698
|
+
scope,
|
|
699
|
+
loadedEnvFiles: overlayEnvFiles(values, provenance, opts.envFiles)
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Overlay `.env` files onto an existing value/provenance set (later files win).
|
|
704
|
+
* Missing files are skipped. Returns the files that were actually loaded. Used
|
|
705
|
+
* both by {@link materializeEnv} and by `seekrit run`'s degraded path, where
|
|
706
|
+
* managed secrets are unavailable but `.env` should still apply.
|
|
707
|
+
*/
|
|
708
|
+
function overlayEnvFiles(values, provenance, envFiles) {
|
|
709
|
+
const loaded = [];
|
|
710
|
+
for (const file of envFiles) {
|
|
687
711
|
if (!existsSync(file)) continue;
|
|
688
|
-
|
|
712
|
+
loaded.push(file);
|
|
689
713
|
for (const [name, value] of Object.entries(parseDotenv(readFileSync(file, "utf8")))) {
|
|
690
714
|
values[name] = value;
|
|
691
715
|
provenance[name] = `dotenv:${file}`;
|
|
692
716
|
}
|
|
693
717
|
}
|
|
694
|
-
return
|
|
695
|
-
values,
|
|
696
|
-
provenance,
|
|
697
|
-
scope,
|
|
698
|
-
loadedEnvFiles
|
|
699
|
-
};
|
|
718
|
+
return loaded;
|
|
700
719
|
}
|
|
701
720
|
/** Print a name → source table to stderr (never the secret values). */
|
|
702
721
|
function printExplain(provenance) {
|
|
@@ -990,14 +1009,39 @@ async function materialize(ctx, options) {
|
|
|
990
1009
|
envFiles: options.envFile ?? [".env"]
|
|
991
1010
|
});
|
|
992
1011
|
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Best-effort variant of {@link materialize} for `seekrit run`: if credentials
|
|
1014
|
+
* are missing or seekrit can't be reached (network, auth, or decryption
|
|
1015
|
+
* failure), log why and fall back to the `.env` overlay alone so the command
|
|
1016
|
+
* still runs. This keeps `seekrit run` working across environments that lack a
|
|
1017
|
+
* token or API — mirroring the `seekrit-run` launcher.
|
|
1018
|
+
*/
|
|
1019
|
+
async function materializeForRun(options) {
|
|
1020
|
+
const envFiles = options.envFile ?? [".env"];
|
|
1021
|
+
try {
|
|
1022
|
+
const ctx = tryBuildContext();
|
|
1023
|
+
if (!ctx) throw new Error("no credentials found (set SEEKRIT_TOKEN / SEEKRIT_DEV_USER or run `seekrit login`)");
|
|
1024
|
+
return await materialize(ctx, options);
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1027
|
+
console.error(`seekrit: continuing without seekrit-managed secrets: ${message}`);
|
|
1028
|
+
const values = {};
|
|
1029
|
+
const provenance = {};
|
|
1030
|
+
overlayEnvFiles(values, provenance, envFiles);
|
|
1031
|
+
return {
|
|
1032
|
+
values,
|
|
1033
|
+
provenance
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
993
1037
|
program.command("run").description("run a command with decrypted secrets injected (process env > .env > app > group)").passThroughOptions().option("--org <slug>").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--with <group=env>", "override one group’s slice for this run", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").argument("<command...>", "command to run (prefix with -- to pass flags)").action(async (commandParts, options) => {
|
|
994
|
-
const
|
|
1038
|
+
const [cmd, ...args] = commandParts;
|
|
1039
|
+
if (!cmd) fail("no command given");
|
|
1040
|
+
const { values, provenance } = await materializeForRun(options);
|
|
995
1041
|
if (options.explain) {
|
|
996
1042
|
for (const name of Object.keys(values)) if (process.env[name] !== void 0 && process.env[name] !== values[name]) provenance[name] = "env";
|
|
997
1043
|
printExplain(provenance);
|
|
998
1044
|
}
|
|
999
|
-
const [cmd, ...args] = commandParts;
|
|
1000
|
-
if (!cmd) fail("no command given");
|
|
1001
1045
|
const child = spawn(cmd, args, {
|
|
1002
1046
|
stdio: "inherit",
|
|
1003
1047
|
env: {
|