@runuai/host 0.9.150 → 0.9.152
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/db/migrations/0027_cloud_credential_foundation.sql +51 -0
- package/db/migrations/0028_leased_engine_runtimes.sql +11 -0
- package/db/migrations/meta/_journal.json +14 -0
- package/db/schema.ts +188 -0
- package/lib/agent-cli.ts +11 -13
- package/lib/agent.ts +177 -7
- package/lib/agents/durable-proc.ts +56 -44
- package/lib/agents/registry.ts +19 -3
- package/lib/agents/session-state.ts +23 -10
- package/lib/agents/transport.ts +101 -69
- package/lib/apple-one-off.ts +277 -0
- package/lib/apple-runtime-recycle.ts +4 -3
- package/lib/attachments.ts +19 -17
- package/lib/capture-flow.ts +319 -0
- package/lib/cloud-credential-cleanup-adapter.ts +123 -0
- package/lib/cloud-credential-host.ts +512 -0
- package/lib/cloud-credential-leases.ts +2544 -0
- package/lib/cloud-credential-runtime.ts +1035 -0
- package/lib/codex-auth.ts +21 -1
- package/lib/container-runtime.ts +76 -0
- package/lib/credential-cleanup-inventory.ts +405 -0
- package/lib/credential-consumers/engine-anchor.ts +1880 -0
- package/lib/credential-consumers/engines.ts +773 -0
- package/lib/credential-consumers/github.ts +594 -0
- package/lib/credential-consumers/mcp.ts +384 -0
- package/lib/credential-consumers/task-files.ts +127 -0
- package/lib/credential-store-pickup.ts +15 -2
- package/lib/credential-store.ts +361 -33
- package/lib/credentials-activation-authority.ts +546 -0
- package/lib/credentials-purge.ts +1006 -0
- package/lib/db.ts +11 -0
- package/lib/docker-exec.ts +3 -1
- package/lib/engine-accounts.ts +42 -32
- package/lib/engine-connect.ts +47 -2
- package/lib/engine-credential-env.ts +50 -0
- package/lib/engine-login.ts +83 -1
- package/lib/env-file.ts +62 -8
- package/lib/env.ts +5 -0
- package/lib/fs-aliases.ts +49 -0
- package/lib/git-clone-credentials.ts +334 -0
- package/lib/git-diff.ts +219 -177
- package/lib/git-identity.ts +59 -14
- package/lib/github-origin.ts +28 -0
- package/lib/github-tokens.ts +56 -21
- package/lib/host-env.ts +17 -4
- package/lib/launch-mode.ts +25 -0
- package/lib/legacy-runtime-retirement.ts +388 -0
- package/lib/machine-exec.ts +14 -3
- package/lib/machine-provider-aws.ts +50 -8
- package/lib/machine-provider-local.ts +4 -0
- package/lib/machine-provider.ts +4 -0
- package/lib/managed-operation-drain.ts +8 -0
- package/lib/managed-runtime.ts +371 -7
- package/lib/mcp-capture.ts +860 -0
- package/lib/mcp-connections.ts +8 -1
- package/lib/mcp-gateway.ts +168 -10
- package/lib/obs.ts +2 -6
- package/lib/orchestrator.ts +1837 -224
- package/lib/person-capture.ts +327 -0
- package/lib/project-env-legacy-tags.ts +130 -0
- package/lib/project-env-task-replica.ts +1752 -0
- package/lib/runner-boot.ts +10 -0
- package/lib/runtime-activation-tail.ts +50 -3
- package/lib/runtime-archive.ts +68 -1
- package/lib/runtime-reconnect.ts +86 -0
- package/lib/runtime-state.ts +429 -13
- package/lib/safe-http.ts +364 -0
- package/lib/secrets-proxy/task-trust.ts +34 -3
- package/lib/sidecar-resume.ts +18 -5
- package/lib/skills.ts +47 -8
- package/lib/standard-image.ts +157 -103
- package/lib/stopped-task-status-queue.ts +68 -10
- package/lib/task-credential-cleanup.ts +525 -0
- package/lib/task-diff-sandbox.ts +464 -0
- package/lib/task-diff.ts +66 -163
- package/lib/task-env-source.ts +68 -14
- package/lib/task-environment/anchor.ts +31 -0
- package/lib/task-environment/apple-container.ts +69 -28
- package/lib/task-environment/docker.ts +101 -20
- package/lib/task-environment/machine-environment.ts +61 -0
- package/lib/task-environment/machine-task-up.ts +459 -96
- package/lib/task-environment/machine.ts +101 -11
- package/lib/task-environment/self.ts +108 -50
- package/lib/task-environment/types.ts +89 -5
- package/lib/task-environment/workspace-files.ts +650 -25
- package/lib/task-environment/workspace-runtime.fixture.ts +50 -0
- package/lib/task-gateway-recovery.ts +4 -1
- package/lib/task-gateway-state.ts +15 -0
- package/lib/task-machine-swap-safety.ts +729 -0
- package/lib/token-broker-adopt.ts +344 -0
- package/lib/token-broker-lambda.ts +56 -2
- package/lib/token-broker-local.ts +28 -4
- package/lib/token-broker-revision.ts +310 -0
- package/lib/token-broker-ssm.ts +368 -28
- package/lib/token-lanes/openai-oauth.ts +7 -3
- package/lib/token-lanes/xai-oauth.ts +7 -3
- package/lib/transcript.ts +9 -5
- package/package.json +6 -2
- package/scripts/agent/task-up.sh +242 -100
- package/src/cloud-credentials.ts +2399 -0
- package/src/event-outbox.ts +8 -6
- package/src/index.ts +1386 -875
- package/src/main.ts +1061 -139
- package/src/outbound-frame.ts +31 -0
- package/src/project-env-policy.ts +125 -10
- package/src/protocol.ts +699 -23
- package/src/ui/server.ts +48 -10
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type TaskUpResult,
|
|
16
16
|
} from "../agent";
|
|
17
17
|
import { dockerCli, type DockerResult } from "../docker-exec";
|
|
18
|
+
import { isLaunchSecretEnvName } from "../engine-credential-env";
|
|
18
19
|
import {
|
|
19
20
|
hostWorkspaceRead,
|
|
20
21
|
hostWorkspaceWrite,
|
|
@@ -23,10 +24,13 @@ import { taskDir } from "../env";
|
|
|
23
24
|
import { PREVIEW_SIDECAR_TASK_LABEL } from "../preview-sidecar";
|
|
24
25
|
import { assertSafeHostTaskId } from "../task-identity";
|
|
25
26
|
import { applyProjectEnvProfile } from "../runtime-authority";
|
|
27
|
+
import { rootAnchoredRequest } from "./anchor";
|
|
26
28
|
import {
|
|
27
29
|
assertTaskEnvironmentExecRequest,
|
|
28
30
|
assertTaskEnvironmentProcessRequest,
|
|
29
31
|
assertTaskEnvironmentSessionRequest,
|
|
32
|
+
type TaskEnvironmentCallOptions,
|
|
33
|
+
type TaskEnvironmentReadOptions,
|
|
30
34
|
type TaskEnvironmentCopyRequest,
|
|
31
35
|
type TaskEnvironmentDescriptor,
|
|
32
36
|
type TaskEnvironmentDetachedSessionRequest,
|
|
@@ -45,6 +49,7 @@ import {
|
|
|
45
49
|
type TaskEnvironmentSpawnRequest,
|
|
46
50
|
type TaskEnvironmentStatus,
|
|
47
51
|
} from "./types";
|
|
52
|
+
import type { TaskWriteEnvironment } from "../credential-consumers/task-files";
|
|
48
53
|
|
|
49
54
|
export const DOCKER_TASK_ENVIRONMENT_PROVIDER = "docker-compose";
|
|
50
55
|
const DOCKER_LOCATOR_SCHEMA_VERSION = 2;
|
|
@@ -77,6 +82,7 @@ export interface DockerTaskEnvironmentDeps {
|
|
|
77
82
|
input: TaskLaunchInput,
|
|
78
83
|
credentials?: TaskUpCredentials,
|
|
79
84
|
envMode?: ProjectEnvMode,
|
|
85
|
+
runtimeEnvironment?: () => TaskWriteEnvironment,
|
|
80
86
|
) => Promise<TaskUpResult>;
|
|
81
87
|
taskEnvironmentDown?: (taskId: string) => Promise<TaskDownResult>;
|
|
82
88
|
taskStatus?: (taskId: string) => Promise<TaskStatusResult>;
|
|
@@ -131,6 +137,9 @@ export function createDockerTaskEnvironmentProvider(
|
|
|
131
137
|
request.input,
|
|
132
138
|
request.credentials,
|
|
133
139
|
request.envMode ?? "loader",
|
|
140
|
+
// ADR-154 §12: the container as task-up has created it, for the
|
|
141
|
+
// runtime-ready writes before uai-init.
|
|
142
|
+
() => new DockerTaskEnvironmentHandle(preparedLocator, resolved, request.envMode ?? "loader"),
|
|
134
143
|
);
|
|
135
144
|
const prepared = parseDockerTaskEnvironmentLocator(preparedLocator);
|
|
136
145
|
// task-up composes its worktree path in shell from the operator's
|
|
@@ -254,6 +263,8 @@ class DockerTaskEnvironmentHandle
|
|
|
254
263
|
{
|
|
255
264
|
readonly descriptor: TaskEnvironmentDescriptor;
|
|
256
265
|
readonly durableIdentity: string;
|
|
266
|
+
/** ADR-154 §6: anchored requests start under the engine supervisor. */
|
|
267
|
+
readonly anchors = true;
|
|
257
268
|
readonly #value: DockerTaskEnvironmentLocatorValue;
|
|
258
269
|
|
|
259
270
|
constructor(
|
|
@@ -289,16 +300,19 @@ class DockerTaskEnvironmentHandle
|
|
|
289
300
|
};
|
|
290
301
|
}
|
|
291
302
|
|
|
292
|
-
async readWorkspaceFile(filePath: string): Promise<Uint8Array | null> {
|
|
303
|
+
async readWorkspaceFile(filePath: string, options?: TaskEnvironmentReadOptions): Promise<Uint8Array | null> {
|
|
304
|
+
options?.admit?.();
|
|
293
305
|
return hostWorkspaceRead(
|
|
294
306
|
this.descriptor,
|
|
295
307
|
path.join(this.#value.hostWorktreePath, "workspace"),
|
|
296
308
|
filePath,
|
|
309
|
+
options,
|
|
297
310
|
);
|
|
298
311
|
}
|
|
299
312
|
|
|
300
|
-
async writeWorkspaceFile(filePath: string, bytes: Uint8Array): Promise<void> {
|
|
301
|
-
|
|
313
|
+
async writeWorkspaceFile(filePath: string, bytes: Uint8Array, options?: TaskEnvironmentCallOptions): Promise<void> {
|
|
314
|
+
options?.admit?.();
|
|
315
|
+
await hostWorkspaceWrite(
|
|
302
316
|
this.descriptor,
|
|
303
317
|
path.join(this.#value.hostWorktreePath, "workspace"),
|
|
304
318
|
filePath,
|
|
@@ -310,16 +324,23 @@ class DockerTaskEnvironmentHandle
|
|
|
310
324
|
assertTaskEnvironmentExecRequest(request);
|
|
311
325
|
const { privileged, ...unprivileged } = request;
|
|
312
326
|
const profiled = applyProjectEnvProfile(
|
|
313
|
-
unprivileged,
|
|
327
|
+
rootAnchoredRequest(unprivileged),
|
|
314
328
|
this.descriptor.envMode ?? "baked",
|
|
315
329
|
"/usr/bin/zsh",
|
|
316
330
|
);
|
|
317
331
|
request.admit?.();
|
|
332
|
+
const invocation = dockerExecArgs(
|
|
333
|
+
this.#value.containerName,
|
|
334
|
+
profiled,
|
|
335
|
+
"interactive",
|
|
336
|
+
privileged === true,
|
|
337
|
+
);
|
|
318
338
|
return capturedCliExec(
|
|
319
339
|
this.deps.spawn,
|
|
320
340
|
"docker",
|
|
321
|
-
|
|
341
|
+
invocation.args,
|
|
322
342
|
profiled,
|
|
343
|
+
invocation.env,
|
|
323
344
|
);
|
|
324
345
|
}
|
|
325
346
|
|
|
@@ -328,15 +349,18 @@ class DockerTaskEnvironmentHandle
|
|
|
328
349
|
): Promise<TaskEnvironmentProcess> {
|
|
329
350
|
assertTaskEnvironmentProcessRequest(request);
|
|
330
351
|
const profiled = applyProjectEnvProfile(
|
|
331
|
-
request,
|
|
352
|
+
rootAnchoredRequest(request),
|
|
332
353
|
this.descriptor.envMode ?? "baked",
|
|
333
354
|
"/usr/bin/zsh",
|
|
334
355
|
);
|
|
356
|
+
const invocation = dockerExecArgs(this.#value.containerName, profiled);
|
|
357
|
+
request.admit?.();
|
|
335
358
|
return streamingCliExec(
|
|
336
359
|
this.deps.spawn,
|
|
337
360
|
"docker",
|
|
338
|
-
|
|
361
|
+
invocation.args,
|
|
339
362
|
profiled,
|
|
363
|
+
invocation.env,
|
|
340
364
|
);
|
|
341
365
|
}
|
|
342
366
|
|
|
@@ -345,15 +369,18 @@ class DockerTaskEnvironmentHandle
|
|
|
345
369
|
): Promise<TaskEnvironmentProcess> {
|
|
346
370
|
assertTaskEnvironmentSessionRequest(request);
|
|
347
371
|
const profiled = applyProjectEnvProfile(
|
|
348
|
-
request,
|
|
372
|
+
rootAnchoredRequest(request),
|
|
349
373
|
this.descriptor.envMode ?? "baked",
|
|
350
374
|
"/usr/bin/zsh",
|
|
351
375
|
);
|
|
376
|
+
const invocation = dockerExecArgs(this.#value.containerName, profiled);
|
|
377
|
+
request.admit?.();
|
|
352
378
|
return streamingCliExec(
|
|
353
379
|
this.deps.spawn,
|
|
354
380
|
"docker",
|
|
355
|
-
|
|
381
|
+
invocation.args,
|
|
356
382
|
profiled,
|
|
383
|
+
invocation.env,
|
|
357
384
|
);
|
|
358
385
|
}
|
|
359
386
|
|
|
@@ -362,17 +389,21 @@ class DockerTaskEnvironmentHandle
|
|
|
362
389
|
): Promise<TaskEnvironmentExecResult> {
|
|
363
390
|
assertTaskEnvironmentSessionRequest(request);
|
|
364
391
|
const profiled = applyProjectEnvProfile(
|
|
365
|
-
request,
|
|
392
|
+
rootAnchoredRequest(request),
|
|
366
393
|
this.descriptor.envMode ?? "baked",
|
|
367
394
|
"/usr/bin/zsh",
|
|
368
395
|
);
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
maxOutputBytes: request.maxOutputBytes,
|
|
374
|
-
},
|
|
396
|
+
const invocation = dockerExecArgs(
|
|
397
|
+
this.#value.containerName,
|
|
398
|
+
profiled,
|
|
399
|
+
"detached",
|
|
375
400
|
);
|
|
401
|
+
request.admit?.();
|
|
402
|
+
const result = await this.deps.docker(invocation.args, {
|
|
403
|
+
timeoutMs: request.launchTimeoutMs,
|
|
404
|
+
maxOutputBytes: request.maxOutputBytes,
|
|
405
|
+
...(invocation.env === undefined ? {} : { env: invocation.env }),
|
|
406
|
+
});
|
|
376
407
|
return {
|
|
377
408
|
exitCode: result.status,
|
|
378
409
|
signal: null,
|
|
@@ -397,6 +428,7 @@ class DockerTaskEnvironmentHandle
|
|
|
397
428
|
`${this.#value.containerName}:${request.source}`,
|
|
398
429
|
request.destination,
|
|
399
430
|
];
|
|
431
|
+
request.admit?.();
|
|
400
432
|
const result = await this.deps.docker(args, {
|
|
401
433
|
timeoutMs: 120_000,
|
|
402
434
|
maxOutputBytes: 1024 * 1024,
|
|
@@ -634,6 +666,8 @@ function requiredPort(value: unknown, name: string): number {
|
|
|
634
666
|
return Number(value);
|
|
635
667
|
}
|
|
636
668
|
|
|
669
|
+
/** The argv of one `docker exec`, and the environment the docker CLI must run
|
|
670
|
+
* with to resolve the credentials it names (cliExecEnv). */
|
|
637
671
|
function dockerExecArgs(
|
|
638
672
|
containerName: string,
|
|
639
673
|
request:
|
|
@@ -644,21 +678,60 @@ function dockerExecArgs(
|
|
|
644
678
|
mode: "interactive" | "detached" = "interactive",
|
|
645
679
|
/** Passed only by exec(), after assertTaskEnvironmentExecRequest. */
|
|
646
680
|
privileged = false,
|
|
647
|
-
): string[] {
|
|
681
|
+
): { args: string[]; env: NodeJS.ProcessEnv | undefined } {
|
|
648
682
|
const args = ["exec", mode === "detached" ? "-d" : "-i"];
|
|
649
683
|
if (privileged) args.push("--privileged");
|
|
650
684
|
if (request.cwd) args.push("--workdir", request.cwd);
|
|
651
685
|
if (request.user) args.push("--user", request.user);
|
|
686
|
+
const execEnv = cliExecEnv(request);
|
|
687
|
+
args.push(...execEnv.args, containerName, ...request.argv);
|
|
688
|
+
return { args, env: execEnv.env };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* The `--env` options of one exec, for a CLI that resolves a bare
|
|
693
|
+
* `--env NAME` from its own environment (docker, Apple `container`), and the
|
|
694
|
+
* environment the CLI child must then run with.
|
|
695
|
+
*
|
|
696
|
+
* An engine credential, or an agent's task token under any of its names, is
|
|
697
|
+
* named bare and its value set in that child's environment only, so it never
|
|
698
|
+
* lands on the runner's argv, which every local user can read with `ps`
|
|
699
|
+
* (ADR-154 §6). The request's value wins over the
|
|
700
|
+
* runner's own variable of the same name (an owned runner may still carry a
|
|
701
|
+
* legacy CLAUDE_CODE_OAUTH_TOKEN), and a credential the request does not
|
|
702
|
+
* carry is never named here; a name in `inheritEnv` still forwards the
|
|
703
|
+
* runner's own value, as that field promises. Every other entry keeps its
|
|
704
|
+
* `NAME=value` form: in the child's environment, a task's HOME or PATH would
|
|
705
|
+
* steer the CLI's own config and credential-helper lookup. With no
|
|
706
|
+
* credential, `env` is undefined and the child inherits the runner's
|
|
707
|
+
* environment as before.
|
|
708
|
+
*/
|
|
709
|
+
export function cliExecEnv(request: {
|
|
710
|
+
inheritEnv?: readonly string[];
|
|
711
|
+
env?: Readonly<Record<string, string>>;
|
|
712
|
+
}): { args: string[]; env: NodeJS.ProcessEnv | undefined } {
|
|
713
|
+
const args: string[] = [];
|
|
652
714
|
for (const name of request.inheritEnv ?? []) {
|
|
653
715
|
if (process.env[name] !== undefined) args.push("--env", name);
|
|
654
716
|
}
|
|
717
|
+
const credentials: Record<string, string> = {};
|
|
655
718
|
for (const [name, value] of Object.entries(request.env ?? {}).sort(([a], [b]) =>
|
|
656
719
|
a.localeCompare(b),
|
|
657
720
|
)) {
|
|
658
|
-
|
|
721
|
+
if (isLaunchSecretEnvName(name)) {
|
|
722
|
+
args.push("--env", name);
|
|
723
|
+
credentials[name] = value;
|
|
724
|
+
} else {
|
|
725
|
+
args.push("--env", `${name}=${value}`);
|
|
726
|
+
}
|
|
659
727
|
}
|
|
660
|
-
|
|
661
|
-
|
|
728
|
+
return {
|
|
729
|
+
args,
|
|
730
|
+
env:
|
|
731
|
+
Object.keys(credentials).length === 0
|
|
732
|
+
? undefined
|
|
733
|
+
: { ...process.env, ...credentials },
|
|
734
|
+
};
|
|
662
735
|
}
|
|
663
736
|
|
|
664
737
|
/** Bounded one-shot exec, generalized over the CLI so the Apple container
|
|
@@ -668,10 +741,14 @@ export function capturedCliExec(
|
|
|
668
741
|
command: string,
|
|
669
742
|
args: readonly string[],
|
|
670
743
|
request: TaskEnvironmentExecRequest,
|
|
744
|
+
/** The child's own environment, when it must differ from the runner's
|
|
745
|
+
* (cliExecEnv). */
|
|
746
|
+
env?: NodeJS.ProcessEnv,
|
|
671
747
|
): Promise<TaskEnvironmentExecResult> {
|
|
672
748
|
return new Promise((resolve) => {
|
|
673
749
|
const child = spawn(command, [...args], {
|
|
674
750
|
stdio: ["pipe", "pipe", "pipe"],
|
|
751
|
+
...(env === undefined ? {} : { env }),
|
|
675
752
|
}) as ChildProcessWithoutNullStreams;
|
|
676
753
|
let stdout: Buffer[] = [];
|
|
677
754
|
let stderr: Buffer[] = [];
|
|
@@ -739,9 +816,12 @@ export function streamingCliExec(
|
|
|
739
816
|
command: string,
|
|
740
817
|
args: readonly string[],
|
|
741
818
|
request: TaskEnvironmentSpawnRequest | TaskEnvironmentSessionRequest,
|
|
819
|
+
/** As for capturedCliExec. */
|
|
820
|
+
env?: NodeJS.ProcessEnv,
|
|
742
821
|
): TaskEnvironmentProcess {
|
|
743
822
|
const child = spawn(command, [...args], {
|
|
744
823
|
stdio: ["pipe", "pipe", "pipe"],
|
|
824
|
+
...(env === undefined ? {} : { env }),
|
|
745
825
|
}) as ChildProcessWithoutNullStreams;
|
|
746
826
|
// Every kill forces settlement within a bounded grace, like the captured
|
|
747
827
|
// runner: a wedged client must not leave an agent session's completion
|
|
@@ -997,6 +1077,7 @@ async function inspectDockerTaskEnvironmentStatus(
|
|
|
997
1077
|
state: state === "running" ? "running" : "stopped",
|
|
998
1078
|
instances: [environment.containerName],
|
|
999
1079
|
workspacePresent,
|
|
1080
|
+
...(state === "paused" || state === "restarting" ? { frozen: true as const } : {}),
|
|
1000
1081
|
};
|
|
1001
1082
|
}
|
|
1002
1083
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A task machine's machine-wide environment, /etc/environment, which sshd's
|
|
3
|
+
* pam_env applies to every session on it: what a self or machine task's
|
|
4
|
+
* processes run beneath their own `env`.
|
|
5
|
+
*
|
|
6
|
+
* ADR-154 §6: it never supplies an engine's credential. A machine-wide value
|
|
7
|
+
* of one (an owner's sign-in left in an older image) never reaches a task's
|
|
8
|
+
* process, leased or not: a leased engine's comes from its admission, and an
|
|
9
|
+
* unleased one's from its own request.
|
|
10
|
+
*/
|
|
11
|
+
import { isEngineCredentialEnvName, isLaunchSecretEnvName } from "../engine-credential-env";
|
|
12
|
+
|
|
13
|
+
export const ETC_ENVIRONMENT_PATH = "/etc/environment";
|
|
14
|
+
|
|
15
|
+
/** What an anchored launch's environment starts from where nothing else
|
|
16
|
+
* gives a PATH: the usual system one. */
|
|
17
|
+
export const BASELINE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* /etc/environment, as pam_env reads it: `KEY=value` lines, optional
|
|
21
|
+
* `export`, optional single or double quotes, `#` comments.
|
|
22
|
+
*/
|
|
23
|
+
export function parseEtcEnvironment(text: string): Record<string, string> {
|
|
24
|
+
const out: Record<string, string> = {};
|
|
25
|
+
for (const raw of text.split("\n")) {
|
|
26
|
+
const line = raw.trim();
|
|
27
|
+
if (!line || line.startsWith("#")) continue;
|
|
28
|
+
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line);
|
|
29
|
+
if (!match) continue;
|
|
30
|
+
let value = match[2]!.trim();
|
|
31
|
+
if (
|
|
32
|
+
value.length >= 2 &&
|
|
33
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
34
|
+
(value.startsWith("'") && value.endsWith("'")))
|
|
35
|
+
) {
|
|
36
|
+
value = value.slice(1, -1);
|
|
37
|
+
}
|
|
38
|
+
out[match[1]!] = value;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** `environment` without any engine's credential. */
|
|
44
|
+
export function withoutEngineCredentials(
|
|
45
|
+
environment: Readonly<Record<string, string>>,
|
|
46
|
+
): Record<string, string> {
|
|
47
|
+
return Object.fromEntries(Object.entries(environment).filter(([name]) => !isEngineCredentialEnvName(name)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `environment` without any launch secret: an engine's credential, or an
|
|
52
|
+
* agent's task token under any of its names. What an anchored launch's
|
|
53
|
+
* baseline takes from the machine-wide environment, which rides its command
|
|
54
|
+
* line: its secrets come from its admission. An unanchored launch keeps the
|
|
55
|
+
* machine-wide values it always had, but for an engine's credential.
|
|
56
|
+
*/
|
|
57
|
+
export function withoutLaunchSecrets(
|
|
58
|
+
environment: Readonly<Record<string, string>>,
|
|
59
|
+
): Record<string, string> {
|
|
60
|
+
return Object.fromEntries(Object.entries(environment).filter(([name]) => !isLaunchSecretEnvName(name)));
|
|
61
|
+
}
|