hereya-cli 0.86.2 → 0.86.3
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/README.md +64 -64
- package/dist/lib/ephemeral-token.d.ts +9 -2
- package/dist/lib/ephemeral-token.js +56 -3
- package/oclif.manifest.json +299 -299
- package/package.json +1 -1
|
@@ -1,22 +1,52 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { clearBackend } from '../backend/index.js';
|
|
3
3
|
const storage = new AsyncLocalStorage();
|
|
4
|
+
/**
|
|
5
|
+
* Env-var names used to propagate the ephemeral token to **subprocesses**
|
|
6
|
+
* (e.g. the `git` invocation that triggers the `hereya credential-helper`
|
|
7
|
+
* grandchild). Async-local-storage doesn't cross process boundaries, so we
|
|
8
|
+
* also stash the token in the environment for the duration of the scope.
|
|
9
|
+
*
|
|
10
|
+
* These are deliberately named differently from `HEREYA_TOKEN` because the
|
|
11
|
+
* existing `HEREYA_TOKEN` semantics call `loginWithToken()` (exchange +
|
|
12
|
+
* keychain persist), which is exactly what ephemeral mode must NOT do.
|
|
13
|
+
*/
|
|
14
|
+
const EPHEMERAL_TOKEN_ENV = 'HEREYA_EPHEMERAL_TOKEN';
|
|
15
|
+
const EPHEMERAL_CLOUD_URL_ENV = 'HEREYA_EPHEMERAL_CLOUD_URL';
|
|
4
16
|
/**
|
|
5
17
|
* Returns the active ephemeral token context, or `undefined` if no
|
|
6
18
|
* `withEphemeralToken` scope is currently in flight.
|
|
19
|
+
*
|
|
20
|
+
* Falls back to the `HEREYA_EPHEMERAL_TOKEN` env var when no async-local-
|
|
21
|
+
* storage context is set — that's how subprocesses (the credential helper
|
|
22
|
+
* grandchild, etc.) inherit the ephemeral context across process
|
|
23
|
+
* boundaries. The env var is set automatically by `withEphemeralToken`.
|
|
7
24
|
*/
|
|
8
25
|
export function getEphemeralToken() {
|
|
9
|
-
|
|
26
|
+
const fromAsyncStorage = storage.getStore();
|
|
27
|
+
if (fromAsyncStorage) {
|
|
28
|
+
return fromAsyncStorage;
|
|
29
|
+
}
|
|
30
|
+
const fromEnv = process.env[EPHEMERAL_TOKEN_ENV];
|
|
31
|
+
if (fromEnv) {
|
|
32
|
+
return {
|
|
33
|
+
cloudUrl: process.env[EPHEMERAL_CLOUD_URL_ENV] || undefined,
|
|
34
|
+
token: fromEnv,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
10
38
|
}
|
|
11
39
|
/**
|
|
12
40
|
* Run `fn` inside an ephemeral-token scope. While `fn` is executing,
|
|
13
|
-
* `getEphemeralToken()` returns the supplied token (and optional cloudUrl)
|
|
41
|
+
* `getEphemeralToken()` returns the supplied token (and optional cloudUrl),
|
|
42
|
+
* and `process.env.HEREYA_EPHEMERAL_TOKEN` is set so subprocesses inherit
|
|
43
|
+
* the same ephemeral context.
|
|
14
44
|
*
|
|
15
45
|
* Critical guarantees:
|
|
16
46
|
* - The token is held in-memory only. It is NEVER passed to the secret
|
|
17
47
|
* manager or written to disk.
|
|
18
48
|
* - After `fn` returns or throws, `getEphemeralToken()` returns `undefined`
|
|
19
|
-
* again.
|
|
49
|
+
* again and `HEREYA_EPHEMERAL_TOKEN` is restored to its prior value.
|
|
20
50
|
* - The cached backend in `src/backend/index.ts` is cleared before and after
|
|
21
51
|
* the scope so that a stale, persisted-credential-backed CloudBackend is
|
|
22
52
|
* never used while the scope is active, and the scope's in-memory backend
|
|
@@ -26,10 +56,33 @@ export function getEphemeralToken() {
|
|
|
26
56
|
export async function withEphemeralToken(token, fn, options) {
|
|
27
57
|
// Drop any cached backend so the next getBackend() call sees the ephemeral context.
|
|
28
58
|
clearBackend();
|
|
59
|
+
// Snapshot existing env-var values so we can restore them on exit.
|
|
60
|
+
const priorTokenEnv = process.env[EPHEMERAL_TOKEN_ENV];
|
|
61
|
+
const priorCloudUrlEnv = process.env[EPHEMERAL_CLOUD_URL_ENV];
|
|
62
|
+
process.env[EPHEMERAL_TOKEN_ENV] = token;
|
|
63
|
+
if (options?.cloudUrl) {
|
|
64
|
+
process.env[EPHEMERAL_CLOUD_URL_ENV] = options.cloudUrl;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
delete process.env[EPHEMERAL_CLOUD_URL_ENV];
|
|
68
|
+
}
|
|
29
69
|
try {
|
|
30
70
|
return await storage.run({ cloudUrl: options?.cloudUrl, token }, fn);
|
|
31
71
|
}
|
|
32
72
|
finally {
|
|
73
|
+
// Restore prior env-var state (no leakage between sibling invocations).
|
|
74
|
+
if (priorTokenEnv === undefined) {
|
|
75
|
+
delete process.env[EPHEMERAL_TOKEN_ENV];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
process.env[EPHEMERAL_TOKEN_ENV] = priorTokenEnv;
|
|
79
|
+
}
|
|
80
|
+
if (priorCloudUrlEnv === undefined) {
|
|
81
|
+
delete process.env[EPHEMERAL_CLOUD_URL_ENV];
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
process.env[EPHEMERAL_CLOUD_URL_ENV] = priorCloudUrlEnv;
|
|
85
|
+
}
|
|
33
86
|
// Drop the in-memory backend bound to the ephemeral token before yielding control.
|
|
34
87
|
clearBackend();
|
|
35
88
|
}
|