@ouro.bot/cli 0.1.0-alpha.761 → 0.1.0-alpha.763

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/changelog.json CHANGED
@@ -1,6 +1,18 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.763",
6
+ "changes": [
7
+ "Keep the Unit 16 host broker response channel open until asynchronous operations return a bounded success or failure envelope."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.762",
12
+ "changes": [
13
+ "Wait for the exact audited Sanctuary container and process to recover health before Unit 16 acceptance continues."
14
+ ]
15
+ },
4
16
  {
5
17
  "version": "0.1.0-alpha.761",
6
18
  "changes": [
@@ -1707,21 +1707,9 @@ function writeClosedInventory(file) {
1707
1707
  chownSync(file, 0, 0)
1708
1708
  }
1709
1709
 
1710
- async function main() {
1711
- if (process.getuid?.() !== 0) throw new Error("host broker must run as root")
1712
- const [profileName, targetContainerId, socket, closedInventory, expectedImage, initialSnapshot] = process.argv.slice(2)
1713
- if (!profileName || !targetContainerId || !socket || !closedInventory || !expectedImage || !initialSnapshot || process.argv.length !== 8) throw new Error("usage: broker <staging|final> <target-container-id> <socket> <closed-inventory> <expected-image-id> <initial-snapshot>")
1714
- activeProfile = targetProfile(profileName)
1715
- activeContainer = activeProfile.containerName
1716
- activeContainerId = text(targetContainerId, "attested target container id", SHA256)
1717
- expectedImageId = text(expectedImage, "expected image id", IMAGE_ID)
1718
- rmSync(socket, { force: true })
1719
- writeClosedInventory(closedInventory)
1720
- const snapshot = await containerSnapshot(expectedImageId)
1721
- const snapshotFd = openSync(initialSnapshot, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600)
1722
- try { writeFileSync(snapshotFd, `${JSON.stringify(snapshot)}\n`); fsyncSync(snapshotFd) } finally { closeSync(snapshotFd) }
1723
- chownSync(initialSnapshot, 0, 0)
1724
- const server = createServer((connection) => {
1710
+ function createBrokerServer(dispatchRequest = dispatch) {
1711
+ const dispatchDrain = createDispatchDrain()
1712
+ const server = createServer({ allowHalfOpen: true }, (connection) => {
1725
1713
  let input = ""
1726
1714
  connection.setEncoding("utf8")
1727
1715
  connection.on("data", (chunk) => {
@@ -1730,14 +1718,31 @@ async function main() {
1730
1718
  })
1731
1719
  connection.on("end", async () => {
1732
1720
  try {
1733
- const result = await dispatchDrain.run(() => dispatch(JSON.parse(input)))
1721
+ const result = await dispatchDrain.run(() => dispatchRequest(JSON.parse(input)))
1734
1722
  const scenario = result?.[ASYNC_RESTART_SCENARIO]
1735
1723
  const completed = scenario ? armRestartAfterResponseClosed(connection, scenario) : undefined
1736
1724
  connection.end(`${JSON.stringify({ ok: true, result })}\n`, completed)
1737
1725
  } catch { connection.end(`${JSON.stringify({ ok: false, error: "host operation failed" })}\n`) }
1738
1726
  })
1739
1727
  })
1740
- const dispatchDrain = createDispatchDrain()
1728
+ return { server, dispatchDrain }
1729
+ }
1730
+
1731
+ async function main() {
1732
+ if (process.getuid?.() !== 0) throw new Error("host broker must run as root")
1733
+ const [profileName, targetContainerId, socket, closedInventory, expectedImage, initialSnapshot] = process.argv.slice(2)
1734
+ if (!profileName || !targetContainerId || !socket || !closedInventory || !expectedImage || !initialSnapshot || process.argv.length !== 8) throw new Error("usage: broker <staging|final> <target-container-id> <socket> <closed-inventory> <expected-image-id> <initial-snapshot>")
1735
+ activeProfile = targetProfile(profileName)
1736
+ activeContainer = activeProfile.containerName
1737
+ activeContainerId = text(targetContainerId, "attested target container id", SHA256)
1738
+ expectedImageId = text(expectedImage, "expected image id", IMAGE_ID)
1739
+ rmSync(socket, { force: true })
1740
+ writeClosedInventory(closedInventory)
1741
+ const snapshot = await containerSnapshot(expectedImageId)
1742
+ const snapshotFd = openSync(initialSnapshot, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600)
1743
+ try { writeFileSync(snapshotFd, `${JSON.stringify(snapshot)}\n`); fsyncSync(snapshotFd) } finally { closeSync(snapshotFd) }
1744
+ chownSync(initialSnapshot, 0, 0)
1745
+ const { server, dispatchDrain } = createBrokerServer()
1741
1746
  server.listen(socket, () => {
1742
1747
  chownSync(socket, 0, 10001)
1743
1748
  chmodSync(socket, 0o660)
@@ -1769,6 +1774,7 @@ export {
1769
1774
  armRestartAfterResponseClosed,
1770
1775
  completeHealthProbeFromReceipt,
1771
1776
  createDispatchDrain,
1777
+ createBrokerServer,
1772
1778
  createHealthProbeOperationCoordinator,
1773
1779
  createOwnerMutationCoordinator,
1774
1780
  createInteractiveRestartDriver,
@@ -129,14 +129,18 @@ start_broker() {
129
129
  /usr/local/bin/node "$TARGET_AUDITOR" "$TARGET_PROFILE" "$IMAGE_ID" >"$PRIVATE_ROOT/deployment-target.json"
130
130
  chmod 0400 "$PRIVATE_ROOT/deployment-target.json"
131
131
  chown 0:0 "$PRIVATE_ROOT/deployment-target.json"
132
- TARGET_CONTAINER_ID=$(/usr/local/bin/node -e '
132
+ TARGET_CONTAINER_BINDING=$(/usr/local/bin/node -e '
133
133
  const fs = require("node:fs"); const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); const deployment = value && value.deployment;
134
134
  if (value?.schemaVersion !== "sanctuary-effective-deployment-v1" || deployment?.schemaVersion !== "sanctuary-deployment-target-v1"
135
135
  || deployment.profile !== process.argv[3] || deployment.targetContainerName !== process.argv[4]
136
- || deployment.targetImageId !== process.argv[2] || !/^[0-9a-f]{64}$/.test(deployment.targetContainerId)) process.exit(1);
137
- process.stdout.write(deployment.targetContainerId);
136
+ || deployment.targetImageId !== process.argv[2] || !/^[0-9a-f]{64}$/.test(deployment.targetContainerId)
137
+ || !Number.isSafeInteger(deployment.targetPid) || deployment.targetPid <= 0 || deployment.targetPid > 4_194_304) process.exit(1);
138
+ process.stdout.write(`${deployment.targetContainerId} ${deployment.targetPid}`);
138
139
  ' "$PRIVATE_ROOT/deployment-target.json" "$IMAGE_ID" "$TARGET_PROFILE" "$PRODUCTION_CONTAINER")
139
- test -n "$TARGET_CONTAINER_ID" || return 1
140
+ set -- $TARGET_CONTAINER_BINDING
141
+ test "$#" -eq 2 || return 1
142
+ TARGET_CONTAINER_ID=$1
143
+ TARGET_CONTAINER_PID=$2
140
144
  /usr/local/bin/node "$BROKER_PROGRAM" "$TARGET_PROFILE" "$TARGET_CONTAINER_ID" "$BROKER_SOCKET" "$CLOSED_INVENTORY" "$IMAGE_ID" "$BROKER_SNAPSHOT" </dev/null >/dev/null 2>&1 &
141
145
  BROKER_PID=$!
142
146
  ATTEMPT=0
@@ -149,6 +153,26 @@ start_broker() {
149
153
  test "$(stat -c '%u:%g %a' "$BROKER_SOCKET")" = "0:10001 660" || return 1
150
154
  test "$(stat -c '%u:%g %a' "$CLOSED_INVENTORY")" = "0:0 600" || return 1
151
155
  test "$(stat -c '%u:%g %a' "$BROKER_SNAPSHOT")" = "0:0 600" || return 1
156
+ await_post_audit_health
157
+ refresh_live_facts
158
+ }
159
+
160
+ await_post_audit_health() {
161
+ POST_AUDIT_ATTEMPT=0
162
+ while test "$POST_AUDIT_ATTEMPT" -lt 120; do
163
+ POST_AUDIT_STATE=$(/usr/bin/timeout -s KILL 20 /usr/bin/docker inspect --format '{{.Id}} {{.Name}} {{.Image}} {{.State.Running}} {{.State.Paused}} {{.State.Restarting}} {{.State.Dead}} {{.State.Pid}} {{if .State.Health}}{{.State.Health.Status}}{{else}}missing{{end}}' "$TARGET_CONTAINER_ID") || return 1
164
+ set -- $POST_AUDIT_STATE
165
+ test "$#" -eq 9 || return 1
166
+ test "$1" = "$TARGET_CONTAINER_ID" && test "$2" = "/$PRODUCTION_CONTAINER" && test "$3" = "$IMAGE_ID" || return 1
167
+ test "$4" = true && test "$6" = false && test "$7" = false || return 1
168
+ test "$8" = "$TARGET_CONTAINER_PID" || return 1
169
+ if test "$5" = false && test "$9" = healthy; then return 0; fi
170
+ test "$5" = true || test "$5" = false || return 1
171
+ test "$9" = starting || test "$9" = unhealthy || return 1
172
+ POST_AUDIT_ATTEMPT=$((POST_AUDIT_ATTEMPT + 1))
173
+ sleep 1
174
+ done
175
+ return 1
152
176
  }
153
177
 
154
178
  prepare_live_facts() {
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.761",
2
+ "runtimeVersion": "0.1.0-alpha.763",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-08-30T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>Mendelow Cloud Butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.761</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.763</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.761",
3
+ "version": "0.1.0-alpha.763",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.761",
9
+ "version": "0.1.0-alpha.763",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.761",
3
+ "version": "0.1.0-alpha.763",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },