refactor: name sub2api manual binding sources in yaml

This commit is contained in:
Codex
2026-06-14 17:07:35 +00:00
parent 6781983383
commit dabadffb99
2 changed files with 221 additions and 35 deletions
+13 -2
View File
@@ -137,16 +137,27 @@ profiles:
configFile: config.toml.socap
authFile: auth.json.socap
manualAccounts:
bindingSources:
active-target-egress-proxy:
enabled: true
kind: proxy
provider: target-egress-proxy
description: Bind a protected manual account to the selected target's YAML-declared egress proxy.
unified-pool-group:
enabled: true
kind: group
provider: pool-group
description: Attach a protected manual account to the unified Codex pool group.
protected:
- accountName: lucianepidgeon@gmail.com
reason: Manually configured in Sub2API; keep outside UniDesk-managed Codex pool and sentinel control.
proxyBinding:
enabled: true
source: target-egress-proxy
source: active-target-egress-proxy
proxyName: platform-infra-sub2api-egress-proxy
groupBinding:
enabled: true
source: pool-group
source: unified-pool-group
publicExposure:
enabled: false
proxyName: platform-infra-sub2api
+208 -33
View File
@@ -156,18 +156,35 @@ interface CodexPoolConfig {
}
interface CodexPoolManualAccountsConfig {
bindingSources: CodexPoolManualBindingSourcesConfig;
protected: CodexPoolManualAccountProtection[];
}
type CodexPoolManualBindingKind = "proxy" | "group";
type CodexPoolManualBindingProvider = "target-egress-proxy" | "pool-group";
interface CodexPoolManualBindingSource {
id: string;
enabled: boolean;
kind: CodexPoolManualBindingKind;
provider: CodexPoolManualBindingProvider;
description: string | null;
}
interface CodexPoolManualBindingSourcesConfig {
items: CodexPoolManualBindingSource[];
byId: Record<string, CodexPoolManualBindingSource>;
}
interface CodexPoolManualAccountProxyBinding {
enabled: boolean;
source: "target-egress-proxy";
source: string;
proxyName: string;
}
interface CodexPoolManualAccountGroupBinding {
enabled: boolean;
source: "pool-group";
source: string;
}
interface CodexPoolManualAccountProtection {
@@ -905,6 +922,10 @@ async function codexPoolSync(config: UniDeskConfig, options: SyncOptions): Promi
defaultAccountLoadFactor: pool.defaultAccountLoadFactor,
defaultSentinelProtect: pool.defaultSentinelProtect,
},
manualAccounts: {
bindingSources: pool.manualAccounts.bindingSources.items.map(manualBindingSourcePlan),
protected: resolvedManualAccountProtections(pool, runtimeTarget),
},
profiles: profiles.map((profile) => ({
profile: profile.profile,
accountName: profile.accountName,
@@ -1514,6 +1535,7 @@ function desiredProfileCapacityTotal(profiles: CodexPoolProfileConfig[], default
function readManualAccountsConfig(value: unknown): CodexPoolManualAccountsConfig {
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.manualAccounts must be a YAML object`);
const bindingSources = readManualBindingSources(value.bindingSources, "manualAccounts.bindingSources");
const protectedRaw = value.protected;
if (!Array.isArray(protectedRaw)) throw new Error(`${codexPoolConfigPath}.manualAccounts.protected must be a YAML array`);
const seen = new Set<string>();
@@ -1529,20 +1551,46 @@ function readManualAccountsConfig(value: unknown): CodexPoolManualAccountsConfig
if (seen.has(normalized)) throw new Error(`${codexPoolConfigPath}.${key}.accountName is duplicated in manualAccounts.protected`);
seen.add(normalized);
const reason = isRecord(entry) ? readManualAccountReason(entry.reason, `${key}.reason`) : null;
const proxyBinding = isRecord(entry) ? readManualAccountProxyBinding(entry.proxyBinding, `${key}.proxyBinding`) : null;
const groupBinding = isRecord(entry) ? readManualAccountGroupBinding(entry.groupBinding, `${key}.groupBinding`) : null;
const proxyBinding = isRecord(entry) ? readManualAccountProxyBinding(entry.proxyBinding, `${key}.proxyBinding`, bindingSources) : null;
const groupBinding = isRecord(entry) ? readManualAccountGroupBinding(entry.groupBinding, `${key}.groupBinding`, bindingSources) : null;
return { accountName, reason, proxyBinding, groupBinding };
});
return { protected: protectedAccounts };
return { bindingSources, protected: protectedAccounts };
}
function readManualAccountProxyBinding(value: unknown, key: string): CodexPoolManualAccountProxyBinding | null {
function readManualBindingSources(value: unknown, key: string): CodexPoolManualBindingSourcesConfig {
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
const items: CodexPoolManualBindingSource[] = [];
const byId: Record<string, CodexPoolManualBindingSource> = {};
for (const [id, rawSource] of Object.entries(value)) {
const path = `${key}.${id}`;
validateManualBindingSourceId(id, path);
if (!isRecord(rawSource)) throw new Error(`${codexPoolConfigPath}.${path} must be a YAML object`);
const source: CodexPoolManualBindingSource = {
id,
enabled: readBooleanConfig(rawSource.enabled, `${path}.enabled`),
kind: readManualBindingKind(rawSource.kind, `${path}.kind`),
provider: readManualBindingProvider(rawSource.provider, `${path}.provider`),
description: readManualAccountReason(rawSource.description, `${path}.description`),
};
if (source.kind === "proxy" && source.provider !== "target-egress-proxy") {
throw new Error(`${codexPoolConfigPath}.${path}.provider must be target-egress-proxy for kind=proxy`);
}
if (source.kind === "group" && source.provider !== "pool-group") {
throw new Error(`${codexPoolConfigPath}.${path}.provider must be pool-group for kind=group`);
}
byId[id] = source;
items.push(source);
}
if (items.length === 0) throw new Error(`${codexPoolConfigPath}.${key} must declare at least one source`);
return { items, byId };
}
function readManualAccountProxyBinding(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig): CodexPoolManualAccountProxyBinding | null {
if (value === undefined || value === null) return null;
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
const enabled = value.enabled === undefined ? true : value.enabled === true;
const source = stringValue(value.source);
if (source === null) throw new Error(`${codexPoolConfigPath}.${key}.source is required`);
if (source !== "target-egress-proxy") throw new Error(`${codexPoolConfigPath}.${key}.source must be target-egress-proxy`);
const enabled = readBooleanConfig(value.enabled, `${key}.enabled`);
const source = readManualBindingSourceRef(value.source, `${key}.source`, bindingSources, "proxy", enabled);
const proxyName = stringValue(value.proxyName);
if (proxyName === null || proxyName.trim().length === 0) throw new Error(`${codexPoolConfigPath}.${key}.proxyName is required`);
validateProxyName(proxyName, `${key}.proxyName`);
@@ -1553,19 +1601,44 @@ function readManualAccountProxyBinding(value: unknown, key: string): CodexPoolMa
};
}
function readManualAccountGroupBinding(value: unknown, key: string): CodexPoolManualAccountGroupBinding | null {
function readManualAccountGroupBinding(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig): CodexPoolManualAccountGroupBinding | null {
if (value === undefined || value === null) return null;
if (!isRecord(value)) throw new Error(`${codexPoolConfigPath}.${key} must be a YAML object`);
const enabled = value.enabled === undefined ? true : value.enabled === true;
const source = stringValue(value.source);
if (source === null) throw new Error(`${codexPoolConfigPath}.${key}.source is required`);
if (source !== "pool-group") throw new Error(`${codexPoolConfigPath}.${key}.source must be pool-group`);
const enabled = readBooleanConfig(value.enabled, `${key}.enabled`);
const source = readManualBindingSourceRef(value.source, `${key}.source`, bindingSources, "group", enabled);
return {
enabled,
source,
};
}
function readManualBindingSourceRef(value: unknown, key: string, bindingSources: CodexPoolManualBindingSourcesConfig, expectedKind: CodexPoolManualBindingKind, bindingEnabled: boolean): string {
const sourceId = stringValue(value);
if (sourceId === null) throw new Error(`${codexPoolConfigPath}.${key} is required`);
validateManualBindingSourceId(sourceId, key);
const source = bindingSources.byId[sourceId];
if (source === undefined) throw new Error(`${codexPoolConfigPath}.${key} references unknown manualAccounts.bindingSources id ${sourceId}`);
if (source.kind !== expectedKind) throw new Error(`${codexPoolConfigPath}.${key} references ${sourceId} with kind=${source.kind}; expected ${expectedKind}`);
if (bindingEnabled && !source.enabled) throw new Error(`${codexPoolConfigPath}.${key} references disabled manualAccounts.bindingSources.${sourceId}`);
return sourceId;
}
function readManualBindingKind(value: unknown, key: string): CodexPoolManualBindingKind {
const text = stringValue(value);
if (text !== "proxy" && text !== "group") throw new Error(`${codexPoolConfigPath}.${key} must be proxy or group`);
return text;
}
function readManualBindingProvider(value: unknown, key: string): CodexPoolManualBindingProvider {
const text = stringValue(value);
if (text !== "target-egress-proxy" && text !== "pool-group") throw new Error(`${codexPoolConfigPath}.${key} must be target-egress-proxy or pool-group`);
return text;
}
function validateManualBindingSourceId(value: string, key: string): void {
if (!/^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$/u.test(value)) throw new Error(`${codexPoolConfigPath}.${key} has an unsupported source id format`);
}
function readManualAccountName(value: unknown, key: string): string | null {
const text = stringValue(value)?.trim() ?? null;
if (text === null || text.length === 0) return null;
@@ -2079,6 +2152,7 @@ function codexPoolConfigSummary(pool: CodexPoolConfig, target: CodexPoolRuntimeT
defaultSentinelProtect: pool.defaultSentinelProtect,
profileCount: pool.profiles.length,
manualAccounts: {
bindingSources: pool.manualAccounts.bindingSources.items.map(manualBindingSourcePlan),
protectedCount: pool.manualAccounts.protected.length,
protected: pool.manualAccounts.protected,
controlPolicy: "manual accounts are not created, credential-updated, pruned, probed, or frozen by UniDesk codex-pool sync/sentinel; optional proxy_id and pool group membership bindings are narrow YAML-controlled exceptions",
@@ -3078,6 +3152,72 @@ function sentinelProfileSecrets(profiles: CodexProfile[]): CodexPoolSentinelProf
}));
}
function resolvedManualAccountProtections(pool: CodexPoolConfig, target: CodexPoolRuntimeTarget): Record<string, unknown>[] {
return pool.manualAccounts.protected.map((account) => ({
accountName: account.accountName,
reason: account.reason,
proxyBinding: resolveManualProxyBinding(account.proxyBinding, pool, target),
groupBinding: resolveManualGroupBinding(account.groupBinding, pool),
}));
}
function resolveManualProxyBinding(binding: CodexPoolManualAccountProxyBinding | null, pool: CodexPoolConfig, target: CodexPoolRuntimeTarget): Record<string, unknown> | null {
if (binding === null) return null;
const source = manualBindingSource(pool, binding.source);
if (source.kind !== "proxy") throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${source.id} must have kind=proxy`);
if (binding.enabled && source.provider === "target-egress-proxy" && (target.egressProxy === null || target.egressProxy.enabled !== true)) {
throw new Error(`${codexPoolConfigPath}.manualAccounts.bindingSources.${source.id} requires ${sub2apiConfigPath}.targets[${target.id}].egressProxy.enabled=true`);
}
return {
enabled: binding.enabled,
source: source.id,
proxyName: binding.proxyName,
sourcePlan: {
...manualBindingSourcePlan(source),
targetEgressProxy: source.provider === "target-egress-proxy" && target.egressProxy !== null
? {
targetId: target.id,
namespace: target.namespace,
serviceName: target.egressProxy.serviceName,
listenPort: target.egressProxy.listenPort,
httpProxy: target.egressProxy.httpProxy,
noProxy: target.egressProxy.noProxy,
}
: null,
},
};
}
function resolveManualGroupBinding(binding: CodexPoolManualAccountGroupBinding | null, pool: CodexPoolConfig): Record<string, unknown> | null {
if (binding === null) return null;
const source = manualBindingSource(pool, binding.source);
if (source.kind !== "group") throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${source.id} must have kind=group`);
return {
enabled: binding.enabled,
source: source.id,
sourcePlan: {
...manualBindingSourcePlan(source),
poolGroupName: source.provider === "pool-group" ? pool.groupName : null,
},
};
}
function manualBindingSource(pool: CodexPoolConfig, sourceId: string): CodexPoolManualBindingSource {
const source = pool.manualAccounts.bindingSources.byId[sourceId];
if (source === undefined) throw new Error(`${codexPoolConfigPath}.manualAccounts binding source ${sourceId} is not declared`);
return source;
}
function manualBindingSourcePlan(source: CodexPoolManualBindingSource): Record<string, unknown> {
return {
id: source.id,
enabled: source.enabled,
kind: source.kind,
provider: source.provider,
description: source.description,
};
}
function targetPublicExposureSummary(target: CodexPoolRuntimeTarget): Record<string, unknown> | null {
const exposure = target.publicExposure;
if (exposure === null) return null;
@@ -4160,7 +4300,7 @@ EXPECTED_ACCOUNT_CAPACITIES = ${JSON.stringify(desiredAccountCapacityMap(pool))}
EXPECTED_ACCOUNT_LOAD_FACTORS = ${JSON.stringify(desiredAccountLoadFactorMap(pool))}
EXPECTED_ACCOUNT_WS_MODES = json.loads(${JSON.stringify(JSON.stringify(desiredAccountWebSocketsV2ModeMap(pool)))})
EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE = json.loads(${JSON.stringify(JSON.stringify(desiredAccountTempUnschedulableMap(pool)))})
MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(pool.manualAccounts.protected))})
MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(resolvedManualAccountProtections(pool, target)))})
SENTINEL_CONFIG = json.loads(${JSON.stringify(JSON.stringify(pool.sentinel))})
TARGET_EGRESS_PROXY = json.loads(${JSON.stringify(JSON.stringify(target.egressProxy))})
MODE = "${mode}"
@@ -4513,16 +4653,31 @@ def find_proxy_by_name(token, name):
return item
return None
def manual_binding_plan(binding, expected_kind):
if not isinstance(binding, dict):
return None
plan = binding.get("sourcePlan")
if not isinstance(plan, dict):
raise RuntimeError("manual account binding is missing resolved sourcePlan")
if plan.get("enabled") is not True:
raise RuntimeError(f"manual account binding source {plan.get('id')} is disabled")
if plan.get("kind") != expected_kind:
raise RuntimeError(f"manual account binding source {plan.get('id')} kind must be {expected_kind}")
return plan
def desired_manual_proxy_payload(protection):
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
if not isinstance(binding, dict) or binding.get("enabled") is not True:
return None
if binding.get("source") != "target-egress-proxy":
raise RuntimeError("manual account proxyBinding source must be target-egress-proxy")
if not isinstance(TARGET_EGRESS_PROXY, dict) or TARGET_EGRESS_PROXY.get("enabled") is not True:
raise RuntimeError("manual account proxyBinding requires an enabled target egressProxy")
service_name = TARGET_EGRESS_PROXY.get("serviceName")
listen_port = TARGET_EGRESS_PROXY.get("listenPort")
plan = manual_binding_plan(binding, "proxy")
if plan.get("provider") != "target-egress-proxy":
raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}")
target_proxy = plan.get("targetEgressProxy")
if not isinstance(target_proxy, dict):
raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} requires an enabled target egressProxy")
service_name = target_proxy.get("serviceName")
listen_port = target_proxy.get("listenPort")
namespace = target_proxy.get("namespace") if isinstance(target_proxy.get("namespace"), str) and target_proxy.get("namespace") else NAMESPACE
proxy_name = binding.get("proxyName")
if not isinstance(service_name, str) or not service_name:
raise RuntimeError("target egressProxy serviceName is missing")
@@ -4533,7 +4688,7 @@ def desired_manual_proxy_payload(protection):
return {
"name": proxy_name,
"protocol": "http",
"host": f"{service_name}.{NAMESPACE}.svc.cluster.local",
"host": f"{service_name}.{namespace}.svc.cluster.local",
"port": listen_port,
"fallback_mode": "none",
"expiry_warn_days": 0,
@@ -4568,6 +4723,8 @@ def manual_proxy_status(token, account, protection):
"action": "not-configured",
"valuesPrinted": False,
}
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
plan = manual_binding_plan(binding, "proxy")
proxy = find_proxy_by_name(token, payload["name"])
runtime_proxy = account.get("proxy") if isinstance(account, dict) and isinstance(account.get("proxy"), dict) else None
binding_aligned = (
@@ -4579,7 +4736,8 @@ def manual_proxy_status(token, account, protection):
"enabled": True,
"ok": binding_aligned,
"action": "validate",
"source": "target-egress-proxy",
"source": plan.get("id"),
"sourceProvider": plan.get("provider"),
"expectedProxyName": payload["name"],
"expectedProtocol": payload["protocol"],
"expectedHost": payload["host"],
@@ -4634,6 +4792,8 @@ def ensure_manual_account_proxy_bindings(token):
})
continue
proxy, proxy_action = ensure_manual_proxy(token, payload)
binding = protection.get("proxyBinding") if isinstance(protection, dict) else None
plan = manual_binding_plan(binding, "proxy")
proxy_id = proxy.get("id") if isinstance(proxy, dict) else None
if proxy_id is None:
raise RuntimeError(f"proxy {payload['name']} has no id")
@@ -4649,6 +4809,8 @@ def ensure_manual_account_proxy_bindings(token):
"enabled": True,
"action": action,
"proxyAction": proxy_action,
"source": plan.get("id"),
"sourceProvider": plan.get("provider"),
"ok": account.get("proxy_id") == proxy_id,
"expectedProxyName": payload["name"],
"expectedProtocol": payload["protocol"],
@@ -4668,17 +4830,21 @@ def ensure_manual_account_proxy_bindings(token):
"valuesPrinted": False,
}
def manual_group_binding_enabled(protection):
def manual_group_binding_plan(protection):
binding = protection.get("groupBinding") if isinstance(protection, dict) else None
if not isinstance(binding, dict) or binding.get("enabled") is not True:
return False
if binding.get("source") != "pool-group":
raise RuntimeError("manual account groupBinding source must be pool-group")
return True
return None
plan = manual_binding_plan(binding, "group")
if plan.get("provider") != "pool-group":
raise RuntimeError(f"manual account groupBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}")
return plan
def manual_group_binding_enabled(protection):
return manual_group_binding_plan(protection) is not None
def manual_group_status(token, account, protection, group_id):
enabled = manual_group_binding_enabled(protection)
if not enabled:
plan = manual_group_binding_plan(protection)
if plan is None:
return {
"enabled": False,
"ok": True,
@@ -4697,7 +4863,8 @@ def manual_group_status(token, account, protection, group_id):
"enabled": True,
"ok": binding_aligned,
"action": "validate",
"source": "pool-group",
"source": plan.get("id"),
"sourceProvider": plan.get("provider"),
"poolGroupName": POOL_GROUP_NAME,
"poolGroupId": group_id,
"bindingAligned": binding_aligned,
@@ -4721,6 +4888,7 @@ def ensure_manual_account_group_bindings(token, group_id):
"valuesPrinted": False,
})
continue
plan = manual_group_binding_plan(protection)
account = find_account_by_name(token, name)
if not isinstance(account, dict):
items.append({
@@ -4764,7 +4932,8 @@ def ensure_manual_account_group_bindings(token, group_id):
"enabled": True,
"ok": binding_aligned,
"action": action,
"source": "pool-group",
"source": plan.get("id") if isinstance(plan, dict) else None,
"sourceProvider": plan.get("provider") if isinstance(plan, dict) else None,
"poolGroupName": POOL_GROUP_NAME,
"poolGroupId": group_id,
"previousGroupIds": existing_group_ids,
@@ -6408,7 +6577,13 @@ def api_key_preview(api_key):
return api_key[:10] + "..." + api_key[-4:]
def run_sync():
global MANUAL_ACCOUNT_PROTECTIONS
payload = json.loads(base64.b64decode(PAYLOAD_B64).decode("utf-8"))
manual_accounts_payload = payload.get("manualAccounts") if isinstance(payload.get("manualAccounts"), dict) else {}
resolved_manual_protections = manual_accounts_payload.get("protected")
if not isinstance(resolved_manual_protections, list):
raise RuntimeError("sync payload has no manualAccounts.protected binding plan")
MANUAL_ACCOUNT_PROTECTIONS = resolved_manual_protections
profiles = payload.get("profiles") or []
prune_removed = bool(payload.get("pruneRemoved"))
sentinel_payload = payload.get("sentinel") if isinstance(payload.get("sentinel"), dict) else {}