@reason-machines/cli-linux-arm64-musl 0.2.381 → 0.2.383
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 +3 -1
- package/bin/ara +2 -2
- package/bin/vm-hands/api-credentials-xppmbg0m.js +2 -0
- package/bin/vm-hands/api-xmpbsm6b.js +2 -0
- package/bin/vm-hands/{ask-relay-fc0jc3t5.js → ask-relay-h8f5dmfh.js} +3 -3
- package/bin/vm-hands/connect-cli-qvfxpk5t.js +2 -0
- package/bin/vm-hands/{custom-endpoint-device-zhpake9f.js → custom-endpoint-device-bm94e9r0.js} +1 -1
- package/bin/vm-hands/desktop-account-3tnm4z5w.js +2 -0
- package/bin/vm-hands/{device-workspace-vbcry9d6.js → device-workspace-0xsj5ykf.js} +1 -1
- package/bin/vm-hands/hands-worker-source-azdrrhsz.js +2 -0
- package/bin/vm-hands/{index-bbk6a9sk.js → index-7f2kwwen.js} +1 -1
- package/bin/vm-hands/{install-receipt-7x81ek76.js → install-receipt-3xyx3rdd.js} +1 -1
- package/bin/vm-hands/local-automation-pvy9revm.js +2 -0
- package/bin/vm-hands/local-capabilities-zfzyknay.js +2 -0
- package/bin/vm-hands/local-desktop-users-ajb81vjm.js +2 -0
- package/bin/vm-hands/local-display-mac-9h6btvvq.js +2 -0
- package/bin/vm-hands/local-display-session-xqncvp30.js +2 -0
- package/bin/vm-hands/local-pty-session-cjas515w.js +2 -0
- package/bin/vm-hands/managed-runtime-s414h5nm.js +2 -0
- package/bin/vm-hands/{network-contract-zqvt1v0t.js → network-contract-dsw79qkc.js} +1 -1
- package/bin/vm-hands/package-smk3nqjs.js +2 -0
- package/bin/vm-hands/permissions-3mkk9mfw.js +2 -0
- package/bin/vm-hands/{reason-hands-protocol-8fxjynte.js → reason-hands-protocol-7d7xj1h3.js} +1 -1
- package/bin/vm-hands/serve-service-txzqn5kj.js +2 -0
- package/bin/vm-hands/status-23eqx9t3.js +96 -0
- package/bin/vm-hands/version-3gj9bqhc.js +2 -0
- package/bin/vm-hands/{vm-hands-entry-lite-7shpbsw8.js → vm-hands-entry-lite-1gxktkpe.js} +2 -2
- package/bin/vm-hands/vm-hands-entry-lite-2e18tm6w.js +245 -0
- package/bin/vm-hands/{vm-hands-entry-lite-g036gtem.js → vm-hands-entry-lite-2t0vcvd8.js} +1 -1
- package/bin/vm-hands/vm-hands-entry-lite-3dc4v5bd.js +11 -0
- package/bin/vm-hands/{vm-hands-entry-lite-hgkf1svv.js → vm-hands-entry-lite-41nygv3p.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-j2q1k9vb.js → vm-hands-entry-lite-51hmj6am.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-zfwg7ehq.js → vm-hands-entry-lite-5n4vsm6b.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-k7wwza46.js → vm-hands-entry-lite-7xbgw4m0.js} +2 -2
- package/bin/vm-hands/vm-hands-entry-lite-8fj07njx.js +3 -0
- package/bin/vm-hands/{vm-hands-entry-lite-3hbe4em3.js → vm-hands-entry-lite-9cmfthkq.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-4q8tsaqv.js → vm-hands-entry-lite-9r5tz2dx.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-45j8qwsf.js → vm-hands-entry-lite-9w5vs9zv.js} +1 -1
- package/bin/vm-hands/vm-hands-entry-lite-a0yfp7s2.js +251 -0
- package/bin/vm-hands/{vm-hands-entry-lite-za55dtte.js → vm-hands-entry-lite-ammr3xbc.js} +2 -2
- package/bin/vm-hands/vm-hands-entry-lite-dh5gwmyf.js +3 -0
- package/bin/vm-hands/{vm-hands-entry-lite-5pm6bhb8.js → vm-hands-entry-lite-e0gdt6r5.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-5kgvss4p.js → vm-hands-entry-lite-e2mjbqmr.js} +2 -2
- package/bin/vm-hands/vm-hands-entry-lite-ejm5er3h.js +7 -0
- package/bin/vm-hands/vm-hands-entry-lite-eqhsewc0.js +105 -0
- package/bin/vm-hands/{vm-hands-entry-lite-mh0xecf3.js → vm-hands-entry-lite-f02906k8.js} +4 -4
- package/bin/vm-hands/vm-hands-entry-lite-f5230h6t.js +67 -0
- package/bin/vm-hands/{vm-hands-entry-lite-zh8p7ndn.js → vm-hands-entry-lite-fb5dzy9x.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-yjn6sk08.js → vm-hands-entry-lite-fcrqyxz0.js} +1 -1
- package/bin/vm-hands/vm-hands-entry-lite-fyvgav60.js +3 -0
- package/bin/vm-hands/vm-hands-entry-lite-g5grabna.js +540 -0
- package/bin/vm-hands/vm-hands-entry-lite-g7h6wjqy.js +3 -0
- package/bin/vm-hands/{vm-hands-entry-lite-34e71nbe.js → vm-hands-entry-lite-g8pj1jxa.js} +174 -16
- package/bin/vm-hands/vm-hands-entry-lite-h1rxv02g.js +458 -0
- package/bin/vm-hands/vm-hands-entry-lite-h8dbw0c7.js +4 -0
- package/bin/vm-hands/{vm-hands-entry-lite-nhtb458c.js → vm-hands-entry-lite-hnsm5t36.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-0asdf4pm.js → vm-hands-entry-lite-jzcc9h71.js} +3 -3
- package/bin/vm-hands/{vm-hands-entry-lite-r6pap1kh.js → vm-hands-entry-lite-ma001vzh.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-anmv8hp7.js → vm-hands-entry-lite-mn64e4pt.js} +286 -104
- package/bin/vm-hands/{vm-hands-entry-lite-9cgm6g8t.js → vm-hands-entry-lite-pk3q774c.js} +1 -1
- package/bin/vm-hands/vm-hands-entry-lite-qef2ndkd.js +6 -0
- package/bin/vm-hands/vm-hands-entry-lite-qnnfvm5r.js +5 -0
- package/bin/vm-hands/vm-hands-entry-lite-r8qg3wp1.js +3 -0
- package/bin/vm-hands/vm-hands-entry-lite-rd0ak4ye.js +4 -0
- package/bin/vm-hands/{vm-hands-entry-lite-00qsbphw.js → vm-hands-entry-lite-rpn0frzb.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-g9rvf5qk.js → vm-hands-entry-lite-rtt2w6c6.js} +1 -1
- package/bin/vm-hands/vm-hands-entry-lite-sex82vb1.js +3 -0
- package/bin/vm-hands/vm-hands-entry-lite-vcwgh8tr.js +346 -0
- package/bin/vm-hands/{vm-hands-entry-lite-dgxxsdwb.js → vm-hands-entry-lite-xrthfx3q.js} +2 -2
- package/bin/vm-hands/{vm-hands-entry-lite-kxdw4myj.js → vm-hands-entry-lite-y2s2w174.js} +1 -1
- package/bin/vm-hands/{vm-hands-entry-lite-hc3p6r9p.js → vm-hands-entry-lite-zjfr0zh8.js} +2 -2
- package/bin/vm-hands/vm-hands-entry-lite.js +1 -1
- package/bin/vm-hands/{vm-hands-executor-qssgfea1.js → vm-hands-executor-9p92j0mv.js} +1 -1
- package/bin/vm-hands/vm-hands-op-runner-ers4cefp.js +2 -0
- package/bin/vm-hands/{vm-hands-runtime-5me6cpmn.js → vm-hands-runtime-en4gj0t0.js} +3 -3
- package/bin/vm-hands/worker-sandbox-tgqbmy6b.js +2 -0
- package/package.json +1 -1
- package/bin/vm-hands/api-af9g18n7.js +0 -2
- package/bin/vm-hands/api-credentials-65mrr3tn.js +0 -2
- package/bin/vm-hands/connect-cli-j478km3s.js +0 -2
- package/bin/vm-hands/desktop-account-5zvgspyw.js +0 -2
- package/bin/vm-hands/hands-worker-source-h0b1j53p.js +0 -2
- package/bin/vm-hands/local-automation-c86tjeft.js +0 -2
- package/bin/vm-hands/local-capabilities-1h99gkt6.js +0 -2
- package/bin/vm-hands/local-desktop-users-0hadyvx1.js +0 -2
- package/bin/vm-hands/local-display-mac-v1pc7177.js +0 -2
- package/bin/vm-hands/local-display-session-tzq0hne8.js +0 -2
- package/bin/vm-hands/local-pty-session-hs7rq03x.js +0 -2
- package/bin/vm-hands/managed-runtime-5dqaaw1x.js +0 -2
- package/bin/vm-hands/package-3cd7cfyv.js +0 -2
- package/bin/vm-hands/permissions-vbyqjbyb.js +0 -2
- package/bin/vm-hands/serve-service-be739c9s.js +0 -2
- package/bin/vm-hands/status-s8ny6men.js +0 -96
- package/bin/vm-hands/version-8s4syrp2.js +0 -2
- package/bin/vm-hands/vm-hands-entry-lite-1085xsma.js +0 -5
- package/bin/vm-hands/vm-hands-entry-lite-4m40gwkf.js +0 -3
- package/bin/vm-hands/vm-hands-entry-lite-62wmnm7g.js +0 -540
- package/bin/vm-hands/vm-hands-entry-lite-98076n5g.js +0 -62
- package/bin/vm-hands/vm-hands-entry-lite-bvbfcp1k.js +0 -4
- package/bin/vm-hands/vm-hands-entry-lite-c01eyep6.js +0 -3
- package/bin/vm-hands/vm-hands-entry-lite-dcynw6t2.js +0 -3
- package/bin/vm-hands/vm-hands-entry-lite-hh24bq0d.js +0 -3
- package/bin/vm-hands/vm-hands-entry-lite-jb2sr2r7.js +0 -3
- package/bin/vm-hands/vm-hands-entry-lite-kt5aj2ax.js +0 -11
- package/bin/vm-hands/vm-hands-entry-lite-n9nnb482.js +0 -459
- package/bin/vm-hands/vm-hands-entry-lite-sb9a1jcn.js +0 -4
- package/bin/vm-hands/vm-hands-entry-lite-xg3d0a2y.js +0 -340
- package/bin/vm-hands/vm-hands-entry-lite-zamt8xp7.js +0 -181
- package/bin/vm-hands/vm-hands-entry-lite-ze9msfq1.js +0 -3
- package/bin/vm-hands/vm-hands-op-runner-b76rjaps.js +0 -2
- package/bin/vm-hands/worker-sandbox-wpzkvwer.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{Tg as H}from"./vm-hands-entry-lite-2e18tm6w.js";import{$g as Q,Vg as m,Wg as q,Xg as L,Yg as B,Zg as W,_g as F,bh as j,dh as k,eh as z,fh as Z,gh as J,hh as K,jh as $,kh as G,mh as M,nh as s,oh as X,qh as _,th as S}from"./vm-hands-entry-lite-ammr3xbc.js";import{Ri as y}from"./vm-hands-entry-lite-sex82vb1.js";import{Cj as p,Dj as x,Ej as b,Fj as w}from"./vm-hands-entry-lite-pk3q774c.js";import{Dk as Y}from"./vm-hands-entry-lite-f5230h6t.js";var o=["cgroup_membership_read_failed","cgroup_v2_membership_absent","cgroup_scope_invalid","cgroup_root_unavailable","cgroup_directory_unavailable","cgroup_limit_read_failed","cgroup_events_read_failed","cgroup_current_read_failed","cgroup_stat_read_failed","boot_id_read_failed","boot_id_invalid","guest_meminfo_read_failed","guest_vmstat_read_failed","memory_counter_invalid","memory_field_missing","memory_capacity_invalid"];var c=String.raw`(function(fs, path) {
|
|
3
3
|
const fail = (message, reasonCode) => { throw Object.assign(Error(message), {reasonCode}); };
|
|
4
4
|
const read = (p, reasonCode, trim=true) => { try { const text=fs.readFileSync(p, "utf8"); return trim?text.trim():text; }
|
|
5
5
|
catch { fail("memory_read", reasonCode); } };
|
|
@@ -64,7 +64,7 @@ import{$g as S,Gg as w,Hg as m,Ig as q,Jg as B,Kg as L,Lg as W,Mg as F,Og as Q,Q
|
|
|
64
64
|
if(!Number.isSafeInteger(limitBytes)||limitBytes<=0||!Number.isSafeInteger(available)||available>limitBytes) fail("memory_capacity", "memory_capacity_invalid");
|
|
65
65
|
return {bootId,oomKills:field(read("/proc/vmstat", "guest_vmstat_read_failed"),"oom_kill"),currentBytes:limitBytes-available,
|
|
66
66
|
limitBytes,inactiveFileBytes:0,capacitySource:"guest",memoryScope:"/proc"};
|
|
67
|
-
})`;var
|
|
67
|
+
})`;var u=String.raw`import os,sys,json,stat,fcntl,time,ctypes
|
|
68
68
|
v=json.load(sys.stdin)
|
|
69
69
|
root=v['root']; paths=list(dict.fromkeys([v['source'],v['snapshot']]))
|
|
70
70
|
marker=('# reason-job-env-owner:'+v['runId']+'\n').encode()
|
|
@@ -309,7 +309,7 @@ function fileOperation(request) {
|
|
|
309
309
|
});
|
|
310
310
|
return { promise, cancel() {} };
|
|
311
311
|
}
|
|
312
|
-
`;var
|
|
312
|
+
`;var a="a3e39eb55160acfc116fa9871ccc594c68be7a642d2331b24baa707c8f601ec0";import{createHash as rt}from"crypto";var bt=Object.freeze({"api.ara.so":"api.reasonmachines.com","api.reasonmachines.ai":"api.reasonmachines.com","api-staging.ara.so":"staging-api.reasonmachines.com","staging-api.reasonmachines.ai":"staging-api.reasonmachines.com"});var d=Object.freeze({trailingSlashPattern:/\/+$/.source,keyPrefix:"http.",keySuffix:"/.extraheader",valuePrefix:"Authorization: Bearer "});var f=Object.freeze({tokenEnv:"ARA_GIT_BROKER_TOKEN",reasonTokenEnv:"REASON_GIT_BROKER_TOKEN",urlEnv:"ARA_GIT_BROKER_URL",keyPrefix:"credential.",keySuffix:"/.helper",value:'!f(){ printf "username=%s\\npassword=%s\\n" "ara-run" "${REASON_GIT_BROKER_TOKEN:-$ARA_GIT_BROKER_TOKEN}"; };f'});var r=/^[A-Za-z_][A-Za-z0-9_]*$/;var T=new Set(["ARA_INFERENCE_TOKEN","AI_GATEWAY_API_KEY","CLOUD_AGENT_AI_GATEWAY_API_KEY","INFERENCE_V2_GATEWAY_API_KEY","VERCEL_AI_GATEWAY_API_KEY","INFERENCE_V2_CONCENTRATE_API_KEY","INFERENCE_V2_OPENAI_API_KEY","INFERENCE_V2_ANTHROPIC_API_KEY","INFERENCE_V2_FINGERPRINT_SECRET","OPENAI_API_KEY","OPENROUTER_API_KEY","ANTHROPIC_API_KEY","CODEX_ACCESS_TOKEN","GITHUB_COPILOT_API_KEY","AZURE_OPENAI_API_KEY","AZURE_OPENAI_CLIENT_SECRET"]),A=new Set(["ARA_OPERATOR_KEY","BL_API_KEY","BLAXEL_API_KEY","NSC_TOKEN","NSC_TOKEN_FILE","NAMESPACE_API_KEY","NAMESPACE_ACCESS_TOKEN","NAMESPACE_TOKEN","NAMESPACE_TOKEN_JSON","NAMESPACE_SANDBOX_TOKEN_JSON","ARA_LOCAL_QA_NAMESPACE_WORKLOAD_TOKEN_FILE","NAMESPACE_SANDBOX_USE_LOCAL_USER_TOKEN","COMPUTE_SERVICE_TOKEN"]),D=["INFISICAL_","NSC_"],v=new Set(["ARA_JOB_ENV","ARA_DELIVERY_BASE_SHA","WORK_ROOT","REPO_DIR","ARA_CONTROL_DIR","ARA_PRESERVE_WORKSPACE","ARA_PINNED_CHECKOUT_REUSE","FOLLOWUP_BRANCH","REPO_URL","TOKENLESS_URL","REPOSITORY_IDENTITY_URL","DEFAULT_BRANCH","BRANCH","BRANCH_GENERATED","BASE_REF","REPO_EMPTY","REPO_WRITABLE","ARA_DELIVERY_BASE_BRANCH","ARA_SERVER_GIT_PUBLICATION","ARA_EMPTY_REPOSITORY_COMMIT","REASON_MODEL","REASON_DELIVERY_BASE_BRANCH","REASON_DELIVERY_BASE_SHA","REASON_EMPTY_REPOSITORY_COMMIT","REASON_SCRATCH_SESSION_ID","REASON_SERVER_GIT_PUBLICATION","REASON_ALLOW_INSTALL_SCRIPTS","SCM_TOKEN","SCM_TOKEN_USER","REASON_OIDC_TOKEN_URL","REASON_OIDC_REQUEST_TOKEN","AWS_CONTAINER_CREDENTIALS_FULL_URI","AWS_CONTAINER_AUTHORIZATION_TOKEN","AWS_CONTAINER_CREDENTIALS_RELATIVE_URI","AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE","BASH_ENV","ENV","BASHOPTS","SHELLOPTS","PS4","IFS","CDPATH","GLOBIGNORE","PROMPT_COMMAND","PATH","HOME","TMPDIR","TMP","TEMP","XDG_CONFIG_HOME","GIT_DIR","GIT_WORK_TREE","GIT_INDEX_FILE","GIT_OBJECT_DIRECTORY","GIT_COMMON_DIR","GIT_NAMESPACE","GIT_CEILING_DIRECTORIES","GIT_DISCOVERY_ACROSS_FILESYSTEM","GIT_SHALLOW_FILE","GIT_ALLOW_PROTOCOL","GIT_PROTOCOL_FROM_USER","GIT_REPLACE_REF_BASE","NODE_OPTIONS","NODE_PATH","NODE_REPL_EXTERNAL_MODULES","CLASSPATH","PYTHONPATH","PYTHONSTARTUP","PYTHONHOME","RUBYOPT","RUBYLIB","PERL5LIB","PERL5OPT","JAVA_TOOL_OPTIONS","_JAVA_OPTIONS","JDK_JAVA_OPTIONS","GEM_PATH","GEM_HOME","GIT_SSH","GIT_SSH_COMMAND","GIT_EXEC_PATH","GIT_TEMPLATE_DIR","GIT_ALTERNATE_OBJECT_DIRECTORIES","GIT_PROXY_COMMAND","GIT_EXTERNAL_DIFF","GIT_PAGER","GIT_EDITOR","GIT_ASKPASS","SSH_ASKPASS","PAGER","EDITOR","TAR_OPTIONS","CURL_HOME","WGETRC","APT_CONFIG","PERLLIB","GIT_GRAFT_FILE","POSIXLY_CORRECT"]),N=["GIT_TRACE","CONTEXT_REPO_","ARA_CHECKPOINT_BOOTSTRAP_","_","ARA_HANDS_","ARA_DEVICE_","ARA_GIT_BROKER_","REASON_HANDS_","REASON_DEVICE_","REASON_GIT_BROKER_","LD_","DYLD_","GIT_CONFIG","BASH_FUNC_"];var n="^(?!(?:"+[...T,...A,...v].join("|")+")$)(?!(?:"+[...D,...N].join("|")+"))"+r.source.slice(1),Lt=new RegExp(n);var U=["HOME","PWD","OLDPWD","TMPDIR","TMP","TEMP","SHELL","USER","LOGNAME","SCM_TOKEN","SCM_TOKEN_USER","GH_TOKEN","GITHUB_TOKEN","GH_ENTERPRISE_TOKEN","GITHUB_ENTERPRISE_TOKEN","GITLAB_TOKEN"],R=["ARA_HANDS_","ARA_DEVICE_","ARA_GIT_BROKER_","REASON_HANDS_","REASON_DEVICE_","REASON_GIT_BROKER_","BUN_"],E="^(?!(?:"+U.join("|")+")$)(?!(?:"+R.join("|")+"))"+n.slice(1),tt="ARA_DELIVERY_BASE_SHA|WORK_ROOT|FOLLOWUP_BRANCH|ARA_PRESERVE_WORKSPACE|ARA_PINNED_CHECKOUT_REUSE|CONTEXT_REPO_[A-Z0-9_]+|REPO_URL|TOKENLESS_URL|REPOSITORY_IDENTITY_URL|DEFAULT_BRANCH|BRANCH|BRANCH_GENERATED|BASE_REF|REPO_EMPTY|REPO_WRITABLE|ARA_DELIVERY_BASE_BRANCH|ARA_SERVER_GIT_PUBLICATION|ARA_EMPTY_REPOSITORY_COMMIT|REASON_MODEL|REASON_DELIVERY_BASE_BRANCH|REASON_DELIVERY_BASE_SHA|REASON_EMPTY_REPOSITORY_COMMIT|REASON_SCRATCH_SESSION_ID|REASON_SERVER_GIT_PUBLICATION|REASON_ALLOW_INSTALL_SCRIPTS",i="^(?:"+tt+"|"+E.slice(1,-1)+")$",et=new RegExp(i);function Qt(t){return et.test(t)}var l=Object.freeze({tokenPath:"/identity/token",awsPath:"/identity/aws",tokenPattern:/^[A-Za-z0-9_.~-]+$/.source}),g=Object.freeze({variables:256,valueBytes:130816,snapshotBytes:1048576}),jt=Object.freeze({valueChars:16384,snapshotBytes:65536});var h=["set -g history-limit 50000","set -g status off","set -g window-size smallest","set -gq fill-character ' '","set -g escape-time 10","set -g prefix None","set -g prefix2 None","set -g terminal-overrides ',*:smcup@:rmcup@'"];var nt=2,it="ara-hands-worker.cjs",P=["/usr/local/go/bin","/opt/node/bin","/usr/local/bin","/usr/sbin","/usr/bin","/sbin","/bin","/usr/local/cargo/bin"],st=P.join(":"),ot=["/opt/homebrew/bin",...P].join(":"),ct='(platform, linuxPath, macosPath) => platform === "darwin" ? macosPath : linuxPath';function It(t,V,C){return t==="darwin"?C:V}var ut=String.raw`(function(anchor, pid, native) {
|
|
313
313
|
const token = native.identity(pid);
|
|
314
314
|
const session = native.session(pid);
|
|
315
315
|
if (!native.same(anchor)) throw new Error("pty_session_identity_lost");
|
|
@@ -317,7 +317,7 @@ function fileOperation(request) {
|
|
|
317
317
|
if (session.id !== anchor[5]) throw new Error("pty_member_session_changed");
|
|
318
318
|
if (!token) return { retry: true, token: null };
|
|
319
319
|
return { retry: false, token };
|
|
320
|
-
})`,
|
|
320
|
+
})`,at=String.raw`(function(native) {
|
|
321
321
|
let probe;
|
|
322
322
|
try {
|
|
323
323
|
try {
|
|
@@ -327,7 +327,7 @@ function fileOperation(request) {
|
|
|
327
327
|
native.signal(probe);
|
|
328
328
|
} finally { if (probe) native.release(probe); }
|
|
329
329
|
} catch (error) { native.close(); throw error; }
|
|
330
|
-
})`,
|
|
330
|
+
})`,dt=String.raw`(function(bootstrap) {
|
|
331
331
|
const { dlopen, ptr } = require("bun:ffi");
|
|
332
332
|
const api = dlopen("libc.so.6", {
|
|
333
333
|
prctl: { args: ["i32", "u64", "u64", "u64", "u64"], returns: "i32" },
|
|
@@ -348,7 +348,7 @@ function fileOperation(request) {
|
|
|
348
348
|
require("node:fs").writeSync(3, "subreaper\n");
|
|
349
349
|
api.execve(ptr(args[0]), ptr(argv), ptr(envp));
|
|
350
350
|
throw new Error("pty_exec_failed");
|
|
351
|
-
})`,
|
|
351
|
+
})`,ft=String.raw`
|
|
352
352
|
"use strict";
|
|
353
353
|
const crypto = require("node:crypto");
|
|
354
354
|
const fs = require("node:fs");
|
|
@@ -356,43 +356,110 @@ const net = require("node:net");
|
|
|
356
356
|
const path = require("node:path");
|
|
357
357
|
const { spawn, spawnSync } = require("node:child_process");
|
|
358
358
|
const { StringDecoder } = require("node:string_decoder");
|
|
359
|
-
const GIT_BROKER_HTTP_HEADER_POLICY = ${JSON.stringify(
|
|
360
|
-
const GIT_BROKER_CREDENTIAL_HELPER_POLICY = ${JSON.stringify(
|
|
359
|
+
const GIT_BROKER_HTTP_HEADER_POLICY = ${JSON.stringify(d)};
|
|
360
|
+
const GIT_BROKER_CREDENTIAL_HELPER_POLICY = ${JSON.stringify(f)};
|
|
361
361
|
const DEVICE_ENVIRONMENT_NAME_RE = new RegExp(${JSON.stringify(i)});
|
|
362
|
-
const DEVICE_CLOUD_ACCESS_POLICY = ${JSON.stringify(
|
|
363
|
-
const DEVICE_ENVIRONMENT_LIMITS = ${JSON.stringify(
|
|
362
|
+
const DEVICE_CLOUD_ACCESS_POLICY = ${JSON.stringify(l)};
|
|
363
|
+
const DEVICE_ENVIRONMENT_LIMITS = ${JSON.stringify(g)};
|
|
364
364
|
|
|
365
|
-
const VERSION = ${
|
|
366
|
-
const WORKER_VERSION = ${
|
|
367
|
-
const WORKER_ENTRYPOINT = ${JSON.stringify(
|
|
368
|
-
const GUEST_MEMORY_UNAVAILABLE_REASONS = ${JSON.stringify(
|
|
369
|
-
const sampleGuestMemory = ${
|
|
370
|
-
const inspectMacPtyMember = ${
|
|
371
|
-
const probeLinuxPtyLibrary = ${
|
|
372
|
-
const
|
|
373
|
-
const
|
|
365
|
+
const VERSION = ${L};
|
|
366
|
+
const WORKER_VERSION = ${nt};
|
|
367
|
+
const WORKER_ENTRYPOINT = ${JSON.stringify(it)};
|
|
368
|
+
const GUEST_MEMORY_UNAVAILABLE_REASONS = ${JSON.stringify(o)};
|
|
369
|
+
const sampleGuestMemory = ${c};
|
|
370
|
+
const inspectMacPtyMember = ${ut};
|
|
371
|
+
const probeLinuxPtyLibrary = ${at};
|
|
372
|
+
const windowsPtyApi = ${H};
|
|
373
|
+
const LINUX_PTY_EXEC_SOURCE = ${JSON.stringify(dt)};
|
|
374
|
+
const CLOSE_CODES = ${JSON.stringify(_)};
|
|
374
375
|
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
375
376
|
const MAX_FRAME_BYTES = ${B};
|
|
376
|
-
const MAX_INPUT_CHARS = ${
|
|
377
|
-
const MAX_STRUCTURED_INPUT_CHARS = ${
|
|
378
|
-
const MAX_OUTPUT_CHARS = ${
|
|
379
|
-
const WORKSPACE_BEFORE_MAX_OUTPUT_CHARS = ${
|
|
380
|
-
const MAX_ERROR_CHARS = ${
|
|
377
|
+
const MAX_INPUT_CHARS = ${W};
|
|
378
|
+
const MAX_STRUCTURED_INPUT_CHARS = ${F};
|
|
379
|
+
const MAX_OUTPUT_CHARS = ${Q};
|
|
380
|
+
const WORKSPACE_BEFORE_MAX_OUTPUT_CHARS = ${S};
|
|
381
|
+
const MAX_ERROR_CHARS = ${j};
|
|
381
382
|
const MAX_LEDGER_IDS = 16384;
|
|
382
383
|
const MAX_CACHED_RESULTS = 256;
|
|
383
|
-
const MAX_JOB_INPUT_CHARS = ${
|
|
384
|
-
const MAX_JOB_OUTPUT_CHARS = ${
|
|
385
|
-
const MAX_JOB_OUTPUT_CHUNKS = ${
|
|
386
|
-
const MAX_JOBS = ${
|
|
384
|
+
const MAX_JOB_INPUT_CHARS = ${k};
|
|
385
|
+
const MAX_JOB_OUTPUT_CHARS = ${z};
|
|
386
|
+
const MAX_JOB_OUTPUT_CHUNKS = ${Z};
|
|
387
|
+
const MAX_JOBS = ${J};
|
|
387
388
|
const MAX_JOB_RECORD_BYTES = 262144;
|
|
388
389
|
const MAX_JOB_JOURNAL_BYTES = MAX_JOBS * MAX_JOB_RECORD_BYTES;
|
|
389
|
-
const MAX_JOB_CONTROL_FRAME_BYTES = ${
|
|
390
|
-
const MAX_PTY_INPUT_BYTES = ${
|
|
391
|
-
const MAX_PTY_OUTPUT_BYTES = ${
|
|
392
|
-
const MAX_PTY_DIMENSION = ${
|
|
390
|
+
const MAX_JOB_CONTROL_FRAME_BYTES = ${K};
|
|
391
|
+
const MAX_PTY_INPUT_BYTES = ${$};
|
|
392
|
+
const MAX_PTY_OUTPUT_BYTES = ${G};
|
|
393
|
+
const MAX_PTY_DIMENSION = ${M};
|
|
393
394
|
const ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/;
|
|
394
395
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
395
|
-
const
|
|
396
|
+
const WINDOWS = process.platform === "win32";
|
|
397
|
+
// Each Bash leader's tree on Windows, children that outlive it included (shared/windows-job-object.ts).
|
|
398
|
+
const windowsJobs = WINDOWS ? ${Y}(require) : null;
|
|
399
|
+
/** Windows has no POSIX owner or mode bits: the user's own profile folders are
|
|
400
|
+
* private by their ACLs, and the worker runs as that ordinary user. */
|
|
401
|
+
function ownedPrivately(stat, mask = 0o077) {
|
|
402
|
+
return WINDOWS || (stat.uid === process.getuid() && (stat.mode & mask) === 0);
|
|
403
|
+
}
|
|
404
|
+
/** Windows cannot open (or fsync) a directory; its renames are durable by NTFS journaling. */
|
|
405
|
+
function syncDirectory(directory) {
|
|
406
|
+
if (WINDOWS) return;
|
|
407
|
+
const directoryFd = fs.openSync(directory, fs.constants.O_RDONLY);
|
|
408
|
+
try { fs.fsyncSync(directoryFd); } finally { fs.closeSync(directoryFd); }
|
|
409
|
+
}
|
|
410
|
+
/** Path containment; Windows paths compare case-insensitively. */
|
|
411
|
+
function pathWithin(candidate, root) {
|
|
412
|
+
const [child, parent] = WINDOWS ? [candidate.toLowerCase(), root.toLowerCase()] : [candidate, root];
|
|
413
|
+
return child === parent || child.startsWith(parent + path.sep);
|
|
414
|
+
}
|
|
415
|
+
/** Git Bash's form of a Windows path (C:\Users\me is /c/Users/me): every path
|
|
416
|
+
* this worker sends or sets as a shell coordinate, since the server and its
|
|
417
|
+
* scripts speak POSIX. Unchanged off Windows. */
|
|
418
|
+
function posixPath(value) {
|
|
419
|
+
if (!WINDOWS) return value;
|
|
420
|
+
const drive = /^([A-Za-z]):(?:[\\/](.*))?$/.exec(value);
|
|
421
|
+
if (!drive) return value.replace(/\\/g, "/");
|
|
422
|
+
return "/" + drive[1].toLowerCase() + (drive[2] ? "/" + drive[2].replace(/\\/g, "/") : "");
|
|
423
|
+
}
|
|
424
|
+
/** A path from the server or the model as Node opens it on Windows: /c/Users/me,
|
|
425
|
+
* C:/Users/me and C:\Users\me name the same file, and /tmp is the shell's
|
|
426
|
+
* TEMP (Git Bash mounts it there). Relative paths are unchanged. */
|
|
427
|
+
function nativePath(value) {
|
|
428
|
+
if (!WINDOWS || typeof value !== "string") return value;
|
|
429
|
+
const temporary = /^\/tmp(?:\/(.*))?$/.exec(value);
|
|
430
|
+
if (temporary) return nativePath(path.join(config.executionEnvironment?.temporaryDirectory ?? config.workRoot, temporary[1] || ""));
|
|
431
|
+
const drive = /^\/([A-Za-z])(?:\/(.*))?$/.exec(value);
|
|
432
|
+
if (drive) return drive[1].toUpperCase() + ":\\" + (drive[2] || "").replace(/\//g, "\\");
|
|
433
|
+
return /^[A-Za-z]:\//.test(value) ? value.replace(/\//g, "\\") : value;
|
|
434
|
+
}
|
|
435
|
+
/** The request with its file path as nativePath gives it (Windows only); the
|
|
436
|
+
* journal and every result keep the request as the server sent it. */
|
|
437
|
+
function nativePathRequest(request) {
|
|
438
|
+
const input = request.input;
|
|
439
|
+
if (!WINDOWS) return request;
|
|
440
|
+
if (["read", "edit", "write"].includes(request.tool) && typeof input.path === "string") {
|
|
441
|
+
return { ...request, input: { ...input, path: nativePath(input.path) } };
|
|
442
|
+
}
|
|
443
|
+
if (request.tool === "pi_tool" && input.tool !== "bash" && typeof input.args.path === "string") {
|
|
444
|
+
return { ...request, input: { ...input, args: { ...input.args, path: nativePath(input.args.path) } } };
|
|
445
|
+
}
|
|
446
|
+
return request;
|
|
447
|
+
}
|
|
448
|
+
const filesystemRoot = (value) => path.parse(value).root === value;
|
|
449
|
+
// A Windows managed job's control endpoint is a named pipe of its own.
|
|
450
|
+
const validWindowsPipe = (value) => WINDOWS && typeof value === "string" && /^\\\\\.\\pipe\\reason-[A-Za-z0-9._-]{1,200}$/.test(value);
|
|
451
|
+
// Windows programs need these system coordinates (Winsock, crypto, profile
|
|
452
|
+
// folders); a composed environment carries them from the worker's own.
|
|
453
|
+
const WINDOWS_SYSTEM_VARIABLES = ["SystemRoot", "SystemDrive", "windir", "ComSpec", "PATHEXT", "USERPROFILE", "USERNAME",
|
|
454
|
+
"USERDOMAIN", "COMPUTERNAME", "APPDATA", "LOCALAPPDATA", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "ProgramW6432",
|
|
455
|
+
"CommonProgramFiles", "CommonProgramFiles(x86)", "CommonProgramW6432", "NUMBER_OF_PROCESSORS", "PROCESSOR_ARCHITECTURE", "OS", "PSModulePath"];
|
|
456
|
+
function windowsSystemVariables() {
|
|
457
|
+
if (!WINDOWS) return {};
|
|
458
|
+
return Object.fromEntries(WINDOWS_SYSTEM_VARIABLES.flatMap((name) => {
|
|
459
|
+
const value = process.env[name];
|
|
460
|
+
return typeof value === "string" && value.length <= 32768 && !value.includes("\0") ? [[name, value]] : [];
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
396
463
|
/** A Reason cloud machine's memory ceiling, which guestd records on every
|
|
397
464
|
* start (/run/sc/memory-max-mib), or 0 elsewhere. Read per command: a resize
|
|
398
465
|
* changes it. */
|
|
@@ -425,10 +492,11 @@ function jobsUserPrefix(model) {
|
|
|
425
492
|
return user ? ["/usr/bin/setpriv", "--reuid=" + user.uid, "--regid=" + gid, "--clear-groups", "--no-new-privs", "--",
|
|
426
493
|
"/usr/bin/env", "HOME=" + user.home, "USER=" + user.username, "LOGNAME=" + user.username] : [];
|
|
427
494
|
}
|
|
428
|
-
const FIXED_CLOUD_CHILD_PATH = ${JSON.stringify(
|
|
429
|
-
const FIXED_MACOS_CLOUD_CHILD_PATH = ${JSON.stringify(
|
|
430
|
-
const selectCloudChildPath = ${
|
|
495
|
+
const FIXED_CLOUD_CHILD_PATH = ${JSON.stringify(st)};
|
|
496
|
+
const FIXED_MACOS_CLOUD_CHILD_PATH = ${JSON.stringify(ot)};
|
|
497
|
+
const selectCloudChildPath = ${ct};
|
|
431
498
|
const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
499
|
+
const FIXED_BASH = WINDOWS ? config.bashExecutable : "/bin/bash";
|
|
432
500
|
const allowedTools = config.allowedTools === undefined
|
|
433
501
|
? ["bash", "repl", "read", "edit", "write"]
|
|
434
502
|
: config.allowedTools;
|
|
@@ -455,7 +523,7 @@ function validEnvironmentConfiguration() {
|
|
|
455
523
|
|| (baseline.variables !== undefined && !validHostVariables(baseline.variables))
|
|
456
524
|
|| ![baseline.home, baseline.temporaryDirectory].every(value => validPath(value) && value.length <= 32768 && !/[\r\n]/.test(value))
|
|
457
525
|
|| typeof baseline.path !== "string" || baseline.path.length > 32768
|
|
458
|
-
|| !baseline.path.split(
|
|
526
|
+
|| !baseline.path.split(path.delimiter).every(value => validPath(value) && !/[\r\n]/.test(value)))) return false;
|
|
459
527
|
if (config.environmentSnapshotPath === undefined) return config.environmentExpectedBinding === undefined;
|
|
460
528
|
const authority = config.environmentExpectedBinding;
|
|
461
529
|
if (!validPath(config.environmentSnapshotPath) || path.basename(config.environmentSnapshotPath) !== "environment.json"
|
|
@@ -492,7 +560,7 @@ const validPrivateJob = config.transport === "local_job_ipc" && config.url === "
|
|
|
492
560
|
&& config.workRoot === config.workspaceAliasPath && config.environmentSnapshotPath === undefined
|
|
493
561
|
&& config.jobEnvSourcePath === undefined && config.spawnPolicy === undefined && config.managedPty === undefined
|
|
494
562
|
&& hasOnlyKeys(config.directShell, ["mode", "executable", "jobId"]) && UUID_RE.test(config.directShell.jobId)
|
|
495
|
-
&& validPath(config.directShell.executable) && ["bash", "zsh"].includes(path.basename(config.directShell.executable))
|
|
563
|
+
&& validPath(config.directShell.executable) && ["bash", "zsh"].includes(path.basename(config.directShell.executable, WINDOWS ? ".exe" : undefined))
|
|
496
564
|
&& (config.directShell.mode === "host" ? config.bashCommandPrefix === undefined
|
|
497
565
|
: config.directShell.mode === "confined" && process.platform === "darwin"
|
|
498
566
|
&& config.bashCommandPrefix?.length === 3 && config.bashCommandPrefix[0] === "/usr/bin/sandbox-exec"
|
|
@@ -509,12 +577,12 @@ function validJobEnvSourcePath() {
|
|
|
509
577
|
if (config.jobEnvSourcePath === undefined || config.jobEnvSourcePath === path.join(config.workRoot, "job.env")) return true;
|
|
510
578
|
// A cloud workspace's job.env is in its Session's root-only control
|
|
511
579
|
// directory (shared/device-workspace.ts), derived from the root alone.
|
|
512
|
-
const cloud = new RegExp(${JSON.stringify(
|
|
580
|
+
const cloud = new RegExp(${JSON.stringify(w.source)}).exec(config.workRoot);
|
|
513
581
|
if (process.platform === "linux" && cloud) {
|
|
514
582
|
const workspaceId = cloud[3];
|
|
515
|
-
const owner = (new RegExp(${JSON.stringify(
|
|
516
|
-
?? (new RegExp(${JSON.stringify(
|
|
517
|
-
return config.jobEnvSourcePath === ${JSON.stringify(
|
|
583
|
+
const owner = (new RegExp(${JSON.stringify(x.source)}).exec(workspaceId) || [])[1]
|
|
584
|
+
?? (new RegExp(${JSON.stringify(b.source)}).exec(workspaceId) || [])[1] ?? workspaceId;
|
|
585
|
+
return config.jobEnvSourcePath === ${JSON.stringify(p)} + "/" + owner + "/job.env";
|
|
518
586
|
}
|
|
519
587
|
// Personal Cloud controls are root-owned and outside the writable grant.
|
|
520
588
|
// Admit only the same canonical session, never a caller-selected private path.
|
|
@@ -544,11 +612,13 @@ if (
|
|
|
544
612
|
(config.terminalCleanupRunId !== undefined && (!UUID_RE.test(config.terminalCleanupRunId) || config.terminalCleanupRunId !== config.browserRunId)) ||
|
|
545
613
|
(config.terminalDaemonNonce !== undefined && (!UUID_RE.test(config.terminalDaemonNonce) || !config.terminalCleanupRunId)) ||
|
|
546
614
|
(config.browserRunId !== undefined && (typeof config.browserRunId !== "string" || !UUID_RE.test(config.browserRunId))) ||
|
|
547
|
-
config.workRoot
|
|
615
|
+
filesystemRoot(config.workRoot) || filesystemRoot(config.workspaceAliasPath) ||
|
|
548
616
|
(config.workRoot !== config.workspaceAliasPath && (
|
|
549
|
-
config.workRoot
|
|
550
|
-
config.workspaceAliasPath
|
|
617
|
+
pathWithin(config.workRoot, config.workspaceAliasPath) ||
|
|
618
|
+
pathWithin(config.workspaceAliasPath, config.workRoot)
|
|
551
619
|
)) ||
|
|
620
|
+
(WINDOWS ? !validPath(config.bashExecutable) || path.basename(config.bashExecutable).toLowerCase() !== "bash.exe"
|
|
621
|
+
: config.bashExecutable !== undefined) ||
|
|
552
622
|
typeof config.launchId !== "string" || !/^[a-f0-9-]{36}$/.test(config.launchId) ||
|
|
553
623
|
!Number.isSafeInteger(config.expiresAtMs) || config.expiresAtMs <= Date.now() ||
|
|
554
624
|
!validTimeout(config.bashTimeoutMs) || !validTimeout(config.replTimeoutMs) ||
|
|
@@ -580,8 +650,8 @@ if (
|
|
|
580
650
|
config.bashCommandPrefix !== undefined || config.jobEnvSourcePath !== undefined
|
|
581
651
|
)) ||
|
|
582
652
|
(config.jobControlSocketPath !== undefined && (
|
|
583
|
-
!config.managedJobs || !validPath(config.jobControlSocketPath) ||
|
|
584
|
-
path.basename(config.jobControlSocketPath) !== "control.sock" || Buffer.byteLength(config.jobControlSocketPath) > 100
|
|
653
|
+
!config.managedJobs || (WINDOWS ? !validWindowsPipe(config.jobControlSocketPath) : !validPath(config.jobControlSocketPath) ||
|
|
654
|
+
path.basename(config.jobControlSocketPath) !== "control.sock" || Buffer.byteLength(config.jobControlSocketPath) > 100)
|
|
585
655
|
)) ||
|
|
586
656
|
!Array.isArray(allowedTools) || allowedTools.length < 1 || allowedTools.length > 5 ||
|
|
587
657
|
new Set(allowedTools).size !== allowedTools.length ||
|
|
@@ -752,7 +822,7 @@ function failure(id, exitCode, error, stdout, stderr, responseGeneration = targe
|
|
|
752
822
|
exitCode: Number.isSafeInteger(exitCode) && exitCode > 0 && exitCode <= 255 ? exitCode : 1,
|
|
753
823
|
stdout: clip(stdout || "", MAX_OUTPUT_CHARS),
|
|
754
824
|
stderr: clip(stderr || "", MAX_OUTPUT_CHARS),
|
|
755
|
-
cwd: config.repoPath,
|
|
825
|
+
cwd: posixPath(config.repoPath),
|
|
756
826
|
error: clip(error, MAX_ERROR_CHARS),
|
|
757
827
|
};
|
|
758
828
|
}
|
|
@@ -765,7 +835,7 @@ function success(id, stdout, stderr, responseGeneration = targetGeneration) {
|
|
|
765
835
|
exitCode: 0,
|
|
766
836
|
stdout: clip(stdout || "", MAX_OUTPUT_CHARS),
|
|
767
837
|
stderr: clip(stderr || "", MAX_OUTPUT_CHARS),
|
|
768
|
-
cwd: config.repoPath,
|
|
838
|
+
cwd: posixPath(config.repoPath),
|
|
769
839
|
};
|
|
770
840
|
}
|
|
771
841
|
// The code alone names the reason: Bun's WebSocket client drops close text.
|
|
@@ -906,7 +976,7 @@ function privateJournalRead(name, maxBytes = MAX_JOB_RECORD_BYTES) {
|
|
|
906
976
|
const fd = fs.openSync(path.join(journalDirectory, name), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
907
977
|
try {
|
|
908
978
|
const stat = fs.fstatSync(fd);
|
|
909
|
-
if (!stat.isFile() ||
|
|
979
|
+
if (!stat.isFile() || !ownedPrivately(stat) || stat.size > maxBytes) {
|
|
910
980
|
throw new Error("invalid_job_journal");
|
|
911
981
|
}
|
|
912
982
|
return { value: JSON.parse(fs.readFileSync(fd, "utf8")), bytes: stat.size };
|
|
@@ -927,8 +997,7 @@ function privateJournalWrite(name, encoded, maxBytes = MAX_JOB_RECORD_BYTES, dur
|
|
|
927
997
|
try { fs.writeFileSync(fd, encoded); if (durable) fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
|
|
928
998
|
fs.renameSync(temp, target);
|
|
929
999
|
if (!durable) return bytes;
|
|
930
|
-
|
|
931
|
-
try { fs.fsyncSync(directoryFd); } finally { fs.closeSync(directoryFd); }
|
|
1000
|
+
syncDirectory(path.dirname(target));
|
|
932
1001
|
return bytes;
|
|
933
1002
|
}
|
|
934
1003
|
function journalScope(generation, fenceId) {
|
|
@@ -958,10 +1027,10 @@ function initializeJobs() {
|
|
|
958
1027
|
// (shared/local-protected-paths.ts) that the file tools and every confinement
|
|
959
1028
|
// refuse. A home-folder worker keeps it in ~/.config/reason, so its Session may
|
|
960
1029
|
// work in the home folder itself, as a Mac's home grant does.
|
|
961
|
-
const PROTECTED_STATE_DIRECTORIES = ${JSON.stringify(
|
|
1030
|
+
const PROTECTED_STATE_DIRECTORIES = ${JSON.stringify(y)};
|
|
962
1031
|
function stateOverlapsWorkspace(state, root) {
|
|
963
|
-
if (
|
|
964
|
-
if (!state
|
|
1032
|
+
if (pathWithin(root, state)) return true;
|
|
1033
|
+
if (!pathWithin(state, root)) return false;
|
|
965
1034
|
const segments = state.slice(root.length + 1).toLowerCase().split(path.sep);
|
|
966
1035
|
return !PROTECTED_STATE_DIRECTORIES.some((sequence) => segments.some((_, start) =>
|
|
967
1036
|
sequence.every((part, offset) => segments[start + offset] === part)));
|
|
@@ -971,7 +1040,7 @@ function prepareJournalDirectory() {
|
|
|
971
1040
|
const candidate = config.managedJobs.journalDirectory;
|
|
972
1041
|
try { initializeJobs(); } catch (error) { if (error.code !== "EEXIST") throw error; }
|
|
973
1042
|
const stat = fs.lstatSync(candidate);
|
|
974
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() ||
|
|
1043
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || !ownedPrivately(stat)) throw new Error("invalid_job_journal");
|
|
975
1044
|
journalDirectory = fs.realpathSync(candidate);
|
|
976
1045
|
for (const root of [config.repoPath, config.workRoot]) {
|
|
977
1046
|
const canonical = fs.realpathSync(root);
|
|
@@ -991,8 +1060,7 @@ function acquireJournalOwnership() {
|
|
|
991
1060
|
executorLaunchId: config.launchId, workerArtifactSha256: config.workerArtifactSha256,
|
|
992
1061
|
pid: process.pid, createdAtMs: Date.now() }));
|
|
993
1062
|
fs.fsyncSync(fd);
|
|
994
|
-
|
|
995
|
-
try { fs.fsyncSync(directoryFd); } finally { fs.closeSync(directoryFd); }
|
|
1063
|
+
syncDirectory(journalDirectory);
|
|
996
1064
|
}
|
|
997
1065
|
function releaseJournalOwnership() {
|
|
998
1066
|
if (!journalOwnership) return;
|
|
@@ -1002,8 +1070,7 @@ function releaseJournalOwnership() {
|
|
|
1002
1070
|
fs.unlinkSync(owned.path);
|
|
1003
1071
|
fs.closeSync(owned.fd);
|
|
1004
1072
|
journalOwnership = null;
|
|
1005
|
-
|
|
1006
|
-
try { fs.fsyncSync(directoryFd); } finally { fs.closeSync(directoryFd); }
|
|
1073
|
+
syncDirectory(journalDirectory);
|
|
1007
1074
|
}
|
|
1008
1075
|
function loadJobs() {
|
|
1009
1076
|
if (!config.managedJobs) return;
|
|
@@ -1084,7 +1151,7 @@ function loadJobs() {
|
|
|
1084
1151
|
for (const name of fs.readdirSync(journalDirectory)) {
|
|
1085
1152
|
if (!/^[0-9a-f]{64}\.job\.json\.tmp$/.test(name)) continue;
|
|
1086
1153
|
const stat = fs.lstatSync(path.join(journalDirectory, name));
|
|
1087
|
-
if (!stat.isFile() ||
|
|
1154
|
+
if (!stat.isFile() || !ownedPrivately(stat) || stat.size > MAX_JOB_RECORD_BYTES) throw new Error("invalid_job_journal");
|
|
1088
1155
|
journalDebrisBytes += stat.size;
|
|
1089
1156
|
reservedBytes += stat.size;
|
|
1090
1157
|
if (reservedBytes > MAX_JOB_JOURNAL_BYTES) throw new Error("job_journal_full");
|
|
@@ -1305,7 +1372,7 @@ async function handleTerminalCleanup(target, claim, raw) {
|
|
|
1305
1372
|
let child, timer, killTimer;
|
|
1306
1373
|
const finish = ok => { clearTimeout(timer); clearTimeout(killTimer); if (!ok) cleanupUnconfirmed = true; resolve(ok); };
|
|
1307
1374
|
try {
|
|
1308
|
-
child = spawn("/usr/bin/python3", ["-I", "-c", ${JSON.stringify(
|
|
1375
|
+
child = spawn("/usr/bin/python3", ["-I", "-c", ${JSON.stringify(u)}],
|
|
1309
1376
|
{ stdio: ["pipe", "ignore", "ignore"], env: { PATH: "/usr/bin:/bin" }, detached: true });
|
|
1310
1377
|
let timedOut = false;
|
|
1311
1378
|
timer = setTimeout(() => { timedOut = true; try { process.kill(-child.pid, "SIGTERM"); } catch { cleanupUnconfirmed = true; }
|
|
@@ -1468,21 +1535,24 @@ async function handleJob(claim, raw, sendReply, isCurrent, fromControl = false)
|
|
|
1468
1535
|
function startJobControl() {
|
|
1469
1536
|
if (!config.jobControlSocketPath) return Promise.resolve();
|
|
1470
1537
|
return new Promise((resolve, reject) => {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1538
|
+
// A Windows named pipe has no directory; a second server on the same name fails to listen.
|
|
1539
|
+
if (!WINDOWS) {
|
|
1540
|
+
const directory = path.dirname(config.jobControlSocketPath);
|
|
1541
|
+
const stat = fs.lstatSync(directory);
|
|
1542
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid()
|
|
1543
|
+
|| (stat.mode & 0o777) !== 0o700 || fs.realpathSync(directory) !== directory
|
|
1544
|
+
|| path.join(directory, "control.sock") !== config.jobControlSocketPath) throw new Error("invalid_job_control_directory");
|
|
1545
|
+
for (const root of [config.repoPath, config.workRoot]) {
|
|
1546
|
+
const canonical = fs.realpathSync(root);
|
|
1547
|
+
if (pathWithin(directory, canonical) || pathWithin(canonical, directory)) {
|
|
1548
|
+
throw new Error("job_control_overlaps_workspace");
|
|
1549
|
+
}
|
|
1480
1550
|
}
|
|
1551
|
+
// lstat also rejects dangling symlinks; never unlink or adopt an existing
|
|
1552
|
+
// endpoint. An exclusive per-launch directory is composition-owned.
|
|
1553
|
+
try { fs.lstatSync(config.jobControlSocketPath); throw new Error("job_control_path_exists"); }
|
|
1554
|
+
catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
1481
1555
|
}
|
|
1482
|
-
// lstat also rejects dangling symlinks; never unlink or adopt an existing
|
|
1483
|
-
// endpoint. An exclusive per-launch directory is composition-owned.
|
|
1484
|
-
try { fs.lstatSync(config.jobControlSocketPath); throw new Error("job_control_path_exists"); }
|
|
1485
|
-
catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
1486
1556
|
// Bind before connecting the relay so a duplicate/stale socket cannot leave
|
|
1487
1557
|
// a second serving worker. Single framed request per connection, bounded
|
|
1488
1558
|
// clients/body/time; the existing job handler owns all mutations.
|
|
@@ -1517,7 +1587,7 @@ function startJobControl() {
|
|
|
1517
1587
|
server.maxConnections = 16;
|
|
1518
1588
|
server.once("error", reject);
|
|
1519
1589
|
server.listen(config.jobControlSocketPath, () => {
|
|
1520
|
-
try { fs.chmodSync(config.jobControlSocketPath, 0o600); }
|
|
1590
|
+
try { if (!WINDOWS) fs.chmodSync(config.jobControlSocketPath, 0o600); }
|
|
1521
1591
|
catch (error) { server.close(); reject(error); return; }
|
|
1522
1592
|
jobControlServer = server;
|
|
1523
1593
|
resolve();
|
|
@@ -1556,9 +1626,17 @@ async function handleRequestCancel(target, claim, raw) {
|
|
|
1556
1626
|
id: request.id, targetId: request.targetId, status });
|
|
1557
1627
|
}
|
|
1558
1628
|
}
|
|
1629
|
+
// Windows has no process groups: a command's tree is its leader's Job Object,
|
|
1630
|
+
// gone once the job has no process left. Without one, it is the leader and what
|
|
1631
|
+
// taskkill /T finds under it, and the tree counts as gone with its leader.
|
|
1559
1632
|
function processGroupAbsent(pgid) {
|
|
1633
|
+
const active = windowsJobs ? windowsJobs.active(pgid) : null;
|
|
1634
|
+
if (active !== null) {
|
|
1635
|
+
if (active === 0) windowsJobs.release(pgid);
|
|
1636
|
+
return active === 0;
|
|
1637
|
+
}
|
|
1560
1638
|
try {
|
|
1561
|
-
process.kill(-pgid, 0);
|
|
1639
|
+
process.kill(WINDOWS ? pgid : -pgid, 0);
|
|
1562
1640
|
if (process.platform === "linux") {
|
|
1563
1641
|
try {
|
|
1564
1642
|
const entries = fs.readdirSync("/proc");
|
|
@@ -1612,6 +1690,14 @@ async function killGroup(child) {
|
|
|
1612
1690
|
if (!child || !Number.isSafeInteger(child.pid)) return;
|
|
1613
1691
|
const pgid = child.pid;
|
|
1614
1692
|
if (processGroupAbsent(pgid)) return;
|
|
1693
|
+
if (WINDOWS) {
|
|
1694
|
+
if (!(windowsJobs && windowsJobs.terminate(pgid))) try {
|
|
1695
|
+
spawnSync(path.join(process.env.SystemRoot || "C:\\Windows", "System32", "taskkill.exe"), ["/PID", String(pgid), "/T", "/F"],
|
|
1696
|
+
{ stdio: "ignore", timeout: 10_000, windowsHide: true });
|
|
1697
|
+
} catch (error) { void error; }
|
|
1698
|
+
if (await waitForProcessGroupAbsent(pgid, 2_000)) return;
|
|
1699
|
+
throw new Error("process_group_cleanup_unconfirmed");
|
|
1700
|
+
}
|
|
1615
1701
|
try { process.kill(-pgid, "SIGTERM"); } catch (error) {
|
|
1616
1702
|
if (error && error.code !== "ESRCH") throw error;
|
|
1617
1703
|
}
|
|
@@ -1956,7 +2042,7 @@ function readPrivateEnvironment() {
|
|
|
1956
2042
|
const parent = path.dirname(candidate);
|
|
1957
2043
|
if (fs.realpathSync(parent) !== parent) throw new Error("environment_unavailable");
|
|
1958
2044
|
const directory = fs.lstatSync(parent);
|
|
1959
|
-
if (!directory.isDirectory() || directory.uid
|
|
2045
|
+
if (!directory.isDirectory() || !(WINDOWS || (directory.uid === process.getuid() && (directory.mode & 0o7777) === 0o700))) throw new Error("environment_unavailable");
|
|
1960
2046
|
for (const root of [config.workRoot, config.repoPath]) {
|
|
1961
2047
|
const canonical = fs.realpathSync(root);
|
|
1962
2048
|
if (stateOverlapsWorkspace(parent, canonical)) throw new Error("environment_unavailable");
|
|
@@ -1969,8 +2055,8 @@ function readPrivateEnvironment() {
|
|
|
1969
2055
|
catch (error) { if (error && error.code === "ENOENT") throw new Error("environment_unavailable"); throw error; }
|
|
1970
2056
|
try {
|
|
1971
2057
|
const before = fs.fstatSync(fd);
|
|
1972
|
-
if (!before.isFile() || before.
|
|
1973
|
-
|| ![0o600, 0o400].includes(before.mode & 0o7777) || before.size < 1 || before.size > DEVICE_ENVIRONMENT_LIMITS.snapshotBytes) throw new Error("environment_unavailable");
|
|
2058
|
+
if (!before.isFile() || before.nlink !== 1
|
|
2059
|
+
|| !(WINDOWS || (before.uid === process.getuid() && [0o600, 0o400].includes(before.mode & 0o7777))) || before.size < 1 || before.size > DEVICE_ENVIRONMENT_LIMITS.snapshotBytes) throw new Error("environment_unavailable");
|
|
1974
2060
|
const bytes = Buffer.alloc(before.size + 1);
|
|
1975
2061
|
let length = 0;
|
|
1976
2062
|
while (length < bytes.length) {
|
|
@@ -2002,7 +2088,7 @@ function validateEnvironmentSnapshot(snapshot) {
|
|
|
2002
2088
|
if (name.length > 128 || !DEVICE_ENVIRONMENT_NAME_RE.test(name) || typeof value !== "string" || Buffer.byteLength(value, "utf8") > DEVICE_ENVIRONMENT_LIMITS.valueBytes || value.includes("\0")) throw new Error("environment_unavailable");
|
|
2003
2089
|
}
|
|
2004
2090
|
if (snapshot.github_auth !== undefined && (snapshot.github_auth !== "local_gh" || snapshot.git !== null
|
|
2005
|
-
|| !["darwin", "linux"].includes(process.platform) || !config.executionEnvironment || config.environmentExpectedBinding.binding.kind !== "workspace_source")) {
|
|
2091
|
+
|| !["darwin", "linux", "win32"].includes(process.platform) || !config.executionEnvironment || config.environmentExpectedBinding.binding.kind !== "workspace_source")) {
|
|
2006
2092
|
throw new Error("environment_unavailable");
|
|
2007
2093
|
}
|
|
2008
2094
|
if (snapshot.git !== null) {
|
|
@@ -2075,7 +2161,7 @@ function environmentForSpawn(piShell = false, confined = false) {
|
|
|
2075
2161
|
const deviceRoot = fs.realpathSync(config.deviceRootPath || config.workRoot);
|
|
2076
2162
|
const workspace = fs.realpathSync(config.workRoot);
|
|
2077
2163
|
const repo = fs.realpathSync(config.repoPath);
|
|
2078
|
-
const within =
|
|
2164
|
+
const within = pathWithin;
|
|
2079
2165
|
if (!within(workspace, deviceRoot) || !within(repo, workspace)) throw new Error("environment_unavailable");
|
|
2080
2166
|
let pool;
|
|
2081
2167
|
if (config.workspacePoolPath !== undefined) {
|
|
@@ -2090,14 +2176,16 @@ function environmentForSpawn(piShell = false, confined = false) {
|
|
|
2090
2176
|
const baseline = config.executionEnvironment;
|
|
2091
2177
|
const temporaryDirectory = baseline?.temporaryDirectory ?? config.workRoot;
|
|
2092
2178
|
const hostVariables = piShell ? baseline?.variables : undefined;
|
|
2093
|
-
const env = { ...hostVariables, ...snapshot.variables,
|
|
2094
|
-
|
|
2179
|
+
const env = { ...hostVariables, ...snapshot.variables, ...windowsSystemVariables(),
|
|
2180
|
+
PATH: baseline?.path ?? (WINDOWS ? process.env.PATH : "/opt/homebrew/bin:" + FIXED_CLOUD_CHILD_PATH),
|
|
2181
|
+
HOME: baseline?.home ?? config.workRoot, PWD: posixPath(config.repoPath), TMPDIR: temporaryDirectory, TMP: temporaryDirectory, TEMP: temporaryDirectory,
|
|
2095
2182
|
SHELL: FIXED_BASH, BASH_ENV: "/dev/null", ENV: "/dev/null",
|
|
2096
2183
|
// Each coordinate under its Reason name and the Ara name every server
|
|
2097
|
-
// script and Session shell before the rename reads
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2184
|
+
// script and Session shell before the rename reads; in Git Bash's form on
|
|
2185
|
+
// Windows, where those scripts compare them with $PWD and pwd -P.
|
|
2186
|
+
REASON_DEVICE_ROOT: posixPath(deviceRoot), REASON_HANDS_WORKSPACE: posixPath(workspace), REASON_HANDS_REPO: posixPath(repo),
|
|
2187
|
+
ARA_DEVICE_ROOT: posixPath(deviceRoot), ARA_HANDS_WORKSPACE: posixPath(workspace), ARA_HANDS_REPO: posixPath(repo),
|
|
2188
|
+
...(pool === undefined ? {} : { REASON_HANDS_WORKSPACE_POOL: posixPath(pool), ARA_HANDS_WORKSPACE_POOL: posixPath(pool) }),
|
|
2101
2189
|
GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_SYSTEM: "/dev/null", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0",
|
|
2102
2190
|
};
|
|
2103
2191
|
const pairs = [["credential.helper", ""], ["http.extraheader", ""]];
|
|
@@ -2226,11 +2314,95 @@ function prepareBashSpawn(argv, extraFileDescriptors, expiresAtMs, signal, onLos
|
|
|
2226
2314
|
let nativePtyOwner = null;
|
|
2227
2315
|
function ptySupported() {
|
|
2228
2316
|
if (!config.managedPty || config.environmentSnapshotPath === undefined || config.spawnPolicy || config.bashCommandPrefix
|
|
2229
|
-
||
|
|
2317
|
+
|| typeof Bun === "undefined") return false;
|
|
2318
|
+
if (process.platform === "win32") return windowsPty().available();
|
|
2319
|
+
if (!["darwin", "linux"].includes(process.platform) || typeof Bun.Terminal !== "function") return false;
|
|
2230
2320
|
if (process.platform === "linux" && !["x64", "arm64"].includes(process.arch)) return false;
|
|
2231
2321
|
try { platformPtyOwner(); return true; } catch { return false; }
|
|
2232
2322
|
}
|
|
2233
2323
|
function platformPtyOwner() { return process.platform === "linux" ? linuxPtyOwner() : macPtyOwner(); }
|
|
2324
|
+
let windowsPtyOwner = null;
|
|
2325
|
+
function windowsPty() { return windowsPtyOwner ||= windowsPtyApi(() => require("bun:ffi"), fs.existsSync); }
|
|
2326
|
+
/** Windows: a ConPTY whose shell and everything it starts share one job
|
|
2327
|
+
* object (windows-pty-source.ts). The open process handle pins the shell's
|
|
2328
|
+
* PID; teardown terminates the job and confirms it has no live process, the
|
|
2329
|
+
* counterpart of the POSIX session sweep. */
|
|
2330
|
+
function windowsPtyOperation(request, managed) {
|
|
2331
|
+
let pty = null, childEnvironment = null;
|
|
2332
|
+
let done = false, cancelled = false, timedOut = false, cleanup = null, exitCode = null, timer = null;
|
|
2333
|
+
const spawnGeneration = targetGeneration, spawnFence = targetFenceId;
|
|
2334
|
+
let resolveResult, acknowledgeSpawn;
|
|
2335
|
+
const promise = new Promise((resolve) => { resolveResult = resolve; });
|
|
2336
|
+
const started = new Promise((resolve) => { acknowledgeSpawn = resolve; });
|
|
2337
|
+
const finish = (error, code) => {
|
|
2338
|
+
if (done) return;
|
|
2339
|
+
done = true; acknowledgeSpawn(false);
|
|
2340
|
+
clearTimeout(timer);
|
|
2341
|
+
resolveResult(error ? failure(request.id, code, error, "", "") : success(request.id, "", ""));
|
|
2342
|
+
};
|
|
2343
|
+
function stopSession() {
|
|
2344
|
+
if (cleanup) return cleanup;
|
|
2345
|
+
cleanup = Promise.resolve().then(async () => {
|
|
2346
|
+
if (!pty) { finish(timedOut ? "timeout" : "cancelled", timedOut ? 124 : 130); return; }
|
|
2347
|
+
try {
|
|
2348
|
+
if (pty.activeProcesses() > 0) pty.terminate();
|
|
2349
|
+
for (const startedAt = Date.now(); pty.activeProcesses() > 0;) {
|
|
2350
|
+
if (Date.now() - startedAt > ${s}) throw new Error("pty_cleanup_timeout");
|
|
2351
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
2352
|
+
}
|
|
2353
|
+
pty.close();
|
|
2354
|
+
if (timedOut) finish("timeout", 124);
|
|
2355
|
+
else if (cancelled) finish("cancelled", 130);
|
|
2356
|
+
else finish(exitCode === 0 ? null : "command_failed", exitCode ?? 1);
|
|
2357
|
+
} catch {
|
|
2358
|
+
// Closing the job handle still ends its processes (KILL_ON_JOB_CLOSE), unconfirmed.
|
|
2359
|
+
try { pty.close(); } catch { /* already closed */ }
|
|
2360
|
+
finish("process_group_cleanup_unconfirmed", 1);
|
|
2361
|
+
}
|
|
2362
|
+
});
|
|
2363
|
+
return cleanup;
|
|
2364
|
+
}
|
|
2365
|
+
function arm(deadline) {
|
|
2366
|
+
clearTimeout(timer);
|
|
2367
|
+
timer = setTimeout(() => { timedOut = true; void stopSession(); }, Math.max(1, deadline - Date.now()));
|
|
2368
|
+
}
|
|
2369
|
+
const canInput = () => done || cleanup || !pty ? "job_not_running" : null;
|
|
2370
|
+
try {
|
|
2371
|
+
childEnvironment = environmentForSpawn();
|
|
2372
|
+
if (stopped || spawnGeneration !== targetGeneration || spawnFence !== targetFenceId
|
|
2373
|
+
|| !validJobLease(managed.leaseExpiresAtMs)) throw new Error("pty_spawn_expired");
|
|
2374
|
+
const env = { ...childEnvironment.env, TERM: "xterm-256color" };
|
|
2375
|
+
const shell = windowsPty().defaultShell(process.env);
|
|
2376
|
+
pty = windowsPty().spawn({ file: shell, args: windowsPty().shellArgs(shell), cwd: config.repoPath, env,
|
|
2377
|
+
cols: request.input.cols, rows: request.input.rows, onData: (bytes) => { if (!done) managed.onBytes(bytes); } });
|
|
2378
|
+
consumedEnvironment = { revision: childEnvironment.snapshot.revision, hash: childEnvironment.hash };
|
|
2379
|
+
managed.onEnvironment(childEnvironment.snapshot);
|
|
2380
|
+
arm(managed.leaseExpiresAtMs);
|
|
2381
|
+
void pty.exited.then((code) => { if (!done && !cleanup) { exitCode = code; void stopSession(); } });
|
|
2382
|
+
acknowledgeSpawn(true);
|
|
2383
|
+
} catch {
|
|
2384
|
+
if (pty) { cancelled = true; void stopSession(); }
|
|
2385
|
+
else finish("spawn_failed", 1);
|
|
2386
|
+
}
|
|
2387
|
+
return { promise, started, supportsCancellation: true, canInput,
|
|
2388
|
+
settle: stopSession,
|
|
2389
|
+
processIdentity() {
|
|
2390
|
+
return pty && !done && !cleanup ? { pid: pty.pid, processGroupId: pty.pid, executorLaunchId: config.launchId } : null;
|
|
2391
|
+
},
|
|
2392
|
+
input(data) {
|
|
2393
|
+
const error = canInput(); if (error) return error;
|
|
2394
|
+
try { pty.write(data); return null; }
|
|
2395
|
+
catch { cancelled = true; void stopSession(); return "job_not_running"; }
|
|
2396
|
+
},
|
|
2397
|
+
resize(cols, rows) {
|
|
2398
|
+
const error = canInput(); if (error) return error;
|
|
2399
|
+
try { pty.resize(cols, rows); return null; }
|
|
2400
|
+
catch { cancelled = true; void stopSession(); return "job_not_running"; }
|
|
2401
|
+
},
|
|
2402
|
+
renew(deadline) { if (done || cleanup) return false; arm(deadline); return true; },
|
|
2403
|
+
cancel() { cancelled = true; return stopSession(); },
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2234
2406
|
/** Only live native process identities, never recovered PIDs. Darwin's audit
|
|
2235
2407
|
* token includes the kernel PID version; stale versions cannot signal a reused
|
|
2236
2408
|
* PID. Keep the trusted session leader alive until its interactive groups are
|
|
@@ -2479,7 +2651,7 @@ function linuxPtyOwner() {
|
|
|
2479
2651
|
// user detaches from the shell (nohup, setsid, disown, or jobs left at exit)
|
|
2480
2652
|
// are not swept. At most TERMINAL_HOST_LIMIT shells per scope.
|
|
2481
2653
|
const TERMINAL_SHELL = "/bin/bash --noprofile --norc -i";
|
|
2482
|
-
const TERMINAL_TMUX_CONFIG = ${JSON.stringify([...
|
|
2654
|
+
const TERMINAL_TMUX_CONFIG = ${JSON.stringify([...h,"new-session -d -s main /bin/bash --noprofile --norc -i","set -g exit-empty on","run-shell -b -d 43200 -C kill-server",""].join(`
|
|
2483
2655
|
`))};
|
|
2484
2656
|
const TERMINAL_HOST_LIMIT = 4;
|
|
2485
2657
|
const terminalViewers = new Map();
|
|
@@ -2587,6 +2759,7 @@ async function chooseTerminalProgram() {
|
|
|
2587
2759
|
return oneShot;
|
|
2588
2760
|
}
|
|
2589
2761
|
function ptyOperation(request, managed, program = TERMINAL_SHELL) {
|
|
2762
|
+
if (process.platform === "win32") return windowsPtyOperation(request, managed);
|
|
2590
2763
|
const native = platformPtyOwner();
|
|
2591
2764
|
let child = null, anchor = null, terminal = null, childEnvironment = null, device = null;
|
|
2592
2765
|
let done = false, cancelled = false, timedOut = false, cleanup = null, exitCode = null;
|
|
@@ -2622,7 +2795,7 @@ function ptyOperation(request, managed, program = TERMINAL_SHELL) {
|
|
|
2622
2795
|
const { members, empty } = native.session(anchor, device);
|
|
2623
2796
|
try {
|
|
2624
2797
|
if (empty && exitCode !== null && subreaper) break;
|
|
2625
|
-
if (Date.now() - startedAt > ${
|
|
2798
|
+
if (Date.now() - startedAt > ${s}) throw new Error("pty_cleanup_timeout");
|
|
2626
2799
|
for (const token of members) {
|
|
2627
2800
|
if (!native.same(anchor)) throw new Error("pty_session_identity_lost");
|
|
2628
2801
|
const id = native.identityKey(token);
|
|
@@ -2638,7 +2811,7 @@ function ptyOperation(request, managed, program = TERMINAL_SHELL) {
|
|
|
2638
2811
|
controlReleased = true;
|
|
2639
2812
|
let exitTimer;
|
|
2640
2813
|
try { await Promise.race([child.exited, new Promise((_, reject) => {
|
|
2641
|
-
exitTimer = setTimeout(() => reject(new Error("pty_anchor_exit_unconfirmed")), ${
|
|
2814
|
+
exitTimer = setTimeout(() => reject(new Error("pty_anchor_exit_unconfirmed")), ${X});
|
|
2642
2815
|
})]); } finally { clearTimeout(exitTimer); }
|
|
2643
2816
|
if (timedOut) finish("timeout", 124);
|
|
2644
2817
|
else if (cancelled) finish("cancelled", 130);
|
|
@@ -2899,7 +3072,9 @@ function bashOperation(request, onProgress, managed, piShell) {
|
|
|
2899
3072
|
command[0], command.slice(1),
|
|
2900
3073
|
{
|
|
2901
3074
|
cwd: config.repoPath,
|
|
2902
|
-
|
|
3075
|
+
// Windows: no process group (taskkill /T stops the tree) and no console window.
|
|
3076
|
+
detached: !WINDOWS,
|
|
3077
|
+
windowsHide: true,
|
|
2903
3078
|
env: validPrivateJob ? process.env : childEnvironment ? childEnvironment.env : {
|
|
2904
3079
|
...process.env,
|
|
2905
3080
|
// A physical Device keeps the CLI-resolved host PATH (including
|
|
@@ -2923,6 +3098,7 @@ function bashOperation(request, onProgress, managed, piShell) {
|
|
|
2923
3098
|
);
|
|
2924
3099
|
// A new leader with this PID proves the retained group of that ID ended.
|
|
2925
3100
|
if (Number.isSafeInteger(child.pid)) backgroundGroups.delete(child.pid);
|
|
3101
|
+
if (windowsJobs && Number.isSafeInteger(child.pid)) windowsJobs.track(child.pid);
|
|
2926
3102
|
} catch (error) {
|
|
2927
3103
|
// The protocol error stays spawn_failed; a coded refusal such as
|
|
2928
3104
|
// local_device_gh_confinement_unavailable or environment_unavailable is
|
|
@@ -3153,15 +3329,19 @@ function replOperation(request) {
|
|
|
3153
3329
|
// timeout and the lease, and background processes retained until the worker
|
|
3154
3330
|
// stops. One departure from stock: a command idle on a watcher's banner is
|
|
3155
3331
|
// stopped (PI_WATCH_BANNER). PI_* variables are not set here.
|
|
3156
|
-
const PI_TOOLS_SHA256 = "${
|
|
3332
|
+
const PI_TOOLS_SHA256 = "${a}";
|
|
3157
3333
|
const piToolsSource = typeof araHandsPiToolsSource === "function" ? araHandsPiToolsSource : null;
|
|
3158
3334
|
let piTools = null;
|
|
3159
3335
|
function loadPiTools() {
|
|
3160
3336
|
if (piTools) return piTools;
|
|
3161
3337
|
const source = piToolsSource();
|
|
3162
3338
|
if (digest(source) !== PI_TOOLS_SHA256) throw new Error("pi_tools_digest_mismatch");
|
|
3339
|
+
// The bundle names itself file:///ara-hands-pi-tools.cjs, which Windows
|
|
3340
|
+
// cannot turn into a path (no drive letter), so loading it would throw.
|
|
3341
|
+
const body = process.platform === "win32"
|
|
3342
|
+
? source.replaceAll("file:///ara-hands-pi-tools.cjs", "file:///C:/ara-hands-pi-tools.cjs") : source;
|
|
3163
3343
|
const module = { exports: {} };
|
|
3164
|
-
new Function("module", "exports", "require", '"use strict";\n' +
|
|
3344
|
+
new Function("module", "exports", "require", '"use strict";\n' + body)(module, module.exports, require);
|
|
3165
3345
|
piTools = module.exports;
|
|
3166
3346
|
return piTools;
|
|
3167
3347
|
}
|
|
@@ -3182,10 +3362,10 @@ const PI_EXIT_STDIO_GRACE_MS = 100;
|
|
|
3182
3362
|
// the token once under quoteDepth layers of single quoting, so the confined
|
|
3183
3363
|
// script is byte-identical to what the Brain's confineBashPayload gives the
|
|
3184
3364
|
// bash tool: srt/bwrap, the payload uid, the network allowlist and allowWrite.
|
|
3185
|
-
const PI_TOOL_CONFINEMENT_TOKEN = "${
|
|
3365
|
+
const PI_TOOL_CONFINEMENT_TOKEN = "${m}";
|
|
3186
3366
|
function validPiToolConfinement(value) {
|
|
3187
3367
|
return hasOnlyKeys(value, ["template", "quoteDepth"]) && typeof value.template === "string"
|
|
3188
|
-
&& value.template.length <= ${
|
|
3368
|
+
&& value.template.length <= ${q} && !value.template.includes("\0")
|
|
3189
3369
|
&& value.template.split(PI_TOOL_CONFINEMENT_TOKEN).length === 2 && [1, 2, 3].includes(value.quoteDepth);
|
|
3190
3370
|
}
|
|
3191
3371
|
// The script T when this command is exactly this policy's confineBashPayload(T)
|
|
@@ -3423,8 +3603,7 @@ function prepareRequestJournal() {
|
|
|
3423
3603
|
try { fs.mkdirSync(directory, { mode: 0o700 }); }
|
|
3424
3604
|
catch (error) { if (error.code !== "EEXIST") throw error; }
|
|
3425
3605
|
const stat = fs.lstatSync(directory);
|
|
3426
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() || stat
|
|
3427
|
-
|| (stat.mode & 0o077) !== 0) throw new Error("invalid_request_journal");
|
|
3606
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || !ownedPrivately(stat)) throw new Error("invalid_request_journal");
|
|
3428
3607
|
// Stream the private directory: receipt count is not a lifetime call cap.
|
|
3429
3608
|
// A crash may leave the fixed temp sibling; only the renamed final record
|
|
3430
3609
|
// is authoritative, so it cannot authorize replay or a completed result.
|
|
@@ -3674,12 +3853,12 @@ async function handleRequest(target, connectionGeneration, claim, raw) {
|
|
|
3674
3853
|
const startOperation = () => request.tool === "bash"
|
|
3675
3854
|
? bashOperation(request, progress)
|
|
3676
3855
|
: request.tool === "pi_tool"
|
|
3677
|
-
? piToolOperation(request, request.input.tool === "bash" ? progress : null, claim.piToolsConfinement)
|
|
3856
|
+
? piToolOperation(nativePathRequest(request), request.input.tool === "bash" ? progress : null, claim.piToolsConfinement)
|
|
3678
3857
|
: request.tool === "repl"
|
|
3679
3858
|
? replOperation(request)
|
|
3680
3859
|
: (config.spawnPolicy || config.bashCommandPrefix) && request.tool !== "read"
|
|
3681
|
-
? payloadFileOperation(request)
|
|
3682
|
-
: fileOperation(request);
|
|
3860
|
+
? payloadFileOperation(nativePathRequest(request))
|
|
3861
|
+
: fileOperation(nativePathRequest(request));
|
|
3683
3862
|
const operation = request.workspaceBefore !== undefined
|
|
3684
3863
|
? workspaceBeforeOperation(request, startOperation) : startOperation();
|
|
3685
3864
|
// Every waiter observes the durable completion, never the raw child result.
|
|
@@ -3986,6 +4165,9 @@ function stop(reason) {
|
|
|
3986
4165
|
return stoppingPromise;
|
|
3987
4166
|
}
|
|
3988
4167
|
process.once("SIGTERM", () => { void stop("SIGTERM"); });
|
|
4168
|
+
// Windows delivers no catchable SIGTERM to a child: its parent ends this
|
|
4169
|
+
// worker's stdin pipe instead (cli/local-hands-relay.ts signalHandsWorker).
|
|
4170
|
+
if (WINDOWS) { process.stdin.once("end", () => { void stop("SIGTERM"); }); process.stdin.once("error", () => { void stop("SIGTERM"); }); process.stdin.resume(); }
|
|
3989
4171
|
process.once("SIGINT", () => { void stop("SIGINT"); });
|
|
3990
4172
|
process.once("SIGHUP", () => { void stop("SIGHUP"); });
|
|
3991
4173
|
setTimeout(() => { void stop("expired"); }, Math.max(1, config.expiresAtMs - Date.now())).unref();
|
|
@@ -3994,5 +4176,5 @@ void startJobControl().then(() => {
|
|
|
3994
4176
|
try { loadJobs(); prepareRequestJournal(); } catch { process.exit(44); }
|
|
3995
4177
|
if (validPrivateJob) markReady(); else connect();
|
|
3996
4178
|
}, () => process.exit(45));
|
|
3997
|
-
`.trimStart(),
|
|
3998
|
-
export{r as
|
|
4179
|
+
`.trimStart(),Ot=rt("sha256").update(ft,"utf8").digest("hex");
|
|
4180
|
+
export{r as Nc,Qt as Oc,l as Pc,g as Qc,nt as Rc,it as Sc,st as Tc,ot as Uc,It as Vc,ut as Wc,at as Xc,ft as Yc,Ot as Zc};
|