@replayio/app-building 1.16.0 → 1.18.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/README.md +7 -23
- package/dist/container.d.ts +3 -1
- package/dist/container.js +12 -5
- package/dist/secrets.d.ts +22 -2
- package/dist/secrets.js +141 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,18 +28,9 @@ import {
|
|
|
28
28
|
const orchestrationVars = loadDotEnv("/path/to/project");
|
|
29
29
|
const infisicalConfig = await getInfisicalConfig(orchestrationVars);
|
|
30
30
|
|
|
31
|
-
// Only pass Infisical credentials to the container — not actual secrets.
|
|
32
|
-
// The container fetches secrets from Infisical at startup and manages them
|
|
33
|
-
// via an internal secrets server. The agent never has direct access to secrets.
|
|
34
|
-
const containerEnvVars: Record<string, string> = {
|
|
35
|
-
INFISICAL_TOKEN: infisicalConfig.token,
|
|
36
|
-
INFISICAL_PROJECT_ID: infisicalConfig.projectId,
|
|
37
|
-
INFISICAL_ENVIRONMENT: infisicalConfig.environment,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
31
|
const config: ContainerConfig = {
|
|
41
32
|
projectRoot: "/path/to/project", // optional — only needed for local Docker operations
|
|
42
|
-
|
|
33
|
+
infisical: infisicalConfig,
|
|
43
34
|
registry: new FileContainerRegistry("/path/to/.container-registry.jsonl"),
|
|
44
35
|
flyToken: orchestrationVars.FLY_API_TOKEN,
|
|
45
36
|
flyApp: orchestrationVars.FLY_APP_NAME,
|
|
@@ -77,7 +68,7 @@ exec-secrets NETLIFY_AUTH_TOKEN NETLIFY_ACCOUNT_SLUG -- netlify deploy --prod
|
|
|
77
68
|
|
|
78
69
|
The secrets server spawns the command with the requested secrets in its environment and **redacts all secret values** from the output.
|
|
79
70
|
|
|
80
|
-
The agent can also run `list-secrets` to see which secrets are available.
|
|
71
|
+
The agent can also run `list-secrets` to see which secrets are available, and `set-branch-secret` to store new branch-level secrets (e.g., `DATABASE_URL` created at deploy time). The server rejects values that have already appeared in logs.
|
|
81
72
|
|
|
82
73
|
## Exported API
|
|
83
74
|
|
|
@@ -85,7 +76,7 @@ The agent can also run `list-secrets` to see which secrets are available.
|
|
|
85
76
|
|
|
86
77
|
| Export | Description |
|
|
87
78
|
|---|---|
|
|
88
|
-
| `ContainerConfig` | Interface bundling all external state: optional `projectRoot` (only needed for local Docker operations), `
|
|
79
|
+
| `ContainerConfig` | Interface bundling all external state: `infisical` (required `InfisicalConfig`), optional `projectRoot` (only needed for local Docker operations), `registry`, optional `flyToken`/`flyApp`/`imageRef`/`webhookUrl`/`webhookSecret`/`detached`/`initialPrompt`/`localPort`/`absorbTasks`. See [Webhooks](#webhooks) and [Container lifecycle](#container-lifecycle) below. |
|
|
89
80
|
| `RepoOptions` | Per-invocation git settings: `repoUrl`, `cloneBranch`, `pushBranch`. |
|
|
90
81
|
| `ContainerRegistry` | Interface for container registry storage. Methods: `log`, `markStopped`, `clearStopped`, `getRecent`, `find`, `findAlive`. |
|
|
91
82
|
| `FileContainerRegistry` | Built-in file-backed implementation of `ContainerRegistry`, backed by a `.jsonl` file. |
|
|
@@ -149,9 +140,11 @@ The agent can also run `list-secrets` to see which secrets are available.
|
|
|
149
140
|
|
|
150
141
|
| Export | Description |
|
|
151
142
|
|---|---|
|
|
143
|
+
| `infisicalLogin(clientId, clientSecret)` | Log in via Universal Auth, returns a short-lived access token. |
|
|
152
144
|
| `getInfisicalConfig(envVars)` | Extract `InfisicalConfig` from env vars and log in. Requires `INFISICAL_CLIENT_ID`, `INFISICAL_CLIENT_SECRET`, `INFISICAL_PROJECT_ID`, `INFISICAL_ENVIRONMENT`. |
|
|
153
145
|
| `fetchGlobalSecrets(config)` | Fetch secrets from the `/global/` path. |
|
|
154
146
|
| `fetchBranchSecrets(config, branch)` | Fetch secrets from `/branches/<branch>/`. |
|
|
147
|
+
| `createBranchSecret(config, branch, name, value)` | Create or update a secret in `/branches/<branch>/`. |
|
|
155
148
|
| `fetchInfisicalSecrets(config, path)` | Raw fetch from any Infisical folder path. |
|
|
156
149
|
|
|
157
150
|
**Types:** `InfisicalConfig`
|
|
@@ -162,13 +155,8 @@ The agent can also run `list-secrets` to see which secrets are available.
|
|
|
162
155
|
const orchestrationVars = loadDotEnv(projectRoot);
|
|
163
156
|
const infisicalConfig = await getInfisicalConfig(orchestrationVars);
|
|
164
157
|
|
|
165
|
-
// Pass only Infisical credentials to the container
|
|
166
158
|
const config: ContainerConfig = {
|
|
167
|
-
|
|
168
|
-
INFISICAL_TOKEN: infisicalConfig.token,
|
|
169
|
-
INFISICAL_PROJECT_ID: infisicalConfig.projectId,
|
|
170
|
-
INFISICAL_ENVIRONMENT: infisicalConfig.environment,
|
|
171
|
-
},
|
|
159
|
+
infisical: infisicalConfig,
|
|
172
160
|
flyToken: orchestrationVars.FLY_API_TOKEN,
|
|
173
161
|
flyApp: orchestrationVars.FLY_APP_NAME,
|
|
174
162
|
...
|
|
@@ -267,11 +255,7 @@ const infisicalConfig = await getInfisicalConfig(orchestrationVars);
|
|
|
267
255
|
|
|
268
256
|
const config: ContainerConfig = {
|
|
269
257
|
projectRoot: "/path/to/project",
|
|
270
|
-
|
|
271
|
-
INFISICAL_TOKEN: infisicalConfig.token,
|
|
272
|
-
INFISICAL_PROJECT_ID: infisicalConfig.projectId,
|
|
273
|
-
INFISICAL_ENVIRONMENT: infisicalConfig.environment,
|
|
274
|
-
},
|
|
258
|
+
infisical: infisicalConfig,
|
|
275
259
|
registry: new FileContainerRegistry("/path/to/.container-registry.jsonl"),
|
|
276
260
|
webhookUrl: "https://example.com/hooks/container-events",
|
|
277
261
|
webhookSecret: "your-webhook-secret",
|
package/dist/container.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContainerRegistry } from "./container-registry";
|
|
2
|
+
import type { InfisicalConfig } from "./secrets";
|
|
2
3
|
export interface AgentState {
|
|
3
4
|
type: "local" | "remote";
|
|
4
5
|
containerName: string;
|
|
@@ -10,7 +11,8 @@ export interface AgentState {
|
|
|
10
11
|
}
|
|
11
12
|
export interface ContainerConfig {
|
|
12
13
|
projectRoot?: string;
|
|
13
|
-
|
|
14
|
+
/** Infisical credentials — required for all containers. */
|
|
15
|
+
infisical: InfisicalConfig;
|
|
14
16
|
registry: ContainerRegistry;
|
|
15
17
|
flyToken?: string;
|
|
16
18
|
flyApp?: string;
|
package/dist/container.js
CHANGED
|
@@ -73,7 +73,14 @@ function findFreePort() {
|
|
|
73
73
|
}
|
|
74
74
|
return port;
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function infisicalEnvVars(config) {
|
|
77
|
+
return {
|
|
78
|
+
INFISICAL_TOKEN: config.token,
|
|
79
|
+
INFISICAL_PROJECT_ID: config.projectId,
|
|
80
|
+
INFISICAL_ENVIRONMENT: config.environment,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function buildContainerEnv(repo, infisical, extra = {}) {
|
|
77
84
|
const env = {
|
|
78
85
|
REPO_URL: repo.repoUrl,
|
|
79
86
|
CLONE_BRANCH: repo.cloneBranch,
|
|
@@ -83,7 +90,7 @@ function buildContainerEnv(repo, envVars, extra = {}) {
|
|
|
83
90
|
GIT_COMMITTER_NAME: "App Builder",
|
|
84
91
|
GIT_COMMITTER_EMAIL: "app-builder@localhost",
|
|
85
92
|
PLAYWRIGHT_BROWSERS_PATH: "/opt/playwright",
|
|
86
|
-
...
|
|
93
|
+
...infisicalEnvVars(infisical),
|
|
87
94
|
...extra,
|
|
88
95
|
};
|
|
89
96
|
if (process.env.DEBUG) {
|
|
@@ -99,7 +106,6 @@ export async function startContainer(config, repo) {
|
|
|
99
106
|
webhookUrl: config.webhookUrl,
|
|
100
107
|
detached: config.detached,
|
|
101
108
|
initialPrompt: config.initialPrompt ? `${config.initialPrompt.slice(0, 100)}...` : undefined,
|
|
102
|
-
envVarKeys: Object.keys(config.envVars),
|
|
103
109
|
});
|
|
104
110
|
debugLog("startContainer repo:", repo);
|
|
105
111
|
buildImage(config);
|
|
@@ -121,7 +127,7 @@ export async function startContainer(config, repo) {
|
|
|
121
127
|
extra.INITIAL_PROMPT = config.initialPrompt;
|
|
122
128
|
if (config.absorbTasks)
|
|
123
129
|
extra.ABSORB_TASKS = "1";
|
|
124
|
-
const containerEnv = buildContainerEnv(repo, config.
|
|
130
|
+
const containerEnv = buildContainerEnv(repo, config.infisical, extra);
|
|
125
131
|
// Build docker run args
|
|
126
132
|
const args = ["run", "-d", "--rm", "--name", containerName];
|
|
127
133
|
// Use explicit port mapping for macOS Docker Desktop compatibility
|
|
@@ -196,6 +202,7 @@ export function spawnTestContainer(config) {
|
|
|
196
202
|
ensureImageExists(config.projectRoot);
|
|
197
203
|
const uniqueId = Math.random().toString(36).slice(2, 8);
|
|
198
204
|
const containerName = `app-building-test-${uniqueId}`;
|
|
205
|
+
const infisicalVars = infisicalEnvVars(config.infisical);
|
|
199
206
|
const args = ["run", "-it", "--rm", "--name", containerName];
|
|
200
207
|
args.push("-v", `${config.projectRoot}:/repo`);
|
|
201
208
|
args.push("-w", "/repo");
|
|
@@ -203,7 +210,7 @@ export function spawnTestContainer(config) {
|
|
|
203
210
|
args.push("--user", `${process.getuid()}:${process.getgid()}`);
|
|
204
211
|
args.push("--env", "HOME=/repo/.agent-home");
|
|
205
212
|
args.push("--env", "PLAYWRIGHT_BROWSERS_PATH=/opt/playwright");
|
|
206
|
-
for (const [k, v] of Object.entries(
|
|
213
|
+
for (const [k, v] of Object.entries(infisicalVars)) {
|
|
207
214
|
args.push("--env", `${k}=${v}`);
|
|
208
215
|
}
|
|
209
216
|
args.push(IMAGE_NAME, "bash");
|
package/dist/secrets.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infisical secrets API.
|
|
3
|
+
*
|
|
4
|
+
* API versions used (per https://infisical.com/docs/api-reference):
|
|
5
|
+
* - Auth: POST /api/v1/auth/universal-auth/login
|
|
6
|
+
* - Secrets: GET/POST/PATCH /api/v4/secrets[/{secretName}]
|
|
7
|
+
* - Folders: POST /api/v2/folders
|
|
8
|
+
*/
|
|
1
9
|
export interface InfisicalConfig {
|
|
2
10
|
token: string;
|
|
3
11
|
projectId: string;
|
|
@@ -5,12 +13,14 @@ export interface InfisicalConfig {
|
|
|
5
13
|
}
|
|
6
14
|
/**
|
|
7
15
|
* Log in to Infisical using Universal Auth (Client ID + Client Secret).
|
|
8
|
-
*
|
|
16
|
+
* POST /api/v1/auth/universal-auth/login
|
|
17
|
+
* Returns a short-lived access token.
|
|
9
18
|
*/
|
|
10
19
|
export declare function infisicalLogin(clientId: string, clientSecret: string): Promise<string>;
|
|
11
20
|
/**
|
|
12
21
|
* Fetch secrets from an Infisical folder path.
|
|
13
|
-
*
|
|
22
|
+
* GET /api/v4/secrets?projectId=…&environment=…&secretPath=…
|
|
23
|
+
* Returns a key→value record.
|
|
14
24
|
*/
|
|
15
25
|
export declare function fetchInfisicalSecrets(config: InfisicalConfig, secretPath: string): Promise<Record<string, string>>;
|
|
16
26
|
/**
|
|
@@ -21,6 +31,16 @@ export declare function fetchGlobalSecrets(config: InfisicalConfig): Promise<Rec
|
|
|
21
31
|
* Fetch per-branch deployment secrets from `/branches/<branch>/`.
|
|
22
32
|
*/
|
|
23
33
|
export declare function fetchBranchSecrets(config: InfisicalConfig, branch: string): Promise<Record<string, string>>;
|
|
34
|
+
/**
|
|
35
|
+
* Create or update a branch secret in Infisical.
|
|
36
|
+
* Creates the folder path if it doesn't exist yet.
|
|
37
|
+
*
|
|
38
|
+
* POST /api/v4/secrets/{name} — create
|
|
39
|
+
* PATCH /api/v4/secrets/{name} — update (if secret already exists)
|
|
40
|
+
*
|
|
41
|
+
* Body: { projectId, environment, secretPath, secretValue, type }
|
|
42
|
+
*/
|
|
43
|
+
export declare function createBranchSecret(config: InfisicalConfig, branch: string, name: string, value: string): Promise<void>;
|
|
24
44
|
/**
|
|
25
45
|
* Extract Infisical config from environment variables and log in.
|
|
26
46
|
* Reads INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_PROJECT_ID,
|
package/dist/secrets.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infisical secrets API.
|
|
3
|
+
*
|
|
4
|
+
* API versions used (per https://infisical.com/docs/api-reference):
|
|
5
|
+
* - Auth: POST /api/v1/auth/universal-auth/login
|
|
6
|
+
* - Secrets: GET/POST/PATCH /api/v4/secrets[/{secretName}]
|
|
7
|
+
* - Folders: POST /api/v2/folders
|
|
8
|
+
*/
|
|
1
9
|
const INFISICAL_API_BASE = "https://app.infisical.com";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Auth
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
2
13
|
/**
|
|
3
14
|
* Log in to Infisical using Universal Auth (Client ID + Client Secret).
|
|
4
|
-
*
|
|
15
|
+
* POST /api/v1/auth/universal-auth/login
|
|
16
|
+
* Returns a short-lived access token.
|
|
5
17
|
*/
|
|
6
18
|
export async function infisicalLogin(clientId, clientSecret) {
|
|
7
19
|
const res = await fetch(`${INFISICAL_API_BASE}/api/v1/auth/universal-auth/login`, {
|
|
@@ -16,30 +28,33 @@ export async function infisicalLogin(clientId, clientSecret) {
|
|
|
16
28
|
const data = (await res.json());
|
|
17
29
|
return data.accessToken;
|
|
18
30
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const body = await res.text().catch(() => "");
|
|
28
|
-
throw new Error(`Infisical API GET ${path} → ${res.status}: ${body}`);
|
|
29
|
-
}
|
|
30
|
-
return res;
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Helpers
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
function authHeaders(config) {
|
|
35
|
+
return {
|
|
36
|
+
Authorization: `Bearer ${config.token}`,
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
};
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
41
|
* Fetch secrets from an Infisical folder path.
|
|
34
|
-
*
|
|
42
|
+
* GET /api/v4/secrets?projectId=…&environment=…&secretPath=…
|
|
43
|
+
* Returns a key→value record.
|
|
35
44
|
*/
|
|
36
45
|
export async function fetchInfisicalSecrets(config, secretPath) {
|
|
37
46
|
const params = new URLSearchParams({
|
|
38
|
-
|
|
47
|
+
projectId: config.projectId,
|
|
39
48
|
environment: config.environment,
|
|
40
49
|
secretPath,
|
|
41
50
|
});
|
|
42
|
-
const res = await
|
|
51
|
+
const res = await fetch(`${INFISICAL_API_BASE}/api/v4/secrets?${params}`, {
|
|
52
|
+
headers: authHeaders(config),
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const body = await res.text().catch(() => "");
|
|
56
|
+
throw new Error(`Infisical GET secrets ${secretPath} → ${res.status}: ${body}`);
|
|
57
|
+
}
|
|
43
58
|
const data = (await res.json());
|
|
44
59
|
const secrets = {};
|
|
45
60
|
for (const s of data.secrets) {
|
|
@@ -59,6 +74,116 @@ export async function fetchGlobalSecrets(config) {
|
|
|
59
74
|
export async function fetchBranchSecrets(config, branch) {
|
|
60
75
|
return fetchInfisicalSecrets(config, `/branches/${branch}/`);
|
|
61
76
|
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Folders
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Ensure all folders in a path exist, creating any missing ones.
|
|
82
|
+
* POST /api/v2/folders — body: { projectId, environment, name, path }
|
|
83
|
+
*
|
|
84
|
+
* Infisical requires folders to exist before secrets can be written into them.
|
|
85
|
+
* We walk each segment of the path and issue a create; a 400 response means
|
|
86
|
+
* the folder already exists (the docs list 400 for "Bad Request" which
|
|
87
|
+
* Infisical returns for duplicate folder names).
|
|
88
|
+
*/
|
|
89
|
+
async function ensureFolder(config, folderPath) {
|
|
90
|
+
const segments = folderPath.split("/").filter(Boolean);
|
|
91
|
+
let parentPath = "/";
|
|
92
|
+
for (const segment of segments) {
|
|
93
|
+
const res = await fetch(`${INFISICAL_API_BASE}/api/v2/folders`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: authHeaders(config),
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
projectId: config.projectId,
|
|
98
|
+
environment: config.environment,
|
|
99
|
+
name: segment,
|
|
100
|
+
path: parentPath,
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
// 200 = created. 400 = folder already exists (Infisical returns 400 for
|
|
104
|
+
// duplicate folder names under the same parent).
|
|
105
|
+
if (res.ok || res.status === 400) {
|
|
106
|
+
parentPath += segment + "/";
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const text = await res.text().catch(() => "");
|
|
110
|
+
throw new Error(`Infisical create folder ${parentPath}${segment} → ${res.status}: ${text}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Secrets — write
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
/**
|
|
117
|
+
* Create or update a branch secret in Infisical.
|
|
118
|
+
* Creates the folder path if it doesn't exist yet.
|
|
119
|
+
*
|
|
120
|
+
* POST /api/v4/secrets/{name} — create
|
|
121
|
+
* PATCH /api/v4/secrets/{name} — update (if secret already exists)
|
|
122
|
+
*
|
|
123
|
+
* Body: { projectId, environment, secretPath, secretValue, type }
|
|
124
|
+
*/
|
|
125
|
+
export async function createBranchSecret(config, branch, name, value) {
|
|
126
|
+
const secretPath = `/branches/${branch}/`;
|
|
127
|
+
const url = `${INFISICAL_API_BASE}/api/v4/secrets/${encodeURIComponent(name)}`;
|
|
128
|
+
const body = {
|
|
129
|
+
projectId: config.projectId,
|
|
130
|
+
environment: config.environment,
|
|
131
|
+
secretPath,
|
|
132
|
+
secretValue: value,
|
|
133
|
+
type: "shared",
|
|
134
|
+
};
|
|
135
|
+
const headers = authHeaders(config);
|
|
136
|
+
// --- Try POST (create) ---------------------------------------------------
|
|
137
|
+
const res = await fetch(url, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers,
|
|
140
|
+
body: JSON.stringify(body),
|
|
141
|
+
});
|
|
142
|
+
if (res.ok)
|
|
143
|
+
return;
|
|
144
|
+
// Secret already exists → PATCH to update
|
|
145
|
+
if (res.status === 400) {
|
|
146
|
+
const patchRes = await fetch(url, {
|
|
147
|
+
method: "PATCH",
|
|
148
|
+
headers,
|
|
149
|
+
body: JSON.stringify(body),
|
|
150
|
+
});
|
|
151
|
+
if (patchRes.ok)
|
|
152
|
+
return;
|
|
153
|
+
const text = await patchRes.text().catch(() => "");
|
|
154
|
+
throw new Error(`Infisical PATCH ${name} → ${patchRes.status}: ${text}`);
|
|
155
|
+
}
|
|
156
|
+
// Folder doesn't exist → create folders then retry POST
|
|
157
|
+
if (res.status === 404) {
|
|
158
|
+
await ensureFolder(config, secretPath);
|
|
159
|
+
const retryRes = await fetch(url, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers,
|
|
162
|
+
body: JSON.stringify(body),
|
|
163
|
+
});
|
|
164
|
+
if (retryRes.ok)
|
|
165
|
+
return;
|
|
166
|
+
// Retry may 400 if another process created it concurrently → try PATCH
|
|
167
|
+
if (retryRes.status === 400) {
|
|
168
|
+
const patchRes = await fetch(url, {
|
|
169
|
+
method: "PATCH",
|
|
170
|
+
headers,
|
|
171
|
+
body: JSON.stringify(body),
|
|
172
|
+
});
|
|
173
|
+
if (patchRes.ok)
|
|
174
|
+
return;
|
|
175
|
+
const text = await patchRes.text().catch(() => "");
|
|
176
|
+
throw new Error(`Infisical PATCH ${name} (after folder creation) → ${patchRes.status}: ${text}`);
|
|
177
|
+
}
|
|
178
|
+
const text = await retryRes.text().catch(() => "");
|
|
179
|
+
throw new Error(`Infisical POST ${name} (after folder creation) → ${retryRes.status}: ${text}`);
|
|
180
|
+
}
|
|
181
|
+
const text = await res.text().catch(() => "");
|
|
182
|
+
throw new Error(`Infisical POST ${name} → ${res.status}: ${text}`);
|
|
183
|
+
}
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Config helper
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
62
187
|
/**
|
|
63
188
|
* Extract Infisical config from environment variables and log in.
|
|
64
189
|
* Reads INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_PROJECT_ID,
|