@sema-agent/server 7.123.0 → 7.124.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/MIGRATION.md +132 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +53 -51
- package/dist/approval-ask-audit-store.d.ts +27 -32
- package/dist/approval-ask-audit-store.js +10 -9
- package/dist/approval-crash-convergence.d.ts +2 -2
- package/dist/approval-crash-convergence.js +5 -5
- package/dist/approval-deny-reasons.d.ts +163 -51
- package/dist/approval-deny-reasons.js +79 -8
- package/dist/approval-policy-settlement.d.ts +4 -1
- package/dist/approval-policy-settlement.js +3 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval-reconciler.js +10 -10
- package/dist/approval.d.ts +1 -1
- package/dist/approval.js +1 -1
- package/dist/audit.js +1 -1
- package/dist/auth-bridge.d.ts +8 -8
- package/dist/auth-bridge.js +6 -6
- package/dist/auth-keys.d.ts +2 -2
- package/dist/auto-mode-face.d.ts +1 -1
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/execution-env.js +1 -0
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +4 -4
- package/dist/boot/org-memory.d.ts +3 -3
- package/dist/boot/org-memory.js +5 -5
- package/dist/boot/resolve-spec.js +7 -7
- package/dist/boot/runner-deps.d.ts +1 -1
- package/dist/boot/runtime-caps.d.ts +1 -1
- package/dist/boot/runtime-caps.js +10 -10
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +2 -2
- package/dist/boot/side-query-lane.d.ts +3 -3
- package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
- package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
- package/dist/boot/stage-03-store-backend.d.ts +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +6 -6
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +6 -5
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -3
- package/dist/boot/stage-09-leader.js +7 -7
- package/dist/boot/stage-10-http-server.d.ts +3 -3
- package/dist/boot/stage-10-http-server.js +19 -19
- package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
- package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
- package/dist/boot/{config-center.js → web-admin.js} +38 -22
- package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
- package/dist/brain.d.ts +1 -1
- package/dist/brain.js +1 -1
- package/dist/budget.d.ts +3 -3
- package/dist/canonical-json.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +12 -5
- package/dist/capabilities/center-wiring.js +11 -2
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/team.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/center-credential-renewer.d.ts +1 -1
- package/dist/center-credential-renewer.js +7 -7
- package/dist/center-credential.d.ts +9 -9
- package/dist/center-credential.js +6 -6
- package/dist/config-catalog.d.ts +7 -10
- package/dist/config-catalog.js +15 -15
- package/dist/config-d-migration.d.ts +32 -0
- package/dist/config-d-migration.js +59 -0
- package/dist/config-invariants.d.ts +14 -4
- package/dist/config-invariants.js +31 -2
- package/dist/config-lkg.d.ts +26 -6
- package/dist/config-lkg.js +43 -14
- package/dist/config-provider.d.ts +23 -19
- package/dist/config-provider.js +24 -26
- package/dist/config-types.d.ts +52 -51
- package/dist/config.d.ts +9 -46
- package/dist/config.js +33 -92
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/drain-entry.d.ts +1 -1
- package/dist/fleet/engine-signal.d.ts +8 -8
- package/dist/fleet/fleet-bus.d.ts +12 -3
- package/dist/fleet/fleet-bus.js +13 -9
- package/dist/fleet/serving-lease.d.ts +3 -3
- package/dist/fleet/serving-lease.js +1 -1
- package/dist/fleet-client.d.ts +7 -7
- package/dist/fleet-client.js +5 -4
- package/dist/fleet-lease.d.ts +3 -3
- package/dist/fleet-lease.js +2 -2
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/hosted-posture.d.ts +4 -4
- package/dist/http/admission.js +4 -4
- package/dist/http/checkpoint-ctx.d.ts +20 -3
- package/dist/http/checkpoint-ctx.js +13 -5
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/health-self-report.d.ts +4 -4
- package/dist/http/health-self-report.js +3 -3
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +31 -18
- package/dist/http/route-ctx.d.ts +8 -0
- package/dist/http/routes/admin-config-refresh.d.ts +1 -1
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.d.ts +1 -1
- package/dist/http/routes/admin-drain.js +5 -4
- package/dist/http/routes/approvals-assistant.js +4 -4
- package/dist/http/routes/capabilities.js +5 -5
- package/dist/http/routes/config-catalog.js +2 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/runs.js +8 -4
- package/dist/http/routes/sessions-list.js +2 -1
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.js +3 -3
- package/dist/http/routes/tasks.js +3 -2
- package/dist/http/run-meta.d.ts +1 -1
- package/dist/http/run-meta.js +2 -2
- package/dist/http/server.d.ts +7 -7
- package/dist/http/server.js +1 -1
- package/dist/http/stored-body-replay.js +3 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/leader/wire.d.ts +2 -2
- package/dist/leader/wire.js +2 -1
- package/dist/main.js +1 -1
- package/dist/mcp-tool-faces.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.js +4 -4
- package/dist/model-provider.d.ts +2 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-url.d.ts +1 -1
- package/dist/model-route-url.js +2 -1
- package/dist/model-select.d.ts +24 -21
- package/dist/model-select.js +22 -17
- package/dist/observability/metrics.d.ts +1 -1
- package/dist/org-memory-admission.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -2
- package/dist/plugins/approval-ask-store-file.js +3 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
- package/dist/plugins/approval-ask-store-memory.js +10 -4
- package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
- package/dist/plugins/approval-ask-store-sql.js +20 -13
- package/dist/plugins/caching-session-store.d.ts +0 -10
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/file-run-store.js +9 -3
- package/dist/plugins/leader-run-store-sql.d.ts +3 -2
- package/dist/plugins/leader-run-store-sql.js +3 -2
- package/dist/plugins/local-session-store.d.ts +20 -27
- package/dist/plugins/local-session-store.js +19 -17
- package/dist/plugins/memory-run-store.js +9 -3
- package/dist/plugins/one-time-migrations.js +26 -2
- package/dist/plugins/scheduler-support.d.ts +26 -7
- package/dist/plugins/scheduler-support.js +96 -53
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +20 -4
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -1
- package/dist/posture-source.d.ts +1 -1
- package/dist/read-face-posture.d.ts +3 -3
- package/dist/read-face-posture.js +1 -1
- package/dist/run-local.js +6 -3
- package/dist/runtime-caps-resolver.d.ts +4 -4
- package/dist/runtime-caps-resolver.js +2 -2
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sealed-key.d.ts +4 -4
- package/dist/sealed-key.js +3 -2
- package/dist/security.d.ts +3 -3
- package/dist/service-version.d.ts +1 -1
- package/dist/service-version.js +5 -0
- package/dist/session-titler.d.ts +0 -11
- package/dist/session-titler.js +3 -5
- package/dist/shared-memory-scope-authorizer.d.ts +4 -4
- package/dist/task-a2a.d.ts +1 -1
- package/dist/tool-approval.d.ts +43 -4
- package/dist/tool-approval.js +147 -98
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +3 -3
- package/dist/trace/redact.d.ts +2 -23
- package/dist/trace/redact.js +1 -6
- package/dist/trace/self-minted-wire.d.ts +147 -3
- package/dist/trace/self-minted-wire.js +50 -9
- package/dist/url-refusal.d.ts +35 -0
- package/dist/url-refusal.js +15 -0
- package/dist/user-authored-text.d.ts +31 -0
- package/dist/user-authored-text.js +18 -0
- package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
- package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
- package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
- package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
- package/dist/{config-center → web-admin-client}/center-request.js +5 -4
- package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
- package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
- package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
- package/dist/{config-center → web-admin-client}/facade.js +2 -2
- package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
- package/dist/{config-center → web-admin-client}/http-client.js +12 -12
- package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/read-face.js +4 -4
- package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
- package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
- package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
- package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
- package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
- package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
- package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
- package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
- package/package.json +2 -2
- package/skills/find-skills.md +2 -2
- package/dist/url-shape.d.ts +0 -31
- package/dist/url-shape.js +0 -41
- /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
- /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
- /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/types.js +0 -0
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { WEB_ADMIN_ENV, formatConfigPath, skillContentHash } from "@sema-agent/settings-schema";
|
|
6
6
|
import { stableStringify } from "@sema-agent/settings-schema/hash";
|
|
7
7
|
import { buildModelRefusalsSignal } from "../model-entry-refusal.js";
|
|
8
8
|
import { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, } from "@sema-agent/core";
|
|
@@ -15,15 +15,16 @@ import { isRosterGateArmed } from "../config.js";
|
|
|
15
15
|
import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg, legacyV1LkgPath, migrateV1Lkg } from "../config-lkg.js";
|
|
16
16
|
import { answerStillCurrent, centerStateBin, credentialIdentityNow, judgeCenterCredentialAtBoot } from "../center-credential.js";
|
|
17
17
|
import { buildCenterCredentialHealthView, createCenterCredentialRenewer } from "../center-credential-renewer.js";
|
|
18
|
-
import { centerManagedConfigKeys } from "../
|
|
18
|
+
import { centerManagedConfigKeys } from "../web-admin-client/apply-effective.js";
|
|
19
|
+
import { logConfigDirMigration, migrateLocalConfigDirectory } from "../config-d-migration.js";
|
|
19
20
|
import { createConfigProvider, raceBootFetch, serviceConfigSourceKind, BOOT_FETCH_DEFERRED } from "../config-provider.js";
|
|
20
21
|
import { createKeyResolver } from "../key-resolver.js";
|
|
21
22
|
import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
22
|
-
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../
|
|
23
|
+
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../web-admin-client/facade.js";
|
|
23
24
|
import { buildConfigHandoff, buildRestartSignal, configHandoffMissing } from "../fleet/engine-signal.js";
|
|
24
|
-
import { createMcpRevocationLedger, enabledMcpEntries } from "../
|
|
25
|
+
import { createMcpRevocationLedger, enabledMcpEntries } from "../web-admin-client/mcp-revocation.js";
|
|
25
26
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
26
|
-
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../
|
|
27
|
+
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../web-admin-client/apply-ledger.js";
|
|
27
28
|
function lkgReadNote(scope) {
|
|
28
29
|
switch (scope) {
|
|
29
30
|
case "whole":
|
|
@@ -32,7 +33,7 @@ function lkgReadNote(scope) {
|
|
|
32
33
|
return "the live candidate's model plane was refused at boot — the persisted last-known-good supplies the model plane only (catalog and the collab templates that reference it); every other group and every restart-to-apply face stays the live candidate's";
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
|
-
export async function
|
|
36
|
+
export async function createWebAdminRuntime(ctx) {
|
|
36
37
|
const { config, logger, metrics, localRoot } = ctx;
|
|
37
38
|
let ccEtag;
|
|
38
39
|
let effective;
|
|
@@ -65,13 +66,13 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
65
66
|
let bootSkillStale = false;
|
|
66
67
|
let pendingRestart;
|
|
67
68
|
let modelRefusals;
|
|
68
|
-
const cc = config.
|
|
69
|
+
const cc = config.webAdmin;
|
|
69
70
|
const bootCredential = cc?.credential.read();
|
|
70
71
|
const bootVerdict = bootCredential === undefined ? undefined : judgeCenterCredentialAtBoot(bootCredential);
|
|
71
72
|
if (bootVerdict?.verdict === "refuse")
|
|
72
73
|
throw new Error(bootVerdict.sentence);
|
|
73
74
|
if (bootVerdict?.verdict === "loud") {
|
|
74
|
-
logger.error("center_credential_unreadable", { key:
|
|
75
|
+
logger.error("center_credential_unreadable", { key: WEB_ADMIN_ENV.tokenFile, failure: bootVerdict.failure, reason: bootVerdict.reason, effect: bootVerdict.effect });
|
|
75
76
|
}
|
|
76
77
|
const credentialRenewer = cc !== undefined && cc.credential.origin.from === "file"
|
|
77
78
|
? createCenterCredentialRenewer({ source: cc.credential, centerUrl: cc.baseUrl, leadMs: config.centerCredentialRenewLeadMs, logger })
|
|
@@ -110,7 +111,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
110
111
|
sealedKeys = await ensureSealedKeyStore(join(config.localDataRoot ?? localRoot, "sealed-key"), logger);
|
|
111
112
|
void reportExecutionPublicKey({
|
|
112
113
|
baseUrl: cc?.baseUrl,
|
|
113
|
-
adminToken: process.env.
|
|
114
|
+
adminToken: process.env[WEB_ADMIN_ENV.adminToken],
|
|
114
115
|
key: sealedKeys.latest,
|
|
115
116
|
...(cc?.worker ? { notes: `worker ${cc.worker}` } : {}),
|
|
116
117
|
logger,
|
|
@@ -122,10 +123,12 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
122
123
|
const configProvider = serviceConfigSourceKind(config) !== undefined
|
|
123
124
|
? createConfigProvider({
|
|
124
125
|
...(config.configProvider ? { provider: config.configProvider } : {}),
|
|
125
|
-
...(cc ? {
|
|
126
|
+
...(cc ? { webAdmin: { baseUrl: cc.baseUrl, credential: cc.credential, worker: cc.worker, dryRun: cc.dryRun } } : {}),
|
|
126
127
|
localDir: localRoot,
|
|
127
128
|
})
|
|
128
129
|
: undefined;
|
|
130
|
+
if (configProvider?.kind === "local")
|
|
131
|
+
logConfigDirMigration(logger, await migrateLocalConfigDirectory(localRoot));
|
|
129
132
|
const rosterGateArmed = isRosterGateArmed(cc);
|
|
130
133
|
const modelReadyState = { ready: !rosterGateArmed };
|
|
131
134
|
const warnDroppedEntries = (r, phase) => {
|
|
@@ -354,8 +357,8 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
354
357
|
}
|
|
355
358
|
const lkgPath = lkgPathFor(identity);
|
|
356
359
|
try {
|
|
357
|
-
if (effectiveObj.skills && config.
|
|
358
|
-
await applyCenterSkills([], effectiveObj.skills, config.
|
|
360
|
+
if (effectiveObj.skills && config.webAdmin) {
|
|
361
|
+
await applyCenterSkills([], effectiveObj.skills, config.webAdmin, logger, undefined, defaultSkillCacheDir(config));
|
|
359
362
|
}
|
|
360
363
|
const enabledSkills = (effectiveObj.skills?.skills ?? []).filter((m) => m.enabled !== false);
|
|
361
364
|
const cacheDir = defaultSkillCacheDir(config);
|
|
@@ -420,8 +423,21 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
420
423
|
logger.warn("config_lkg_unusable", { path: lkgPath, reason: "center credential identity changed while the LKG was being read — not used", note: "env fallback serves; the current identity's next pull re-establishes its own LKG" });
|
|
421
424
|
return null;
|
|
422
425
|
}
|
|
423
|
-
if (read.migrated !== undefined)
|
|
424
|
-
|
|
426
|
+
if (read.migrated !== undefined) {
|
|
427
|
+
const m = read.migrated;
|
|
428
|
+
logger.info("config_lkg_migrated", {
|
|
429
|
+
to: lkgPath,
|
|
430
|
+
fromFormat: m.fromFormat,
|
|
431
|
+
...(m.note !== undefined ? { note: m.note } : {}),
|
|
432
|
+
receipts: m.receipts.map((r) => ({ migration: r.migration, document: r.document, path: formatConfigPath(r.path), appliedAs: r.appliedAs })),
|
|
433
|
+
});
|
|
434
|
+
try {
|
|
435
|
+
await saveLkg(lkgPath, cc?.worker, identity, read.lkg.effective, read.lkg.etag);
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
logger.warn("config_lkg_save_failed", { path: lkgPath, err: String(err), note: "the migrated LKG is used from memory this boot; the on-disk copy is migrated again on the next read-back and replaced by the next clean candidate" });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
425
441
|
logger.info("config_loaded_lkg", { path: lkgPath, savedAt: read.lkg.savedAt, why, scope, note: lkgReadNote(scope) });
|
|
426
442
|
const lkgEffective = read.lkg.effective;
|
|
427
443
|
candidateIdentity.set(lkgEffective, identity);
|
|
@@ -648,8 +664,8 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
648
664
|
return (await manualRefresh()) === "failed" ? "failed" : "triggered";
|
|
649
665
|
},
|
|
650
666
|
async applyCenterCapabilities(skills) {
|
|
651
|
-
if (effective?.skills && config.
|
|
652
|
-
skills = await applyCenterSkills(skills, effective.skills, config.
|
|
667
|
+
if (effective?.skills && config.webAdmin) {
|
|
668
|
+
skills = await applyCenterSkills(skills, effective.skills, config.webAdmin, logger, undefined, config.configLkgEnabled ? defaultSkillCacheDir(config) : undefined);
|
|
653
669
|
}
|
|
654
670
|
if (effective?.plugins !== undefined) {
|
|
655
671
|
const pluginOut = await applyCenterPlugins(skills, effective, {
|
|
@@ -705,7 +721,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
705
721
|
Object.assign(scenarios, overlay);
|
|
706
722
|
Object.assign(scenarioDetails, centerScenarioDetails(effective.scenarios.scenarios, builtinScenarioNames, scenarioDeps));
|
|
707
723
|
if (Object.keys(overlay).length > 0)
|
|
708
|
-
logger.info("
|
|
724
|
+
logger.info("web_admin_scenarios", { scenarios: Object.keys(overlay), ...(shadows.length > 0 ? { shadowsBuiltin: shadows } : {}) });
|
|
709
725
|
}
|
|
710
726
|
},
|
|
711
727
|
initKeyResolver() {
|
|
@@ -785,7 +801,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
785
801
|
};
|
|
786
802
|
const restartLogFields = (signal) => signal ? { restartRequired: true, restartReasons: signal.reasons } : {};
|
|
787
803
|
if (configProvider) {
|
|
788
|
-
const ccRef = config.
|
|
804
|
+
const ccRef = config.webAdmin;
|
|
789
805
|
let refreshInFlight = false;
|
|
790
806
|
const refreshTick = async (arrival) => {
|
|
791
807
|
if (refreshInFlight)
|
|
@@ -895,7 +911,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
895
911
|
note: "a group of this candidate was refused — the live config keeps serving the prior values for it; the LKG and the ETag stay on the last FULLY applied generation (this tree is re-judged next tick)",
|
|
896
912
|
});
|
|
897
913
|
const partialSignal = signalRestart({ applied: "partial", effective: r.effective, baseline: effective, refused: refused.map((g) => g.group) });
|
|
898
|
-
logger.info("
|
|
914
|
+
logger.info("web_admin_refreshed", {
|
|
899
915
|
version: r.effective.version,
|
|
900
916
|
note: "partially applied — the refused group(s) keep their prior values (see config_candidate_partially_applied); the other groups hot-applied; restart fields as on a complete tick",
|
|
901
917
|
...restartLogFields(partialSignal),
|
|
@@ -904,7 +920,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
904
920
|
}
|
|
905
921
|
const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
|
|
906
922
|
const signal = signalRestart({ applied: "complete", effective: r.effective, ...(r.etag !== undefined ? { etag: r.etag } : {}), baseline: effective, lkgPersisted });
|
|
907
|
-
logger.info("
|
|
923
|
+
logger.info("web_admin_refreshed", {
|
|
908
924
|
version: r.effective.version,
|
|
909
925
|
note: "collab + runtime governance (incl. approvalRequire) + limits + models/roles/roster hot-applied; skills/mcp/a2a/scenarios are restart-to-apply",
|
|
910
926
|
...restartLogFields(signal),
|
|
@@ -915,7 +931,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
915
931
|
}
|
|
916
932
|
}
|
|
917
933
|
catch (err) {
|
|
918
|
-
logger.warn("
|
|
934
|
+
logger.warn("web_admin_refresh_failed", { err: String(err), note: "etag NOT advanced — the same candidate is re-fetched and re-applied next poll" });
|
|
919
935
|
return "failed";
|
|
920
936
|
}
|
|
921
937
|
finally {
|
|
@@ -1044,4 +1060,4 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
1044
1060
|
},
|
|
1045
1061
|
};
|
|
1046
1062
|
}
|
|
1047
|
-
//# sourceMappingURL=
|
|
1063
|
+
//# sourceMappingURL=web-admin.js.map
|
|
@@ -51,9 +51,9 @@ export type WebFetchSummarizeLane = NonNullable<NonNullable<WebFetchConfig["summ
|
|
|
51
51
|
export interface WebFetchSummarizeLaneCtx {
|
|
52
52
|
/** 与 `RunnerDeps.brain` **同一只**实例(wrapper 在外面包,不改这只)。 */
|
|
53
53
|
brain: Brain;
|
|
54
|
-
/** 活配置引用:目录/角色表被
|
|
54
|
+
/** 活配置引用:目录/角色表被 web-admin 就地热应用(`mutateInPlace`),按调用取值才跟得上。 */
|
|
55
55
|
config: ServiceConfig;
|
|
56
|
-
/** 活引用取值 —— registry 热应用整个换 resolver 引用(`boot/
|
|
56
|
+
/** 活引用取值 —— registry 热应用整个换 resolver 引用(`boot/web-admin.ts` 每次 apply 重铸)。 */
|
|
57
57
|
getKeyResolver: () => KeyResolver;
|
|
58
58
|
logger: Logger;
|
|
59
59
|
}
|
package/dist/brain.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Brain, type BreakerState, type Model } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
3
|
import { type SealedCustodyPoison } from "./key-resolver.js";
|
|
4
|
-
import { type RestartSliceCtx } from "./
|
|
4
|
+
import { type RestartSliceCtx } from "./web-admin-client/restart-signal.js";
|
|
5
5
|
/**
|
|
6
6
|
* 配了快切层(failover ≥2 路 / 断路器)时,**每路**的重试上限。
|
|
7
7
|
*
|
package/dist/brain.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import { isModelKeyPoison, modelKeyPoisonIdentity } from "./key-resolver.js";
|
|
7
7
|
import { dispatchBrainLane } from "./model-provider.js";
|
|
8
8
|
import { openaiRouteBaseUrl } from "./model-route-endpoint.js";
|
|
9
|
-
import { catalogEntryFingerprint } from "./
|
|
9
|
+
import { catalogEntryFingerprint } from "./web-admin-client/restart-signal.js";
|
|
10
10
|
import { buildDisplaySafeRefusal, buildDisplaySafeUrl } from "./trace/redact.js";
|
|
11
11
|
import { createModelReachBrain } from "./model-unreachable-notice.js";
|
|
12
12
|
function isOffDeploymentRoot(baseUrl, config) {
|
package/dist/budget.d.ts
CHANGED
|
@@ -23,13 +23,13 @@ import { type PromptManifestTracker } from "./observability/prompt-manifest.js";
|
|
|
23
23
|
export declare function buildPricing(models: Record<string, Model>): Record<string, ModelPricing>;
|
|
24
24
|
/**
|
|
25
25
|
* 缝合审 M3:`capabilities.pricingConfigured` 的判据 —— 提成函数放在 pricing 自己的文件里,因为
|
|
26
|
-
* **这个表是热变的**(`boot/
|
|
26
|
+
* **这个表是热变的**(`boot/web-admin.ts` 的 `mutateInPlace(pricing, buildPricing(config.models))`,
|
|
27
27
|
* 那行注释自己写着 `hot: cost/model changes`)。旧码在 `main.ts` 的 capabilities 字面量里把它算成一个
|
|
28
|
-
* **boot 时的布尔快照**,于是运维在
|
|
28
|
+
* **boot 时的布尔快照**,于是运维在 web-admin 里加上/撤掉价目表之后,`/v1/capabilities` 还报旧值 ——
|
|
29
29
|
* 而这个键的**全部用途**就是给消费端消歧「costMicroUsd=0 是免费跑,还是根本没配价目表」,报错了就是
|
|
30
30
|
* 把消歧信号变成误导信号。main.ts 侧改用 getter(读时求值)。
|
|
31
31
|
*
|
|
32
|
-
* 同一条论证在同一个字面量里**已经执行过一次**:紧邻的 `restartState: () =>
|
|
32
|
+
* 同一条论证在同一个字面量里**已经执行过一次**:紧邻的 `restartState: () => webAdmin.restartState()`
|
|
33
33
|
* 带着注释「A live getter (not a snapshot) so /health always reads current」—— 兄弟键漏了。本仓第 7 例同形。
|
|
34
34
|
*
|
|
35
35
|
* 🔴 B-089(判据本身,同一条「0 不是缺席」的病):此前判的是「**有没有一项正价**」
|
package/dist/canonical-json.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## 为什么只能有一个
|
|
6
6
|
* 合并前本仓有 **5 处**手写复刻(memory-origin-fence / permission-rule-store-sql / bench-l8 artifact /
|
|
7
|
-
*
|
|
7
|
+
* web-admin restart-signal / mcp-revocation),每处一份「排 key、递归、拼串」——同一个算法五份实现,
|
|
8
8
|
* 语义在边缘(undefined 值键、函数成员、非有限数)各自漂了一点。指纹型代码的病灶恰恰在边缘:同一份内容算出
|
|
9
9
|
* 两个指纹 = 落库 checksum 失配 / 误签 restart / 包完整性自校验对不上。
|
|
10
10
|
*
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* · `bigint`:`JSON.stringify` 抛 TypeError,原样上抛。
|
|
30
30
|
*
|
|
31
31
|
* ## 不在本文件里的第二形(如实登记)
|
|
32
|
-
* `src/
|
|
32
|
+
* `src/web-admin-client/restart-signal.ts#stableStringify`(mcp-revocation 同源改调)是**另一个语义**:undefined 值键
|
|
33
33
|
* **保留为 `"k":null`**。两族在 JSON 形输入上逐字节相同(golden 交叉读数已证),分歧只在进程内构造的
|
|
34
34
|
* `{k: undefined}` 形——restart 指纹的输入正是进程内切片对象,该形原则上可达,而 `{k:undefined}` 与 `{}` 是否
|
|
35
35
|
* 同指纹是 restart-signal 轴上的**行为**(多签/少签一次 fleet 滚动重启),不是序列化器的私事 ⇒ 本批不并
|
|
@@ -85,7 +85,7 @@ export interface ApplyPluginsResult {
|
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* 装载器**会物化**的插件(按声明序)—— 每条声明过**同一只**判定 {@link validatePluginDecl},只留 `ok` 的
|
|
88
|
-
* `{name, url, sha?}`。restart 指纹(`
|
|
88
|
+
* `{name, url, sha?}`。restart 指纹(`web-admin-client/restart-signal.ts` 的 skills 片,S-668 波 0)读的就是它:
|
|
89
89
|
* 装载器不读的字段(notes / version)、整条跳过的声明(`enabled:false` / marketplace 源 / 白名单外 host / 坏形)
|
|
90
90
|
* 在这里一并消失,于是「重启之后什么都不会变」的发布不签重启(codex 对抗复审 r1 [medium]);顺序保留 ——
|
|
91
91
|
* 撞名让位按声明序,对调两条是真变化。`allowHosts` 与装载时同一份(部署事实 `PLUGINS_ALLOW_HOSTS`)。
|
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
* 与运维面板都读不出「额度现在没人在判」。本键把它们收成一个读面(调用方面;运维面仍是 `/health` 与 `GET /v1/diagnostics/wiring`)。
|
|
6
6
|
*
|
|
7
7
|
* ## 形与 present-iff
|
|
8
|
-
* 整个键只在接了中心(`config.
|
|
8
|
+
* 整个键只在接了中心(`config.webAdmin` 在场)时出现;`lease:"absent"`(中心三件不齐 ⇒ 没装租约管理器)时 `leaseHealth` /
|
|
9
9
|
* `failMode` / `caller` 三键缺席。「键缺席」于是两义:老 worker / 没接中心 —— 同一体的 `version` 能分。
|
|
10
10
|
* · `effectivePull` = 最近一次实拉成功(`lastPull === "ok"`;LKG 起服 / 从未成功 / 最近一拍失败 = false);
|
|
11
|
-
* · `effectiveRevision` = 与 `/health.configAppliedVersion` 同一只 ledger 视图(`
|
|
11
|
+
* · `effectiveRevision` = 与 `/health.configAppliedVersion` 同一只 ledger 视图(`web-admin-client/pull-state.ts`);
|
|
12
12
|
* · `lease` / `leaseHealth` / `failMode` / `caller` = 租约管理器的 `snapshot()` 与生效姿态(与执行的那一只同源);
|
|
13
13
|
* · `usageReport` = 用量环装没装(装配事实:累加器与环同一只谓词 `centerLaneConfigured`);
|
|
14
|
-
* · `issuer` = 鉴权桥 issuer 显式 / 回退(`authBridgeIssuerOf`;接了中心 ⇒ 桥恒装 ⇒ 没有 `none` 这一词)
|
|
14
|
+
* · `issuer` = 鉴权桥 issuer 显式 / 回退(`authBridgeIssuerOf`;接了中心 ⇒ 桥恒装 ⇒ 没有 `none` 这一词);
|
|
15
|
+
* · `worker` = 引擎向中心登记用的 worker 名(取值口 = `config.webAdmin.worker`,排空口身份四元读的同一只)。**present-iff 配了名且名可披露**:
|
|
16
|
+
* 没配 ⇒ 缺席(不拼不猜);配了但不是标识符形 / 是秘密形(契约 `isDisclosableIdentity`,「引擎要不要把一个名字写出去」问的那一只)⇒ 按无名处置,
|
|
17
|
+
* 键缺席,不打码。它住在这张**有鉴权**的能力面、不住无鉴权 `/health`(`http/health-self-report.ts` 头注:worker 名刻意不上无鉴权面)——
|
|
18
|
+
* 壳附着后用它拼网页会话链接(`<webBaseUrl>/chat?agent=<worker>&session=<id>`)。
|
|
15
19
|
* caps 路由自己**不 admit**(不打中心):读的是最近一次谈判留下的状态。
|
|
16
20
|
*/
|
|
17
|
-
import type { CenterPullView } from "../
|
|
21
|
+
import type { CenterPullView } from "../web-admin-client/pull-state.js";
|
|
18
22
|
import type { CallerLease, FleetLeaseManager, LeaseFailMode, LeaseHealth, LeaseSnapshot } from "../fleet-lease.js";
|
|
19
23
|
import type { ServiceConfig } from "../config.js";
|
|
20
24
|
/** `issuer` 的词表(两词:接了中心 ⇒ 桥恒装,`none` 结构上到不了,闭集里不留死词)。 */
|
|
@@ -32,6 +36,7 @@ export interface CenterWiring {
|
|
|
32
36
|
readonly failMode?: LeaseFailMode;
|
|
33
37
|
readonly usageReport: boolean;
|
|
34
38
|
readonly issuer: CenterIssuer;
|
|
39
|
+
readonly worker?: string;
|
|
35
40
|
readonly caller?: {
|
|
36
41
|
readonly lease: CallerLease;
|
|
37
42
|
readonly retryAfterSec?: number;
|
|
@@ -47,6 +52,8 @@ export interface CenterWiringFacts {
|
|
|
47
52
|
} | undefined;
|
|
48
53
|
readonly usageReport: boolean;
|
|
49
54
|
readonly issuer: CenterIssuer;
|
|
55
|
+
/** 配置里的 worker 名(`config.webAdmin.worker`;缺席 = 没配)。可披露性在投影里判,不在取值处判。 */
|
|
56
|
+
readonly worker: string | undefined;
|
|
50
57
|
}
|
|
51
58
|
/** 输入 → caps 投影(纯函数,`build*` = 纯数据)。 */
|
|
52
59
|
export declare function buildCenterWiring(f: CenterWiringFacts): CenterWiring;
|
|
@@ -55,7 +62,7 @@ export declare function buildCenterWiring(f: CenterWiringFacts): CenterWiring;
|
|
|
55
62
|
* 没接中心 ⇒ `undefined`(键整个缺席)。`now` 注入只给测试(与租约管理器同钟才判得出过期);生产缺省 `Date.now`。
|
|
56
63
|
*/
|
|
57
64
|
export declare function createCenterWiringReader(a: {
|
|
58
|
-
readonly config: Pick<ServiceConfig, "
|
|
65
|
+
readonly config: Pick<ServiceConfig, "webAdmin" | "authBridgeIssuer">;
|
|
59
66
|
readonly pullState: () => CenterPullView;
|
|
60
67
|
readonly fleetLease: Pick<FleetLeaseManager, "snapshot" | "failMode"> | undefined;
|
|
61
68
|
readonly usageReport: boolean;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { authBridgeIssuerOf } from "../auth-bridge.js";
|
|
2
|
+
import { isDisclosableIdentity } from "@sema-agent/settings-schema";
|
|
2
3
|
export const CENTER_ISSUER_WORDS = ["explicit", "fallback"];
|
|
3
4
|
export function buildCenterWiring(f) {
|
|
4
|
-
const base = {
|
|
5
|
+
const base = {
|
|
6
|
+
effectivePull: f.pull.lastPull === "ok",
|
|
7
|
+
effectiveRevision: f.pull.effectiveRevision,
|
|
8
|
+
usageReport: f.usageReport,
|
|
9
|
+
issuer: f.issuer,
|
|
10
|
+
...(f.worker !== undefined && isDisclosableIdentity(f.worker) ? { worker: f.worker } : {}),
|
|
11
|
+
};
|
|
5
12
|
if (f.lease === undefined)
|
|
6
13
|
return { ...base, lease: "absent" };
|
|
7
14
|
return { ...base, lease: "configured", leaseHealth: f.lease.snapshot.leaseHealth, failMode: f.lease.failMode, caller: f.lease.snapshot.caller };
|
|
8
15
|
}
|
|
9
16
|
export function createCenterWiringReader(a) {
|
|
10
|
-
|
|
17
|
+
const webAdmin = a.config.webAdmin;
|
|
18
|
+
if (webAdmin === undefined)
|
|
11
19
|
return undefined;
|
|
12
20
|
const issuer = authBridgeIssuerOf(a.config);
|
|
13
21
|
if (issuer === undefined)
|
|
@@ -19,6 +27,7 @@ export function createCenterWiringReader(a) {
|
|
|
19
27
|
lease: lease === undefined ? undefined : { failMode: lease.failMode, snapshot: lease.snapshot(principal, now()) },
|
|
20
28
|
usageReport: a.usageReport,
|
|
21
29
|
issuer: issuer.source,
|
|
30
|
+
worker: webAdmin.worker,
|
|
22
31
|
});
|
|
23
32
|
}
|
|
24
33
|
//# sourceMappingURL=center-wiring.js.map
|
|
@@ -42,7 +42,7 @@ export declare const COMPOSITE_CAPABILITY_MEMBERS: {
|
|
|
42
42
|
readonly fleet: readonly ["stream", "sessionScope", "observe", "resume", "bgNotifyFailClosed", "steerPriority", "hookFailureNotice", "leaseClient", "generationEcho"];
|
|
43
43
|
readonly memoryEngine: readonly ["backend", "vectorMode"];
|
|
44
44
|
readonly store: readonly ["declared", "backend", "durable", "degradedReason", "sql"];
|
|
45
|
-
readonly centerWiring: readonly ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "caller"];
|
|
45
|
+
readonly centerWiring: readonly ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "worker", "caller"];
|
|
46
46
|
readonly webSearch: readonly ["backend"];
|
|
47
47
|
readonly background: readonly ["listFace", "exitFaces"];
|
|
48
48
|
readonly taskSettings: readonly ["permissions", "permissionMode", "model", "outputStyle", "env", "hooks"];
|
|
@@ -3,7 +3,7 @@ export const COMPOSITE_CAPABILITY_MEMBERS = {
|
|
|
3
3
|
fleet: ["stream", "sessionScope", "observe", "resume", "bgNotifyFailClosed", "steerPriority", "hookFailureNotice", "leaseClient", "generationEcho"],
|
|
4
4
|
memoryEngine: ["backend", "vectorMode"],
|
|
5
5
|
store: ["declared", "backend", "durable", "degradedReason", "sql"],
|
|
6
|
-
centerWiring: ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "caller"],
|
|
6
|
+
centerWiring: ["effectivePull", "effectiveRevision", "lease", "leaseHealth", "failMode", "usageReport", "issuer", "worker", "caller"],
|
|
7
7
|
webSearch: ["backend"],
|
|
8
8
|
background: ["listFace", "exitFaces"],
|
|
9
9
|
taskSettings: ["permissions", "permissionMode", "model", "outputStyle", "env", "hooks"],
|
|
@@ -222,12 +222,12 @@ export function centerScenarios(deps, specs, builtinNames, logger) {
|
|
|
222
222
|
if (spec.enabled === false)
|
|
223
223
|
continue;
|
|
224
224
|
if (typeof spec.name !== "string" || !SCENARIO_NAME_RE.test(spec.name)) {
|
|
225
|
-
logger?.warn("
|
|
225
|
+
logger?.warn("web_admin_scenario_invalid", { name: String(spec?.name), reason: "name must match " + String(SCENARIO_NAME_RE) });
|
|
226
226
|
continue;
|
|
227
227
|
}
|
|
228
228
|
const toolset = Object.hasOwn(TOOLSETS, spec.toolset) ? TOOLSETS[spec.toolset] : undefined;
|
|
229
229
|
if (!toolset) {
|
|
230
|
-
logger?.warn("
|
|
230
|
+
logger?.warn("web_admin_scenario_invalid", { name: spec.name, reason: `unknown toolset "${String(spec.toolset)}" (have: ${Object.keys(TOOLSETS).join(", ")})` });
|
|
231
231
|
continue;
|
|
232
232
|
}
|
|
233
233
|
const name = spec.name;
|
|
@@ -83,7 +83,7 @@ export interface TeamTemplate {
|
|
|
83
83
|
systemPrompt?: string;
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
-
/** Built-in team templates (placeholder until the
|
|
86
|
+
/** Built-in team templates (placeholder until the web-admin supplies them). */
|
|
87
87
|
export declare const BUILTIN_TEAMS: Record<string, TeamTemplate>;
|
|
88
88
|
export declare function registerTeams(extra: Record<string, TeamTemplate>): void;
|
|
89
89
|
export declare function getTeam(name: string): TeamTemplate | undefined;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* 在本仓缺省场景下那会连 Agent 与 Task 四件一起卷进候选(与 CC 姿态相反),所以我们**刻意不开**;
|
|
19
19
|
* 🔴 该判断在 core 5.40.0 / 7.15.0 上**逐字仍成立**(亲验):两次收窄动的都是「候选集是谁」,而
|
|
20
20
|
* Agent / Task 四件是 **caller spec 工具**,收窄后照样在候选池里 —— 不开 auto 的理由没有被消解。
|
|
21
|
-
* 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(
|
|
21
|
+
* 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(web-admin 曾有同形近名 `deferModelPlane`,
|
|
22
22
|
* 与本轴无关,已于 S-668 波 0 退役)。机器钉见 test/tool-defer.test.ts 的 [ref] 件⑤ 格 —— 这条「零接线」正是本仓
|
|
23
23
|
* 在两个 core 世代之间行为逐字不变的**全部**依据,所以它值一根钉而不只是一句注。
|
|
24
24
|
* - per-tool `ToolSpec.defer:true` 是本 helper 施加的那一位。**7.15.0 起它是一次「进候选集」的声明,
|
|
@@ -59,7 +59,7 @@ export declare function buildCenterCredentialHealthView(health: CenterCredential
|
|
|
59
59
|
export interface CenterCredentialRenewerDeps {
|
|
60
60
|
/** 文件形凭证来源(续签器只从它的取值被触发;文件路径取自它的 `origin`)。 */
|
|
61
61
|
readonly source: CenterCredentialSource;
|
|
62
|
-
/** `
|
|
62
|
+
/** `webAdmin.url` —— 续签只向与它同源的 `issuer` 发。 */
|
|
63
63
|
readonly centerUrl: string;
|
|
64
64
|
/** `CENTER_CREDENTIAL_RENEW_LEAD_MS`(已过 {@link parseCenterCredentialRenewLeadMs});0 = 关续签。 */
|
|
65
65
|
readonly leadMs: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { closeSync, fchmodSync, fsyncSync, openSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { WEB_ADMIN_ENV, WebAdminCredentialEnvelope, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
|
|
5
5
|
import { ACCESS_TOKEN_TTL_SECONDS, OAuthErrorResponse, TokenGrantResponse } from "@sema-agent/settings-schema/api/auth";
|
|
6
|
-
import { CENTER_PULL_TIMEOUT_MS, centerTokenRefresh } from "./
|
|
6
|
+
import { CENTER_PULL_TIMEOUT_MS, centerTokenRefresh } from "./web-admin-client/center-request.js";
|
|
7
7
|
export const CENTER_CREDENTIAL_RENEW_LEAD_KEY = "CENTER_CREDENTIAL_RENEW_LEAD_MS";
|
|
8
8
|
export const CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS = 5 * 60_000;
|
|
9
9
|
export const CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS = CENTER_PULL_TIMEOUT_MS;
|
|
@@ -42,7 +42,7 @@ function readFileNow(path) {
|
|
|
42
42
|
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
43
43
|
return code === "ENOENT" ? { kind: "no-session" } : { kind: "unreadable", code };
|
|
44
44
|
}
|
|
45
|
-
const file =
|
|
45
|
+
const file = parseWebAdminCredentialFile(text);
|
|
46
46
|
return file.kind === "envelope" ? { kind: "session", envelope: file.envelope, refreshDigest: digest16(file.envelope.refreshToken) } : { kind: "no-session" };
|
|
47
47
|
}
|
|
48
48
|
const TMP_RANDOM_BYTES = 6;
|
|
@@ -83,7 +83,7 @@ function sessionExpiryOf(read) {
|
|
|
83
83
|
export function createCenterCredentialRenewer(deps) {
|
|
84
84
|
const { source, centerUrl, leadMs, logger } = deps;
|
|
85
85
|
if (source.origin.from !== "file")
|
|
86
|
-
throw new Error(`center credential renewer needs the ${
|
|
86
|
+
throw new Error(`center credential renewer needs the ${WEB_ADMIN_ENV.tokenFile} form (got ${source.origin.from})`);
|
|
87
87
|
const path = source.origin.path;
|
|
88
88
|
const now = deps.now ?? Date.now;
|
|
89
89
|
const tickMs = Math.min(Math.max(leadMs / 2, 1000), 60_000);
|
|
@@ -147,7 +147,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
147
147
|
return;
|
|
148
148
|
unavailableStreak = read.failure;
|
|
149
149
|
logger.warn("center_credential_unreadable", {
|
|
150
|
-
key:
|
|
150
|
+
key: WEB_ADMIN_ENV.tokenFile,
|
|
151
151
|
failure: read.failure,
|
|
152
152
|
reason: read.reason,
|
|
153
153
|
note: "no center request carries this credential until the file holds a usable one again (each consumer takes its own failure arm); warned once per unavailability streak",
|
|
@@ -172,7 +172,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
172
172
|
subject = refreshDigest;
|
|
173
173
|
held = undefined;
|
|
174
174
|
if (reason === "issuer_mismatch") {
|
|
175
|
-
logger.warn("center_credential_issuer_mismatch", { ...fields, note:
|
|
175
|
+
logger.warn("center_credential_issuer_mismatch", { ...fields, note: `the sign-in envelope was issued by a different center than ${WEB_ADMIN_ENV.url} — its refresh token is never sent there; the access token is used until it expires, then sign in again against this center` });
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
178
|
logger.warn("center_credential_renew_failed", {
|
|
@@ -297,7 +297,7 @@ export function createCenterCredentialRenewer(deps) {
|
|
|
297
297
|
judgeRefusal(usedDigest, "malformed_grant", { status: res.status });
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
300
|
-
const next =
|
|
300
|
+
const next = WebAdminCredentialEnvelope.safeParse({
|
|
301
301
|
kind: envelope.kind,
|
|
302
302
|
accessToken: grant.data.access_token,
|
|
303
303
|
refreshToken: grant.data.refresh_token,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
/** 凭证种类闭集。新增一个种类 = 本文件与 `
|
|
1
|
+
import { type WebAdminCredentialDeclaration } from "@sema-agent/settings-schema";
|
|
2
|
+
/** 凭证种类闭集。新增一个种类 = 本文件与 `web-admin-client/center-request.ts` 的穷举 switch 当场编译红。 */
|
|
3
3
|
export type CenterCredentialKind = "pull-token" | "user-jwt" | "user-session" | "none";
|
|
4
4
|
/** 一次取值失败的成因闭集(见模块头注「四个成因」;boot 判决 {@link judgeCenterCredentialAtBoot} 对它穷举)。 */
|
|
5
5
|
export type CenterCredentialFailure = "missing" | "malformed" | "multi_tenant_session" | "expired";
|
|
@@ -84,7 +84,7 @@ export declare class CenterCredentialUnavailableError extends Error {
|
|
|
84
84
|
* 凭证来源的构造入参:settings-schema 的声明形,文件形**必须**带上本部署的租户姿态(多租户拒人的会话信封,见模块头注)——
|
|
85
85
|
* 姿态只在文件形有意义(env 值形是部署注入的拉取令牌,永远不是信封),所以只在那一臂上必填:漏传 = 编译红,不是静默单租户。
|
|
86
86
|
*/
|
|
87
|
-
export type CenterCredentialSourceSpec = Exclude<
|
|
87
|
+
export type CenterCredentialSourceSpec = Exclude<WebAdminCredentialDeclaration, {
|
|
88
88
|
from: "file";
|
|
89
89
|
}> | {
|
|
90
90
|
readonly from: "file";
|
|
@@ -92,16 +92,16 @@ export type CenterCredentialSourceSpec = Exclude<CenterCredentialDeclaration, {
|
|
|
92
92
|
readonly multiTenant: boolean;
|
|
93
93
|
};
|
|
94
94
|
/**
|
|
95
|
-
* 读两个声明键 —— 判定全交 settings-schema 的判官 `
|
|
95
|
+
* 读两个声明键 —— 判定全交 settings-schema 的判官 `judgeWebAdminCredentialDeclaration`(S-742:二选一规则的**唯一**实现;空串 = 未设、
|
|
96
96
|
* 不 trim;两个都设 ⇒ throw,boot 拒启,拒句点名两个键、不回显值)。本仓只多读方自己的那一步:文件路径按本进程 cwd 解析成绝对
|
|
97
97
|
* 路径(之后 cwd 变了也读同一个文件 —— 纯契约不碰 `node:path`,这一步归读方)。
|
|
98
98
|
*/
|
|
99
|
-
export declare function readCenterCredentialDeclaration(env?: Readonly<Record<string, string | undefined>>):
|
|
99
|
+
export declare function readCenterCredentialDeclaration(env?: Readonly<Record<string, string | undefined>>): WebAdminCredentialDeclaration;
|
|
100
100
|
/** 拉取令牌形的身份 = worker(拉取令牌代表 worker,不是人)。种类判别与 formatVersion 1 LKG 的一次性迁移
|
|
101
101
|
* (`config-lkg.ts` `migrateV1Lkg`:v1 信封只记 worker,只有这一形的身份推得出来)共用这一只。 */
|
|
102
102
|
export declare function pullTokenIdentity(worker: string | undefined): string;
|
|
103
103
|
/**
|
|
104
|
-
* 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `
|
|
104
|
+
* 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `SEMA_WEB_ADMIN_WORKER`(拉取令牌形的身份)。
|
|
105
105
|
* `readFile` / `now` 只为测试注入。
|
|
106
106
|
*/
|
|
107
107
|
export declare function createCenterCredentialSource(spec: CenterCredentialSourceSpec, worker: string | undefined, deps?: {
|
|
@@ -128,13 +128,13 @@ export declare const CENTER_CREDENTIAL_REFUSAL_CODES: {
|
|
|
128
128
|
};
|
|
129
129
|
export type CenterCredentialRefusalCode = (typeof CENTER_CREDENTIAL_REFUSAL_CODES)[keyof typeof CENTER_CREDENTIAL_REFUSAL_CODES];
|
|
130
130
|
/**
|
|
131
|
-
* S-698 —— boot 读到的凭证怎么处置(`boot/
|
|
131
|
+
* S-698 —— boot 读到的凭证怎么处置(`boot/web-admin.ts` 执行,规则只在这里):
|
|
132
132
|
* · `missing` / `expired` ⇒ 响亮(error 级一行),照常起服 —— 前者是「此刻没有凭证」(登出形,文件出现即恢复),
|
|
133
133
|
* 后者续签器立刻续签;
|
|
134
134
|
* · `malformed` / `multi_tenant_session` ⇒ **拒启**,拒句带码、点名文件键,不回显内容(`reason` 由本模块铸,本来就不含内容)。
|
|
135
135
|
*/
|
|
136
136
|
export declare function judgeCenterCredentialAtBoot(read: CenterCredentialRead): CenterCredentialBootVerdict;
|
|
137
|
-
/** 需要作用域 worker 的拉取令牌(`wpt_<self>`)却没配 `
|
|
137
|
+
/** 需要作用域 worker 的拉取令牌(`wpt_<self>`)却没配 `SEMA_WEB_ADMIN_WORKER` ⇒ 中心对 per-principal caps
|
|
138
138
|
* 逐次 403(caps fail-closed 拒全部 workflow)。boot 期一次性诊断用;判据只读本模块的种类判别。 */
|
|
139
139
|
export declare function scopedTokenNeedsWorker(read: CenterCredentialRead, worker: string | undefined): boolean;
|
|
140
140
|
/**
|
|
@@ -148,7 +148,7 @@ export declare function centerStateBin(worker: string | undefined, identity: str
|
|
|
148
148
|
export declare function credentialIdentityNow(source: CenterCredentialSource): string | undefined;
|
|
149
149
|
/**
|
|
150
150
|
* [ref] E-15 ② —— 一次中心往返的答复**仍属于此刻的凭证**:发出它所用的身份 = 此刻的身份。
|
|
151
|
-
* 拉配置(`boot/
|
|
151
|
+
* 拉配置(`boot/web-admin.ts` `pullEffective`)与两条 `?principal=` 视图(`web-admin-client/http-client.ts`)
|
|
152
152
|
* 都只问这一只;不成立 ⇒ 答复丢弃,当这次拉取失败(各走各的失败臂)。
|
|
153
153
|
*/
|
|
154
154
|
export declare function answerStillCurrent(source: CenterCredentialSource, usedIdentity: string | undefined): usedIdentity is string;
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { resolve as resolvePath } from "node:path";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
|
-
import {
|
|
5
|
+
import { WEB_ADMIN_ENV, judgeWebAdminCredentialDeclaration, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
|
|
6
6
|
export class CenterCredentialUnavailableError extends Error {
|
|
7
7
|
reason;
|
|
8
8
|
constructor(reason) {
|
|
@@ -12,7 +12,7 @@ export class CenterCredentialUnavailableError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
export function readCenterCredentialDeclaration(env = process.env) {
|
|
15
|
-
const declared =
|
|
15
|
+
const declared = judgeWebAdminCredentialDeclaration(env, "access");
|
|
16
16
|
return declared.from === "file" ? { from: "file", path: resolvePath(declared.path) } : declared;
|
|
17
17
|
}
|
|
18
18
|
const digest16 = (s) => createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
|
|
@@ -43,8 +43,8 @@ export function pullTokenIdentity(worker) {
|
|
|
43
43
|
const NONE = { ok: true, kind: "none", identity: "none" };
|
|
44
44
|
const PARSER_EMPTY_REASON = "empty";
|
|
45
45
|
function readCredentialText(text, spec, worker, now) {
|
|
46
|
-
const where = `${
|
|
47
|
-
const file =
|
|
46
|
+
const where = `${WEB_ADMIN_ENV.tokenFile} ${spec.path}`;
|
|
47
|
+
const file = parseWebAdminCredentialFile(text);
|
|
48
48
|
switch (file.kind) {
|
|
49
49
|
case "bare":
|
|
50
50
|
return classify(file.bearer, worker);
|
|
@@ -105,7 +105,7 @@ export function createCenterCredentialSource(spec, worker, deps = {}) {
|
|
|
105
105
|
}
|
|
106
106
|
catch (err) {
|
|
107
107
|
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
108
|
-
return { ok: false, failure: "missing", reason: `${
|
|
108
|
+
return { ok: false, failure: "missing", reason: `${WEB_ADMIN_ENV.tokenFile} ${spec.path} is unreadable (${code})` };
|
|
109
109
|
}
|
|
110
110
|
return readCredentialText(text, spec, worker, now());
|
|
111
111
|
};
|
|
@@ -155,7 +155,7 @@ export function judgeCenterCredentialAtBoot(read) {
|
|
|
155
155
|
return {
|
|
156
156
|
verdict: "refuse",
|
|
157
157
|
code,
|
|
158
|
-
sentence: `center credential refused at boot [${code}]: ${read.reason}. The file must hold one bare token line, or the sign-in envelope the shell writes (a
|
|
158
|
+
sentence: `center credential refused at boot [${code}]: ${read.reason}. The file must hold one bare token line, or the sign-in envelope the shell writes (a WebAdminCredentialEnvelope JSON object); sign in again so the shell rewrites it, or fix the file — its content is never echoed.`,
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
case "multi_tenant_session": {
|