orkestrator 2.34.4-beta → 2.34.5-beta

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 CHANGED
@@ -57,6 +57,10 @@ bunx orkestrator \
57
57
  --allow-non-tailscale-bind
58
58
  ```
59
59
 
60
+ Only the default data directory's backend defaults to port 34121. With another
61
+ `--data-dir` and no `--port`, the backend takes a free port that changes on each
62
+ start, so pass `--port` if remote clients need a stable address.
63
+
60
64
  Or install Bun when needed and start the service in one command:
61
65
 
62
66
  ```bash
@@ -94,6 +98,14 @@ points at the installed set and the service uses it by default for that data
94
98
  directory — restart a running service to pick up a new one. Pass `--data-dir`
95
99
  when the service does, and see `orkestrator toolchain install --help`.
96
100
 
101
+ ## Cursor and Pi
102
+
103
+ The Cursor and Pi bridges ship without their SDKs, which are too large or too
104
+ platform-specific to bundle. The first time one starts, the backend installs
105
+ the SDK it pins into `<data-dir>/bridge-runtimes/` with
106
+ `bun install --frozen-lockfile`, so that first start needs access to the npm
107
+ registry. Later starts, and restarts, reuse it.
108
+
97
109
  ## Client commands
98
110
 
99
111
  Select the backend explicitly. A running backend publishes a private
@@ -9,7 +9,10 @@
9
9
  * - A release that has not reached readiness (a backend-instance.json naming
10
10
  * its process and version) gets two boots. After that the launcher points
11
11
  * `current` back at `previous` and records the rollback, which the restored
12
- * backend reports as the outcome of its update.
12
+ * backend reports as the outcome of its update. A release restored this way
13
+ * runs with the serve arguments it had before the install that replaced it.
14
+ * - Each boot that exits without reaching readiness is recorded, so
15
+ * `service install` can stop waiting and report it.
13
16
  * - When the backend exits after writing `restart-requested` (its self-update
14
17
  * just switched `current`), the launcher starts the new release at once.
15
18
  * - SIGTERM and SIGINT are forwarded; the launcher then exits with the
@@ -69,10 +72,16 @@ function readState() {
69
72
  const value = readJson(layout.stateFile) ?? {};
70
73
  return {
71
74
  version: 1,
75
+ ...(service.installGeneration ? { installGeneration: service.installGeneration } : {}),
72
76
  lastReadyVersion: typeof value.lastReadyVersion === "string" ? value.lastReadyVersion : null,
73
77
  bootAttempts:
74
- value.bootAttempts && typeof value.bootAttempts === "object" ? value.bootAttempts : {},
78
+ (!service.installGeneration || value.installGeneration === service.installGeneration) &&
79
+ value.bootAttempts &&
80
+ typeof value.bootAttempts === "object"
81
+ ? value.bootAttempts
82
+ : {},
75
83
  rollback: value.rollback ?? null,
84
+ bootFailure: value.bootFailure ?? null,
76
85
  };
77
86
  }
78
87
 
@@ -109,8 +118,21 @@ if (!service || typeof service.bun !== "string" || !Array.isArray(service.args))
109
118
  process.exit(1);
110
119
  }
111
120
 
121
+ /**
122
+ * After a rollback `current` and `previous` name the same release. Only then do
123
+ * the arguments recorded for it apply: the same version reached by a later
124
+ * self-update runs with the service's own.
125
+ */
126
+ function serveArguments(version, previousVersion) {
127
+ const target = service.rollbackTarget;
128
+ return target?.version === version && previousVersion === version && Array.isArray(target.args)
129
+ ? target.args
130
+ : service.args;
131
+ }
132
+
112
133
  let child = null;
113
134
  let stopping = false;
135
+ let failedBoots = { version: null, exits: 0 };
114
136
  for (const signal of ["SIGTERM", "SIGINT"]) {
115
137
  process.on(signal, () => {
116
138
  stopping = true;
@@ -150,6 +172,7 @@ while (!stopping) {
150
172
  pointLink(layout.current, previousTarget);
151
173
  state.bootAttempts[version] = 0;
152
174
  state.rollback = {
175
+ ...(service.installGeneration ? { installGeneration: service.installGeneration } : {}),
153
176
  failedVersion: version,
154
177
  restoredVersion: previousVersion,
155
178
  at: new Date().toISOString(),
@@ -172,9 +195,10 @@ while (!stopping) {
172
195
  rmSync(layout.restartMarker, { force: true });
173
196
  log(`starting ${version}`);
174
197
  let exited = false;
198
+ const serveArgs = serveArguments(version, path.basename(linked(layout.previous) ?? ""));
175
199
  child = spawn(
176
200
  service.bun,
177
- [entry, "serve", ...service.args, "--data-dir", dataDir, "--standalone-updater", root],
201
+ [entry, "serve", ...serveArgs, "--data-dir", dataDir, "--standalone-updater", root],
178
202
  { stdio: "inherit", env: process.env },
179
203
  );
180
204
  const exit = new Promise((resolve) =>
@@ -188,7 +212,9 @@ while (!stopping) {
188
212
  const next = readState();
189
213
  next.lastReadyVersion = version;
190
214
  next.bootAttempts[version] = 0;
215
+ next.bootFailure = null;
191
216
  writeState(next);
217
+ failedBoots = { version: null, exits: 0 };
192
218
  log(`${version} is ready`);
193
219
  }
194
220
  if (!ready && !exited) {
@@ -212,5 +238,18 @@ while (!stopping) {
212
238
  continue;
213
239
  }
214
240
  log(`${version} exited (code ${code ?? "none"}, signal ${signal ?? "none"})`);
241
+ if (!ready) {
242
+ failedBoots = {
243
+ version,
244
+ exits: failedBoots.version === version ? failedBoots.exits + 1 : 1,
245
+ };
246
+ const next = readState();
247
+ next.bootFailure = {
248
+ ...failedBoots,
249
+ ...(service.installGeneration ? { installGeneration: service.installGeneration } : {}),
250
+ at: new Date().toISOString(),
251
+ };
252
+ writeState(next);
253
+ }
215
254
  await sleep(ready ? CRASH_DELAY_MS : RETRY_DELAY_MS);
216
255
  }