@task-handoff/node-agent 0.0.24 → 0.0.25-alpha.1
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/dist/cli.js +1 -1
- package/dist/node-update-worker.js +1 -1
- package/package.json +2 -1
- package/runtime-artifacts/{controlled-instance-runtime-0.0.24-linux-universal.manifest.json → controlled-instance-runtime-0.0.25-alpha.1-linux-universal.manifest.json} +2 -2
- package/runtime-artifacts/controlled-instance-runtime-0.0.25-alpha.1-linux-universal.tar.gz +0 -0
- package/runtime-artifacts/controlled-instance-runtime-0.0.25-alpha.1-linux-universal.tar.gz.sha256 +1 -0
- package/runtime-artifacts/controlled-instance-runtime-0.0.24-linux-universal.tar.gz +0 -0
- package/runtime-artifacts/controlled-instance-runtime-0.0.24-linux-universal.tar.gz.sha256 +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=require("node:fs"),t=require("node:path"),{spawnSync:n}=require("node:child_process"),{Command:
|
|
2
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=require("node:fs"),t=require("node:path"),{spawnSync:n}=require("node:child_process"),{Command:o,InvalidArgumentError:r}=require("commander"),a=require("semver"),s=require("write-file-atomic"),{ControlPlaneHealthResponseSchema:i}=require("../packages/protocol/src/control-plane.ts"),{acquireServerUpdateLock:c,cleanUpServerUpdateLockOnSignals:d,globalPrefixFromModulePath:f}=require("../packages/core/src/core/server-update-installation.ts");function l(e){const t=i.safeParse(e);return t.success?t.data.data.build.packageVersion:void 0}function u(e){if(!t.isAbsolute(e))throw new r("must be an absolute path");return t.resolve(e)}const p=(new o).name("task-handoff-node-update-worker").description("Apply a detached TaskHandoff node update.").requiredOption("--job-file <path>","persisted update job file").requiredOption("--target-version <version>","exact semantic version",function(e){if(e.trim()!==e||/^[v=]/.test(e)||null===a.valid(e))throw new r("must be an exact semantic version");return e}).option("--service <name>","systemd service to restart","task-handoff-node-agent.service").option("--npm-command <path>","npm executable",process.env.TASK_HANDOFF_NPM_COMMAND||"npm").option("--control-plane-health-url <url>","local control-plane health endpoint",function(e){let t;try{t=new URL(e)}catch{throw new r("must be a valid loopback HTTP URL")}if(!["http:","https:"].includes(t.protocol)||!["127.0.0.1","localhost","::1"].includes(t.hostname))throw new r("must be a valid loopback HTTP URL");return t.toString()}).option("--install-prefix <path>","authoritative npm global prefix",u).option("--node-agent-ipc-path <path>","node-agent readiness socket",u).option("--registry <url>","npm registry URL").option("--standalone","complete the temporary job after service verification").parse(process.argv).opts(),h=p.jobFile,m=p.targetVersion,g=p.service,w=p.npmCommand,y=p.controlPlaneHealthUrl,v=p.installPrefix,S=p.nodeAgentIpcPath,E=p.registry,k=new Set(["succeeded","degraded","failed"]),_=new Set(["@task-handoff/node-agent","@task-handoff/server"]),A=["/etc/task-handoff/control-plane.env","/etc/task-handoff/node-agent.env","/etc/systemd/system/task-handoff-control-plane.service","/etc/systemd/system/task-handoff-node-agent.service"];function T(n,o){const r=JSON.parse(e.readFileSync(h,"utf8"));process.env.TASK_HANDOFF_UPDATE_WORKER_TEST_CAS_HOOK&&require(t.resolve(process.env.TASK_HANDOFF_UPDATE_WORKER_TEST_CAS_HOOK))({jobFile:h,observed:r});const a=`${h}.worker-lock`;e.mkdirSync(a);try{const t=JSON.parse(e.readFileSync(h,"utf8"));if(k.has(t.status)||!n.includes(t.status))return!1;const r="function"==typeof o?o(t):o,a={...t,...r,updatedAt:(new Date).toISOString()};return s.sync(h,`${JSON.stringify(a,null,2)}\n`,{encoding:"utf8"}),!0}finally{e.rmdirSync(a)}}function O(e,t){const o=n(e,t,{stdio:"inherit"});if(0!==o.status)throw new Error(`${e} exited with status ${o.status??"unknown"}`)}function b(n,o,r){const a=r?t.join(o,...r.split("/"),"node_modules",...n.split("/"),"package.json"):t.join(o,...n.split("/"),"package.json");try{return JSON.parse(e.readFileSync(a,"utf8")).version}catch(e){if("ENOENT"===e?.code)return;throw e}}function $(e,t){const n=b(e,t);if(n!==m)throw new Error(`Updated ${e} verification failed: expected ${m}, found ${n||"unknown"}.`)}function R(e){const t=["view",`${e}@${m}`,"dist.integrity","--json"];E&&t.push("--registry",E);const o=n(w,t,{encoding:"utf8"});if(0!==o.status)throw new Error(`Could not verify the ${e} npm artifact integrity.`);let r;try{r=JSON.parse(o.stdout)}catch{throw new Error(`npm returned invalid ${e} artifact integrity metadata.`)}if("string"!=typeof r||!/^sha(?:256|384|512)-[A-Za-z0-9+/=]+$/.test(r))throw new Error(`npm returned invalid ${e} artifact integrity metadata.`);return r}async function N(t,n=m,o=6e4){const r=Date.now()+o;let a="not reachable";for(;Date.now()<r;){try{if(process.env.TASK_HANDOFF_UPDATE_WORKER_TEST_HEALTH_FILE){const t=l(JSON.parse(e.readFileSync(process.env.TASK_HANDOFF_UPDATE_WORKER_TEST_HEALTH_FILE,"utf8")));if(t===n)return;a=`reported version ${String(t||"unknown")}`,await new Promise(e=>setTimeout(e,10));continue}const o=await fetch(t,{headers:{"cache-control":"no-cache"},signal:AbortSignal.timeout(2e3)}),r=l(await o.json().catch(()=>({})));if(o.ok&&r===n)return;a=o.ok?`reported version ${String(r||"unknown")}`:`returned HTTP ${o.status}`}catch(e){a=e instanceof Error?e.message:String(e)}await new Promise(e=>setTimeout(e,500))}throw new Error(`Control plane did not become healthy at ${t} with version ${n}: ${a}.`)}function D(t,n=3e4){const o=Date.now()+n;for(;Date.now()<o;){try{if(e.statSync(t).isSocket())return}catch{}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,250)}throw new Error(`Node agent socket was not ready after ${n}ms: ${t}`)}function F(e,t,n){const o=["install","--global","--prefix",e,`${t}@${n}`];E&&o.push("--registry",E),O(w,o)}!async function(){let o,r,a;try{if(!T(["queued"],e=>({status:"updating-node",rollout:{...e.rollout,phase:"updating-node"},startedAt:(new Date).toISOString(),error:void 0})))return;o=c(process.env.TASK_HANDOFF_SERVER_UPDATE_LOCK_PATH||void 0),r=d(o);const i=function(){const t=JSON.parse(e.readFileSync(h,"utf8")),n=`@${m}#`;if("string"!=typeof t.artifactRef||!t.artifactRef.startsWith("npm:")||!t.artifactRef.includes(n))throw new Error(`Update job does not pin npm integrity for version ${m}.`);const o=t.artifactRef.indexOf(n),r=t.artifactRef.slice(4,o),a=t.artifactRef.slice(o+n.length);if(!_.has(r)||!a)throw new Error("Update job does not identify a supported immutable npm artifact.");const s=R(r);if(s!==a)throw new Error(`${r} npm artifact integrity mismatch: expected ${a}, found ${String(s||"unknown")}.`);return{packageName:r,integrity:s}}(),l=i.packageName,u=f(process.argv[1]);if(v&&u&&v!==u)throw Object.assign(new Error(`Update prefix ${v} does not match the running package prefix ${u}.`),{code:"UPDATE_INSTALL_PREFIX_MISMATCH"});const E=u?void 0:n(w,["prefix","--global"],{encoding:"utf8"});if(E&&0!==E.status)throw new Error("Could not determine the npm global prefix.");const k=v||u||E?.stdout.trim();if(!k)throw new Error("Could not determine the npm global prefix.");const P=n(w,["root","--global","--prefix",k],{encoding:"utf8"});if(0!==P.status)throw new Error("Could not determine the npm global module root.");const U=P.stdout.trim(),H=[l];"@task-handoff/server"===l&&void 0!==b("@task-handoff/node-agent",U)&&H.push("@task-handoff/node-agent");const j=new Map(H.map(e=>[e,b(e,U)])),x="@task-handoff/server"===l?function(){const e=process.env.TASK_HANDOFF_UPDATE_WORKER_TEST_CONFIGURATION_ROOT;return e?A.map(n=>t.join(e,n.slice(1))):A}().map(t=>{try{const n=e.statSync(t);return{file:t,contents:e.readFileSync(t),mode:n.mode}}catch(e){if("ENOENT"===e?.code)return{file:t};throw e}}):[];let I=!1;a=async()=>{for(const e of[...H].reverse()){const t=j.get(e);t&&F(k,e,t)}if("@task-handoff/server"===l){I&&(function(n){for(const o of n)o.contents?(e.mkdirSync(t.dirname(o.file),{recursive:!0}),s.sync(o.file,o.contents,{mode:o.mode})):e.rmSync(o.file,{force:!0})}(x),O("systemctl",["daemon-reload"])),O("systemctl",["restart","task-handoff-control-plane.service"]);const n=j.get("@task-handoff/server");y&&n&&await N(y,n)}O("systemctl",["restart",g]),S&&D(S)};const L=new Map([[l,i.integrity]]);for(const e of H.slice(1))L.set(e,R(e));for(const e of H){if(F(k,e,m),R(e)!==L.get(e))throw new Error(`${e} npm artifact integrity changed during installation.`);$(e,U)}if("@task-handoff/server"===l){if(function(e){for(const t of["@task-handoff/control-plane","@task-handoff/node-agent","@task-handoff/controlled-instance"]){const n=b(t,e,"@task-handoff/server")||b(t,e);if(n!==m)throw new Error(`Updated @task-handoff/server does not provide ${t} ${m}; found ${n||"unknown"}.`)}}(U),!y)throw new Error("A control-plane health URL is required for a complete server update.");I=!0,function(e){O(t.join(e,"bin","task-handoff"),["install","--preserve-current","--materialize-only"])}(k),O("systemctl",["restart","task-handoff-control-plane.service"]),await N(y)}if(!T(["updating-node"],e=>({status:"restarting-node",rollout:{...e.rollout,phase:"restarting-node"}})))return;O("systemctl",["restart",g]),S&&D(S),p.standalone&&T(["restarting-node"],e=>({status:"succeeded",rollout:{...e.rollout,phase:"succeeded",nodeVersion:m},completedAt:(new Date).toISOString()}))}catch(e){let t;if(a)try{await a()}catch(e){t=e}const n=[e instanceof Error?e.message:String(e),t?`Rollback failed: ${t instanceof Error?t.message:String(t)}`:void 0].filter(Boolean).join(" ");T(["queued","updating-node","restarting-node"],t=>({status:"failed",rollout:{...t.rollout,phase:"failed"},error:{code:e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code?e.code:"NODE_UPDATE_FAILED",message:n,retryable:e&&"object"==typeof e&&"code"in e&&"SERVER_UPDATE_ALREADY_RUNNING"===e.code},completedAt:(new Date).toISOString()})),console.error(e),process.exitCode=1}finally{r?.(),o?.()}}();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@task-handoff/node-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25-alpha.1",
|
|
4
4
|
"description": "Prebuilt TaskHandoff node agent runtime.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"node": ">=24.15.0 <25"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@fastify/compress": "9.2.0",
|
|
23
24
|
"@fastify/cookie": "11.1.1",
|
|
24
25
|
"@fastify/static": "8.3.0",
|
|
25
26
|
"@fastify/websocket": "11.2.0",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageName": "@task-handoff/controlled-instance",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25-alpha.1",
|
|
4
4
|
"platform": "linux",
|
|
5
5
|
"arch": "universal",
|
|
6
6
|
"formatVersion": 1,
|
|
7
7
|
"launcherAbi": 1,
|
|
8
8
|
"entrypoint": "dist/controlled-instance-cli.js",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "7e7326d92ad7fe6564ecc9dfa265baa58df37d3195ba619bb422b974d58d4ddb"
|
|
10
10
|
}
|
|
Binary file
|
package/runtime-artifacts/controlled-instance-runtime-0.0.25-alpha.1-linux-universal.tar.gz.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
591d48d559b048f679dba0873d6dae95e9f0999aee4340a0a2baeac1969f4548 controlled-instance-runtime-0.0.25-alpha.1-linux-universal.tar.gz
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a9389708253338ad96c8e15433c3013e3c40f9f967a38e38874fbf52c729841c controlled-instance-runtime-0.0.24-linux-universal.tar.gz
|