@byok-sdk/keys 0.4.3 → 0.6.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 +36 -0
- package/dist/bin/pi-provider-launcher.js +326 -94
- package/dist/bin/pi-provider-launcher.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +71 -7
- package/dist/index.js.map +1 -1
- package/dist/pi-model-config.d.ts +51 -0
- package/dist/pi-provider-launcher-core.d.ts +52 -2
- package/dist/pi-provider-projection.d.ts +31 -0
- package/dist/provider-profile.d.ts +47 -0
- package/dist/registry.d.ts +4 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @byok-sdk/keys
|
|
2
2
|
|
|
3
|
+
Pi launcher configuration is explicit, from the published 0.5.0 onward.
|
|
4
|
+
Set `pi_model` on `ProviderRegistry.configure` for profiles used by Pi:
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const pi_model = {
|
|
8
|
+
contextWindow: 1_000_000,
|
|
9
|
+
maxTokens: 131_072,
|
|
10
|
+
reasoning: true,
|
|
11
|
+
thinkingLevel: 'low',
|
|
12
|
+
thinkingLevelMap: {
|
|
13
|
+
off: null, minimal: null, low: 'low', medium: null,
|
|
14
|
+
high: 'high', xhigh: null, max: 'max',
|
|
15
|
+
},
|
|
16
|
+
compat: {
|
|
17
|
+
supportsStore: false, supportsDeveloperRole: false,
|
|
18
|
+
supportsReasoningEffort: true, supportsUsageInStreaming: true,
|
|
19
|
+
maxTokensField: 'max_tokens', thinkingFormat: 'zai', zaiToolStream: true,
|
|
20
|
+
},
|
|
21
|
+
} satisfies PiModelConfig;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
These illustrate declared GLM-5.3-Flash/Pi model settings, not Host input budgets
|
|
25
|
+
or automatic defaults. Import `PiModelConfig` from this package. The bounded
|
|
26
|
+
`PiModelConfigSchema` rejects unknown fields, incomplete level maps and
|
|
27
|
+
unsupported selected levels; it accepts no identity, URL, header or secret.
|
|
28
|
+
Use the exact provider's authoritative configuration. Missing `pi_model`
|
|
29
|
+
permits direct provider transports but rejects Pi admission and launch.
|
|
30
|
+
Configuration changes alter the profile hash and registry revision, fencing
|
|
31
|
+
stale tasks before credential access. The launcher projects the selected
|
|
32
|
+
thinking level through its own argv, not delegated overrides.
|
|
33
|
+
|
|
34
|
+
The SQLite profile schema changes in this candidate. Existing stores are
|
|
35
|
+
rejected and preserved: explicitly provision a separate current-schema store
|
|
36
|
+
after reviewing the old configuration. Do not delete an old store or infer its
|
|
37
|
+
missing model settings. No live-store conversion is performed by the SDK.
|
|
38
|
+
|
|
3
39
|
Key-based BYOK: a validated provider profile, credential-backed auth headers, and
|
|
4
40
|
direct transports to OpenAI-compatible and Anthropic providers.
|
|
5
41
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import path2, { dirname, isAbsolute } from 'path';
|
|
2
3
|
import { spawn } from 'child_process';
|
|
3
|
-
import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import path2, { dirname } from 'path';
|
|
6
4
|
import { z } from 'zod';
|
|
7
5
|
import { createHash } from 'crypto';
|
|
6
|
+
import { promises, mkdirSync, existsSync, chmodSync } from 'fs';
|
|
7
|
+
import { parseImplementationSpawnBinding, assertImplementationSpawnBinding, projectKeysPiInheritedEnvironment } from '@byok-sdk/implementation-identity';
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
|
|
10
10
|
// src/errors.ts
|
|
@@ -193,6 +193,54 @@ function isPrivateNetworkLiteral(hostname) {
|
|
|
193
193
|
}
|
|
194
194
|
return parts[0] === 10 || parts[0] === 127 || parts[0] === 169 && parts[1] === 254 || parts[0] === 172 && (parts[1] ?? 0) >= 16 && (parts[1] ?? 0) <= 31 || parts[0] === 192 && parts[1] === 168;
|
|
195
195
|
}
|
|
196
|
+
var PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
197
|
+
var effort = z.string().min(1).max(64).regex(/^[a-zA-Z0-9_-]+$/u).nullable();
|
|
198
|
+
var PiModelConfigSchema = z.object({
|
|
199
|
+
contextWindow: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
200
|
+
maxTokens: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
201
|
+
reasoning: z.boolean(),
|
|
202
|
+
thinkingLevel: z.enum(PI_THINKING_LEVELS),
|
|
203
|
+
thinkingLevelMap: z.object({
|
|
204
|
+
off: effort,
|
|
205
|
+
minimal: effort,
|
|
206
|
+
low: effort,
|
|
207
|
+
medium: effort,
|
|
208
|
+
high: effort,
|
|
209
|
+
xhigh: effort,
|
|
210
|
+
max: effort
|
|
211
|
+
}).strict(),
|
|
212
|
+
compat: z.object({
|
|
213
|
+
supportsStore: z.boolean().optional(),
|
|
214
|
+
supportsDeveloperRole: z.boolean().optional(),
|
|
215
|
+
supportsReasoningEffort: z.boolean().optional(),
|
|
216
|
+
supportsUsageInStreaming: z.boolean().optional(),
|
|
217
|
+
maxTokensField: z.enum(["max_completion_tokens", "max_tokens"]).optional(),
|
|
218
|
+
thinkingFormat: z.enum([
|
|
219
|
+
"openai",
|
|
220
|
+
"openrouter",
|
|
221
|
+
"deepseek",
|
|
222
|
+
"together",
|
|
223
|
+
"baseten",
|
|
224
|
+
"zai",
|
|
225
|
+
"qwen",
|
|
226
|
+
"chat-template",
|
|
227
|
+
"qwen-chat-template",
|
|
228
|
+
"string-thinking",
|
|
229
|
+
"ant-ling"
|
|
230
|
+
]).optional(),
|
|
231
|
+
zaiToolStream: z.boolean().optional()
|
|
232
|
+
}).strict()
|
|
233
|
+
}).strict().superRefine((config, ctx) => {
|
|
234
|
+
if (config.maxTokens > config.contextWindow) {
|
|
235
|
+
ctx.addIssue({ code: "custom", path: ["maxTokens"], message: "Pi maximum output cannot exceed its context window" });
|
|
236
|
+
}
|
|
237
|
+
if (config.reasoning && config.thinkingLevelMap[config.thinkingLevel] === null) {
|
|
238
|
+
ctx.addIssue({ code: "custom", path: ["thinkingLevel"], message: "Pi thinking level is not supported by the declared model" });
|
|
239
|
+
}
|
|
240
|
+
if (!config.reasoning && config.thinkingLevel !== "off") {
|
|
241
|
+
ctx.addIssue({ code: "custom", path: ["thinkingLevel"], message: "Non-reasoning Pi models require thinking off" });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
196
244
|
|
|
197
245
|
// src/provider-profile.ts
|
|
198
246
|
var PROVIDER_PROFILE_REF_PATTERN = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u;
|
|
@@ -251,6 +299,8 @@ var ModelProviderProfileSchema = z.object({
|
|
|
251
299
|
enabled: z.boolean(),
|
|
252
300
|
kind: z.literal("model"),
|
|
253
301
|
model: boundedString("model", 160),
|
|
302
|
+
// Direct transports do not use Pi; Pi admission requires this explicit configuration.
|
|
303
|
+
pi_model: PiModelConfigSchema.optional(),
|
|
254
304
|
profile_ref: ProviderProfileRefSchema,
|
|
255
305
|
provider_kind: z.enum(MODEL_PROVIDER_KINDS),
|
|
256
306
|
updated_at: isoTimestamp("updated_at")
|
|
@@ -309,6 +359,7 @@ function exactProviderProfileBinding(profileInput, requiredCapabilities = profil
|
|
|
309
359
|
capabilities: normalizedCapabilities,
|
|
310
360
|
kind: profile.kind,
|
|
311
361
|
model: profile.model,
|
|
362
|
+
...profile.pi_model === void 0 ? {} : { pi_model: profile.pi_model },
|
|
312
363
|
profile_ref: profile.profile_ref,
|
|
313
364
|
provider_kind: profile.provider_kind
|
|
314
365
|
});
|
|
@@ -539,13 +590,13 @@ function assertKeychainPath(keychainPath) {
|
|
|
539
590
|
}
|
|
540
591
|
return keychainPath;
|
|
541
592
|
}
|
|
542
|
-
|
|
543
|
-
// src/pi-provider-projection.ts
|
|
544
593
|
var PI_PROJECTED_KEY_ENV = "PI_PROVIDER_API_KEY";
|
|
594
|
+
var PI_LAUNCHER_RUNTIME_ENTRIES = ["pi-rpc", "pi-prepared"];
|
|
545
595
|
function piProjectionProviderId(profileRef) {
|
|
546
596
|
return `byok-sdk-${profileRef}`;
|
|
547
597
|
}
|
|
548
598
|
function buildPiProviderProjection(profile) {
|
|
599
|
+
const { thinkingLevel: _, ...modelSettings } = requirePiModelConfig(profile);
|
|
549
600
|
const projectedProviderId = piProjectionProviderId(profile.profile_ref);
|
|
550
601
|
return {
|
|
551
602
|
providers: {
|
|
@@ -556,6 +607,7 @@ function buildPiProviderProjection(profile) {
|
|
|
556
607
|
...profile.auth_mode === "bearer" ? { authHeader: true } : {},
|
|
557
608
|
models: [
|
|
558
609
|
{
|
|
610
|
+
...modelSettings,
|
|
559
611
|
id: profile.model,
|
|
560
612
|
name: profile.display_name,
|
|
561
613
|
input: [
|
|
@@ -569,10 +621,34 @@ function buildPiProviderProjection(profile) {
|
|
|
569
621
|
};
|
|
570
622
|
}
|
|
571
623
|
function buildPiProviderArgs(profile, delegatedArgs) {
|
|
624
|
+
const config = requirePiModelConfig(profile);
|
|
625
|
+
if (delegatedArgs.length > 128) throw new Error("Pi launcher delegated argument limit exceeded");
|
|
572
626
|
let modeCount = 0;
|
|
627
|
+
let extensionCount = 0;
|
|
628
|
+
const singleFlags = /* @__PURE__ */ new Set();
|
|
573
629
|
for (let index = 0; index < delegatedArgs.length; index += 1) {
|
|
574
630
|
const flag = delegatedArgs[index];
|
|
575
|
-
if (flag
|
|
631
|
+
if (typeof flag !== "string" || /[\u0000\r\n]/u.test(flag)) throw new Error("Pi launcher argument must be single-line");
|
|
632
|
+
if (flag !== "--extension") {
|
|
633
|
+
if (singleFlags.has(flag)) throw new Error(`Pi launcher duplicate argument ${flag}`);
|
|
634
|
+
singleFlags.add(flag);
|
|
635
|
+
}
|
|
636
|
+
if (flag === "--no-tools" || flag === "--no-skills" || flag === "--no-extensions") continue;
|
|
637
|
+
if (flag === "--config") {
|
|
638
|
+
const value = delegatedArgs[++index];
|
|
639
|
+
if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
|
|
640
|
+
throw new Error("Pi launcher --config requires an absolute single-line path");
|
|
641
|
+
}
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (flag === "--extension") {
|
|
645
|
+
const value = delegatedArgs[++index];
|
|
646
|
+
if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
|
|
647
|
+
throw new Error("Pi launcher --extension requires an absolute single-line path");
|
|
648
|
+
}
|
|
649
|
+
if (++extensionCount > 16) throw new Error("Pi launcher extension limit exceeded");
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
576
652
|
if (flag === "--mode") {
|
|
577
653
|
modeCount += 1;
|
|
578
654
|
const value = delegatedArgs[index + 1];
|
|
@@ -582,7 +658,7 @@ function buildPiProviderArgs(profile, delegatedArgs) {
|
|
|
582
658
|
}
|
|
583
659
|
if (flag === "--session" || flag === "--tools" || flag === "--exclude-tools") {
|
|
584
660
|
const value = delegatedArgs[index + 1];
|
|
585
|
-
if (!value || value.startsWith("--")) {
|
|
661
|
+
if (!value || value.startsWith("--") || /[\u0000\r\n]/u.test(value)) {
|
|
586
662
|
throw new Error(`${flag} requires a value`);
|
|
587
663
|
}
|
|
588
664
|
index += 1;
|
|
@@ -591,52 +667,47 @@ function buildPiProviderArgs(profile, delegatedArgs) {
|
|
|
591
667
|
throw new Error(`Pi launcher does not allow delegated argument ${flag ?? "<missing>"}`);
|
|
592
668
|
}
|
|
593
669
|
if (modeCount !== 1) throw new Error("Pi launcher requires exactly one --mode rpc");
|
|
670
|
+
if (singleFlags.has("--no-tools") && singleFlags.has("--tools")) {
|
|
671
|
+
throw new Error("Pi launcher cannot combine --no-tools and --tools");
|
|
672
|
+
}
|
|
594
673
|
return [
|
|
595
674
|
...delegatedArgs,
|
|
596
675
|
"--provider",
|
|
597
676
|
piProjectionProviderId(profile.profile_ref),
|
|
598
677
|
"--model",
|
|
599
|
-
profile.model
|
|
678
|
+
profile.model,
|
|
679
|
+
"--thinking",
|
|
680
|
+
config.thinkingLevel
|
|
600
681
|
];
|
|
601
682
|
}
|
|
683
|
+
function buildPiPreparedArgs(delegatedArgs) {
|
|
684
|
+
if (delegatedArgs.length !== 2 || delegatedArgs[0] !== "--config") {
|
|
685
|
+
throw new Error("Pi prepared launcher requires exactly --config <path>");
|
|
686
|
+
}
|
|
687
|
+
const value = delegatedArgs[1];
|
|
688
|
+
if (typeof value !== "string" || !isAbsolute(value) || /[\u0000\r\n]/u.test(value)) {
|
|
689
|
+
throw new Error("Pi launcher --config requires an absolute single-line path");
|
|
690
|
+
}
|
|
691
|
+
return [...delegatedArgs];
|
|
692
|
+
}
|
|
693
|
+
function requirePiModelConfig(profile) {
|
|
694
|
+
if (profile.pi_model === void 0) throw new Error("Pi execution requires explicit pi_model configuration");
|
|
695
|
+
return PiModelConfigSchema.parse(profile.pi_model);
|
|
696
|
+
}
|
|
602
697
|
|
|
603
698
|
// src/pi-provider-launcher-core.ts
|
|
604
|
-
var PI_CHILD_BASE_ENV_NAMES = [
|
|
605
|
-
"PATH",
|
|
606
|
-
"HOME",
|
|
607
|
-
"USERPROFILE",
|
|
608
|
-
"TMPDIR",
|
|
609
|
-
"TEMP",
|
|
610
|
-
"TMP",
|
|
611
|
-
"LANG",
|
|
612
|
-
"TZ",
|
|
613
|
-
"TERM",
|
|
614
|
-
"SHELL",
|
|
615
|
-
"HTTP_PROXY",
|
|
616
|
-
"HTTPS_PROXY",
|
|
617
|
-
"NO_PROXY",
|
|
618
|
-
"ALL_PROXY",
|
|
619
|
-
"http_proxy",
|
|
620
|
-
"https_proxy",
|
|
621
|
-
"no_proxy",
|
|
622
|
-
"all_proxy"
|
|
623
|
-
];
|
|
624
|
-
var PI_CHILD_WINDOWS_ENV_NAMES = [
|
|
625
|
-
"SystemRoot",
|
|
626
|
-
"COMSPEC",
|
|
627
|
-
"PATHEXT",
|
|
628
|
-
"windir",
|
|
629
|
-
"SYSTEMDRIVE",
|
|
630
|
-
"PROGRAMFILES",
|
|
631
|
-
"APPDATA",
|
|
632
|
-
"LOCALAPPDATA"
|
|
633
|
-
];
|
|
634
699
|
function parsePiProviderLauncherOptions(args) {
|
|
635
700
|
const separator = args.indexOf("--");
|
|
636
701
|
const ownArgs = separator < 0 ? args : args.slice(0, separator);
|
|
637
702
|
const piArgs = separator < 0 ? [] : args.slice(separator + 1);
|
|
638
703
|
const allowedFlags = /* @__PURE__ */ new Set([
|
|
639
704
|
"--pi-bin",
|
|
705
|
+
"--pi-entry",
|
|
706
|
+
"--launch-binding",
|
|
707
|
+
"--pi-cwd",
|
|
708
|
+
"--pi-fixed-args",
|
|
709
|
+
"--pi-config-digest",
|
|
710
|
+
"--runtime-entry",
|
|
640
711
|
"--profile-db",
|
|
641
712
|
"--provider",
|
|
642
713
|
"--model",
|
|
@@ -667,6 +738,11 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
667
738
|
if (value === void 0) throw new Error(`${flag} requires a value`);
|
|
668
739
|
return value;
|
|
669
740
|
};
|
|
741
|
+
const rawRuntimeEntry = required("--runtime-entry");
|
|
742
|
+
if (!PI_LAUNCHER_RUNTIME_ENTRIES.includes(rawRuntimeEntry)) {
|
|
743
|
+
throw new Error(`--runtime-entry must be one of [${PI_LAUNCHER_RUNTIME_ENTRIES.join(", ")}]`);
|
|
744
|
+
}
|
|
745
|
+
const runtimeEntry = rawRuntimeEntry;
|
|
670
746
|
const rawProfileRef = required("--provider");
|
|
671
747
|
const profileRef = ProviderProfileRefSchema.safeParse(rawProfileRef);
|
|
672
748
|
if (!profileRef.success) {
|
|
@@ -725,8 +801,53 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
725
801
|
requiredCapabilities: parsedCapabilities.data
|
|
726
802
|
};
|
|
727
803
|
}
|
|
804
|
+
const piEntry = values.get("--pi-entry");
|
|
805
|
+
if (piEntry !== void 0 && (!path2.isAbsolute(piEntry) || /[\u0000\r\n]/u.test(piEntry))) {
|
|
806
|
+
throw new Error("--pi-entry requires an absolute single-line path");
|
|
807
|
+
}
|
|
808
|
+
let launchBinding;
|
|
809
|
+
let piCwd;
|
|
810
|
+
let piFixedArgs;
|
|
811
|
+
if (!validateOnly || ["--launch-binding", "--pi-cwd", "--pi-fixed-args"].some((flag) => values.has(flag))) {
|
|
812
|
+
let rawBinding;
|
|
813
|
+
let rawFixedArgs;
|
|
814
|
+
try {
|
|
815
|
+
rawBinding = JSON.parse(required("--launch-binding"));
|
|
816
|
+
} catch {
|
|
817
|
+
throw new Error("--launch-binding requires a valid JSON binding");
|
|
818
|
+
}
|
|
819
|
+
launchBinding = parseImplementationSpawnBinding(rawBinding);
|
|
820
|
+
if (launchBinding === void 0) throw new Error("invalid implementation spawn binding");
|
|
821
|
+
piCwd = required("--pi-cwd");
|
|
822
|
+
try {
|
|
823
|
+
rawFixedArgs = JSON.parse(required("--pi-fixed-args"));
|
|
824
|
+
} catch {
|
|
825
|
+
throw new Error("--pi-fixed-args requires a JSON array");
|
|
826
|
+
}
|
|
827
|
+
if (!Array.isArray(rawFixedArgs) || rawFixedArgs.some((arg) => typeof arg !== "string")) {
|
|
828
|
+
throw new Error("--pi-fixed-args requires a JSON array of strings");
|
|
829
|
+
}
|
|
830
|
+
piFixedArgs = rawFixedArgs;
|
|
831
|
+
if (launchBinding.command !== required("--pi-bin") || launchBinding.entry !== piEntry || launchBinding.cwd !== piCwd || JSON.stringify(launchBinding.fixedArgv) !== JSON.stringify(piFixedArgs)) {
|
|
832
|
+
throw new Error("launcher arguments differ from implementation spawn binding");
|
|
833
|
+
}
|
|
834
|
+
if (launchBinding.envCommitments.PI_CODING_AGENT_SESSION_DIR !== sessionDir || launchBinding.envCommitments.PI_CODING_AGENT_DIR === void 0) {
|
|
835
|
+
throw new Error("launcher session/projection directories must match binding commitments");
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const piConfigDigest = values.get("--pi-config-digest");
|
|
839
|
+
if ((!validateOnly || piConfigDigest !== void 0) && !/^[0-9a-f]{64}$/u.test(piConfigDigest ?? "")) {
|
|
840
|
+
throw new Error("--pi-config-digest requires 64 lowercase hexadecimal characters");
|
|
841
|
+
}
|
|
842
|
+
if (piArgs.some((arg) => arg === "--config-digest" || arg.startsWith("--config-digest="))) {
|
|
843
|
+
throw new Error("delegated --config-digest override is forbidden");
|
|
844
|
+
}
|
|
728
845
|
return {
|
|
846
|
+
...piConfigDigest === void 0 ? {} : { piConfigDigest },
|
|
847
|
+
...piEntry === void 0 ? {} : { piEntry },
|
|
848
|
+
runtimeEntry,
|
|
729
849
|
piBin: required("--pi-bin"),
|
|
850
|
+
...launchBinding === void 0 ? {} : { launchBinding, piCwd, piFixedArgs },
|
|
730
851
|
profileDbPath,
|
|
731
852
|
profileRef: profileRef.data,
|
|
732
853
|
modelId,
|
|
@@ -738,6 +859,20 @@ function parsePiProviderLauncherOptions(args) {
|
|
|
738
859
|
piArgs
|
|
739
860
|
};
|
|
740
861
|
}
|
|
862
|
+
function assertPiPreparedProviderProfile(profile) {
|
|
863
|
+
if (profile.adapter === "anthropic") {
|
|
864
|
+
throw new ByokKeysError(
|
|
865
|
+
"PROVIDER_PROFILE_INVALID",
|
|
866
|
+
`${profile.profile_ref} speaks the anthropic adapter; the prepared runtime entry compiles openai-completions only`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
if (profile.auth_mode === "none") {
|
|
870
|
+
throw new ByokKeysError(
|
|
871
|
+
"PROVIDER_PROFILE_INVALID",
|
|
872
|
+
`${profile.profile_ref} declares auth_mode "none"; the prepared runtime entry requires a provider credential`
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
741
876
|
async function resolvePiProviderSecret(profile, createStore) {
|
|
742
877
|
if (profile.auth_mode === "none") return void 0;
|
|
743
878
|
const secrets = createStore();
|
|
@@ -757,26 +892,139 @@ async function resolvePiProviderSecret(profile, createStore) {
|
|
|
757
892
|
return secret;
|
|
758
893
|
}
|
|
759
894
|
function buildPiProviderChildEnvironment(options) {
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const result = {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const isExact = exactNames.has(platformName);
|
|
770
|
-
const isPrefixed = platform === "win32" ? platformName.startsWith("LC_") || platformName.startsWith("XDG_") : name.startsWith("LC_") || name.startsWith("XDG_");
|
|
771
|
-
if (isExact || isPrefixed) result[name] = value;
|
|
772
|
-
}
|
|
773
|
-
result.PI_CODING_AGENT_DIR = options.projectionDir;
|
|
774
|
-
result.PI_CODING_AGENT_SESSION_DIR = options.sessionDir;
|
|
775
|
-
if (options.secret !== void 0) {
|
|
776
|
-
result[PI_PROJECTED_KEY_ENV] = options.secret;
|
|
777
|
-
}
|
|
895
|
+
const binding = parseImplementationSpawnBinding(options.binding);
|
|
896
|
+
if (binding === void 0) throw new Error("invalid implementation spawn binding");
|
|
897
|
+
if (binding.envCommitments.PI_CODING_AGENT_SESSION_DIR !== options.sessionDir || binding.envCommitments.PI_CODING_AGENT_DIR === void 0) {
|
|
898
|
+
throw new Error("launcher session/projection directories must match binding commitments");
|
|
899
|
+
}
|
|
900
|
+
const result = {
|
|
901
|
+
...projectKeysPiInheritedEnvironment(options.ambient, options.platform),
|
|
902
|
+
...binding.envCommitments
|
|
903
|
+
};
|
|
778
904
|
return result;
|
|
779
905
|
}
|
|
906
|
+
var WINDOWS_PROJECTION_ACL_SCRIPT = String.raw`
|
|
907
|
+
$ErrorActionPreference = 'Stop'
|
|
908
|
+
try {
|
|
909
|
+
$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
|
|
910
|
+
$acl = Get-Acl -LiteralPath ([string]$request.path)
|
|
911
|
+
$sidType = [System.Security.Principal.SecurityIdentifier]
|
|
912
|
+
$rules = @($acl.Access | ForEach-Object {
|
|
913
|
+
$sid = if ($_.IdentityReference -is [System.Security.Principal.SecurityIdentifier]) { $_.IdentityReference.Value } else { try { $_.IdentityReference.Translate($sidType).Value } catch { $null } }
|
|
914
|
+
[ordered]@{
|
|
915
|
+
sid = $sid
|
|
916
|
+
allow = $_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow
|
|
917
|
+
fullControl = ($_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl
|
|
918
|
+
inherits = ($_.InheritanceFlags -band 3) -eq 3 -and $_.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None
|
|
919
|
+
}
|
|
920
|
+
})
|
|
921
|
+
[ordered]@{
|
|
922
|
+
reparsePoint = ((Get-Item -LiteralPath ([string]$request.path) -Force).Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0
|
|
923
|
+
owner = $acl.GetOwner($sidType).Value
|
|
924
|
+
currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
925
|
+
protected = $acl.AreAccessRulesProtected
|
|
926
|
+
rules = $rules
|
|
927
|
+
} | ConvertTo-Json -Depth 4 -Compress
|
|
928
|
+
} catch {
|
|
929
|
+
[Console]::Error.WriteLine('Pi projection ACL query failed')
|
|
930
|
+
exit 1
|
|
931
|
+
}
|
|
932
|
+
`;
|
|
933
|
+
async function assertWindowsPiProjectionAcl(directory, options = {}) {
|
|
934
|
+
const systemRoot = options.systemRoot ?? process.env.SystemRoot;
|
|
935
|
+
if (systemRoot === void 0 || !path2.win32.isAbsolute(systemRoot) || /[\u0000\r\n]/u.test(systemRoot)) {
|
|
936
|
+
throw new Error("Windows SystemRoot must be an absolute path");
|
|
937
|
+
}
|
|
938
|
+
const result = await (options.run ?? runCommand)(
|
|
939
|
+
path2.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
|
|
940
|
+
["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(WINDOWS_PROJECTION_ACL_SCRIPT, "utf16le").toString("base64")],
|
|
941
|
+
JSON.stringify({ path: directory })
|
|
942
|
+
);
|
|
943
|
+
if (result.exitCode !== 0) throw new Error("Pi projection ACL query failed");
|
|
944
|
+
let acl;
|
|
945
|
+
try {
|
|
946
|
+
acl = JSON.parse(result.stdout);
|
|
947
|
+
} catch {
|
|
948
|
+
throw new Error("pi_projection_acl_invalid_json");
|
|
949
|
+
}
|
|
950
|
+
const recordObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
951
|
+
if (!recordObject(acl) || Object.keys(acl).sort().join(",") !== "currentUser,owner,protected,reparsePoint,rules" || typeof acl.owner !== "string" || !/^S-1-[0-9-]+$/u.test(acl.owner) || typeof acl.currentUser !== "string" || !/^S-1-[0-9-]+$/u.test(acl.currentUser) || typeof acl.protected !== "boolean" || typeof acl.reparsePoint !== "boolean" || !Array.isArray(acl.rules)) {
|
|
952
|
+
throw new Error("pi_projection_acl_invalid_shape");
|
|
953
|
+
}
|
|
954
|
+
if (acl.reparsePoint) throw new Error("pi_projection_reparse_point: Pi projection path must be a non-symlink directory");
|
|
955
|
+
if (acl.owner !== acl.currentUser) throw new Error("pi_projection_owner_mismatch: Pi projection directory must be owned by the current user");
|
|
956
|
+
if (!acl.protected) throw new Error("pi_projection_acl_unprotected");
|
|
957
|
+
const principals = /* @__PURE__ */ new Set([acl.owner, "S-1-5-18", "S-1-5-32-544"]);
|
|
958
|
+
let ownerControl = false;
|
|
959
|
+
for (const rule of acl.rules) {
|
|
960
|
+
if (!recordObject(rule) || Object.keys(rule).sort().join(",") !== "allow,fullControl,inherits,sid" || typeof rule.sid !== "string" || !/^S-1-[0-9-]+$/u.test(rule.sid) || typeof rule.allow !== "boolean" || typeof rule.fullControl !== "boolean" || typeof rule.inherits !== "boolean") {
|
|
961
|
+
throw new Error("pi_projection_acl_invalid_ace");
|
|
962
|
+
}
|
|
963
|
+
if (!principals.has(rule.sid) || !rule.allow) throw new Error("pi_projection_acl_unauthorized_ace");
|
|
964
|
+
if (rule.sid === acl.owner && rule.fullControl && rule.inherits) ownerControl = true;
|
|
965
|
+
}
|
|
966
|
+
if (!ownerControl) throw new Error("pi_projection_acl_owner_access_missing");
|
|
967
|
+
}
|
|
968
|
+
async function assertPiProjectionDirectory(projectionDir, expectedDir) {
|
|
969
|
+
if (projectionDir !== expectedDir || !path2.isAbsolute(projectionDir) || path2.normalize(projectionDir) !== projectionDir) {
|
|
970
|
+
throw new Error("pi_projection_path_mismatch: Pi projection directory differs from committed path");
|
|
971
|
+
}
|
|
972
|
+
const stat = await promises.lstat(projectionDir);
|
|
973
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("pi_projection_not_directory_or_symlink: Pi projection path must be a non-symlink directory");
|
|
974
|
+
if (await promises.realpath(projectionDir) !== projectionDir) throw new Error("pi_projection_path_not_canonical: Pi projection directory parents must be canonical, without symlinks");
|
|
975
|
+
if (process.platform === "win32") {
|
|
976
|
+
await assertWindowsPiProjectionAcl(projectionDir);
|
|
977
|
+
} else {
|
|
978
|
+
if (stat.uid !== process.getuid()) throw new Error("pi_projection_owner_mismatch: Pi projection directory must be owned by the current uid");
|
|
979
|
+
if ((stat.mode & 4095) !== 448) throw new Error("pi_projection_mode_mismatch: Pi projection directory must have mode 0700");
|
|
980
|
+
}
|
|
981
|
+
if ((await promises.readdir(projectionDir)).length !== 0) throw new Error("pi_projection_not_empty: Pi projection directory must be empty");
|
|
982
|
+
}
|
|
983
|
+
async function startPiProvider(profile, options, dependencies) {
|
|
984
|
+
const binding = options.launchBinding;
|
|
985
|
+
if (options.validateOnly || binding === void 0 || options.piCwd === void 0 || options.piFixedArgs === void 0 || !/^[0-9a-f]{64}$/u.test(options.piConfigDigest ?? "")) {
|
|
986
|
+
throw new Error("Pi launch requires an explicit spawn binding, cwd and fixed args");
|
|
987
|
+
}
|
|
988
|
+
const projection = buildPiProviderProjection(profile);
|
|
989
|
+
const delegated = options.runtimeEntry === "pi-prepared" ? buildPiPreparedArgs(options.piArgs) : buildPiProviderArgs(profile, options.piArgs);
|
|
990
|
+
const env = buildPiProviderChildEnvironment({
|
|
991
|
+
ambient: dependencies.ambient,
|
|
992
|
+
binding,
|
|
993
|
+
sessionDir: options.sessionDir});
|
|
994
|
+
const actual = { command: options.piBin, entry: options.piEntry, fixedArgv: [...options.piFixedArgs], cwd: options.piCwd, env };
|
|
995
|
+
await assertImplementationSpawnBinding(binding, actual);
|
|
996
|
+
const projectionDir = env.PI_CODING_AGENT_DIR;
|
|
997
|
+
await assertPiProjectionDirectory(projectionDir, binding.envCommitments.PI_CODING_AGENT_DIR);
|
|
998
|
+
await ensurePiSessionDirectory(options.sessionDir);
|
|
999
|
+
const modelsPath = path2.join(projectionDir, "models.json");
|
|
1000
|
+
let created = false;
|
|
1001
|
+
const cleanup = async () => {
|
|
1002
|
+
if (created) {
|
|
1003
|
+
await promises.unlink(modelsPath);
|
|
1004
|
+
created = false;
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
try {
|
|
1008
|
+
const file = await promises.open(modelsPath, "wx", 384);
|
|
1009
|
+
created = true;
|
|
1010
|
+
try {
|
|
1011
|
+
await file.writeFile(`${JSON.stringify(projection)}
|
|
1012
|
+
`);
|
|
1013
|
+
} finally {
|
|
1014
|
+
await file.close();
|
|
1015
|
+
}
|
|
1016
|
+
const secret = await resolvePiProviderSecret(profile, dependencies.createSecretStore);
|
|
1017
|
+
if (secret !== void 0) env[PI_PROJECTED_KEY_ENV] = secret;
|
|
1018
|
+
const childArgs = [...actual.entry === void 0 ? [] : [actual.entry], ...actual.fixedArgv, `--config-digest=${options.piConfigDigest}`, ...delegated];
|
|
1019
|
+
const spawnChild = dependencies.spawn ?? spawn;
|
|
1020
|
+
await assertImplementationSpawnBinding(binding, actual);
|
|
1021
|
+
const child = spawnChild(actual.command, childArgs, { env, cwd: actual.cwd, stdio: "inherit" });
|
|
1022
|
+
return { child, cleanup };
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
await cleanup();
|
|
1025
|
+
throw error;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
780
1028
|
async function ensurePiSessionDirectory(sessionDir) {
|
|
781
1029
|
const firstCreated = await promises.mkdir(sessionDir, { recursive: true, mode: 448 });
|
|
782
1030
|
if (firstCreated !== void 0) {
|
|
@@ -823,19 +1071,19 @@ function loadSqliteModule() {
|
|
|
823
1071
|
);
|
|
824
1072
|
}
|
|
825
1073
|
}
|
|
826
|
-
function openSqliteDatabase(
|
|
1074
|
+
function openSqliteDatabase(path3, options, faults) {
|
|
827
1075
|
const { DatabaseSync } = loadSqliteModule();
|
|
828
1076
|
const readOnly = options?.readOnly === true;
|
|
829
|
-
if (
|
|
830
|
-
mkdirSync(dirname(
|
|
1077
|
+
if (path3 !== ":memory:" && !readOnly) {
|
|
1078
|
+
mkdirSync(dirname(path3), { mode: SECURE_DIR_MODE, recursive: true });
|
|
831
1079
|
}
|
|
832
|
-
const database = new DatabaseSync(
|
|
1080
|
+
const database = new DatabaseSync(path3, {
|
|
833
1081
|
timeout: DEFAULT_BUSY_TIMEOUT_MS,
|
|
834
1082
|
...options
|
|
835
1083
|
});
|
|
836
1084
|
try {
|
|
837
1085
|
faults?.onStep?.("after-open");
|
|
838
|
-
if (
|
|
1086
|
+
if (path3 !== ":memory:" && !readOnly) {
|
|
839
1087
|
database.exec("PRAGMA journal_mode = WAL");
|
|
840
1088
|
faults?.onStep?.("after-wal");
|
|
841
1089
|
database.exec("PRAGMA synchronous = FULL");
|
|
@@ -878,6 +1126,7 @@ CREATE TABLE IF NOT EXISTS provider_profile (
|
|
|
878
1126
|
base_url TEXT NOT NULL,
|
|
879
1127
|
auth_mode TEXT NOT NULL CHECK (auth_mode IN (${sqlList(PROVIDER_AUTH_MODES)})),
|
|
880
1128
|
model TEXT NOT NULL,
|
|
1129
|
+
pi_model TEXT,
|
|
881
1130
|
capabilities TEXT NOT NULL,
|
|
882
1131
|
enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),
|
|
883
1132
|
created_at TEXT NOT NULL,
|
|
@@ -887,7 +1136,7 @@ CREATE TABLE IF NOT EXISTS provider_profile (
|
|
|
887
1136
|
function normalizeTableDdl(sql) {
|
|
888
1137
|
return sql.replace(/\bIF\s+NOT\s+EXISTS\b/giu, "").replace(/\s+/gu, " ").trim().replace(/;$/u, "").trim();
|
|
889
1138
|
}
|
|
890
|
-
function assertProviderProfileSchemaIsCurrent(database,
|
|
1139
|
+
function assertProviderProfileSchemaIsCurrent(database, path3) {
|
|
891
1140
|
const row = database.prepare(
|
|
892
1141
|
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'provider_profile'"
|
|
893
1142
|
).get();
|
|
@@ -896,7 +1145,7 @@ function assertProviderProfileSchemaIsCurrent(database, path4) {
|
|
|
896
1145
|
if (normalizeTableDdl(stored) === normalizeTableDdl(SCHEMA)) return;
|
|
897
1146
|
throw new ByokKeysError(
|
|
898
1147
|
"PROVIDER_STORE_SCHEMA_STALE",
|
|
899
|
-
`Provider profile store at ${
|
|
1148
|
+
`Provider profile store at ${path3} was created by a different @byok-sdk/keys schema; preserve this store and explicitly provision a separate current-schema store before continuing`
|
|
900
1149
|
);
|
|
901
1150
|
}
|
|
902
1151
|
var ENABLED_INDEX = `
|
|
@@ -978,8 +1227,8 @@ var SqliteProviderProfileStore = class {
|
|
|
978
1227
|
this.#database.prepare(
|
|
979
1228
|
`INSERT INTO provider_profile (
|
|
980
1229
|
profile_ref, provider_kind, kind, adapter, display_name, base_url,
|
|
981
|
-
auth_mode, model, capabilities, enabled, created_at, updated_at
|
|
982
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1230
|
+
auth_mode, model, pi_model, capabilities, enabled, created_at, updated_at
|
|
1231
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
983
1232
|
ON CONFLICT(profile_ref) DO UPDATE SET
|
|
984
1233
|
provider_kind = excluded.provider_kind,
|
|
985
1234
|
adapter = excluded.adapter,
|
|
@@ -987,6 +1236,7 @@ var SqliteProviderProfileStore = class {
|
|
|
987
1236
|
base_url = excluded.base_url,
|
|
988
1237
|
auth_mode = excluded.auth_mode,
|
|
989
1238
|
model = excluded.model,
|
|
1239
|
+
pi_model = excluded.pi_model,
|
|
990
1240
|
capabilities = excluded.capabilities,
|
|
991
1241
|
enabled = excluded.enabled,
|
|
992
1242
|
updated_at = excluded.updated_at`
|
|
@@ -999,6 +1249,7 @@ var SqliteProviderProfileStore = class {
|
|
|
999
1249
|
validated.base_url,
|
|
1000
1250
|
validated.auth_mode,
|
|
1001
1251
|
validated.model,
|
|
1252
|
+
validated.pi_model === void 0 ? null : JSON.stringify(validated.pi_model),
|
|
1002
1253
|
JSON.stringify(validated.capabilities),
|
|
1003
1254
|
validated.enabled ? 1 : 0,
|
|
1004
1255
|
validated.created_at,
|
|
@@ -1037,6 +1288,7 @@ function parseRow(row) {
|
|
|
1037
1288
|
}
|
|
1038
1289
|
return parseModelProviderProfile({
|
|
1039
1290
|
...row,
|
|
1291
|
+
pi_model: row.pi_model === null ? void 0 : JSON.parse(row.pi_model),
|
|
1040
1292
|
capabilities,
|
|
1041
1293
|
enabled: row.enabled === 1
|
|
1042
1294
|
});
|
|
@@ -1343,7 +1595,7 @@ async function run(options) {
|
|
|
1343
1595
|
path: options.profileDbPath,
|
|
1344
1596
|
readOnly: true
|
|
1345
1597
|
});
|
|
1346
|
-
let
|
|
1598
|
+
let cleanup;
|
|
1347
1599
|
try {
|
|
1348
1600
|
const profile = await profiles.get(options.profileRef);
|
|
1349
1601
|
if (profile === void 0) {
|
|
@@ -1357,33 +1609,15 @@ async function run(options) {
|
|
|
1357
1609
|
if (options.expectedBinding !== void 0) {
|
|
1358
1610
|
assertExactProviderProfileBinding(profile, options.expectedBinding);
|
|
1359
1611
|
}
|
|
1612
|
+
if (options.runtimeEntry === "pi-prepared") assertPiPreparedProviderProfile(profile);
|
|
1613
|
+
buildPiProviderProjection(profile);
|
|
1360
1614
|
if (options.validateOnly) return 0;
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
() => createSecretStore(
|
|
1364
|
-
options.secretServicePrefix,
|
|
1365
|
-
options.macosKeychainPath
|
|
1366
|
-
)
|
|
1367
|
-
);
|
|
1368
|
-
projectionDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-pi-provider-"));
|
|
1369
|
-
await promises.chmod(projectionDir, 448).catch(() => {
|
|
1370
|
-
});
|
|
1371
|
-
await ensurePiSessionDirectory(options.sessionDir);
|
|
1372
|
-
await promises.writeFile(
|
|
1373
|
-
path2.join(projectionDir, "models.json"),
|
|
1374
|
-
`${JSON.stringify(buildPiProviderProjection(profile))}
|
|
1375
|
-
`,
|
|
1376
|
-
{ mode: 384 }
|
|
1377
|
-
);
|
|
1378
|
-
const child = spawn(options.piBin, buildPiProviderArgs(profile, options.piArgs), {
|
|
1379
|
-
env: buildPiProviderChildEnvironment({
|
|
1380
|
-
ambient: process.env,
|
|
1381
|
-
projectionDir,
|
|
1382
|
-
sessionDir: options.sessionDir,
|
|
1383
|
-
secret
|
|
1384
|
-
}),
|
|
1385
|
-
stdio: "inherit"
|
|
1615
|
+
const launched = await startPiProvider(profile, options, {
|
|
1616
|
+
ambient: process.env,
|
|
1617
|
+
createSecretStore: () => createSecretStore(options.secretServicePrefix, options.macosKeychainPath)
|
|
1386
1618
|
});
|
|
1619
|
+
cleanup = launched.cleanup;
|
|
1620
|
+
const child = launched.child;
|
|
1387
1621
|
const forward = (signal) => {
|
|
1388
1622
|
if (!child.killed) child.kill(signal);
|
|
1389
1623
|
};
|
|
@@ -1404,9 +1638,7 @@ async function run(options) {
|
|
|
1404
1638
|
}
|
|
1405
1639
|
} finally {
|
|
1406
1640
|
await profiles.close();
|
|
1407
|
-
|
|
1408
|
-
await promises.rm(projectionDir, { recursive: true, force: true });
|
|
1409
|
-
}
|
|
1641
|
+
await cleanup?.();
|
|
1410
1642
|
}
|
|
1411
1643
|
}
|
|
1412
1644
|
async function main() {
|