@sjawhar/pi-legion-envoy 1.0.0 → 1.1.0

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
@@ -104,7 +104,9 @@ Configure the shared `envoy.json` with:
104
104
 
105
105
  The user file is `~/.config/opencode/envoy.json`; a
106
106
  `<cwd>/.opencode/envoy.json` file shallow-merges over it. `DISPATCH_URL` and
107
- `DISPATCH_TOKEN` override the file values for one process. Omitting
107
+ `DISPATCH_TOKEN` override the file values for one process; `DISPATCH_TOKEN_FILE`
108
+ (a path whose trimmed contents are the token — how the Legion daemon delivers it
109
+ to a pane) wins over both and never falls back when unreadable. Omitting
108
110
  `dispatch.serverUrl` while `dispatch.enabled` is true targets
109
111
  `http://localhost:8766`, the Go server's listen address. Invalid configuration,
110
112
  an invalid URL, or an empty token leaves the twelve tools unavailable and reports
package/dist/envoy.js CHANGED
@@ -31189,7 +31189,7 @@ ${envelope.payload ?? ""}`;
31189
31189
  }
31190
31190
 
31191
31191
  // ../envoy-client/src/dispatch-config.ts
31192
- import { readFileSync } from "fs";
31192
+ import { readFileSync as readFileSync2 } from "fs";
31193
31193
  import { homedir } from "os";
31194
31194
  import * as path from "path";
31195
31195
 
@@ -31198,6 +31198,21 @@ function messageFor(error) {
31198
31198
  return error instanceof Error ? error.message : String(error);
31199
31199
  }
31200
31200
 
31201
+ // ../envoy-client/src/secret-file.ts
31202
+ import { readFileSync } from "fs";
31203
+ function readSecretFile(variable, filePath) {
31204
+ let contents;
31205
+ try {
31206
+ contents = readFileSync(filePath, "utf8");
31207
+ } catch (error) {
31208
+ throw new Error(`${variable} names ${filePath}, which could not be read: ${messageFor(error)}`);
31209
+ }
31210
+ const value = contents.trim();
31211
+ if (value.length === 0)
31212
+ throw new Error(`${variable} names ${filePath}, which is empty`);
31213
+ return value;
31214
+ }
31215
+
31201
31216
  // ../envoy-client/src/dispatch-config.ts
31202
31217
  var DEFAULT_SERVER_URL = "http://localhost:8766";
31203
31218
  function normalizeDispatchUrl(url2) {
@@ -31234,7 +31249,7 @@ function describeSchemaIssue(filePath, error) {
31234
31249
  function readEnvoyFile(filePath) {
31235
31250
  let raw;
31236
31251
  try {
31237
- raw = readFileSync(filePath, "utf-8");
31252
+ raw = readFileSync2(filePath, "utf-8");
31238
31253
  } catch {
31239
31254
  return { kind: "absent" };
31240
31255
  }
@@ -31267,8 +31282,22 @@ function resolveDispatchConfig(env, options = {}) {
31267
31282
  };
31268
31283
  const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
31269
31284
  const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
31270
- const token = env.DISPATCH_TOKEN ?? merged.token ?? null;
31271
- const tokenSource = env.DISPATCH_TOKEN === undefined ? "dispatch.token" : "DISPATCH_TOKEN";
31285
+ let token;
31286
+ let tokenSource;
31287
+ if (env.DISPATCH_TOKEN_FILE !== undefined) {
31288
+ tokenSource = "DISPATCH_TOKEN_FILE";
31289
+ try {
31290
+ token = readSecretFile(tokenSource, env.DISPATCH_TOKEN_FILE);
31291
+ } catch (error) {
31292
+ return { enabled: false, url: url2.url, token: null, error: messageFor(error) };
31293
+ }
31294
+ } else if (env.DISPATCH_TOKEN !== undefined) {
31295
+ tokenSource = "DISPATCH_TOKEN";
31296
+ token = env.DISPATCH_TOKEN;
31297
+ } else {
31298
+ tokenSource = "dispatch.token";
31299
+ token = merged.token ?? null;
31300
+ }
31272
31301
  if (url2.error !== null)
31273
31302
  return { enabled: false, url: null, token, error: url2.error };
31274
31303
  if (url2.url === null)
package/dist/legion.js CHANGED
@@ -30432,6 +30432,21 @@ function messageFor(error) {
30432
30432
  var import_nats2 = __toESM(require_mod4(), 1);
30433
30433
  import { logger as logger2 } from "@oh-my-pi/pi-utils";
30434
30434
 
30435
+ // ../envoy-client/src/secret-file.ts
30436
+ import { readFileSync } from "fs";
30437
+ function readSecretFile(variable, filePath) {
30438
+ let contents;
30439
+ try {
30440
+ contents = readFileSync(filePath, "utf8");
30441
+ } catch (error) {
30442
+ throw new Error(`${variable} names ${filePath}, which could not be read: ${messageFor(error)}`);
30443
+ }
30444
+ const value = contents.trim();
30445
+ if (value.length === 0)
30446
+ throw new Error(`${variable} names ${filePath}, which is empty`);
30447
+ return value;
30448
+ }
30449
+
30435
30450
  // src/legion/classify.ts
30436
30451
  function classifySession(env) {
30437
30452
  if (env.LEGION_CONTROLLER !== undefined && env.LEGION_TREE !== undefined) {
@@ -30457,10 +30472,20 @@ function requiredEnvironment(env, key) {
30457
30472
  throw new Error(`${key} is required for Legion`);
30458
30473
  return value;
30459
30474
  }
30475
+ function requiredSecret(env, key) {
30476
+ const fileKey = `${key}_FILE`;
30477
+ const file = env[fileKey];
30478
+ if (file !== undefined)
30479
+ return readSecretFile(fileKey, file);
30480
+ return requiredEnvironment(env, key);
30481
+ }
30460
30482
  function requiredControllerCapability(env) {
30483
+ if (env.LEGION_CONTROLLER_SECRET_FILE !== undefined) {
30484
+ return readSecretFile("LEGION_CONTROLLER_SECRET_FILE", env.LEGION_CONTROLLER_SECRET_FILE);
30485
+ }
30461
30486
  const secret = env.LEGION_CONTROLLER_SECRET;
30462
30487
  if (!secret) {
30463
- throw new Error("LEGION_CONTROLLER_SECRET is required to claim the controller. " + "Launch OMP with LEGION_CONTROLLER_SECRET in its environment before running " + "/legion-claim-controller.");
30488
+ throw new Error("LEGION_CONTROLLER_SECRET or LEGION_CONTROLLER_SECRET_FILE is required to claim the controller. " + "Launch OMP with one of them in its environment before running /legion-claim-controller.");
30464
30489
  }
30465
30490
  return secret;
30466
30491
  }
@@ -31612,7 +31637,7 @@ ${envelope.payload ?? ""}`;
31612
31637
  }
31613
31638
 
31614
31639
  // ../envoy-client/src/dispatch-config.ts
31615
- import { readFileSync } from "fs";
31640
+ import { readFileSync as readFileSync2 } from "fs";
31616
31641
  import { homedir } from "os";
31617
31642
  import * as path3 from "path";
31618
31643
  var DEFAULT_SERVER_URL = "http://localhost:8766";
@@ -31650,7 +31675,7 @@ function describeSchemaIssue(filePath, error) {
31650
31675
  function readEnvoyFile(filePath) {
31651
31676
  let raw;
31652
31677
  try {
31653
- raw = readFileSync(filePath, "utf-8");
31678
+ raw = readFileSync2(filePath, "utf-8");
31654
31679
  } catch {
31655
31680
  return { kind: "absent" };
31656
31681
  }
@@ -31683,8 +31708,22 @@ function resolveDispatchConfig(env, options = {}) {
31683
31708
  };
31684
31709
  const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
31685
31710
  const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
31686
- const token = env.DISPATCH_TOKEN ?? merged.token ?? null;
31687
- const tokenSource = env.DISPATCH_TOKEN === undefined ? "dispatch.token" : "DISPATCH_TOKEN";
31711
+ let token;
31712
+ let tokenSource;
31713
+ if (env.DISPATCH_TOKEN_FILE !== undefined) {
31714
+ tokenSource = "DISPATCH_TOKEN_FILE";
31715
+ try {
31716
+ token = readSecretFile(tokenSource, env.DISPATCH_TOKEN_FILE);
31717
+ } catch (error) {
31718
+ return { enabled: false, url: url2.url, token: null, error: messageFor(error) };
31719
+ }
31720
+ } else if (env.DISPATCH_TOKEN !== undefined) {
31721
+ tokenSource = "DISPATCH_TOKEN";
31722
+ token = env.DISPATCH_TOKEN;
31723
+ } else {
31724
+ tokenSource = "dispatch.token";
31725
+ token = merged.token ?? null;
31726
+ }
31688
31727
  if (url2.error !== null)
31689
31728
  return { enabled: false, url: null, token, error: url2.error };
31690
31729
  if (url2.url === null)
@@ -33985,7 +34024,7 @@ function legionExtension(pi) {
33985
34024
  return createLegionDaemonClient(requiredEnvironment(process.env, "LEGION_DAEMON_URL"), fetch, {
33986
34025
  recoveryToken: (sessionId) => {
33987
34026
  if (capability !== undefined && sessionId === capability.sessionID) {
33988
- return requiredEnvironment(process.env, "LEGION_BOOT_TOKEN");
34027
+ return requiredSecret(process.env, "LEGION_BOOT_TOKEN");
33989
34028
  }
33990
34029
  throw new Error(`Legion session ${sessionId} has no persisted recovery token`);
33991
34030
  },
@@ -34068,7 +34107,7 @@ function legionExtension(pi) {
34068
34107
  return;
34069
34108
  if (bootstrap)
34070
34109
  return bootstrap;
34071
- const bootToken = requiredEnvironment(process.env, "LEGION_BOOT_TOKEN");
34110
+ const bootToken = requiredSecret(process.env, "LEGION_BOOT_TOKEN");
34072
34111
  bootstrap = (async () => {
34073
34112
  const { sessionFile, agentId } = await persistedTranscript(context);
34074
34113
  const started = await (async () => {
@@ -34135,7 +34174,7 @@ function legionExtension(pi) {
34135
34174
  return;
34136
34175
  if (bootstrap)
34137
34176
  return bootstrap;
34138
- const bootToken = requiredEnvironment(process.env, "LEGION_BOOT_TOKEN");
34177
+ const bootToken = requiredSecret(process.env, "LEGION_BOOT_TOKEN");
34139
34178
  const workspace = requiredEnvironment(process.env, "LEGION_WORKSPACE");
34140
34179
  bootstrap = (async () => {
34141
34180
  const { sessionFile, agentId } = await persistedTranscript(context);
@@ -18,8 +18,9 @@ until that PR merges, this skill's contract is not yet runnable on `main`.
18
18
  The Legion extension claims `legion-<project>-controller` and registers controller readiness
19
19
  with the daemon during session startup. Do not handle a wake unless that startup succeeded.
20
20
 
21
- For an interactive takeover, start OMP with `LEGION_CONTROLLER_SECRET` and
22
- `LEGION_DAEMON_URL` in its environment, then run:
21
+ For an interactive takeover, start OMP with `LEGION_CONTROLLER_SECRET` (or
22
+ `LEGION_CONTROLLER_SECRET_FILE`, a path to a file holding it) and `LEGION_DAEMON_URL` in its
23
+ environment, then run:
23
24
 
24
25
  ```text
25
26
  /legion-claim-controller
@@ -19,8 +19,9 @@ until that PR merges, this skill's contract is not yet runnable on `main`.
19
19
 
20
20
  The daemon spawns you as a separate `omp --mode rpc` process (behind `legion worker-shim`,
21
21
  in a tmux pane) with `LEGION_TREE`, `LEGION_ISSUE`, `LEGION_ROLE`, `LEGION_GENERATION`,
22
- `LEGION_BOOT_TOKEN`, `LEGION_DAEMON_URL`, `LEGION_STATE_DIR`, and `LEGION_WORKSPACE` in your
23
- environment (`LEGION_PROJECT` is also supplied, but nothing reads it). The extension
22
+ `LEGION_BOOT_TOKEN_FILE` (a 0600 file under `$LEGION_STATE_DIR/secrets` holding your boot token;
23
+ the extension reads it for you), `LEGION_DAEMON_URL`, `LEGION_STATE_DIR`, and `LEGION_WORKSPACE`
24
+ in your environment (`LEGION_PROJECT` is also supplied, but nothing reads it). The extension
24
25
  completes the boot handshake for you at session start — it registers with the daemon, claims
25
26
  your role, and signals readiness. You never call `envoy_role_set` yourself.
26
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [