@akagilnc/pi-workflow-roles 0.1.3737 → 0.1.3744
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 +4 -6
- package/README.zh-CN.md +4 -6
- package/dist/acp-host/production-host.js +278 -201
- package/dist/public-cli/config.js +2 -102
- package/dist/public-cli/host-providers.js +188 -0
- package/dist/public-cli/main.js +350 -324
- package/dist/public-cli/option-definitions.js +1 -4
- package/dist/public-role-summons.js +8 -4
- package/package.json +1 -1
- package/src/public-cli/cli.ts +38 -71
- package/src/public-cli/config.ts +3 -153
- package/src/public-cli/host-providers.ts +246 -0
- package/src/public-cli/option-definitions.ts +1 -4
- package/src/public-role-summons.ts +14 -7
|
@@ -1068,7 +1068,7 @@ const SUPPORT_COMMAND_HELP = {
|
|
|
1068
1068
|
},
|
|
1069
1069
|
config: {
|
|
1070
1070
|
command: "config",
|
|
1071
|
-
summary: "Persistent seat model, labor-engine, host, and auto-resume defaults.",
|
|
1071
|
+
summary: "Persistent seat model, labor-engine, host, and auto-resume defaults. Host providers live in ~/.ak-roles/host-providers.json (owner-edited).",
|
|
1072
1072
|
usage: [
|
|
1073
1073
|
"ak-role config set <seat> <provider/model[:thinking]> [<seat> <spec> ...]",
|
|
1074
1074
|
"ak-role config unset <gatekeeper|inspector|notary>",
|
|
@@ -1077,8 +1077,6 @@ const SUPPORT_COMMAND_HELP = {
|
|
|
1077
1077
|
"ak-role config set-host <seat> <name>",
|
|
1078
1078
|
"ak-role config unset-host <seat>",
|
|
1079
1079
|
"ak-role config set-auto-resume-limit <N>",
|
|
1080
|
-
"ak-role config set-provider-alias <provider> <host> <alias>",
|
|
1081
|
-
"ak-role config unset-provider-alias <provider> <host>",
|
|
1082
1080
|
],
|
|
1083
1081
|
examples: [
|
|
1084
1082
|
"ak-role config set judge openai-codex/gpt-5.6-sol:high",
|
|
@@ -1086,7 +1084,6 @@ const SUPPORT_COMMAND_HELP = {
|
|
|
1086
1084
|
"ak-role config set-engine judge opus",
|
|
1087
1085
|
"ak-role config set-host judge grok-build",
|
|
1088
1086
|
"ak-role config set-auto-resume-limit 3",
|
|
1089
|
-
"ak-role config set-provider-alias xai hermes xai-oauth",
|
|
1090
1087
|
],
|
|
1091
1088
|
},
|
|
1092
1089
|
help: {
|
|
@@ -70,9 +70,6 @@ function projectSeatEngine(seat) {
|
|
|
70
70
|
function projectSeatHost(seat) {
|
|
71
71
|
return seat.host === void 0 ? {} : { host: seat.host };
|
|
72
72
|
}
|
|
73
|
-
function projectSeatModel(seat) {
|
|
74
|
-
return seat.selection === void 0 ? {} : { model: seat.selection };
|
|
75
|
-
}
|
|
76
73
|
async function createSummonEnv(options) {
|
|
77
74
|
const [{ piDurablePrincipalAuthority }, { appendPiSessionCustomEntry, createPiRoleTurnHost }] = await Promise.all([
|
|
78
75
|
import("./pi/durable-principal.js"),
|
|
@@ -122,6 +119,13 @@ async function createSummonEnv(options) {
|
|
|
122
119
|
}
|
|
123
120
|
};
|
|
124
121
|
}
|
|
122
|
+
const { loadHostProvidersTable, projectHostFacingProvider } = await import("./public-cli/host-providers.js");
|
|
123
|
+
const hostFacingSelection = options.seat.selection === void 0 ? void 0 : projectHostFacingProvider(
|
|
124
|
+
options.seat.selection,
|
|
125
|
+
hostName,
|
|
126
|
+
loadHostProvidersTable(options.home),
|
|
127
|
+
options.home
|
|
128
|
+
);
|
|
125
129
|
return {
|
|
126
130
|
home: options.home,
|
|
127
131
|
principalAuthority,
|
|
@@ -131,7 +135,7 @@ async function createSummonEnv(options) {
|
|
|
131
135
|
roleTurnHost,
|
|
132
136
|
cwd: options.cwd,
|
|
133
137
|
credentials: options.credentials,
|
|
134
|
-
...
|
|
138
|
+
...hostFacingSelection === void 0 ? {} : { model: hostFacingSelection },
|
|
135
139
|
...projectSeatEngine(options.seat),
|
|
136
140
|
...projectSeatHost(options.seat)
|
|
137
141
|
};
|
package/package.json
CHANGED
package/src/public-cli/cli.ts
CHANGED
|
@@ -28,14 +28,17 @@ import {
|
|
|
28
28
|
setPersistentSeatConfig,
|
|
29
29
|
setPersistentSeatEngine,
|
|
30
30
|
setPersistentSeatHost,
|
|
31
|
-
setProviderHostAlias,
|
|
32
|
-
unsetProviderHostAlias,
|
|
33
31
|
validatePublicCliConfigAxes,
|
|
34
32
|
type CredentialProviders,
|
|
35
33
|
type EffectiveSeat,
|
|
36
34
|
type InvocationModelOverride,
|
|
37
35
|
type PublicCliConfig,
|
|
38
36
|
} from "./config.ts";
|
|
37
|
+
import {
|
|
38
|
+
loadHostProvidersTable,
|
|
39
|
+
projectHostFacingProvider,
|
|
40
|
+
renderHostProvidersTable,
|
|
41
|
+
} from "./host-providers.ts";
|
|
39
42
|
import { seatModelOnly } from "./registry.ts";
|
|
40
43
|
import { CliUsageError } from "./cli-errors.ts";
|
|
41
44
|
import type { CliIo } from "./cli-io.ts";
|
|
@@ -428,23 +431,36 @@ function createRoleEnvironment(
|
|
|
428
431
|
|
|
429
432
|
// #617 DK-3: resume and new legs share one seat resolution — model/host/engine
|
|
430
433
|
// come from the live seat table (invocation flag → persistent → default).
|
|
434
|
+
// #788 expectation 2: select a registered host first; only then project the
|
|
435
|
+
// host-facing provider (table > unique directory > fail). Bad host never
|
|
436
|
+
// reaches model resolution.
|
|
437
|
+
const roleTurnHost = resolveRoleTurnHost(env, {
|
|
438
|
+
role,
|
|
439
|
+
seat: options.seat,
|
|
440
|
+
principalAuthority: env.principalAuthority!,
|
|
441
|
+
...(extraPiArgs === undefined ? {} : { extraPiArgs }),
|
|
442
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
443
|
+
});
|
|
444
|
+
const hostFacingSelection =
|
|
445
|
+
options.seat.selection === undefined
|
|
446
|
+
? undefined
|
|
447
|
+
: projectHostFacingProvider(
|
|
448
|
+
options.seat.selection,
|
|
449
|
+
options.seat.host,
|
|
450
|
+
loadHostProvidersTable(options.home),
|
|
451
|
+
options.home,
|
|
452
|
+
);
|
|
431
453
|
return {
|
|
432
454
|
home: options.home,
|
|
433
455
|
principalAuthority: env.principalAuthority!,
|
|
434
456
|
agentDir: options.agentDir,
|
|
435
457
|
sessionAppender: appendPiSessionCustomEntry,
|
|
436
458
|
packageRoot: env.packageRoot,
|
|
437
|
-
roleTurnHost
|
|
438
|
-
role,
|
|
439
|
-
seat: options.seat,
|
|
440
|
-
principalAuthority: env.principalAuthority!,
|
|
441
|
-
...(extraPiArgs === undefined ? {} : { extraPiArgs }),
|
|
442
|
-
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
443
|
-
}),
|
|
459
|
+
roleTurnHost,
|
|
444
460
|
cwd: options.cwd,
|
|
445
461
|
...(options.credentials === undefined ? {} : { credentials: options.credentials }),
|
|
446
462
|
...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
|
|
447
|
-
...(
|
|
463
|
+
...(hostFacingSelection === undefined ? {} : { model: hostFacingSelection }),
|
|
448
464
|
...projectSeatEngine(options.seat),
|
|
449
465
|
...projectSeatHost(options.seat),
|
|
450
466
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
@@ -771,7 +787,7 @@ function renderHelp(): string {
|
|
|
771
787
|
"Persistent config: ak-role config set <seat> <provider/model[:thinking]> | unset <gatekeeper|inspector|notary>",
|
|
772
788
|
"Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
|
|
773
789
|
"Persistent host (callable roles): ak-role config set-host <seat> <name> | unset-host <seat>",
|
|
774
|
-
"
|
|
790
|
+
"Host providers: ~/.ak-roles/host-providers.json (owner-edited; table > unique host directory > fail)",
|
|
775
791
|
"Host resolution: --host → persistent seat host → pi (resume uses the same order; #617)",
|
|
776
792
|
"Effective seats: ak-role roles",
|
|
777
793
|
);
|
|
@@ -900,7 +916,7 @@ function renderConfigDisplaySeat(row: ConfigDisplaySeat): string {
|
|
|
900
916
|
return `${row.seat}\t${row.source}\t${model}\t${engine}\t${host}`;
|
|
901
917
|
}
|
|
902
918
|
|
|
903
|
-
function renderConfig(config: PublicCliConfig): string {
|
|
919
|
+
function renderConfig(config: PublicCliConfig, home: string): string {
|
|
904
920
|
const lines: string[] = ["seat\tsource\tmodel\tengine\thost"];
|
|
905
921
|
const rows = projectConfigDisplaySeats(config);
|
|
906
922
|
if (rows.length === 0) {
|
|
@@ -912,17 +928,9 @@ function renderConfig(config: PublicCliConfig): string {
|
|
|
912
928
|
}
|
|
913
929
|
// #422: show the effective auto-resume ceiling (configured value or default).
|
|
914
930
|
lines.push(`autoResumeLimit\t${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
|
|
915
|
-
// #
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
for (const provider of Object.keys(aliases).sort()) {
|
|
919
|
-
const byHost = aliases[provider]!;
|
|
920
|
-
for (const host of Object.keys(byHost).sort()) {
|
|
921
|
-
lines.push(`providerAlias\t${provider}\t${host}\t${byHost[host]}`);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
return `${lines.join("\n")}\n`;
|
|
931
|
+
// #788: owner host-providers table as written on disk (separate file).
|
|
932
|
+
const hostProvidersBlock = renderHostProvidersTable(loadHostProvidersTable(home));
|
|
933
|
+
return `${lines.join("\n")}\n${hostProvidersBlock}`;
|
|
926
934
|
}
|
|
927
935
|
|
|
928
936
|
async function runConfigCommand(
|
|
@@ -942,7 +950,7 @@ async function runConfigCommand(
|
|
|
942
950
|
);
|
|
943
951
|
return 0;
|
|
944
952
|
}
|
|
945
|
-
io.stdout(renderConfig(config));
|
|
953
|
+
io.stdout(renderConfig(config, home));
|
|
946
954
|
return 0;
|
|
947
955
|
}
|
|
948
956
|
|
|
@@ -971,7 +979,7 @@ async function runConfigCommand(
|
|
|
971
979
|
config = setPersistentSeatConfig(config, seat, parseModelSpec(spec));
|
|
972
980
|
}
|
|
973
981
|
await savePublicCliConfig(config, home);
|
|
974
|
-
io.stdout(renderConfig(config));
|
|
982
|
+
io.stdout(renderConfig(config, home));
|
|
975
983
|
return 0;
|
|
976
984
|
}
|
|
977
985
|
|
|
@@ -993,7 +1001,7 @@ async function runConfigCommand(
|
|
|
993
1001
|
seat,
|
|
994
1002
|
);
|
|
995
1003
|
await savePublicCliConfig(config, home);
|
|
996
|
-
io.stdout(renderConfig(config));
|
|
1004
|
+
io.stdout(renderConfig(config, home));
|
|
997
1005
|
return 0;
|
|
998
1006
|
}
|
|
999
1007
|
|
|
@@ -1011,7 +1019,7 @@ async function runConfigCommand(
|
|
|
1011
1019
|
throw new CliUsageError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
1012
1020
|
}
|
|
1013
1021
|
await savePublicCliConfig(config, home);
|
|
1014
|
-
io.stdout(renderConfig(config));
|
|
1022
|
+
io.stdout(renderConfig(config, home));
|
|
1015
1023
|
return 0;
|
|
1016
1024
|
}
|
|
1017
1025
|
|
|
@@ -1035,7 +1043,7 @@ async function runConfigCommand(
|
|
|
1035
1043
|
);
|
|
1036
1044
|
}
|
|
1037
1045
|
await savePublicCliConfig(config, home);
|
|
1038
|
-
io.stdout(renderConfig(config));
|
|
1046
|
+
io.stdout(renderConfig(config, home));
|
|
1039
1047
|
return 0;
|
|
1040
1048
|
}
|
|
1041
1049
|
|
|
@@ -1057,7 +1065,7 @@ async function runConfigCommand(
|
|
|
1057
1065
|
);
|
|
1058
1066
|
}
|
|
1059
1067
|
await savePublicCliConfig(config, home);
|
|
1060
|
-
io.stdout(renderConfig(config));
|
|
1068
|
+
io.stdout(renderConfig(config, home));
|
|
1061
1069
|
return 0;
|
|
1062
1070
|
}
|
|
1063
1071
|
|
|
@@ -1090,48 +1098,7 @@ async function runConfigCommand(
|
|
|
1090
1098
|
let config = await loadAndValidateConfig(home, packageRoot);
|
|
1091
1099
|
config = setAutoResumeLimit(config, converted);
|
|
1092
1100
|
await savePublicCliConfig(config, home);
|
|
1093
|
-
io.stdout(renderConfig(config));
|
|
1094
|
-
return 0;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
// #778: owner-registered provider→host alias (opaque strings; no host catalog).
|
|
1098
|
-
if (args[0] === "set-provider-alias") {
|
|
1099
|
-
if (args.length !== 4) {
|
|
1100
|
-
throw new CliUsageError(
|
|
1101
|
-
"usage: ak-role config set-provider-alias <provider> <host> <alias>",
|
|
1102
|
-
);
|
|
1103
|
-
}
|
|
1104
|
-
let config = await loadAndValidateConfig(home, packageRoot);
|
|
1105
|
-
try {
|
|
1106
|
-
config = setProviderHostAlias(config, args[1]!, args[2]!, args[3]!);
|
|
1107
|
-
} catch (error) {
|
|
1108
|
-
throw new CliUsageError(
|
|
1109
|
-
error instanceof Error ? error.message : String(error),
|
|
1110
|
-
{ cause: error },
|
|
1111
|
-
);
|
|
1112
|
-
}
|
|
1113
|
-
await savePublicCliConfig(config, home);
|
|
1114
|
-
io.stdout(renderConfig(config));
|
|
1115
|
-
return 0;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
if (args[0] === "unset-provider-alias") {
|
|
1119
|
-
if (args.length !== 3) {
|
|
1120
|
-
throw new CliUsageError(
|
|
1121
|
-
"usage: ak-role config unset-provider-alias <provider> <host>",
|
|
1122
|
-
);
|
|
1123
|
-
}
|
|
1124
|
-
let config = await loadAndValidateConfig(home, packageRoot);
|
|
1125
|
-
try {
|
|
1126
|
-
config = unsetProviderHostAlias(config, args[1]!, args[2]!);
|
|
1127
|
-
} catch (error) {
|
|
1128
|
-
throw new CliUsageError(
|
|
1129
|
-
error instanceof Error ? error.message : String(error),
|
|
1130
|
-
{ cause: error },
|
|
1131
|
-
);
|
|
1132
|
-
}
|
|
1133
|
-
await savePublicCliConfig(config, home);
|
|
1134
|
-
io.stdout(renderConfig(config));
|
|
1101
|
+
io.stdout(renderConfig(config, home));
|
|
1135
1102
|
return 0;
|
|
1136
1103
|
}
|
|
1137
1104
|
|
package/src/public-cli/config.ts
CHANGED
|
@@ -54,15 +54,6 @@ export type PersistentSeatConfig = {
|
|
|
54
54
|
host?: string;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* #778: owner-registered provider name by host. Keyed seat-table provider →
|
|
59
|
-
* host → host-facing provider. Package code carries the map only — no built-in
|
|
60
|
-
* pairs. Unregistered (provider, host) pairs pass the seat provider through.
|
|
61
|
-
*/
|
|
62
|
-
export type ProviderHostAliases = Readonly<
|
|
63
|
-
Record<string, Readonly<Record<string, string>>>
|
|
64
|
-
>;
|
|
65
|
-
|
|
66
57
|
export type PublicCliConfig = {
|
|
67
58
|
seats: Partial<Record<PublicConfigurableSeat, PersistentSeatConfig>>;
|
|
68
59
|
/**
|
|
@@ -72,11 +63,6 @@ export type PublicCliConfig = {
|
|
|
72
63
|
* bound (ADR 0035).
|
|
73
64
|
*/
|
|
74
65
|
autoResumeLimit?: number;
|
|
75
|
-
/**
|
|
76
|
-
* #778: owner-registered provider→host aliases. Sibling of `seats`; never
|
|
77
|
-
* invents pairs. Absent or empty means pure pass-through.
|
|
78
|
-
*/
|
|
79
|
-
providerAliases?: ProviderHostAliases;
|
|
80
66
|
/**
|
|
81
67
|
* #592: opaque seat rows this build does not own. Carried through every
|
|
82
68
|
* parse→save cycle so a write never silently erases neighboring-line rows
|
|
@@ -307,81 +293,6 @@ export function setAutoResumeLimit(
|
|
|
307
293
|
return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
|
|
308
294
|
}
|
|
309
295
|
|
|
310
|
-
function requireNonEmptyToken(value: string, label: string): string {
|
|
311
|
-
const trimmed = value.trim();
|
|
312
|
-
if (trimmed === "") {
|
|
313
|
-
throw new Error(`${label} must be a non-empty string`);
|
|
314
|
-
}
|
|
315
|
-
return trimmed;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* #778: register one owner provider→host alias. Opaque strings only — no host
|
|
320
|
-
* catalog check and no built-in provider pairs. Sibling keys (seats included)
|
|
321
|
-
* are preserved.
|
|
322
|
-
*/
|
|
323
|
-
export function setProviderHostAlias(
|
|
324
|
-
config: PublicCliConfig,
|
|
325
|
-
provider: string,
|
|
326
|
-
host: string,
|
|
327
|
-
alias: string,
|
|
328
|
-
): PublicCliConfig {
|
|
329
|
-
const from = requireNonEmptyToken(provider, "provider");
|
|
330
|
-
const hostName = requireNonEmptyToken(host, "host");
|
|
331
|
-
const to = requireNonEmptyToken(alias, "alias");
|
|
332
|
-
const previous = config.providerAliases ?? {};
|
|
333
|
-
return {
|
|
334
|
-
...config,
|
|
335
|
-
providerAliases: {
|
|
336
|
-
...previous,
|
|
337
|
-
[from]: {
|
|
338
|
-
...(previous[from] ?? {}),
|
|
339
|
-
[hostName]: to,
|
|
340
|
-
},
|
|
341
|
-
},
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/** #778: drop one owner provider→host alias. Empty map collapses to absent. */
|
|
346
|
-
export function unsetProviderHostAlias(
|
|
347
|
-
config: PublicCliConfig,
|
|
348
|
-
provider: string,
|
|
349
|
-
host: string,
|
|
350
|
-
): PublicCliConfig {
|
|
351
|
-
const from = requireNonEmptyToken(provider, "provider");
|
|
352
|
-
const hostName = requireNonEmptyToken(host, "host");
|
|
353
|
-
const previous = config.providerAliases;
|
|
354
|
-
if (previous === undefined || previous[from] === undefined) return config;
|
|
355
|
-
const { [hostName]: _dropped, ...restHosts } = previous[from]!;
|
|
356
|
-
const nextForProvider =
|
|
357
|
-
Object.keys(restHosts).length === 0 ? undefined : restHosts;
|
|
358
|
-
const { [from]: _provider, ...restProviders } = previous;
|
|
359
|
-
const nextAliases =
|
|
360
|
-
nextForProvider === undefined
|
|
361
|
-
? restProviders
|
|
362
|
-
: { ...restProviders, [from]: nextForProvider };
|
|
363
|
-
if (Object.keys(nextAliases).length === 0) {
|
|
364
|
-
const { providerAliases: _gone, ...rest } = config;
|
|
365
|
-
return rest;
|
|
366
|
-
}
|
|
367
|
-
return { ...config, providerAliases: nextAliases };
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* #778: project seat-table provider through owner aliases for one host.
|
|
372
|
-
* Unregistered pairs return the seat selection unchanged (pass-through).
|
|
373
|
-
*/
|
|
374
|
-
export function applyProviderHostAlias(
|
|
375
|
-
selection: SeatModelConfig | undefined,
|
|
376
|
-
host: string,
|
|
377
|
-
aliases: ProviderHostAliases | undefined,
|
|
378
|
-
): SeatModelConfig | undefined {
|
|
379
|
-
if (selection === undefined || aliases === undefined) return selection;
|
|
380
|
-
const mapped = aliases[selection.provider]?.[host];
|
|
381
|
-
if (mapped === undefined) return selection;
|
|
382
|
-
return { ...selection, provider: mapped };
|
|
383
|
-
}
|
|
384
|
-
|
|
385
296
|
/**
|
|
386
297
|
* Config-parse seam: persistent call axes belong to PUBLIC_CALLABLE_ROLES;
|
|
387
298
|
* engine names need only path-safety syntax (no closed material catalog;
|
|
@@ -476,7 +387,6 @@ export function buildSeatModelCliArgs(model: SeatModelConfig | undefined): strin
|
|
|
476
387
|
function serializePublicCliConfig(config: PublicCliConfig): {
|
|
477
388
|
seats: Record<string, unknown>;
|
|
478
389
|
autoResumeLimit?: number;
|
|
479
|
-
providerAliases?: ProviderHostAliases;
|
|
480
390
|
} {
|
|
481
391
|
return {
|
|
482
392
|
// Known seats win on any key clash; by construction the two maps are
|
|
@@ -488,48 +398,9 @@ function serializePublicCliConfig(config: PublicCliConfig): {
|
|
|
488
398
|
...(config.autoResumeLimit === undefined
|
|
489
399
|
? {}
|
|
490
400
|
: { autoResumeLimit: config.autoResumeLimit }),
|
|
491
|
-
...(config.providerAliases === undefined ||
|
|
492
|
-
Object.keys(config.providerAliases).length === 0
|
|
493
|
-
? {}
|
|
494
|
-
: { providerAliases: config.providerAliases }),
|
|
495
401
|
};
|
|
496
402
|
}
|
|
497
403
|
|
|
498
|
-
function parseProviderHostAliases(value: unknown): ProviderHostAliases {
|
|
499
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
500
|
-
throw new Error("public CLI config.providerAliases must be an object");
|
|
501
|
-
}
|
|
502
|
-
const out: Record<string, Record<string, string>> = {};
|
|
503
|
-
for (const [provider, byHost] of Object.entries(value as Record<string, unknown>)) {
|
|
504
|
-
if (provider.trim() === "") {
|
|
505
|
-
throw new Error("public CLI config.providerAliases provider key must be non-empty");
|
|
506
|
-
}
|
|
507
|
-
if (byHost === null || typeof byHost !== "object" || Array.isArray(byHost)) {
|
|
508
|
-
throw new Error(
|
|
509
|
-
`public CLI config.providerAliases[${provider}] must be an object`,
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
const hosts: Record<string, string> = {};
|
|
513
|
-
for (const [host, alias] of Object.entries(byHost as Record<string, unknown>)) {
|
|
514
|
-
if (host.trim() === "") {
|
|
515
|
-
throw new Error(
|
|
516
|
-
`public CLI config.providerAliases[${provider}] host key must be non-empty`,
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
if (typeof alias !== "string" || alias.trim() === "") {
|
|
520
|
-
throw new Error(
|
|
521
|
-
`public CLI config.providerAliases[${provider}][${host}] must be a non-empty string`,
|
|
522
|
-
);
|
|
523
|
-
}
|
|
524
|
-
hosts[host] = alias;
|
|
525
|
-
}
|
|
526
|
-
if (Object.keys(hosts).length > 0) {
|
|
527
|
-
out[provider] = hosts;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return out;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
404
|
function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
534
405
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
535
406
|
throw new Error("public CLI config must be an object");
|
|
@@ -537,7 +408,6 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
|
537
408
|
const record = value as {
|
|
538
409
|
seats?: unknown;
|
|
539
410
|
autoResumeLimit?: unknown;
|
|
540
|
-
providerAliases?: unknown;
|
|
541
411
|
unknownSeats?: unknown;
|
|
542
412
|
};
|
|
543
413
|
// #422 round-trip preservation: the sibling top-level key must survive every
|
|
@@ -546,15 +416,6 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
|
546
416
|
if (record.autoResumeLimit !== undefined) {
|
|
547
417
|
autoResumeLimit = parseAutoResumeLimit(record.autoResumeLimit);
|
|
548
418
|
}
|
|
549
|
-
// #778: same survival duty as autoResumeLimit — owner aliases must not vanish
|
|
550
|
-
// on an unrelated seat write.
|
|
551
|
-
let providerAliases: ProviderHostAliases | undefined;
|
|
552
|
-
if (record.providerAliases !== undefined) {
|
|
553
|
-
const parsed = parseProviderHostAliases(record.providerAliases);
|
|
554
|
-
if (Object.keys(parsed).length > 0) {
|
|
555
|
-
providerAliases = parsed;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
419
|
// #592: opaque bucket may already be present on an in-memory round-trip
|
|
559
420
|
// (load→set→save calls parse again). Carry it before seats iteration so a
|
|
560
421
|
// disk-shaped seats map and a memory-shaped bucket both survive.
|
|
@@ -572,7 +433,6 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
|
|
|
572
433
|
const withOpaque = (seats: PublicCliConfig["seats"]): PublicCliConfig => ({
|
|
573
434
|
seats,
|
|
574
435
|
...(autoResumeLimit === undefined ? {} : { autoResumeLimit }),
|
|
575
|
-
...(providerAliases === undefined ? {} : { providerAliases }),
|
|
576
436
|
...(Object.keys(unknownSeats).length === 0 ? {} : { unknownSeats }),
|
|
577
437
|
});
|
|
578
438
|
if (record.seats === undefined) {
|
|
@@ -818,23 +678,13 @@ export function resolveEffectiveSeat(
|
|
|
818
678
|
}
|
|
819
679
|
}
|
|
820
680
|
|
|
821
|
-
|
|
681
|
+
// Seat axes only. Host-facing provider projection (#788) runs after the host
|
|
682
|
+
// is selected as registered — never before (bad host must not reach model).
|
|
683
|
+
return attachHostAxis(
|
|
822
684
|
attachEngineAxis(modelSeat, config, invocation),
|
|
823
685
|
config,
|
|
824
686
|
invocation,
|
|
825
687
|
);
|
|
826
|
-
// #778: host-facing provider is the seat-table value after owner aliases.
|
|
827
|
-
// Disk seats stay as written; only the effective selection is projected.
|
|
828
|
-
const selection = applyProviderHostAlias(
|
|
829
|
-
withAxes.selection,
|
|
830
|
-
withAxes.host,
|
|
831
|
-
config.providerAliases,
|
|
832
|
-
);
|
|
833
|
-
if (selection === undefined) {
|
|
834
|
-
const { selection: _dropped, ...rest } = withAxes;
|
|
835
|
-
return rest;
|
|
836
|
-
}
|
|
837
|
-
return { ...withAxes, selection };
|
|
838
688
|
}
|
|
839
689
|
|
|
840
690
|
export function effectiveSeatConfigurations(
|