sessionpipe 0.4.0 → 0.5.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.
@@ -0,0 +1,31 @@
1
+ export declare const LAUNCHER = "sessionpipe-hook";
2
+ export declare function dataDir(env?: NodeJS.ProcessEnv): string;
3
+ export declare const launcherPath: (env?: NodeJS.ProcessEnv) => string;
4
+ export declare const usesLauncher: (platform?: NodeJS.Platform) => boolean;
5
+ /** The launcher, in sh. No node at all prints what Antigravity needs and exits 0: the
6
+ * hook must stay invisible to the harness whatever is missing. */
7
+ export declare function launcherScript(node: string, hook: string, version: string): string;
8
+ /** Copy the hook and worker out of the npm install and (re)write the launcher. The
9
+ * worker goes first, so a hook that starts mid-copy never spawns a missing one. */
10
+ export declare function installLauncher(o: {
11
+ distDir: string;
12
+ node: string;
13
+ version: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ }): {
16
+ launcher: string;
17
+ changed: boolean;
18
+ };
19
+ export declare function removeLauncher(env?: NodeJS.ProcessEnv): void;
20
+ export interface LauncherState {
21
+ file: string;
22
+ present: boolean;
23
+ /** The node the launcher names, and whether it can still run. */
24
+ node: string | null;
25
+ nodeOk: boolean;
26
+ version: string | null;
27
+ problems: string[];
28
+ }
29
+ /** What `doctor` says about the launcher. */
30
+ export declare function launcherState(version: string, env?: NodeJS.ProcessEnv): LauncherState;
31
+ //# sourceMappingURL=launcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAG3C,wBAAgB,OAAO,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGpE;AACD,eAAO,MAAM,YAAY,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,MAA2C,CAAC;AAChH,eAAO,MAAM,YAAY,GAAI,WAAU,MAAM,CAAC,QAA2B,KAAG,OAA+B,CAAC;AAI5G;mEACmE;AACnE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CASlF;AAkBD;oFACoF;AACpF,wBAAgB,eAAe,CAAC,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GAAG;IAC/G,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB,CASA;AAED,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAEzE;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,CAwClG"}
package/dist/run.d.ts CHANGED
@@ -20,7 +20,9 @@ export declare function sweepJobs(state: string, olderThanMs?: number, limit?: n
20
20
  export declare function log(state: string, line: string): void;
21
21
  /** <state>/facts/<harness>/<session>.json — what the machine learned about a session. */
22
22
  export declare function factsState(state: string): FactsState;
23
- export declare function buildSinks(sinks: SinkConfig[]): Sink[];
23
+ export declare function buildSinks(sinks: SinkConfig[], o?: {
24
+ locked?: (name: string) => void;
25
+ }): Sink[];
24
26
  /** Run one hook job end to end: events → outbox → sinks. Returns what happened. */
25
27
  export declare function runJob(job: Job, opts?: {
26
28
  state?: string;
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,UAAU,EAMf,MAAM,EAIN,KAAK,IAAI,EACT,KAAK,UAAU,EAOhB,MAAM,mBAAmB,CAAC;AAqB3B,QAAA,MAAM,OAAO,QAAkF,CAAC;AAEhG,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAM1D;AAED;;;2BAG2B;AAC3B,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,SAAO,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB9F;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASrD;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAkBpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,CAWtD;AAED,mFAAmF;AACnF,wBAAsB,MAAM,CAC1B,GAAG,EAAE,GAAG,EACR,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,OAAO,CAAC;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CA6GjE;AAKD;;;;;4DAK4D;AAC5D,wBAAsB,KAAK,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,UAAU,EAAE,EACzB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmEjC;AAaD,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,eAAO,MAAM,WAAW,QAAO,MAAkC,CAAC;AAClE,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAAkC,CAAC;AAC3E,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,OAAqC,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,UAAU,EAMf,MAAM,EAIN,KAAK,IAAI,EACT,KAAK,UAAU,EAOhB,MAAM,mBAAmB,CAAC;AAsB3B,QAAA,MAAM,OAAO,QAAkF,CAAC;AAEhG,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAM1D;AAED;;;2BAG2B;AAC3B,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,SAAO,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB9F;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASrD;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAkBpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,GAAE;IAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAAG,IAAI,EAAE,CAkBnG;AAED,mFAAmF;AACnF,wBAAsB,MAAM,CAC1B,GAAG,EAAE,GAAG,EACR,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,OAAO,CAAC;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CA6GjE;AAKD;;;;;4DAK4D;AAC5D,wBAAsB,KAAK,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,UAAU,EAAE,EACzB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAqEjC;AAaD,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,eAAO,MAAM,WAAW,QAAO,MAAkC,CAAC;AAClE,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAAkC,CAAC;AAC3E,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,OAAqC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Homebrew's versioned Cellar path dies on `brew upgrade node`; its stable links survive. */
2
+ export declare function stableNode(exe: string): string;
3
+ export declare function viaNpx(distDir: string): boolean;
4
+ /** Install the running version persistently and hand off the original command before side effects. */
5
+ export declare function rerunGlobally(argv: string[], out: (s: string) => void): void;
6
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAQA,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAe9C;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,sGAAsG;AACtG,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAwC5E"}
@@ -0,0 +1,39 @@
1
+ import { type SinkConfig } from "@sessionpipe/core";
2
+ export type SecretStore = "keychain" | "secret-service";
3
+ export type StoreName = SecretStore | "file";
4
+ export type Runner = (cmd: string, args: string[], input?: string) => {
5
+ status: number | null;
6
+ stdout: string;
7
+ };
8
+ export declare const defaultRunner: Runner;
9
+ /** A sink's item. The machine has one config, so a sink's name is enough — except
10
+ * where SESSIONPIPE_CONFIG points at another (a second profile, a test): then the
11
+ * item names that config too, so two configs' sinks of one name never share a key. */
12
+ export declare const sinkAccount: (name: string, what?: "token" | "secret") => string;
13
+ export declare const controlAccount: (machine: string) => string;
14
+ export declare function secretGet(store: SecretStore, account: string, run?: Runner): string | null;
15
+ export declare function secretSet(store: SecretStore, account: string, value: string, run?: Runner): boolean;
16
+ export declare function secretDel(store: SecretStore, account: string, run?: Runner): void;
17
+ /** Can this process write, read back and delete an item in the store right now? */
18
+ export declare function probe(store: SecretStore, run?: Runner): boolean;
19
+ /** The best place for secrets on this machine, from this session. SESSIONPIPE_SECRETS
20
+ * (file | keychain | secret-service) overrides. The Secret Service is used only in a
21
+ * desktop session: on a server its keyring is locked whenever nobody is logged in,
22
+ * which is exactly when the daemon needs its token. */
23
+ export declare function bestStore(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, run?: Runner): StoreName;
24
+ export declare const storeLabel: (s: StoreName | undefined) => string;
25
+ /** The sink with its token and secret filled in from its store. Null when they live in
26
+ * a store this process can't read right now (a keychain locked in an ssh session):
27
+ * the caller leaves the events in the outbox rather than sending them unsigned. */
28
+ export declare function sinkWithSecrets(s: SinkConfig, run?: Runner): SinkConfig | null;
29
+ /** Store a sink's secrets in `to` (or back in the file) and save the config. */
30
+ export declare function moveSinkSecrets(to: StoreName, o?: {
31
+ run?: Runner;
32
+ file?: string;
33
+ }): {
34
+ moved: string[];
35
+ kept: string[];
36
+ };
37
+ /** Forget a sink's secrets wherever they are (sink remove, uninstall). */
38
+ export declare function dropSinkSecrets(s: SinkConfig, run?: Runner): void;
39
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAcA,OAAO,EAA0B,KAAK,UAAU,EAAe,MAAM,mBAAmB,CAAC;AAEzF,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAC;AACxD,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAI7C,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAChH,eAAO,MAAM,aAAa,EAAE,MAQ3B,CAAC;AAEF;;uFAEuF;AACvF,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,OAAM,OAAO,GAAG,QAAkB,KAAG,MAK9E,CAAC;AACF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAA8B,CAAC;AAQhF,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAQzG;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAalH;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAGhG;AAED,mFAAmF;AACnF,wBAAgB,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAK9E;AAED;;;wDAGwD;AACxD,wBAAgB,SAAS,CACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,GAAG,GAAE,MAAsB,GAC1B,SAAS,CAaX;AAED,eAAO,MAAM,UAAU,GAAI,GAAG,SAAS,GAAG,SAAS,KAAG,MAC2D,CAAC;AAElH;;oFAEoF;AACpF,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,GAAE,MAAsB,GAAG,UAAU,GAAG,IAAI,CAM7F;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAC7B,EAAE,EAAE,SAAS,EACb,CAAC,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CA4CrC;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAIhF"}
package/dist/worker.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // sessionpipe 0.4.0 · Apache-2.0 · https://sessionpipe.org
2
+ // sessionpipe 0.5.0 · Apache-2.0 · https://sessionpipe.org
3
3
 
4
4
  // src/worker.ts
5
5
  import { readFileSync as readFileSync9, unlinkSync as unlinkSync5 } from "node:fs";
@@ -243,7 +243,7 @@ function readAntigravity(lines, from) {
243
243
  // ../core/src/adapters/claude-shaped.ts
244
244
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
245
245
  import path3 from "node:path";
246
- var OURS = /sessionpipe[^"\n]*[\\/]hook\.js"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
246
+ var OURS = /(?:sessionpipe[^"\n]*[\\/]hook\.js|[\\/]sessionpipe-hook)"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
247
247
  var isOurs = (h) => Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === "string" && OURS.test(x.command));
248
248
  function readJsonFile(file) {
249
249
  let raw;
@@ -1210,7 +1210,7 @@ import path6 from "node:path";
1210
1210
  // ../core/src/adapters/codex-config.ts
1211
1211
  var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?$/m;
1212
1212
  var NOTIFY_KEY = /^[ \t]*notify[ \t]*=/m;
1213
- var OURS2 = /hook\.js",\s*"codex",\s*"notify"/;
1213
+ var OURS2 = /(?:hook\.js|sessionpipe-hook)",\s*"codex",\s*"notify"/;
1214
1214
  function scan(line, st) {
1215
1215
  let i = 0;
1216
1216
  while (i < line.length) {
@@ -2618,6 +2618,37 @@ var HttpSink = class {
2618
2618
  }
2619
2619
  };
2620
2620
 
2621
+ // src/secrets.ts
2622
+ import { spawnSync } from "node:child_process";
2623
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
2624
+ var SERVICE = "sessionpipe";
2625
+ var defaultRunner = (cmd, args, input) => {
2626
+ const r = spawnSync(cmd, args, {
2627
+ input: input ?? "",
2628
+ encoding: "utf8",
2629
+ timeout: 8e3,
2630
+ stdio: ["pipe", "pipe", "ignore"]
2631
+ });
2632
+ return { status: r.error ? null : r.status, stdout: r.stdout ?? "" };
2633
+ };
2634
+ var sinkAccount = (name, what = "token") => {
2635
+ const scope = process.env.SESSIONPIPE_CONFIG ? `@${createHash4("sha256").update(configFile()).digest("hex").slice(0, 8)}` : "";
2636
+ return `sink:${name}:${what}${scope}`;
2637
+ };
2638
+ function secretGet(store, account, run = defaultRunner) {
2639
+ const r = store === "keychain" ? run("security", ["find-generic-password", "-s", SERVICE, "-a", account, "-w"]) : run("secret-tool", ["lookup", "service", SERVICE, "account", account]);
2640
+ if (r.status !== 0) return null;
2641
+ const v = r.stdout.replace(/\r?\n$/, "");
2642
+ return v || null;
2643
+ }
2644
+ function sinkWithSecrets(s, run = defaultRunner) {
2645
+ if (!s.token_in) return s;
2646
+ const token = secretGet(s.token_in, sinkAccount(s.name), run);
2647
+ if (!token) return null;
2648
+ const secret = secretGet(s.token_in, sinkAccount(s.name, "secret"), run);
2649
+ return { ...s, token, ...secret ? { secret } : {} };
2650
+ }
2651
+
2621
2652
  // src/run.ts
2622
2653
  async function tellDaemon(state, job2, session, transcript) {
2623
2654
  const sock = socketPath(state);
@@ -2698,10 +2729,15 @@ function factsState(state) {
2698
2729
  }
2699
2730
  };
2700
2731
  }
2701
- function buildSinks(sinks) {
2732
+ function buildSinks(sinks, o = {}) {
2702
2733
  const out = [];
2703
- for (const s of sinks) {
2704
- if (s.paused) continue;
2734
+ for (const raw of sinks) {
2735
+ if (raw.paused) continue;
2736
+ const s = sinkWithSecrets(raw);
2737
+ if (!s) {
2738
+ o.locked?.(raw.name);
2739
+ continue;
2740
+ }
2705
2741
  const tier = Math.min(s.tier, s.max_tier ?? 3);
2706
2742
  if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
2707
2743
  else if (s.url.startsWith("file:"))
@@ -2810,7 +2846,9 @@ var BATCH = 50;
2810
2846
  var MAX_PER_RUN = 500;
2811
2847
  async function flush(state, sinkConfigs, outbox, first) {
2812
2848
  const delivered = {};
2813
- const sinks = buildSinks(sinkConfigs);
2849
+ const sinks = buildSinks(sinkConfigs, {
2850
+ locked: (name) => log(state, `sink ${name}: its token is in a keychain this process can't read; left for later`)
2851
+ });
2814
2852
  const refs = outbox.sessions();
2815
2853
  if (first) {
2816
2854
  const k = `${first.harness}/${safeId(first.session)}`;