@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
package/scripts/agent/task-up.sh
CHANGED
|
@@ -181,17 +181,33 @@ reject_workspace_runtime_shadow() {
|
|
|
181
181
|
# post-materialization recheck below keeps refusing — by then the root must
|
|
182
182
|
# be empty by construction, so an entry there is a bug, not a user file.
|
|
183
183
|
quarantine_workspace_runtime_shadow() {
|
|
184
|
-
local path="$task_workspace/.tool-versions" aside_dir aside
|
|
184
|
+
local path="$task_workspace/.tool-versions" aside_dir aside attempt note
|
|
185
185
|
if [ -L "$path" ] || [ -e "$path" ]; then
|
|
186
186
|
aside_dir="$task_workspace/.uai/refused"
|
|
187
187
|
reject_unsafe_host_directory "$task_workspace/.uai" "the task control directory"
|
|
188
188
|
reject_unsafe_host_directory "$aside_dir" "the refused-files directory"
|
|
189
189
|
mkdir -p "$aside_dir"
|
|
190
|
+
# Never onto an entry the task left there: mv moves into a directory, or
|
|
191
|
+
# one a link leads to, outside the workspace (#cb3j0z). The task's
|
|
192
|
+
# container is gone, so nothing races these checks.
|
|
190
193
|
aside="$aside_dir/tool-versions.$(date +%s)"
|
|
194
|
+
attempt=0
|
|
195
|
+
while [ -L "$aside" ] || [ -e "$aside" ]; do
|
|
196
|
+
attempt=$((attempt + 1))
|
|
197
|
+
aside="$aside_dir/tool-versions.$(date +%s).$attempt"
|
|
198
|
+
done
|
|
191
199
|
if ! mv -f "$path" "$aside" 2>/dev/null || [ -L "$path" ] || [ -e "$path" ]; then
|
|
192
200
|
reject_workspace_runtime_shadow
|
|
193
201
|
fi
|
|
194
|
-
|
|
202
|
+
# Nor written through a link or special file in the note's place: that
|
|
203
|
+
# is removed first (rm never follows a link), and the note is written
|
|
204
|
+
# only to nothing or a regular file.
|
|
205
|
+
note="$aside_dir/README.txt"
|
|
206
|
+
if [ -L "$note" ] || { [ -e "$note" ] && [ ! -f "$note" ]; }; then
|
|
207
|
+
rm -f "$note" 2>/dev/null || true
|
|
208
|
+
fi
|
|
209
|
+
if [ ! -L "$note" ] && { [ ! -e "$note" ] || [ -f "$note" ]; }; then
|
|
210
|
+
cat > "$note" <<'NOTE'
|
|
195
211
|
Uai moved a /workspace/.tool-versions aside before starting this task.
|
|
196
212
|
|
|
197
213
|
A .tool-versions at the workspace root is outside the runtime proof and would
|
|
@@ -200,6 +216,7 @@ to start a container while one exists. The file is kept here, untouched, under
|
|
|
200
216
|
its original content. Pin runtimes in a project's own .tool-versions instead
|
|
201
217
|
(/workspace/<project>/.tool-versions), which the materializer proves.
|
|
202
218
|
NOTE
|
|
219
|
+
fi
|
|
203
220
|
log "moved a workspace-root .tool-versions aside to ${aside#"$task_workspace"/} (it would override the runtimes proved for this task; pin per project instead)"
|
|
204
221
|
fi
|
|
205
222
|
}
|
|
@@ -640,6 +657,25 @@ task_uai_dir_physical="$task_dir_physical/.uai"
|
|
|
640
657
|
# 3. Bare mirrors + worktrees, one per selected project.
|
|
641
658
|
# -----------------------------------------------------------------------------
|
|
642
659
|
|
|
660
|
+
# ADR-154 §12: a task on cloud credentials tells the runner where it is
|
|
661
|
+
# with an event file in this private directory (event-clone-done, then
|
|
662
|
+
# event-runtime-ready), and finds the runner's answer to runtime-ready there
|
|
663
|
+
# too. Never on stderr, which a subprocess shares; the path is unset here so
|
|
664
|
+
# no process task-up starts inherits it; nothing blocks.
|
|
665
|
+
uai_handshake_dir="${UAI_TASK_HANDSHAKE_DIR:-}"
|
|
666
|
+
unset UAI_TASK_HANDSHAKE_DIR
|
|
667
|
+
# A task on cloud credentials: every host Git operation runs with nothing of
|
|
668
|
+
# the runner's own (no home, so no .netrc, ~/.gitconfig or ~/.ssh/config),
|
|
669
|
+
# by exactly the route its lease names, and never falls back to another.
|
|
670
|
+
uai_leased_git=0
|
|
671
|
+
if [ -n "$uai_handshake_dir" ]; then
|
|
672
|
+
uai_leased_git=1
|
|
673
|
+
fi
|
|
674
|
+
# ADR-154 §6: no default slot. A task on cloud credentials gets none of the
|
|
675
|
+
# runner owner's own engine sign-ins: not their tokens in its env (the
|
|
676
|
+
# runner strips them from ours), not their config directories in its home.
|
|
677
|
+
uai_on_cloud_credentials=$uai_leased_git
|
|
678
|
+
|
|
643
679
|
# Prepare the optional SSH identity. Connected users fetch over HTTPS with
|
|
644
680
|
# their per-host GitHub credential; SSH is used for Git transport only when the
|
|
645
681
|
# task owner has not connected GitHub. `IdentitiesOnly=yes` +
|
|
@@ -650,30 +686,59 @@ task_uai_dir_physical="$task_dir_physical/.uai"
|
|
|
650
686
|
# $UAI_TASK_IDENTITY_DIR by the orchestrator) so clone + push run as them. A
|
|
651
687
|
# task must never borrow the operator's shared identity: that would authorize
|
|
652
688
|
# one user as a different principal.
|
|
689
|
+
#
|
|
690
|
+
# ADR-154 §12: a task on cloud credentials gets no key file. The runner
|
|
691
|
+
# holds the owner's key in its own memory behind a private agent socket for
|
|
692
|
+
# exactly as long as this clone ($UAI_TASK_SSH_AUTH_SOCK, with the public
|
|
693
|
+
# key naming the identity); ssh asks that agent and nothing else.
|
|
653
694
|
if [ -n "${UAI_TASK_IDENTITY_DIR:-}" ] && [ -f "${UAI_TASK_IDENTITY_DIR}/id_ed25519" ]; then
|
|
654
695
|
uai_identity_key="$UAI_TASK_IDENTITY_DIR/id_ed25519"
|
|
655
696
|
log "git over SSH using the task creator's per-user key"
|
|
656
697
|
else
|
|
657
698
|
uai_identity_key=""
|
|
658
699
|
fi
|
|
700
|
+
uai_task_ssh_auth_sock="${UAI_TASK_SSH_AUTH_SOCK:-}"
|
|
701
|
+
uai_task_ssh_public_key="${UAI_TASK_SSH_PUBLIC_KEY:-}"
|
|
702
|
+
unset UAI_TASK_SSH_AUTH_SOCK UAI_TASK_SSH_PUBLIC_KEY
|
|
659
703
|
uai_has_ssh_identity=0
|
|
704
|
+
uai_leased_ssh=0
|
|
705
|
+
uai_known_hosts="$UAI_DATA_DIR/identity/known_hosts"
|
|
660
706
|
if [ -n "$uai_identity_key" ] && [ -f "$uai_identity_key" ]; then
|
|
661
707
|
uai_has_ssh_identity=1
|
|
662
|
-
uai_known_hosts="$UAI_DATA_DIR/identity/known_hosts"
|
|
663
708
|
GIT_SSH_COMMAND="ssh -i $uai_identity_key -o IdentitiesOnly=yes -o IdentityAgent=none -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
664
709
|
[ -f "$uai_known_hosts" ] && GIT_SSH_COMMAND="$GIT_SSH_COMMAND -o UserKnownHostsFile=$uai_known_hosts"
|
|
665
710
|
export GIT_SSH_COMMAND
|
|
666
711
|
log "git over SSH using $uai_identity_key (1Password agent bypassed)"
|
|
712
|
+
elif [ -n "$uai_task_ssh_auth_sock" ] && [ -S "$uai_task_ssh_auth_sock" ] && [ -f "$uai_task_ssh_public_key" ]; then
|
|
713
|
+
uai_has_ssh_identity=1
|
|
714
|
+
uai_leased_ssh=1
|
|
715
|
+
GIT_SSH_COMMAND="ssh -F /dev/null -i $uai_task_ssh_public_key -o IdentitiesOnly=yes -o IdentityAgent=$uai_task_ssh_auth_sock -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
716
|
+
[ -f "$uai_known_hosts" ] && GIT_SSH_COMMAND="$GIT_SSH_COMMAND -o UserKnownHostsFile=$uai_known_hosts"
|
|
717
|
+
export GIT_SSH_COMMAND
|
|
718
|
+
log "git over SSH using the task owner's key, held in the runner's memory"
|
|
667
719
|
else
|
|
668
720
|
log "no SSH identity for the task owner — SSH transport fallback unavailable"
|
|
669
721
|
fi
|
|
670
722
|
|
|
671
|
-
# Only
|
|
672
|
-
# a private per-task `git credential-cache` daemon
|
|
673
|
-
#
|
|
723
|
+
# Only a non-secret path reaches this script. The task owner's token lives in
|
|
724
|
+
# a private per-task `git credential-cache` daemon, or, for a task on cloud
|
|
725
|
+
# credentials (ADR-154 §12), in the runner's own memory behind the helper
|
|
726
|
+
# $UAI_GITHUB_CREDENTIAL_HELPER asks for exactly as long as this clone. It is
|
|
727
|
+
# never placed in the environment, argv, remote URL, mirror config, Compose,
|
|
728
|
+
# or logs. Only the daemon's token is installed in the container below; a
|
|
729
|
+
# task on cloud credentials has its GitHub configuration written by the
|
|
730
|
+
# runner, which records it for cleanup.
|
|
674
731
|
github_credential_socket="${UAI_GITHUB_CREDENTIAL_SOCKET:-}"
|
|
675
|
-
|
|
676
|
-
|
|
732
|
+
github_credential_helper="${UAI_GITHUB_CREDENTIAL_HELPER:-}"
|
|
733
|
+
unset UAI_GITHUB_CREDENTIAL_SOCKET UAI_GITHUB_CREDENTIAL_HELPER
|
|
734
|
+
if [ -n "$github_credential_helper" ]; then
|
|
735
|
+
github_git_helper="$github_credential_helper"
|
|
736
|
+
elif [ -n "$github_credential_socket" ]; then
|
|
737
|
+
github_git_helper="cache --socket=$github_credential_socket"
|
|
738
|
+
else
|
|
739
|
+
github_git_helper=""
|
|
740
|
+
fi
|
|
741
|
+
if [ -n "$github_git_helper" ]; then
|
|
677
742
|
uai_git_transport="https"
|
|
678
743
|
else
|
|
679
744
|
# Refined after each GitHub fetch below: select SSH only when it was the
|
|
@@ -683,6 +748,67 @@ else
|
|
|
683
748
|
fi
|
|
684
749
|
uai_used_ssh_transport=0
|
|
685
750
|
|
|
751
|
+
# ADR-154 §12: a task on cloud credentials runs every host Git operation in an
|
|
752
|
+
# environment rebuilt from an allowlist (env -i), never the runner process's
|
|
753
|
+
# own: an ambient GIT_CONFIG_COUNT and its numbered pairs, GIT_EXEC_PATH, an
|
|
754
|
+
# askpass or an agent socket could otherwise redirect or answer its route.
|
|
755
|
+
# What passes is what finds git and ssh and reaches the network (the PATH, the
|
|
756
|
+
# temporary directory, proxies, the CA bundle), never a credential, a
|
|
757
|
+
# configuration or a route.
|
|
758
|
+
uai_git_passthrough=()
|
|
759
|
+
if [ "$uai_leased_git" = "1" ]; then
|
|
760
|
+
for uai_git_name in PATH TMPDIR http_proxy https_proxy all_proxy no_proxy \
|
|
761
|
+
HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY SSL_CERT_FILE SSL_CERT_DIR; do
|
|
762
|
+
if [ -n "${!uai_git_name:-}" ]; then
|
|
763
|
+
uai_git_passthrough+=("$uai_git_name=${!uai_git_name}")
|
|
764
|
+
fi
|
|
765
|
+
done
|
|
766
|
+
fi
|
|
767
|
+
|
|
768
|
+
# Runs git with the NAME=VALUE assignments before `--`, then `git "$@"`: for a
|
|
769
|
+
# task on cloud credentials in those, the allowlist and no home alone;
|
|
770
|
+
# otherwise on top of this process's environment, which the wrappers below
|
|
771
|
+
# narrow themselves.
|
|
772
|
+
uai_git() {
|
|
773
|
+
local uai_assign=()
|
|
774
|
+
while [ "$#" -gt 0 ] && [ "$1" != "--" ]; do
|
|
775
|
+
uai_assign+=("$1")
|
|
776
|
+
shift
|
|
777
|
+
done
|
|
778
|
+
shift
|
|
779
|
+
if [ "$uai_leased_git" = "1" ]; then
|
|
780
|
+
env -i ${uai_git_passthrough[@]+"${uai_git_passthrough[@]}"} \
|
|
781
|
+
HOME=/nonexistent XDG_CONFIG_HOME=/nonexistent LC_ALL=C \
|
|
782
|
+
GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 \
|
|
783
|
+
${uai_assign[@]+"${uai_assign[@]}"} git "$@"
|
|
784
|
+
else
|
|
785
|
+
env ${uai_assign[@]+"${uai_assign[@]}"} git "$@"
|
|
786
|
+
fi
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
# ADR-154 §12: a task on cloud credentials that GitHub refused says which of
|
|
790
|
+
# its owner's credentials was refused (the lease's GitHub sign-in or SSH key)
|
|
791
|
+
# and where to fix that: Uai's Settings, never this host.
|
|
792
|
+
emit_leased_github_denial() {
|
|
793
|
+
case "$1" in
|
|
794
|
+
token)
|
|
795
|
+
emit_err "GITHUB_TOKEN_ACCESS_DENIED" \
|
|
796
|
+
"GitHub denied the task owner's GitHub sign-in access to $2. Confirm that the Uai GitHub App is installed on the owning account or organization, this repository is included in its grant, and the owner can read it. Then start the task again." \
|
|
797
|
+
"$3"
|
|
798
|
+
;;
|
|
799
|
+
ssh)
|
|
800
|
+
emit_err "GITHUB_SSH_ACCESS_DENIED" \
|
|
801
|
+
"GitHub denied the task owner's SSH key access to $2. Register that key with an account that can read the repository (and authorize it for SAML SSO when required), or connect GitHub in Settings. Then start the task again." \
|
|
802
|
+
"$3"
|
|
803
|
+
;;
|
|
804
|
+
*)
|
|
805
|
+
emit_err "GITHUB_CONNECTION_REQUIRED" \
|
|
806
|
+
"The task owner has neither a GitHub sign-in nor an SSH key in Uai that can read $2. Connect GitHub in Settings, then start the task again." \
|
|
807
|
+
"$3"
|
|
808
|
+
;;
|
|
809
|
+
esac
|
|
810
|
+
}
|
|
811
|
+
|
|
686
812
|
git_with_github_credential() (
|
|
687
813
|
unset GIT_ASKPASS SSH_ASKPASS SSH_AUTH_SOCK GIT_CONFIG \
|
|
688
814
|
GIT_CONFIG_PARAMETERS GIT_TRACE GIT_TRACE2 GIT_TRACE_CURL \
|
|
@@ -690,13 +816,13 @@ git_with_github_credential() (
|
|
|
690
816
|
export LC_ALL=C GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
|
|
691
817
|
# If a container ever poisoned a shared mirror's URL config, fail closed
|
|
692
818
|
# instead of silently switching this operation to an SSH principal.
|
|
693
|
-
GIT_SSH_COMMAND=/bin/false GIT_TERMINAL_PROMPT=0
|
|
819
|
+
uai_git GIT_SSH_COMMAND=/bin/false GIT_TERMINAL_PROMPT=0 -- \
|
|
694
820
|
-c core.hooksPath=/dev/null \
|
|
695
821
|
-c core.fsmonitor=false \
|
|
696
822
|
-c http.extraHeader= \
|
|
697
823
|
-c http.sslVerify=true \
|
|
698
824
|
-c credential.helper= \
|
|
699
|
-
-c "credential.helper
|
|
825
|
+
-c "credential.helper=$github_git_helper" \
|
|
700
826
|
"$@"
|
|
701
827
|
)
|
|
702
828
|
|
|
@@ -707,7 +833,8 @@ git_with_github_ssh() (
|
|
|
707
833
|
GIT_CONFIG_PARAMETERS GIT_TRACE GIT_TRACE2 GIT_TRACE_CURL \
|
|
708
834
|
GIT_TRACE_PACKET GIT_TRACE_REDACT
|
|
709
835
|
export LC_ALL=C GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
|
|
710
|
-
|
|
836
|
+
# The SSH command chosen above: the task owner's identity, nothing else.
|
|
837
|
+
uai_git "GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-/bin/false}" GIT_TERMINAL_PROMPT=0 -- \
|
|
711
838
|
-c core.hooksPath=/dev/null \
|
|
712
839
|
-c core.fsmonitor=false \
|
|
713
840
|
-c 'url.git@github.com:.insteadOf=https://github.com/' \
|
|
@@ -721,7 +848,7 @@ git_anonymous_https() (
|
|
|
721
848
|
GIT_CONFIG_PARAMETERS GIT_TRACE GIT_TRACE2 GIT_TRACE_CURL \
|
|
722
849
|
GIT_TRACE_PACKET GIT_TRACE_REDACT
|
|
723
850
|
export LC_ALL=C GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
|
|
724
|
-
GIT_SSH_COMMAND=/bin/false GIT_TERMINAL_PROMPT=0
|
|
851
|
+
uai_git GIT_SSH_COMMAND=/bin/false GIT_TERMINAL_PROMPT=0 -- \
|
|
725
852
|
-c core.hooksPath=/dev/null \
|
|
726
853
|
-c core.fsmonitor=false \
|
|
727
854
|
-c http.extraHeader= \
|
|
@@ -744,11 +871,11 @@ git_host_control() (
|
|
|
744
871
|
# This helper is for local repository bookkeeping and public/local remotes.
|
|
745
872
|
# It must never inherit the operator's SSH agent or credential helper. Every
|
|
746
873
|
# authenticated GitHub fetch goes through one of the explicit helpers above.
|
|
747
|
-
GIT_CONFIG_COUNT=3 \
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
874
|
+
uai_git GIT_CONFIG_COUNT=3 \
|
|
875
|
+
GIT_CONFIG_KEY_0=core.hooksPath GIT_CONFIG_VALUE_0=/dev/null \
|
|
876
|
+
GIT_CONFIG_KEY_1=core.fsmonitor GIT_CONFIG_VALUE_1=false \
|
|
877
|
+
GIT_CONFIG_KEY_2=advice.defaultBranchName GIT_CONFIG_VALUE_2=false \
|
|
878
|
+
GIT_SSH_COMMAND=/bin/false GIT_TERMINAL_PROMPT=0 -- \
|
|
752
879
|
-c credential.helper= \
|
|
753
880
|
"$@"
|
|
754
881
|
)
|
|
@@ -937,7 +1064,15 @@ if [ "$shared_files_mode" != "off" ]; then
|
|
|
937
1064
|
[ -n "$shared_owner_user" ] && mkdir -p "$shared_root/users/$shared_owner_user"
|
|
938
1065
|
# Marker for the preamble: only containers that actually carry the mounts
|
|
939
1066
|
# get the "## Shared files" briefing (pre-feature containers don't).
|
|
940
|
-
|
|
1067
|
+
# Never written through a link or special file the task left in its place
|
|
1068
|
+
# (#cb3j0z): that is removed first (rm never follows a link), and nothing
|
|
1069
|
+
# is written where anything remains. The task's container is gone, so
|
|
1070
|
+
# nothing races this.
|
|
1071
|
+
files_mounted="$task_workspace/.uai/files-mounted"
|
|
1072
|
+
if [ -L "$files_mounted" ] || { [ -e "$files_mounted" ] && [ ! -f "$files_mounted" ]; }; then
|
|
1073
|
+
rm -f "$files_mounted" 2>/dev/null || true
|
|
1074
|
+
fi
|
|
1075
|
+
[ -L "$files_mounted" ] || [ -e "$files_mounted" ] || : > "$files_mounted"
|
|
941
1076
|
fi
|
|
942
1077
|
|
|
943
1078
|
# Iterate using jq -c so each project is a single JSON object per line.
|
|
@@ -1062,7 +1197,7 @@ while IFS= read -r project_obj; do
|
|
|
1062
1197
|
# the next attempt starts fresh rather than re-using a half-init.
|
|
1063
1198
|
fetch_err=""
|
|
1064
1199
|
fetch_failed=0
|
|
1065
|
-
if [ -n "$github_https_url" ] && [ -n "$
|
|
1200
|
+
if [ -n "$github_https_url" ] && [ -n "$github_git_helper" ]; then
|
|
1066
1201
|
github_fetch_mode="token"
|
|
1067
1202
|
if ! fetch_err=$(git_with_github_credential \
|
|
1068
1203
|
-C "$mirror_dir" fetch origin 2>&1); then
|
|
@@ -1075,8 +1210,9 @@ while IFS= read -r project_obj; do
|
|
|
1075
1210
|
-C "$mirror_dir" fetch origin 2>&1); then
|
|
1076
1211
|
fetch_failed=1
|
|
1077
1212
|
# github.com requires an authenticated SSH handshake even for public
|
|
1078
|
-
# repositories. Preserve the no-credential public-repo path
|
|
1079
|
-
|
|
1213
|
+
# repositories. Preserve the no-credential public-repo path, except
|
|
1214
|
+
# for a key a task's lease names: its route fails closed.
|
|
1215
|
+
if [ "$uai_leased_ssh" != "1" ] && git_anonymous_https -C "$mirror_dir" fetch origin \
|
|
1080
1216
|
>/dev/null 2>&1; then
|
|
1081
1217
|
github_fetch_mode="anonymous"
|
|
1082
1218
|
fetch_failed=0
|
|
@@ -1108,6 +1244,9 @@ while IFS= read -r project_obj; do
|
|
|
1108
1244
|
*"Authentication failed"*|*"Invalid username or password"*|*"HTTP 401"*|\
|
|
1109
1245
|
*"HTTP 403"*|*"HTTP 404"*|*"requested URL returned error: 401"*|\
|
|
1110
1246
|
*"requested URL returned error: 403"*|*"requested URL returned error: 404"*)
|
|
1247
|
+
if [ "$uai_leased_git" = "1" ]; then
|
|
1248
|
+
emit_leased_github_denial "$github_fetch_mode" "$repo_url" "bare-clone ($project_id)"
|
|
1249
|
+
fi
|
|
1111
1250
|
if [ "$github_fetch_mode" = "token" ]; then
|
|
1112
1251
|
emit_err "GITHUB_TOKEN_ACCESS_DENIED" \
|
|
1113
1252
|
"GitHub denied the connected user's access to $repo_url. Confirm that the Uai GitHub App is installed on the owning account or organization, this repository is included in its grant, and the connected user can write to it. Then retry the task." \
|
|
@@ -1148,7 +1287,7 @@ while IFS= read -r project_obj; do
|
|
|
1148
1287
|
step "FETCH_FAILED" "git fetch origin ($project_id)"
|
|
1149
1288
|
fetch_ok=0
|
|
1150
1289
|
fetch_err=""
|
|
1151
|
-
if [ -n "$github_https_url" ] && [ -n "$
|
|
1290
|
+
if [ -n "$github_https_url" ] && [ -n "$github_git_helper" ]; then
|
|
1152
1291
|
github_fetch_mode="token"
|
|
1153
1292
|
if fetch_err=$(git_with_github_credential \
|
|
1154
1293
|
-C "$mirror_dir" fetch origin 2>&1); then
|
|
@@ -1164,7 +1303,8 @@ while IFS= read -r project_obj; do
|
|
|
1164
1303
|
else
|
|
1165
1304
|
github_fetch_mode="none"
|
|
1166
1305
|
fi
|
|
1167
|
-
|
|
1306
|
+
# A key a task's lease names never falls back to another principal.
|
|
1307
|
+
if [ "$fetch_ok" != "1" ] && [ "$uai_leased_ssh" != "1" ]; then
|
|
1168
1308
|
anonymous_err=""
|
|
1169
1309
|
if anonymous_err=$(git_anonymous_https \
|
|
1170
1310
|
-C "$mirror_dir" fetch origin 2>&1); then
|
|
@@ -1185,6 +1325,9 @@ while IFS= read -r project_obj; do
|
|
|
1185
1325
|
*"Authentication failed"*|*"Invalid username or password"*|*"HTTP 401"*|\
|
|
1186
1326
|
*"HTTP 403"*|*"HTTP 404"*|*"requested URL returned error: 401"*|\
|
|
1187
1327
|
*"requested URL returned error: 403"*|*"requested URL returned error: 404"*)
|
|
1328
|
+
if [ "$uai_leased_git" = "1" ]; then
|
|
1329
|
+
emit_leased_github_denial "$github_fetch_mode" "$repo_url" "git fetch origin ($project_id)"
|
|
1330
|
+
fi
|
|
1188
1331
|
case "$github_fetch_mode" in
|
|
1189
1332
|
token)
|
|
1190
1333
|
emit_err "GITHUB_TOKEN_ACCESS_DENIED" \
|
|
@@ -1385,6 +1528,12 @@ while IFS= read -r project_obj; do
|
|
|
1385
1528
|
release_project_lock
|
|
1386
1529
|
done < <(jq -c '.[]' <<<"$projects_json")
|
|
1387
1530
|
|
|
1531
|
+
# Every host Git operation is over: a task on cloud credentials stops being
|
|
1532
|
+
# served its owner's GitHub token and SSH key now.
|
|
1533
|
+
if [ -n "$uai_handshake_dir" ]; then
|
|
1534
|
+
: > "$uai_handshake_dir/event-clone-done"
|
|
1535
|
+
fi
|
|
1536
|
+
|
|
1388
1537
|
# Git and the checked-out files were created by the host. Reconcile their
|
|
1389
1538
|
# group/modes only after every host Git operation and before Compose exposes
|
|
1390
1539
|
# them to uid 1000. The host-only marker avoids recursively walking a large
|
|
@@ -1430,7 +1579,7 @@ fi
|
|
|
1430
1579
|
share_task_tree_with_container "$task_uai_dir/repos" \
|
|
1431
1580
|
"task-private Git repositories"
|
|
1432
1581
|
|
|
1433
|
-
if [ -z "$
|
|
1582
|
+
if [ -z "$github_git_helper" ] && [ "$uai_used_ssh_transport" = "1" ]; then
|
|
1434
1583
|
uai_git_transport="ssh"
|
|
1435
1584
|
fi
|
|
1436
1585
|
|
|
@@ -1627,13 +1776,13 @@ is_reserved_task_env_name() {
|
|
|
1627
1776
|
NODE_PATH | SHELLOPTS | BASHOPTS | PS4 | BASH_ENV | ENV | \
|
|
1628
1777
|
__UAI_ENV_* | __CURSOR_SANDBOX_ENV_RESTORE | GROK_LOGIN_ENV | \
|
|
1629
1778
|
NODE_EXTRA_CA_CERTS | SSL_CERT_FILE | REQUESTS_CA_BUNDLE | \
|
|
1630
|
-
|
|
1779
|
+
[Aa][Ss][Dd][Ff]_* | [Cc][Oo][Rr][Ee][Pp][Aa][Cc][Kk]_* | \
|
|
1631
1780
|
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Pp][Rr][Ee][Ff][Ii][Xx] | \
|
|
1632
1781
|
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Cc][Aa][Cc][Hh][Ee] | \
|
|
1633
1782
|
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Ll][Oo][Gg][Ss][_-][Dd][Ii][Rr] | \
|
|
1634
1783
|
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Uu][Mm][Aa][Ss][Kk] | UAI_* | \
|
|
1635
1784
|
CLAUDE_CODE_OAUTH_TOKEN | \
|
|
1636
|
-
ANTHROPIC_API_KEY | ANTHROPIC_AUTH_TOKEN | XAI_API_KEY | \
|
|
1785
|
+
ANTHROPIC_API_KEY | ANTHROPIC_AUTH_TOKEN | XAI_API_KEY | CURSOR_API_KEY | \
|
|
1637
1786
|
PLAYWRIGHT_BROWSERS_PATH)
|
|
1638
1787
|
return 0
|
|
1639
1788
|
;;
|
|
@@ -1641,6 +1790,30 @@ is_reserved_task_env_name() {
|
|
|
1641
1790
|
return 1
|
|
1642
1791
|
}
|
|
1643
1792
|
|
|
1793
|
+
# A baked task's project env, as both runtimes receive it (Docker's Compose
|
|
1794
|
+
# override, Apple's env file): values JSON on stdin; $1 the declared keys
|
|
1795
|
+
# (JSON array), $2 the preview URL env (JSON object). Declared env names
|
|
1796
|
+
# only, minus every name is_reserved_task_env_name reserves (the one
|
|
1797
|
+
# authority, never a second list) and the preview URL names.
|
|
1798
|
+
filter_baked_project_env() {
|
|
1799
|
+
local declared="$1" preview="$2" values reserved='[]' key
|
|
1800
|
+
values=$(cat)
|
|
1801
|
+
while IFS= read -r key; do
|
|
1802
|
+
if is_reserved_task_env_name "$key"; then
|
|
1803
|
+
reserved=$(jq -c --arg key "$key" '. + [$key]' <<<"$reserved")
|
|
1804
|
+
fi
|
|
1805
|
+
done < <(printf '%s' "$values" | jq -r 'keys[] | select(test("^[A-Za-z_][A-Za-z0-9_]*$"))')
|
|
1806
|
+
printf '%s' "$values" | jq -c \
|
|
1807
|
+
--argjson declared "$declared" \
|
|
1808
|
+
--argjson preview "$preview" \
|
|
1809
|
+
--argjson reserved "$reserved" '
|
|
1810
|
+
with_entries(.key as $key | select($declared | index($key)))
|
|
1811
|
+
| with_entries(select(.key | test("^[A-Za-z_][A-Za-z0-9_]*$")))
|
|
1812
|
+
| delpaths($reserved | map([.]))
|
|
1813
|
+
| delpaths($preview | keys | map([.]))
|
|
1814
|
+
'
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1644
1817
|
# Names that the loader owns are non-secret metadata. Mount the exact launch
|
|
1645
1818
|
# set read-only so uai-init can remove even the empty clears inherited from a
|
|
1646
1819
|
# derived image before it starts code-server. The editor daemon itself never
|
|
@@ -1844,13 +2017,15 @@ mv -f "$managed_env_keys_tmp" "$managed_env_keys_file"
|
|
|
1844
2017
|
# in the container env (rather than the host's ~/.claude mount, which broke
|
|
1845
2018
|
# with EROFS on session-env writes) means both the orchestrator's headless
|
|
1846
2019
|
# claude and the code-server terminal's interactive claude authenticate.
|
|
1847
|
-
|
|
1848
|
-
"
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2020
|
+
if [ "$uai_on_cloud_credentials" != "1" ]; then
|
|
2021
|
+
printf ' %s: "${%s:-}"\n' \
|
|
2022
|
+
"CLAUDE_CODE_OAUTH_TOKEN" "CLAUDE_CODE_OAUTH_TOKEN"
|
|
2023
|
+
# Pasted engine API keys (ADR-070) ride the same at-up interpolation, so the
|
|
2024
|
+
# terminal CLIs authenticate like the adapters' exec-time env forwarding.
|
|
2025
|
+
printf ' %s: "${%s:-}"\n' "ANTHROPIC_API_KEY" "ANTHROPIC_API_KEY"
|
|
2026
|
+
printf ' %s: "${%s:-}"\n' "ANTHROPIC_AUTH_TOKEN" "ANTHROPIC_AUTH_TOKEN"
|
|
2027
|
+
printf ' %s: "${%s:-}"\n' "XAI_API_KEY" "XAI_API_KEY"
|
|
2028
|
+
fi
|
|
1854
2029
|
# ADR-053: point Playwright at the shared browser cache volume.
|
|
1855
2030
|
printf ' PLAYWRIGHT_BROWSERS_PATH: "/opt/pw-browsers"\n'
|
|
1856
2031
|
# No GH_TOKEN/GITHUB_TOKEN in the container env (ADR-027). `gh` prefers such
|
|
@@ -2184,65 +2359,8 @@ step "COMPOSE_UP_FAILED" "start task container"
|
|
|
2184
2359
|
preview_env_raw=$(jq -r '.[0].preview_env // "{}"' <<<"$task_json")
|
|
2185
2360
|
filtered_project_env='{}'
|
|
2186
2361
|
if [ "$UAI_PROJECT_ENV_MODE" = "baked" ]; then
|
|
2187
|
-
filtered_project_env=$(printf '%s' "$project_env_json"
|
|
2188
|
-
|
|
2189
|
-
--argjson preview "$preview_env_raw" '
|
|
2190
|
-
.
|
|
2191
|
-
| with_entries(.key as $key | select($declared | index($key)))
|
|
2192
|
-
| with_entries(select(.key | test("^GIT_CONFIG($|_)") | not))
|
|
2193
|
-
| with_entries(select(.key | test(
|
|
2194
|
-
"^(ASDF_|COREPACK_)|^npm_config_(prefix|cache|logs[_-]dir|umask)$";
|
|
2195
|
-
"i"
|
|
2196
|
-
) | not))
|
|
2197
|
-
| with_entries(select(.key | test("^UAI_") | not))
|
|
2198
|
-
| with_entries(select(.key | test("^__UAI_ENV_") | not))
|
|
2199
|
-
| del(
|
|
2200
|
-
.GH_TOKEN,
|
|
2201
|
-
.GITHUB_TOKEN,
|
|
2202
|
-
.GH_CONFIG_DIR,
|
|
2203
|
-
.GH_HOST,
|
|
2204
|
-
.GIT_DIR,
|
|
2205
|
-
.GIT_WORK_TREE,
|
|
2206
|
-
.GIT_COMMON_DIR,
|
|
2207
|
-
.GIT_OBJECT_DIRECTORY,
|
|
2208
|
-
.GIT_ALTERNATE_OBJECT_DIRECTORIES,
|
|
2209
|
-
.GIT_INDEX_FILE,
|
|
2210
|
-
.GIT_EXEC_PATH,
|
|
2211
|
-
.GIT_ASKPASS,
|
|
2212
|
-
.SSH_ASKPASS,
|
|
2213
|
-
.GIT_SSH,
|
|
2214
|
-
.GIT_SSH_COMMAND,
|
|
2215
|
-
.HOME,
|
|
2216
|
-
.XDG_CONFIG_HOME,
|
|
2217
|
-
.XDG_DATA_HOME,
|
|
2218
|
-
.XDG_CACHE_HOME,
|
|
2219
|
-
.PATH,
|
|
2220
|
-
.SHELL,
|
|
2221
|
-
.LD_PRELOAD,
|
|
2222
|
-
.LD_AUDIT,
|
|
2223
|
-
.LD_LIBRARY_PATH,
|
|
2224
|
-
.NODE_OPTIONS,
|
|
2225
|
-
.NODE_PATH,
|
|
2226
|
-
.SHELLOPTS,
|
|
2227
|
-
.BASHOPTS,
|
|
2228
|
-
.PS4,
|
|
2229
|
-
.BASH_ENV,
|
|
2230
|
-
.ENV,
|
|
2231
|
-
.NODE_EXTRA_CA_CERTS,
|
|
2232
|
-
.SSL_CERT_FILE,
|
|
2233
|
-
.REQUESTS_CA_BUNDLE,
|
|
2234
|
-
.__CURSOR_SANDBOX_ENV_RESTORE,
|
|
2235
|
-
.GROK_LOGIN_ENV,
|
|
2236
|
-
.UAI_GIT_TRANSPORT,
|
|
2237
|
-
.CLAUDE_CODE_OAUTH_TOKEN,
|
|
2238
|
-
.ANTHROPIC_API_KEY,
|
|
2239
|
-
.ANTHROPIC_AUTH_TOKEN,
|
|
2240
|
-
.XAI_API_KEY,
|
|
2241
|
-
.PLAYWRIGHT_BROWSERS_PATH,
|
|
2242
|
-
.UAI_EDITOR_THEME
|
|
2243
|
-
)
|
|
2244
|
-
| delpaths($preview | keys | map([.]))
|
|
2245
|
-
')
|
|
2362
|
+
filtered_project_env=$(printf '%s' "$project_env_json" \
|
|
2363
|
+
| filter_baked_project_env "$union_env_keys_json" "$preview_env_raw")
|
|
2246
2364
|
fi
|
|
2247
2365
|
compose_env_override=""
|
|
2248
2366
|
if [ "$TASK_RUNTIME_MODE" = "docker" ]; then
|
|
@@ -2352,10 +2470,12 @@ if [ "$TASK_RUNTIME_MODE" = "apple-container" ]; then
|
|
|
2352
2470
|
printf 'PS4=\n'
|
|
2353
2471
|
printf 'BASH_ENV=\n'
|
|
2354
2472
|
printf 'ENV=\n'
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2473
|
+
if [ "$uai_on_cloud_credentials" != "1" ]; then
|
|
2474
|
+
printf 'CLAUDE_CODE_OAUTH_TOKEN=%s\n' "${CLAUDE_CODE_OAUTH_TOKEN:-}"
|
|
2475
|
+
printf 'ANTHROPIC_API_KEY=%s\n' "${ANTHROPIC_API_KEY:-}"
|
|
2476
|
+
printf 'ANTHROPIC_AUTH_TOKEN=%s\n' "${ANTHROPIC_AUTH_TOKEN:-}"
|
|
2477
|
+
printf 'XAI_API_KEY=%s\n' "${XAI_API_KEY:-}"
|
|
2478
|
+
fi
|
|
2359
2479
|
printf 'PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers\n'
|
|
2360
2480
|
if [ "$UAI_PROJECT_ENV_MODE" = "loader" ]; then
|
|
2361
2481
|
while IFS= read -r ekey; do
|
|
@@ -3295,7 +3415,7 @@ fi
|
|
|
3295
3415
|
# CLI atomically rewrites the file on every start, which can invalidate
|
|
3296
3416
|
# the bind mount and leave a stale dentry, after which claude refuses to
|
|
3297
3417
|
# start. docker cp gives the container its own stable copy. Best-effort.
|
|
3298
|
-
if [ -f "$UAI_OWNER_HOME/.claude.json" ]; then
|
|
3418
|
+
if [ "$uai_on_cloud_credentials" != "1" ] && [ -f "$UAI_OWNER_HOME/.claude.json" ]; then
|
|
3299
3419
|
crt cp "$UAI_OWNER_HOME/.claude.json" \
|
|
3300
3420
|
"$app_container":/home/node/.claude.json >/dev/null 2>&1 \
|
|
3301
3421
|
|| log "warning: crt cp of .claude.json failed; claude may need re-login"
|
|
@@ -3313,6 +3433,7 @@ fi
|
|
|
3313
3433
|
if crt exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
3314
3434
|
/usr/bin/mkdir -p /home/node/.codex >/dev/null; then
|
|
3315
3435
|
for codex_item in auth.json config.toml AGENTS.md version.json installation_id rules; do
|
|
3436
|
+
[ "$uai_on_cloud_credentials" = "1" ] && continue
|
|
3316
3437
|
if [ -e "$UAI_OWNER_HOME/.codex/$codex_item" ]; then
|
|
3317
3438
|
# ADR-135: the task's copy must not carry the rotating refresh token —
|
|
3318
3439
|
# N copies racing to refresh it revoke the whole family. Replace it with
|
|
@@ -3348,16 +3469,16 @@ fi
|
|
|
3348
3469
|
# session state) so the container's kimi runs on the operator's subscription.
|
|
3349
3470
|
crt exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
3350
3471
|
/usr/bin/mkdir -p /home/node/.kimi-code/credentials >/dev/null 2>&1 || true
|
|
3351
|
-
if [ -e "$UAI_OWNER_HOME/.kimi-code/config.toml" ]; then
|
|
3472
|
+
if [ "$uai_on_cloud_credentials" != "1" ] && [ -e "$UAI_OWNER_HOME/.kimi-code/config.toml" ]; then
|
|
3352
3473
|
crt cp "$UAI_OWNER_HOME/.kimi-code/config.toml" \
|
|
3353
3474
|
"$app_container":/home/node/.kimi-code/config.toml >/dev/null 2>&1 \
|
|
3354
3475
|
|| log "warning: crt cp of .kimi-code/config.toml failed; kimi may need re-login"
|
|
3355
3476
|
fi
|
|
3356
|
-
if [ -e "$UAI_OWNER_HOME/.kimi-code/device_id" ]; then
|
|
3477
|
+
if [ "$uai_on_cloud_credentials" != "1" ] && [ -e "$UAI_OWNER_HOME/.kimi-code/device_id" ]; then
|
|
3357
3478
|
crt cp "$UAI_OWNER_HOME/.kimi-code/device_id" \
|
|
3358
3479
|
"$app_container":/home/node/.kimi-code/device_id >/dev/null 2>&1 || true
|
|
3359
3480
|
fi
|
|
3360
|
-
if [ -e "$UAI_OWNER_HOME/.kimi-code/credentials/kimi-code.json" ]; then
|
|
3481
|
+
if [ "$uai_on_cloud_credentials" != "1" ] && [ -e "$UAI_OWNER_HOME/.kimi-code/credentials/kimi-code.json" ]; then
|
|
3361
3482
|
crt cp "$UAI_OWNER_HOME/.kimi-code/credentials/kimi-code.json" \
|
|
3362
3483
|
"$app_container":/home/node/.kimi-code/credentials/kimi-code.json >/dev/null 2>&1 \
|
|
3363
3484
|
|| log "warning: crt cp of .kimi-code credential failed; kimi may need re-login"
|
|
@@ -3373,6 +3494,7 @@ crt exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
|
3373
3494
|
crt exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
3374
3495
|
/usr/bin/mkdir -p /home/node/.grok >/dev/null 2>&1 || true
|
|
3375
3496
|
for grok_item in auth.json config.toml models_cache.json agent_id; do
|
|
3497
|
+
[ "$uai_on_cloud_credentials" = "1" ] && continue
|
|
3376
3498
|
if [ -e "$UAI_OWNER_HOME/.grok/$grok_item" ]; then
|
|
3377
3499
|
if [ "$grok_item" = auth.json ] && command -v jq >/dev/null 2>&1; then
|
|
3378
3500
|
brokered_auth="$(mktemp "${TMPDIR:-/tmp}/uai-grok-auth.XXXXXX")"
|
|
@@ -3401,6 +3523,7 @@ crt exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
|
3401
3523
|
# (provisionEngineAccounts) since they live in the sealed DB, not on disk here.
|
|
3402
3524
|
# The shared parent and this leaf were created with node ownership above.
|
|
3403
3525
|
for oc_item in auth.json config.json; do
|
|
3526
|
+
[ "$uai_on_cloud_credentials" = "1" ] && continue
|
|
3404
3527
|
if [ -e "$UAI_OWNER_HOME/.local/share/opencode/$oc_item" ]; then
|
|
3405
3528
|
crt cp "$UAI_OWNER_HOME/.local/share/opencode/$oc_item" \
|
|
3406
3529
|
"$app_container":/home/node/.local/share/opencode/ >/dev/null 2>&1 \
|
|
@@ -3443,6 +3566,25 @@ quarantine_runtime_authority_violation() {
|
|
|
3443
3566
|
quarantine_untrusted_app_container
|
|
3444
3567
|
}
|
|
3445
3568
|
|
|
3569
|
+
# ADR-154 §12: the runner writes a task on cloud credentials its owner's
|
|
3570
|
+
# GitHub configuration and SSH key, recorded for cleanup, now: after the
|
|
3571
|
+
# managed-auth scrub, before uai-init installs anything that may need them.
|
|
3572
|
+
# task-up waits for its answer, bounded, and stops on anything but continue.
|
|
3573
|
+
if [ -n "$uai_handshake_dir" ]; then
|
|
3574
|
+
: > "$uai_handshake_dir/event-runtime-ready"
|
|
3575
|
+
uai_waited=0
|
|
3576
|
+
while [ ! -f "$uai_handshake_dir/runtime-ready" ] && [ "$uai_waited" -lt 3000 ]; do
|
|
3577
|
+
sleep 0.2
|
|
3578
|
+
uai_waited=$((uai_waited + 1))
|
|
3579
|
+
done
|
|
3580
|
+
uai_runtime_ready=$(cat "$uai_handshake_dir/runtime-ready" 2>/dev/null || true)
|
|
3581
|
+
if [ "$uai_runtime_ready" != "continue" ]; then
|
|
3582
|
+
emit_err "CREDENTIALS_UNAVAILABLE" \
|
|
3583
|
+
"Uai could not write the task's GitHub sign-in and SSH key into it. Start the task again." \
|
|
3584
|
+
"write the task's credentials"
|
|
3585
|
+
fi
|
|
3586
|
+
fi
|
|
3587
|
+
|
|
3446
3588
|
init_warning=""
|
|
3447
3589
|
init_rc=0
|
|
3448
3590
|
crt exec "${task_runtime_exec_env[@]}" "$app_container" \
|