@sentropic/h2a-runtime 0.85.13 → 0.85.14

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,50 @@
1
+ /**
2
+ * launch-context — record WHICH launch options produced a managed local tmux session,
3
+ * so `h2a ls`/inspect can surface gateway on/off, the active model mapping, the h2a
4
+ * side-window command and the relaunch line WITHOUT the user remembering raw tmux
5
+ * `show-options`. Spec: docs/specs/2026-07-11-tmux-launch-context-and-gateway-model-mappings.md
6
+ *
7
+ * This module is PURE and testable: it builds the diagnostic key/value set and formats it.
8
+ * The tmux side effects (set-option/show-options) live in tmux.ts. It NEVER records a secret —
9
+ * it reads only ANTHROPIC_BASE_URL (a URL) and the PRESENCE of OPENAI_MODEL_MAP (a flag, not its
10
+ * content); tokens/api-keys are never read or stored, and free strings are redaction-scrubbed.
11
+ */
12
+ /** Managed-session option prefix; kept distinct from @remote_agent_* / @display_name. */
13
+ export declare const LAUNCH_OPTION_PREFIX = "@remote_launch_";
14
+ export interface LaunchContextInput {
15
+ profile: string;
16
+ cwd: string;
17
+ label?: string | undefined;
18
+ /** CLI-native resume argv (e.g. ["--resume", id] / ["resume", id]); only the id is kept. */
19
+ resumeArgs?: ReadonlyArray<string> | undefined;
20
+ /** h2a side-window command line, if one was requested. */
21
+ h2aCommand?: string | undefined;
22
+ }
23
+ export interface LaunchContext {
24
+ profile: string;
25
+ cwd: string;
26
+ label?: string;
27
+ /** the resume conversation id (ids are safe; never a secret). */
28
+ resume?: string;
29
+ gateway: "on" | "off";
30
+ /** only surfaced when local/non-secret (localhost / 127.0.0.1). */
31
+ gatewayBaseUrl?: string;
32
+ /** summary of the active model-map source, e.g. "env:OPENAI_MODEL_MAP" or "catalog-default". */
33
+ modelMap: string;
34
+ h2a?: string;
35
+ }
36
+ /** Defence-in-depth: scrub anything token-shaped from a free string before it is stored. */
37
+ export declare function redactSecrets(s: string): string;
38
+ /**
39
+ * Build the diagnostic launch context from the launcher inputs + the environment. Pure: pass
40
+ * `env` for tests. Reads ONLY ANTHROPIC_BASE_URL + the presence of OPENAI_MODEL_MAP — never a
41
+ * token/api-key.
42
+ */
43
+ export declare function buildLaunchContext(input: LaunchContextInput, env?: NodeJS.ProcessEnv): LaunchContext;
44
+ /** The tmux `set-option` key/value pairs for a launch context (undefined fields omitted). */
45
+ export declare function launchContextOptions(ctx: LaunchContext): Array<[string, string]>;
46
+ /** Reconstruct a launch context from a tmux option reader (`show-options -qv`). */
47
+ export declare function parseLaunchContext(readOption: (key: string) => string | undefined): LaunchContext | undefined;
48
+ /** Human-friendly multi-line render for `h2a inspect` / an `ls --context` block. */
49
+ export declare function formatLaunchContext(ctx: LaunchContext): string;
50
+ //# sourceMappingURL=launch-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-context.d.ts","sourceRoot":"","sources":["../src/launch-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,yFAAyF;AACzF,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAC/C,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,IAAI,GAAG,KAAK,CAAC;IACtB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gGAAgG;IAChG,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAKD,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,kBAAkB,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,aAAa,CAmBf;AAED,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAYhF;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAC9C,aAAa,GAAG,SAAS,CAoB3B;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAW9D"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * launch-context — record WHICH launch options produced a managed local tmux session,
3
+ * so `h2a ls`/inspect can surface gateway on/off, the active model mapping, the h2a
4
+ * side-window command and the relaunch line WITHOUT the user remembering raw tmux
5
+ * `show-options`. Spec: docs/specs/2026-07-11-tmux-launch-context-and-gateway-model-mappings.md
6
+ *
7
+ * This module is PURE and testable: it builds the diagnostic key/value set and formats it.
8
+ * The tmux side effects (set-option/show-options) live in tmux.ts. It NEVER records a secret —
9
+ * it reads only ANTHROPIC_BASE_URL (a URL) and the PRESENCE of OPENAI_MODEL_MAP (a flag, not its
10
+ * content); tokens/api-keys are never read or stored, and free strings are redaction-scrubbed.
11
+ */
12
+ /** Managed-session option prefix; kept distinct from @remote_agent_* / @display_name. */
13
+ export const LAUNCH_OPTION_PREFIX = "@remote_launch_";
14
+ const SECRET_RE = /(sk-[A-Za-z0-9_-]{8,}|Bearer\s+[A-Za-z0-9._-]+|eyJ[A-Za-z0-9._-]{10,}|[A-Za-z0-9_-]{32,})/g;
15
+ /** Defence-in-depth: scrub anything token-shaped from a free string before it is stored. */
16
+ export function redactSecrets(s) {
17
+ return s.replace(SECRET_RE, "«redacted»");
18
+ }
19
+ function isLocalUrl(url) {
20
+ return /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(url);
21
+ }
22
+ /**
23
+ * Build the diagnostic launch context from the launcher inputs + the environment. Pure: pass
24
+ * `env` for tests. Reads ONLY ANTHROPIC_BASE_URL + the presence of OPENAI_MODEL_MAP — never a
25
+ * token/api-key.
26
+ */
27
+ export function buildLaunchContext(input, env = process.env) {
28
+ const base = (env.ANTHROPIC_BASE_URL ?? "").trim();
29
+ const ctx = {
30
+ profile: input.profile,
31
+ cwd: input.cwd,
32
+ gateway: base ? "on" : "off",
33
+ modelMap: env.OPENAI_MODEL_MAP?.trim() ? "env:OPENAI_MODEL_MAP" : "catalog-default",
34
+ };
35
+ if (input.label && input.label.trim())
36
+ ctx.label = input.label.trim();
37
+ if (base && isLocalUrl(base))
38
+ ctx.gatewayBaseUrl = base;
39
+ const resumeArgs = input.resumeArgs ?? [];
40
+ if (resumeArgs.length > 0) {
41
+ const id = resumeArgs[resumeArgs.length - 1];
42
+ if (id && id !== resumeArgs[0])
43
+ ctx.resume = redactSecrets(id);
44
+ }
45
+ if (input.h2aCommand && input.h2aCommand.trim()) {
46
+ ctx.h2a = redactSecrets(input.h2aCommand.trim());
47
+ }
48
+ return ctx;
49
+ }
50
+ /** The tmux `set-option` key/value pairs for a launch context (undefined fields omitted). */
51
+ export function launchContextOptions(ctx) {
52
+ const out = [
53
+ [`${LAUNCH_OPTION_PREFIX}profile`, ctx.profile],
54
+ [`${LAUNCH_OPTION_PREFIX}cwd`, ctx.cwd],
55
+ [`${LAUNCH_OPTION_PREFIX}gateway`, ctx.gateway],
56
+ [`${LAUNCH_OPTION_PREFIX}model_map`, ctx.modelMap],
57
+ ];
58
+ if (ctx.label)
59
+ out.push([`${LAUNCH_OPTION_PREFIX}label`, ctx.label]);
60
+ if (ctx.resume)
61
+ out.push([`${LAUNCH_OPTION_PREFIX}resume`, ctx.resume]);
62
+ if (ctx.gatewayBaseUrl)
63
+ out.push([`${LAUNCH_OPTION_PREFIX}gateway_base_url`, ctx.gatewayBaseUrl]);
64
+ if (ctx.h2a)
65
+ out.push([`${LAUNCH_OPTION_PREFIX}h2a`, ctx.h2a]);
66
+ return out;
67
+ }
68
+ /** Reconstruct a launch context from a tmux option reader (`show-options -qv`). */
69
+ export function parseLaunchContext(readOption) {
70
+ const profile = readOption(`${LAUNCH_OPTION_PREFIX}profile`);
71
+ const cwd = readOption(`${LAUNCH_OPTION_PREFIX}cwd`);
72
+ if (!profile || !cwd)
73
+ return undefined; // no launch context recorded on this session
74
+ const gatewayRaw = readOption(`${LAUNCH_OPTION_PREFIX}gateway`);
75
+ const ctx = {
76
+ profile,
77
+ cwd,
78
+ gateway: gatewayRaw === "on" ? "on" : "off",
79
+ modelMap: readOption(`${LAUNCH_OPTION_PREFIX}model_map`) ?? "catalog-default",
80
+ };
81
+ const label = readOption(`${LAUNCH_OPTION_PREFIX}label`);
82
+ if (label)
83
+ ctx.label = label;
84
+ const resume = readOption(`${LAUNCH_OPTION_PREFIX}resume`);
85
+ if (resume)
86
+ ctx.resume = resume;
87
+ const url = readOption(`${LAUNCH_OPTION_PREFIX}gateway_base_url`);
88
+ if (url)
89
+ ctx.gatewayBaseUrl = url;
90
+ const h2a = readOption(`${LAUNCH_OPTION_PREFIX}h2a`);
91
+ if (h2a)
92
+ ctx.h2a = h2a;
93
+ return ctx;
94
+ }
95
+ /** Human-friendly multi-line render for `h2a inspect` / an `ls --context` block. */
96
+ export function formatLaunchContext(ctx) {
97
+ const lines = [
98
+ ` profile: ${ctx.profile}`,
99
+ ` cwd: ${ctx.cwd}`,
100
+ ctx.label ? ` label: ${ctx.label}` : undefined,
101
+ ` gateway: ${ctx.gateway}${ctx.gatewayBaseUrl ? ` (ANTHROPIC_BASE_URL=${ctx.gatewayBaseUrl})` : ""}`,
102
+ ` model-map: ${ctx.modelMap}`,
103
+ ctx.h2a ? ` h2a: ${ctx.h2a}` : undefined,
104
+ ctx.resume ? ` resume: ${ctx.resume}` : undefined,
105
+ ];
106
+ return lines.filter((l) => l !== undefined).join("\n");
107
+ }
108
+ //# sourceMappingURL=launch-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-context.js","sourceRoot":"","sources":["../src/launch-context.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AACnD,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,eAAe,CAAC;IAC/B,cAAc,EAAE,eAAe,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EACP,YAAY,GACZ,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;CACvB;AAgED,wBAAgB,gBAAgB,IAAI,iBAAiB,EAAE,CAGtD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GACf,WAAW,GAAG,SAAS,CAMzB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAQ5E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAiD1E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAI9D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,sBAAqB,GAAG;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE,CASA"}
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AACnD,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,eAAe,CAAC;IAC/B,cAAc,EAAE,eAAe,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EACP,YAAY,GACZ,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;CACvB;AAqFD,wBAAgB,gBAAgB,IAAI,iBAAiB,EAAE,CAGtD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GACf,WAAW,GAAG,SAAS,CAMzB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAQ5E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAiD1E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAI9D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,sBAAqB,GAAG;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE,CASA"}
@@ -1,5 +1,27 @@
1
1
  const CODEX_CAPABILITIES = ["streaming", "tools", "reasoning_effort"];
2
2
  const DEFAULT_MODEL_CATALOG = [
3
+ {
4
+ id: "gpt-5.6-luna",
5
+ provider: "codex",
6
+ upstreamModel: "gpt-5.6-luna",
7
+ accountPool: "codex",
8
+ inputProtocol: "anthropic.messages",
9
+ outputProtocol: "anthropic.messages",
10
+ capabilities: [...CODEX_CAPABILITIES],
11
+ defaultPolicy: "round-robin",
12
+ aliases: ["claude-opus-4-8"],
13
+ },
14
+ {
15
+ id: "gpt-5.6-sol",
16
+ provider: "codex",
17
+ upstreamModel: "gpt-5.6-sol",
18
+ accountPool: "codex",
19
+ inputProtocol: "anthropic.messages",
20
+ outputProtocol: "anthropic.messages",
21
+ capabilities: [...CODEX_CAPABILITIES],
22
+ defaultPolicy: "round-robin",
23
+ aliases: ["claude-fable-5", "fable-5"],
24
+ },
3
25
  {
4
26
  id: "gpt-5.5",
5
27
  provider: "codex",
@@ -10,7 +32,6 @@ const DEFAULT_MODEL_CATALOG = [
10
32
  capabilities: [...CODEX_CAPABILITIES],
11
33
  defaultPolicy: "round-robin",
12
34
  aliases: [
13
- "claude-opus-4-8",
14
35
  "claude-opus-4-7",
15
36
  "claude-opus-4-6",
16
37
  "claude-sonnet-4-6",
@@ -1 +1 @@
1
- {"version":3,"file":"model-catalog.js","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"AA8BA,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,kBAAkB,CAAU,CAAC;AAE/E,MAAM,qBAAqB,GAAwB;IACjD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,SAAS;QACxB,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;QAC5B,OAAO,EAAE;YACP,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,mBAAmB;YACnB,mBAAmB;YACnB,2BAA2B;SAC5B;KACF;IACD;QACE,EAAE,EAAE,qBAAqB;QACzB,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,qBAAqB;QACpC,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;KAC7B;CACF,CAAC;AAEF,IAAI,QAAQ,GAA+B,IAAI,CAAC;AAChD,IAAI,YAAY,GAAkC,IAAI,CAAC;AAEvD,SAAS,gBAAgB;IACvB,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClC,YAAY,GAAG,EAAE,CAAC;QAClB,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAGrD,CAAC;IACF,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,EAAE;QACF,QAAQ,EAAE,OAAgB;QAC1B,aAAa;QACb,WAAW,EAAE,OAAgB;QAC7B,aAAa,EAAE,oBAA6B;QAC5C,cAAc,EAAE,oBAA6B;QAC7C,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAsB;KACtC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,QAAQ;QAAE,QAAQ,GAAG,CAAC,GAAG,qBAAqB,EAAE,GAAG,iBAAiB,EAAE,CAAC,CAAC;IAC7E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,QAAQ,GAAG,IAAI,CAAC;IAChB,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAgB;IAEhB,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACtE,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,aAAa;QAC5D,OAAO,WAAW,CAAC;IACrB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,WAAW,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,OAAO;QACL,WAAW;QACX,aAAa,EAAE,aAAa;QAC5B,WAAW,EAAE,kBAAkB;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO;YACL,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,WAAW;YAC1B,WAAW,EAAE,OAAO;YACpB,aAAa,EAAE,aAAa;YAC5B,WAAW,EAAE,eAAe;SAC7B,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvC,IAAI,KAAK,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO;gBACL,cAAc,EAAE,KAAK;gBACrB,cAAc,EAAE,KAAK,CAAC,EAAE;gBACxB,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,YAAY;aAC1B,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO;gBACL,cAAc,EAAE,KAAK;gBACrB,cAAc,EAAE,KAAK,CAAC,EAAE;gBACxB,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,eAAe;aAC7B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,KAAK;YACpB,WAAW,EAAE,OAAO;YACpB,aAAa,EAAE,aAAa;YAC5B,WAAW,EAAE,iBAAiB;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAO,GAAG,gBAAgB,EAAE;IAI/D,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC5B,GAAG,KAAK;YACR,MAAM,EAAE,OAAgB;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"model-catalog.js","sourceRoot":"","sources":["../../src/llm-gateway-runtime/model-catalog.ts"],"names":[],"mappings":"AA8BA,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,kBAAkB,CAAU,CAAC;AAE/E,MAAM,qBAAqB,GAAwB;IACjD;QACE,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,cAAc;QAC7B,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;QAC5B,OAAO,EAAE,CAAC,iBAAiB,CAAC;KAC7B;IACD;QACE,EAAE,EAAE,aAAa;QACjB,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,aAAa;QAC5B,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;QAC5B,OAAO,EAAE,CAAC,gBAAgB,EAAE,SAAS,CAAC;KACvC;IACD;QACE,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,SAAS;QACxB,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;QAC5B,OAAO,EAAE;YACP,iBAAiB;YACjB,iBAAiB;YACjB,mBAAmB;YACnB,mBAAmB;YACnB,2BAA2B;SAC5B;KACF;IACD;QACE,EAAE,EAAE,qBAAqB;QACzB,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,qBAAqB;QACpC,WAAW,EAAE,OAAO;QACpB,aAAa,EAAE,oBAAoB;QACnC,cAAc,EAAE,oBAAoB;QACpC,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAa;KAC7B;CACF,CAAC;AAEF,IAAI,QAAQ,GAA+B,IAAI,CAAC;AAChD,IAAI,YAAY,GAAkC,IAAI,CAAC;AAEvD,SAAS,gBAAgB;IACvB,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClC,YAAY,GAAG,EAAE,CAAC;QAClB,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAGrD,CAAC;IACF,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,EAAE;QACF,QAAQ,EAAE,OAAgB;QAC1B,aAAa;QACb,WAAW,EAAE,OAAgB;QAC7B,aAAa,EAAE,oBAA6B;QAC5C,cAAc,EAAE,oBAA6B;QAC7C,YAAY,EAAE,CAAC,GAAG,kBAAkB,CAAC;QACrC,aAAa,EAAE,aAAsB;KACtC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,QAAQ;QAAE,QAAQ,GAAG,CAAC,GAAG,qBAAqB,EAAE,GAAG,iBAAiB,EAAE,CAAC,CAAC;IAC7E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,QAAQ,GAAG,IAAI,CAAC;IAChB,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAgB;IAEhB,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACtE,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,aAAa;QAC5D,OAAO,WAAW,CAAC;IACrB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,WAAW,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,OAAO;QACL,WAAW;QACX,aAAa,EAAE,aAAa;QAC5B,WAAW,EAAE,kBAAkB;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO;YACL,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,WAAW;YAC1B,WAAW,EAAE,OAAO;YACpB,aAAa,EAAE,aAAa;YAC5B,WAAW,EAAE,eAAe;SAC7B,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvC,IAAI,KAAK,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO;gBACL,cAAc,EAAE,KAAK;gBACrB,cAAc,EAAE,KAAK,CAAC,EAAE;gBACxB,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,YAAY;aAC1B,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO;gBACL,cAAc,EAAE,KAAK;gBACrB,cAAc,EAAE,KAAK,CAAC,EAAE;gBACxB,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,WAAW,EAAE,eAAe;aAC7B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,KAAK;YACpB,WAAW,EAAE,OAAO;YACpB,aAAa,EAAE,aAAa;YAC5B,WAAW,EAAE,iBAAiB;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAO,GAAG,gBAAgB,EAAE;IAI/D,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC5B,GAAG,KAAK;YACR,MAAM,EAAE,OAAgB;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the auth token-file model (D1), server/issuer-bound.
3
+ *
4
+ * Spec §3. The refresh/access tokens live in `~/.sentropic/h2a-auth.json` (0600), namespaced PER
5
+ * SERVER, so a token minted for one sentropic server/issuer is NEVER usable against another (an
6
+ * attacker-supplied `--server` cannot harvest a 39-auth token). Mirrors the atomic-0600 write used
7
+ * by llm-mesh.ts; no network here.
8
+ */
9
+ export interface SentropicAuthToken {
10
+ /** the sentropic server base URL this token is bound to (routing + anti-exfil key). */
11
+ server: string;
12
+ /** the 39-auth issuer that minted it. */
13
+ issuer: string;
14
+ accessToken: string;
15
+ refreshToken?: string;
16
+ /** ISO expiry of the access token, if known. */
17
+ expiresAt?: string;
18
+ /** the authenticated subject (sub), if known. */
19
+ subject?: string;
20
+ }
21
+ export declare function authFilePath(dir?: string): string;
22
+ /** Normalize a server URL to a stable namespace key (scheme+host+port, no trailing slash/path). */
23
+ export declare function serverKey(server: string): string;
24
+ /**
25
+ * The token bound to `server`, or undefined. SECURITY: only ever returns a token whose stored
26
+ * `server` matches the REQUESTED server — the per-server namespace + this check are what stop a
27
+ * token going to the wrong (possibly attacker) server.
28
+ */
29
+ export declare function readAuth(server: string, dir?: string): SentropicAuthToken | undefined;
30
+ /** Persist a token under its own server namespace (0600, atomic). Rejects a self-inconsistent token. */
31
+ export declare function writeAuth(token: SentropicAuthToken, dir?: string): void;
32
+ export declare function isExpired(token: SentropicAuthToken, now?: number): boolean;
33
+ /**
34
+ * Throw unless `token` is bound to `requestedServer`. Call BEFORE sending a token anywhere, so a
35
+ * 39-auth token issued for server A is never transmitted to a different `--server`.
36
+ */
37
+ export declare function assertServerBound(token: SentropicAuthToken, requestedServer: string): void;
38
+ //# sourceMappingURL=auth-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-token.d.ts","sourceRoot":"","sources":["../../src/sentropic/auth-token.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AASD,wBAAgB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjD;AAmBD,mGAAmG;AACnG,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAKrF;AAED,wGAAwG;AACxG,wBAAgB,SAAS,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAKvE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAItF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAM1F"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the auth token-file model (D1), server/issuer-bound.
3
+ *
4
+ * Spec §3. The refresh/access tokens live in `~/.sentropic/h2a-auth.json` (0600), namespaced PER
5
+ * SERVER, so a token minted for one sentropic server/issuer is NEVER usable against another (an
6
+ * attacker-supplied `--server` cannot harvest a 39-auth token). Mirrors the atomic-0600 write used
7
+ * by llm-mesh.ts; no network here.
8
+ */
9
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ function sentropicDir(dir) {
13
+ return dir ?? join(homedir(), ".sentropic");
14
+ }
15
+ export function authFilePath(dir) {
16
+ return join(sentropicDir(dir), "h2a-auth.json");
17
+ }
18
+ function readFile(dir) {
19
+ try {
20
+ return JSON.parse(readFileSync(authFilePath(dir), "utf8"));
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ }
26
+ function writeFileAtomic(value, dir) {
27
+ const path = authFilePath(dir);
28
+ mkdirSync(dirname(path), { recursive: true });
29
+ const tmp = `${path}.tmp.${process.pid}`;
30
+ writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n", { mode: 0o600 });
31
+ chmodSync(tmp, 0o600);
32
+ renameSync(tmp, path);
33
+ }
34
+ /** Normalize a server URL to a stable namespace key (scheme+host+port, no trailing slash/path). */
35
+ export function serverKey(server) {
36
+ try {
37
+ const u = new URL(server);
38
+ return `${u.protocol}//${u.host}`;
39
+ }
40
+ catch {
41
+ return server.replace(/\/+$/, "");
42
+ }
43
+ }
44
+ /**
45
+ * The token bound to `server`, or undefined. SECURITY: only ever returns a token whose stored
46
+ * `server` matches the REQUESTED server — the per-server namespace + this check are what stop a
47
+ * token going to the wrong (possibly attacker) server.
48
+ */
49
+ export function readAuth(server, dir) {
50
+ const key = serverKey(server);
51
+ const tok = readFile(dir)[key];
52
+ if (!tok)
53
+ return undefined;
54
+ return serverKey(tok.server) === key ? tok : undefined;
55
+ }
56
+ /** Persist a token under its own server namespace (0600, atomic). Rejects a self-inconsistent token. */
57
+ export function writeAuth(token, dir) {
58
+ assertServerBound(token, token.server);
59
+ const file = readFile(dir);
60
+ file[serverKey(token.server)] = token;
61
+ writeFileAtomic(file, dir);
62
+ }
63
+ export function isExpired(token, now = Date.now()) {
64
+ if (!token.expiresAt)
65
+ return false;
66
+ const t = Date.parse(token.expiresAt);
67
+ return Number.isNaN(t) ? false : t <= now;
68
+ }
69
+ /**
70
+ * Throw unless `token` is bound to `requestedServer`. Call BEFORE sending a token anywhere, so a
71
+ * 39-auth token issued for server A is never transmitted to a different `--server`.
72
+ */
73
+ export function assertServerBound(token, requestedServer) {
74
+ if (serverKey(token.server) !== serverKey(requestedServer)) {
75
+ throw new Error(`sentropic auth: token is bound to ${serverKey(token.server)}, refusing to use it against ${serverKey(requestedServer)}`);
76
+ }
77
+ }
78
+ //# sourceMappingURL=auth-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-token.js","sourceRoot":"","sources":["../../src/sentropic/auth-token.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the create-or-bind DECISION (C3), pure and dependency-free.
3
+ *
4
+ * Spec: docs/specs/2026-07-11-sentropic-h2a-enrollment.md §4. Decides, for one local h2a workspace
5
+ * (identified by its durable `ws:<sha256>` fingerprint — NOT its name), what to do against the set
6
+ * of sentropic workspaces the caller is authorized to see:
7
+ * - name ≠ identity: a pre-existing workspace is NEVER auto-adopted by name;
8
+ * - lookup is scoped to caller-authorized workspaces (NO global first-binding-wins);
9
+ * - explicit `--to <wsId>` requires workspace ADMIN (viewer/member cannot bind);
10
+ * - multiple visible bindings for a fingerprint → the caller must select explicitly;
11
+ * - the (workspaceId, fingerprint) PAIR is what later routes uploads — never the fingerprint alone.
12
+ *
13
+ * This module holds NO IO: the caller passes in the authorized workspaces + visible bindings (which
14
+ * it fetched from the server) and gets a plan back. Capability separation (bind ≠ upload) is a
15
+ * property of the plan consumer, not of this decision.
16
+ */
17
+ export type WorkspaceRole = "viewer" | "member" | "admin";
18
+ export interface WorkspaceRef {
19
+ workspaceId: string;
20
+ name: string;
21
+ role: WorkspaceRole;
22
+ }
23
+ /** A recorded (workspace, fingerprint) binding visible to the caller. */
24
+ export interface Binding {
25
+ workspaceId: string;
26
+ fingerprint: string;
27
+ }
28
+ export type BindPlan = {
29
+ action: "create";
30
+ fingerprint: string;
31
+ suggestedName: string;
32
+ } | {
33
+ action: "bind";
34
+ fingerprint: string;
35
+ workspaceId: string;
36
+ } | {
37
+ action: "already-bound";
38
+ fingerprint: string;
39
+ workspaceId: string;
40
+ } | {
41
+ action: "ambiguous";
42
+ fingerprint: string;
43
+ candidates: string[];
44
+ } | {
45
+ action: "needs-admin";
46
+ fingerprint: string;
47
+ workspaceId: string;
48
+ } | {
49
+ action: "unknown-target";
50
+ fingerprint: string;
51
+ workspaceId: string;
52
+ };
53
+ export interface PlanInput {
54
+ /** the local workspace's durable identity ("ws:<sha256>"). */
55
+ fingerprint: string;
56
+ /** a SUGGESTED display name for a create (name ≠ identity). */
57
+ localName: string;
58
+ /** sentropic workspaces the caller may see — the lookup is scoped to THIS set only. */
59
+ authorizedWorkspaces: ReadonlyArray<WorkspaceRef>;
60
+ /** (workspace, fingerprint) bindings visible to the caller. */
61
+ existingBindings: ReadonlyArray<Binding>;
62
+ /** explicit `--to <workspaceId>` target, if the human named one. */
63
+ explicitTo?: string;
64
+ }
65
+ /** Stable key for a (workspace, fingerprint) binding — the pair, never the fingerprint alone. */
66
+ export declare function bindingKey(workspaceId: string, fingerprint: string): string;
67
+ /** The destination a transcript upload routes to — the bound PAIR, not the fingerprint. */
68
+ export declare function uploadRouteTarget(binding: Binding): {
69
+ workspaceId: string;
70
+ fingerprint: string;
71
+ };
72
+ /**
73
+ * Plan the create-or-bind for one workspace. Pure. The result is a single explicit action the CLI
74
+ * confirms (batch mode aggregates one plan per workspace into a printed plan + one confirmation).
75
+ */
76
+ export declare function planCreateOrBind(input: PlanInput): BindPlan;
77
+ /** Batch: one plan per workspace, for a printed plan + one confirmation (C3). */
78
+ export declare function planBatch(inputs: ReadonlyArray<PlanInput>): BindPlan[];
79
+ //# sourceMappingURL=bind-decision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind-decision.d.ts","sourceRoot":"","sources":["../../src/sentropic/bind-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,GAChB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAClE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,SAAS;IACxB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,oBAAoB,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAClD,+DAA+D;IAC/D,gBAAgB,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iGAAiG;AACjG,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAEhG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAmC3D;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,QAAQ,EAAE,CAEtE"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the create-or-bind DECISION (C3), pure and dependency-free.
3
+ *
4
+ * Spec: docs/specs/2026-07-11-sentropic-h2a-enrollment.md §4. Decides, for one local h2a workspace
5
+ * (identified by its durable `ws:<sha256>` fingerprint — NOT its name), what to do against the set
6
+ * of sentropic workspaces the caller is authorized to see:
7
+ * - name ≠ identity: a pre-existing workspace is NEVER auto-adopted by name;
8
+ * - lookup is scoped to caller-authorized workspaces (NO global first-binding-wins);
9
+ * - explicit `--to <wsId>` requires workspace ADMIN (viewer/member cannot bind);
10
+ * - multiple visible bindings for a fingerprint → the caller must select explicitly;
11
+ * - the (workspaceId, fingerprint) PAIR is what later routes uploads — never the fingerprint alone.
12
+ *
13
+ * This module holds NO IO: the caller passes in the authorized workspaces + visible bindings (which
14
+ * it fetched from the server) and gets a plan back. Capability separation (bind ≠ upload) is a
15
+ * property of the plan consumer, not of this decision.
16
+ */
17
+ /** Stable key for a (workspace, fingerprint) binding — the pair, never the fingerprint alone. */
18
+ export function bindingKey(workspaceId, fingerprint) {
19
+ return `${workspaceId} ${fingerprint}`;
20
+ }
21
+ /** The destination a transcript upload routes to — the bound PAIR, not the fingerprint. */
22
+ export function uploadRouteTarget(binding) {
23
+ return { workspaceId: binding.workspaceId, fingerprint: binding.fingerprint };
24
+ }
25
+ /**
26
+ * Plan the create-or-bind for one workspace. Pure. The result is a single explicit action the CLI
27
+ * confirms (batch mode aggregates one plan per workspace into a printed plan + one confirmation).
28
+ */
29
+ export function planCreateOrBind(input) {
30
+ const { fingerprint, localName, authorizedWorkspaces, existingBindings, explicitTo } = input;
31
+ const authorized = new Map(authorizedWorkspaces.map((w) => [w.workspaceId, w]));
32
+ // Bindings for THIS fingerprint that fall within the caller-authorized set (scoped lookup).
33
+ const scopedMatches = existingBindings.filter((b) => b.fingerprint === fingerprint && authorized.has(b.workspaceId));
34
+ // --- explicit target path: name ≠ identity, admin-gated ---
35
+ if (explicitTo !== undefined) {
36
+ const target = authorized.get(explicitTo);
37
+ if (!target)
38
+ return { action: "unknown-target", fingerprint, workspaceId: explicitTo };
39
+ if (scopedMatches.some((b) => b.workspaceId === explicitTo)) {
40
+ return { action: "already-bound", fingerprint, workspaceId: explicitTo };
41
+ }
42
+ if (target.role !== "admin") {
43
+ return { action: "needs-admin", fingerprint, workspaceId: explicitTo };
44
+ }
45
+ return { action: "bind", fingerprint, workspaceId: explicitTo };
46
+ }
47
+ // --- auto path: NO global first-wins; decide from the scoped matches only ---
48
+ if (scopedMatches.length === 0) {
49
+ return { action: "create", fingerprint, suggestedName: localName };
50
+ }
51
+ if (scopedMatches.length === 1) {
52
+ return { action: "already-bound", fingerprint, workspaceId: scopedMatches[0].workspaceId };
53
+ }
54
+ // multiple visible bindings → the human must select explicitly (never auto-pick).
55
+ return {
56
+ action: "ambiguous",
57
+ fingerprint,
58
+ candidates: scopedMatches.map((b) => b.workspaceId).sort(),
59
+ };
60
+ }
61
+ /** Batch: one plan per workspace, for a printed plan + one confirmation (C3). */
62
+ export function planBatch(inputs) {
63
+ return inputs.map(planCreateOrBind);
64
+ }
65
+ //# sourceMappingURL=bind-decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind-decision.js","sourceRoot":"","sources":["../../src/sentropic/bind-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAqCH,iGAAiG;AACjG,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,WAAmB;IACjE,OAAO,GAAG,WAAW,IAAI,WAAW,EAAE,CAAC;AACzC,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgB;IAC/C,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC7F,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhF,4FAA4F;IAC5F,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CACtE,CAAC;IAEF,6DAA6D;IAC7D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;QACvF,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC,EAAE,CAAC;YAC5D,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;QAC3E,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;QACzE,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;IAClE,CAAC;IAED,+EAA+E;IAC/E,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IAC9F,CAAC;IACD,kFAAkF;IAClF,OAAO;QACL,MAAM,EAAE,WAAW;QACnB,WAAW;QACX,UAAU,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE;KAC3D,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,SAAS,CAAC,MAAgC;IACxD,OAAO,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the D1 auth flow SHAPE (PKCE authorization_code + device-code
3
+ * fallback), pure. Spec §3. The actual HTTP exchange is injected as a SEAM (`TokenExchange`) — this
4
+ * module owns only the deterministic, testable parts: building the /authorize request (PKCE S256 +
5
+ * state), and the device-code polling state machine (backoff, slow_down, expiry). No live 39-auth
6
+ * call is wired in Lot 0 (that leg + the server are later lots).
7
+ *
8
+ * pkceS256 is mirrored from packages/h2a/.../oauth/crypto.ts on purpose: h2a-runtime must not import
9
+ * the @sentropic/h2a core (anti-cycle: core stays a leaf). It is 3 lines of node:crypto.
10
+ */
11
+ import type { SentropicAuthToken } from "./auth-token.js";
12
+ /** A fresh PKCE S256 pair. The verifier is held locally; the challenge goes on the wire. */
13
+ export declare function pkceS256(): {
14
+ verifier: string;
15
+ challenge: string;
16
+ };
17
+ export declare function randomState(byteLength?: number): string;
18
+ export interface AuthorizeConfig {
19
+ authorizeUrl: string;
20
+ clientId: string;
21
+ redirectUri: string;
22
+ scope: string;
23
+ }
24
+ export interface AuthorizeRequest {
25
+ /** the full /authorize URL to open in the browser. */
26
+ url: string;
27
+ /** the PKCE verifier to keep locally for the token exchange. */
28
+ verifier: string;
29
+ /** the CSRF/replay state to match on the callback. */
30
+ state: string;
31
+ }
32
+ /** Build the browser authorization_code+PKCE request (deterministic given the injected pkce/state). */
33
+ export declare function buildAuthorizeRequest(cfg: AuthorizeConfig, pkce?: {
34
+ verifier: string;
35
+ challenge: string;
36
+ }, state?: string): AuthorizeRequest;
37
+ /** Verify the callback state matches the one we issued (CSRF/replay guard). */
38
+ export declare function verifyCallbackState(issued: string, returned: string): boolean;
39
+ export type DevicePollStatus = "pending" | "slow_down" | "authorized" | "denied" | "expired";
40
+ export interface DevicePollResult {
41
+ status: DevicePollStatus;
42
+ token?: SentropicAuthToken;
43
+ }
44
+ /**
45
+ * Next poll delay for the device-code loop: honour `slow_down` by growing the interval, and never
46
+ * poll past `expiresAt`. Pure — the loop driver supplies the clock.
47
+ */
48
+ export declare function nextDevicePollDelayMs(currentMs: number, gotSlowDown: boolean, { minMs, maxMs, growthMs }?: {
49
+ minMs?: number;
50
+ maxMs?: number;
51
+ growthMs?: number;
52
+ }): number;
53
+ export declare function deviceFlowExpired(expiresAtMs: number, now: number): boolean;
54
+ /** Injected HTTP exchange (browser code→token, or device poll→token). Not implemented in Lot 0. */
55
+ export type TokenExchange = (args: {
56
+ server: string;
57
+ verifier?: string;
58
+ code?: string;
59
+ deviceCode?: string;
60
+ }) => Promise<SentropicAuthToken>;
61
+ //# sourceMappingURL=enroll-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enroll-flow.d.ts","sourceRoot":"","sources":["../../src/sentropic/enroll-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,4FAA4F;AAC5F,wBAAgB,QAAQ,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAIlE;AAED,wBAAgB,WAAW,CAAC,UAAU,SAAK,GAAG,MAAM,CAEnD;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uGAAuG;AACvG,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,IAAI,GAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAe,EAC1D,KAAK,GAAE,MAAsB,GAC5B,gBAAgB,CAUlB;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7E;AAID,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,OAAO,EACpB,EAAE,KAAY,EAAE,KAAa,EAAE,QAAe,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3G,MAAM,CAGR;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE3E;AAID,mGAAmG;AACnG,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * sentropic enrollment — Lot 0: the D1 auth flow SHAPE (PKCE authorization_code + device-code
3
+ * fallback), pure. Spec §3. The actual HTTP exchange is injected as a SEAM (`TokenExchange`) — this
4
+ * module owns only the deterministic, testable parts: building the /authorize request (PKCE S256 +
5
+ * state), and the device-code polling state machine (backoff, slow_down, expiry). No live 39-auth
6
+ * call is wired in Lot 0 (that leg + the server are later lots).
7
+ *
8
+ * pkceS256 is mirrored from packages/h2a/.../oauth/crypto.ts on purpose: h2a-runtime must not import
9
+ * the @sentropic/h2a core (anti-cycle: core stays a leaf). It is 3 lines of node:crypto.
10
+ */
11
+ import { createHash, randomBytes } from "node:crypto";
12
+ /** A fresh PKCE S256 pair. The verifier is held locally; the challenge goes on the wire. */
13
+ export function pkceS256() {
14
+ const verifier = randomBytes(32).toString("base64url");
15
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
16
+ return { verifier, challenge };
17
+ }
18
+ export function randomState(byteLength = 32) {
19
+ return randomBytes(byteLength).toString("base64url");
20
+ }
21
+ /** Build the browser authorization_code+PKCE request (deterministic given the injected pkce/state). */
22
+ export function buildAuthorizeRequest(cfg, pkce = pkceS256(), state = randomState()) {
23
+ const u = new URL(cfg.authorizeUrl);
24
+ u.searchParams.set("response_type", "code");
25
+ u.searchParams.set("client_id", cfg.clientId);
26
+ u.searchParams.set("redirect_uri", cfg.redirectUri);
27
+ u.searchParams.set("scope", cfg.scope);
28
+ u.searchParams.set("state", state);
29
+ u.searchParams.set("code_challenge", pkce.challenge);
30
+ u.searchParams.set("code_challenge_method", "S256");
31
+ return { url: u.toString(), verifier: pkce.verifier, state };
32
+ }
33
+ /** Verify the callback state matches the one we issued (CSRF/replay guard). */
34
+ export function verifyCallbackState(issued, returned) {
35
+ return issued.length > 0 && issued === returned;
36
+ }
37
+ /**
38
+ * Next poll delay for the device-code loop: honour `slow_down` by growing the interval, and never
39
+ * poll past `expiresAt`. Pure — the loop driver supplies the clock.
40
+ */
41
+ export function nextDevicePollDelayMs(currentMs, gotSlowDown, { minMs = 1000, maxMs = 15000, growthMs = 5000 } = {}) {
42
+ const base = Math.max(minMs, currentMs);
43
+ return Math.min(maxMs, gotSlowDown ? base + growthMs : base);
44
+ }
45
+ export function deviceFlowExpired(expiresAtMs, now) {
46
+ return now >= expiresAtMs;
47
+ }
48
+ //# sourceMappingURL=enroll-flow.js.map