@kontextmind/kxm 0.7.167 → 0.7.169
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +30 -0
- package/docs/contributing/ci-and-release.md +6 -1
- package/docs/operations/runtime-sync.md +2 -1
- package/docs/operations/troubleshooting.md +4 -0
- package/docs/operations/upgrade.md +18 -3
- package/docs/operations.md +70 -7
- package/docs/reference/cli-reference.md +21 -6
- package/docs/reference/config-reference.md +28 -13
- package/docs/start/install.md +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +170 -36
- package/plugins/kxm/dist/cli.js +451 -132
- package/plugins/kxm/dist/extension.js +184 -53
- package/plugins/kxm/dist/mcp-server.js +97 -40
- package/plugins/kxm/dist/runtime-supervisor.js +240 -47
- package/plugins/kxm/dist/runtime.js +240 -47
- package/plugins/kxm/dist/server.js +9 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +3 -2
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +4 -2
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -4
- package/plugins/kxm/src/cli/hub.ts +42 -7
- package/plugins/kxm/src/cli/system.ts +3 -1
- package/plugins/kxm/src/cli/types.ts +5 -3
- package/plugins/kxm/src/cli.ts +2 -1
- package/plugins/kxm/src/engine.ts +105 -8
- package/plugins/kxm/src/hub-binding.ts +301 -55
- package/plugins/kxm/src/hub-identity.ts +17 -9
- package/plugins/kxm/src/kxm-update.ts +29 -6
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +63 -1
- package/plugins/kxm/src/runtime-supervisor.ts +22 -4
- package/scripts/kxm-publish-npm.mjs +47 -1
|
@@ -17261,7 +17261,28 @@ var RESOURCE_SCHEMA = Object.freeze({
|
|
|
17261
17261
|
var IDENTIFIER2 = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
|
|
17262
17262
|
var WINDOWS_RESERVED = /^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³]|conin\$|conout\$|clock\$)$/i;
|
|
17263
17263
|
var BUILTIN_EXECUTORS = ["local", "ssh", "exe-dev"];
|
|
17264
|
-
var
|
|
17264
|
+
var TOOL_PRESET_PROFILES = Object.freeze({
|
|
17265
|
+
coordinator: "read-only",
|
|
17266
|
+
"read-only": "read-only",
|
|
17267
|
+
"workspace-writer": "edit",
|
|
17268
|
+
"tests-writer": "edit"
|
|
17269
|
+
});
|
|
17270
|
+
var BUILTIN_TOOL_PRESETS = Object.keys(TOOL_PRESET_PROFILES);
|
|
17271
|
+
function toolPresetProfile(preset) {
|
|
17272
|
+
if (!Object.hasOwn(TOOL_PRESET_PROFILES, preset)) return void 0;
|
|
17273
|
+
return TOOL_PRESET_PROFILES[preset];
|
|
17274
|
+
}
|
|
17275
|
+
function toolPresetExceedsRoleMessage(preset, roleId, rolePermission, rolePreset) {
|
|
17276
|
+
const profile = toolPresetProfile(preset);
|
|
17277
|
+
if (profile !== "edit") return void 0;
|
|
17278
|
+
if (rolePermission === "read-only") {
|
|
17279
|
+
return `tools.preset ${preset} exceeds role ${roleId} permission read-only`;
|
|
17280
|
+
}
|
|
17281
|
+
if (rolePreset !== void 0 && toolPresetProfile(rolePreset) === "read-only") {
|
|
17282
|
+
return `tools.preset ${preset} exceeds role ${roleId} preset ${rolePreset}`;
|
|
17283
|
+
}
|
|
17284
|
+
return void 0;
|
|
17285
|
+
}
|
|
17265
17286
|
var SECRET_VALUE_PATTERNS = [
|
|
17266
17287
|
/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----/,
|
|
17267
17288
|
/\b(?:ghp|github_pat)_[A-Za-z0-9_]{20,}\b/,
|
|
@@ -17661,6 +17682,15 @@ function objectValue(value) {
|
|
|
17661
17682
|
function stringValue(value) {
|
|
17662
17683
|
return typeof value === "string" ? value : void 0;
|
|
17663
17684
|
}
|
|
17685
|
+
function roleById(roles, roleId) {
|
|
17686
|
+
const direct = roles.get(roleId);
|
|
17687
|
+
if (direct) return direct;
|
|
17688
|
+
const folded = roleId.toLocaleLowerCase("en-US");
|
|
17689
|
+
for (const role of roles.values()) {
|
|
17690
|
+
if ((role.id ?? "").toLocaleLowerCase("en-US") === folded) return role;
|
|
17691
|
+
}
|
|
17692
|
+
return void 0;
|
|
17693
|
+
}
|
|
17664
17694
|
function numberValue(value, fallback) {
|
|
17665
17695
|
return typeof value === "number" ? value : fallback;
|
|
17666
17696
|
}
|
|
@@ -18175,10 +18205,29 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
18175
18205
|
if (executor && !executors.has(executor)) issues.push(issue3("reference", "executor_unknown", agent.logicalPath, `executor ${executor} is not registered`));
|
|
18176
18206
|
const preset = stringValue(objectValue(agent.value.tools)?.preset);
|
|
18177
18207
|
if (preset && !presets.has(preset)) issues.push(issue3("reference", "tool_preset_unknown", agent.logicalPath, `tool preset ${preset} is not registered`));
|
|
18208
|
+
const roleId = stringValue(agent.value.role);
|
|
18209
|
+
const role = roleId ? roleById(roles, roleId) : void 0;
|
|
18210
|
+
const rolePermission = stringValue(role?.value.permission);
|
|
18211
|
+
if (preset && role && (rolePermission === "edit" || rolePermission === "read-only")) {
|
|
18212
|
+
const message = toolPresetExceedsRoleMessage(
|
|
18213
|
+
preset,
|
|
18214
|
+
role.id ?? roleId ?? "role",
|
|
18215
|
+
rolePermission,
|
|
18216
|
+
stringValue(objectValue(role.value.tools)?.preset)
|
|
18217
|
+
);
|
|
18218
|
+
if (message) issues.push(issue3("semantic", "tool_preset_exceeds_role", agent.logicalPath, message));
|
|
18219
|
+
}
|
|
18178
18220
|
for (const repositoryId of Object.keys(objectValue(agent.value.repositories) ?? {})) {
|
|
18179
18221
|
if (!repositoryIds.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", agent.logicalPath, `references unknown repository ${repositoryId}`));
|
|
18180
18222
|
}
|
|
18181
18223
|
}
|
|
18224
|
+
for (const role of roles.values()) {
|
|
18225
|
+
const permission = stringValue(role.value.permission);
|
|
18226
|
+
const rolePreset = stringValue(objectValue(role.value.tools)?.preset);
|
|
18227
|
+
if (permission === "read-only" && rolePreset && toolPresetProfile(rolePreset) === "edit") {
|
|
18228
|
+
issues.push(issue3("semantic", "tool_preset_exceeds_role", role.logicalPath, `tools.preset ${rolePreset} exceeds permission read-only`));
|
|
18229
|
+
}
|
|
18230
|
+
}
|
|
18182
18231
|
validateModelReferences(models, issues);
|
|
18183
18232
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
18184
18233
|
if (!lookupKxmResource(workflows.values(), defaultWorkflow, "workflow")) issues.push(issue3("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
@@ -29377,6 +29426,68 @@ function unsupportedAgentStepTimeout(step, limitMs) {
|
|
|
29377
29426
|
detail: "step timeoutMs is wider than project limits.agentStepTimeoutMs"
|
|
29378
29427
|
};
|
|
29379
29428
|
}
|
|
29429
|
+
function narrowerPermission(left, right) {
|
|
29430
|
+
return left === "read-only" || right === "read-only" ? "read-only" : "edit";
|
|
29431
|
+
}
|
|
29432
|
+
function stepRepositoryPermission(step) {
|
|
29433
|
+
return Object.values(step.repositories).some((access) => access === "write") ? "edit" : "read-only";
|
|
29434
|
+
}
|
|
29435
|
+
function stepAccessDetail(step) {
|
|
29436
|
+
const entries = Object.entries(step.repositories);
|
|
29437
|
+
if (entries.length === 0) return "none";
|
|
29438
|
+
return entries.map(([id, access]) => `${id} ${access}`).sort((left, right) => left < right ? -1 : left > right ? 1 : 0).join(", ");
|
|
29439
|
+
}
|
|
29440
|
+
function yamlToolsPreset(doc) {
|
|
29441
|
+
const tools = doc?.tools;
|
|
29442
|
+
if (!tools || typeof tools !== "object" || Array.isArray(tools)) return void 0;
|
|
29443
|
+
const preset = tools.preset;
|
|
29444
|
+
return typeof preset === "string" && preset.length > 0 ? preset : void 0;
|
|
29445
|
+
}
|
|
29446
|
+
function resolveToolDispatch(projectRoot, step, agentId) {
|
|
29447
|
+
const stepPermission = stepRepositoryPermission(step);
|
|
29448
|
+
if (step.kind !== "agent" && step.kind !== "moa") return { permission: stepPermission };
|
|
29449
|
+
const agentFile = findYamlBasename(join18(projectRoot, ".kxm", "agents"), agentId, "agent") ?? agentId;
|
|
29450
|
+
const agent = readYamlFile(join18(projectRoot, ".kxm", "agents", `${agentFile}.yaml`));
|
|
29451
|
+
const roleId = typeof agent?.role === "string" ? agent.role : "";
|
|
29452
|
+
if (!roleId) return { permission: stepPermission };
|
|
29453
|
+
const roleDoc = readYamlFile(join18(projectRoot, ".kxm", "roles", `${roleId}.yaml`));
|
|
29454
|
+
const rawPermission = roleDoc?.permission;
|
|
29455
|
+
const rolePermission = rawPermission === "edit" || rawPermission === "read-only" ? rawPermission : void 0;
|
|
29456
|
+
if (!rolePermission) return { permission: stepPermission };
|
|
29457
|
+
const rolePreset = yamlToolsPreset(roleDoc);
|
|
29458
|
+
const agentPreset = yamlToolsPreset(agent);
|
|
29459
|
+
const rolePresetProfile = rolePreset ? toolPresetProfile(rolePreset) : void 0;
|
|
29460
|
+
if (rolePreset && rolePresetProfile === "edit" && rolePermission === "read-only") {
|
|
29461
|
+
return {
|
|
29462
|
+
error: {
|
|
29463
|
+
reason: "step_unsupported",
|
|
29464
|
+
field: "tools.preset",
|
|
29465
|
+
detail: `tools.preset ${rolePreset} exceeds role ${roleId} permission read-only`
|
|
29466
|
+
}
|
|
29467
|
+
};
|
|
29468
|
+
}
|
|
29469
|
+
if (agentPreset) {
|
|
29470
|
+
const exceeds = toolPresetExceedsRoleMessage(agentPreset, roleId, rolePermission, rolePreset);
|
|
29471
|
+
if (exceeds) {
|
|
29472
|
+
return { error: { reason: "step_unsupported", field: "tools.preset", detail: exceeds } };
|
|
29473
|
+
}
|
|
29474
|
+
}
|
|
29475
|
+
let chain = rolePermission;
|
|
29476
|
+
if (rolePresetProfile) chain = narrowerPermission(chain, rolePresetProfile);
|
|
29477
|
+
const agentProfile = agentPreset ? toolPresetProfile(agentPreset) : void 0;
|
|
29478
|
+
if (agentProfile) chain = narrowerPermission(chain, agentProfile);
|
|
29479
|
+
const permission = narrowerPermission(chain, stepPermission);
|
|
29480
|
+
if (stepPermission === "edit" && permission === "read-only") {
|
|
29481
|
+
return {
|
|
29482
|
+
error: {
|
|
29483
|
+
reason: "step_unsupported",
|
|
29484
|
+
field: "permission",
|
|
29485
|
+
detail: `role permission ${rolePermission} resolves to ${chain}; step access ${stepAccessDetail(step)}`
|
|
29486
|
+
}
|
|
29487
|
+
};
|
|
29488
|
+
}
|
|
29489
|
+
return { permission };
|
|
29490
|
+
}
|
|
29380
29491
|
function executingSingletonAttempt(plan, state) {
|
|
29381
29492
|
const current = state.currentStep;
|
|
29382
29493
|
if (!current || current.panel.order.length !== 1) return void 0;
|
|
@@ -29569,10 +29680,17 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
|
|
|
29569
29680
|
if (unsupported) return { kind: "return", state, handoff: { ...unsupported, stepId } };
|
|
29570
29681
|
const wideTimeout = unsupportedAgentStepTimeout(step, projectAgentStepTimeoutMs(context, run));
|
|
29571
29682
|
if (wideTimeout) return { kind: "return", state, handoff: { ...wideTimeout, stepId } };
|
|
29683
|
+
const firstListed = step.assignments.allowedAgents[0];
|
|
29684
|
+
const firstAgentId = step.kind === "agent" || step.kind === "moa" ? firstListed ? firstListed : step.agent : void 0;
|
|
29685
|
+
if (firstAgentId) {
|
|
29686
|
+
const toolDispatch = resolveToolDispatch(context.projectRoot, step, firstAgentId);
|
|
29687
|
+
if ("error" in toolDispatch) {
|
|
29688
|
+
return { kind: "return", state, handoff: { ...toolDispatch.error, stepId } };
|
|
29689
|
+
}
|
|
29690
|
+
}
|
|
29572
29691
|
let resolvedRoute;
|
|
29573
29692
|
if (producerId !== "driver-simulated") {
|
|
29574
|
-
const
|
|
29575
|
-
const agentId = allowed && allowed.length > 0 && allowed[0] ? allowed[0] : step.kind === "agent" || step.kind === "moa" ? step.agent : "coordinator";
|
|
29693
|
+
const agentId = firstAgentId ?? (step.kind === "agent" || step.kind === "moa" ? step.agent : "coordinator");
|
|
29576
29694
|
const routeResult = resolveProducerRoute(context.projectRoot, step, agentId);
|
|
29577
29695
|
if ("error" in routeResult) {
|
|
29578
29696
|
return { kind: "return", state, handoff: { ...routeResult.error, stepId } };
|
|
@@ -29692,6 +29810,10 @@ function birthMember(context, input) {
|
|
|
29692
29810
|
const born = folded.currentStep?.panel.order.length ?? 0;
|
|
29693
29811
|
const allowed = input.step.assignments.allowedAgents;
|
|
29694
29812
|
const agentId = allowed && allowed.length > born && allowed[born] ? allowed[born] : input.step.kind === "agent" || input.step.kind === "moa" ? input.step.agent : "coordinator";
|
|
29813
|
+
const toolDispatch = resolveToolDispatch(context.projectRoot, input.step, agentId);
|
|
29814
|
+
if ("error" in toolDispatch) {
|
|
29815
|
+
return { handoff: { ...toolDispatch.error, stepId: input.stepId } };
|
|
29816
|
+
}
|
|
29695
29817
|
let resolvedRoute = input.resolvedRoute;
|
|
29696
29818
|
if (!resolvedRoute && input.producerId && input.producerId !== "driver-simulated") {
|
|
29697
29819
|
const routeResult = resolveProducerRoute(context.projectRoot, input.step, agentId);
|
|
@@ -29781,7 +29903,7 @@ function birthMember(context, input) {
|
|
|
29781
29903
|
stepAttempt: input.stepAttempt,
|
|
29782
29904
|
objective: promptText,
|
|
29783
29905
|
allowedOutcomes: [...input.step.outcomes],
|
|
29784
|
-
permissionCeiling:
|
|
29906
|
+
permissionCeiling: toolDispatch.permission
|
|
29785
29907
|
},
|
|
29786
29908
|
acceptanceCriteria: input.step.requiredEvidence.map((ev) => ({
|
|
29787
29909
|
id: ev.key,
|
|
@@ -29826,7 +29948,7 @@ function birthMember(context, input) {
|
|
|
29826
29948
|
prompt: input.step.instructions ? `${input.step.instructions}
|
|
29827
29949
|
|
|
29828
29950
|
${generatedPrompt}` : generatedPrompt,
|
|
29829
|
-
permission:
|
|
29951
|
+
permission: toolDispatch.permission,
|
|
29830
29952
|
...(input.step.kind === "agent" || input.step.kind === "moa") && input.step.timeoutMs !== void 0 ? { timeoutMs: input.step.timeoutMs } : {},
|
|
29831
29953
|
contextPacket,
|
|
29832
29954
|
...resolvedRoute?.effort !== void 0 ? { thinking: resolvedRoute.effort } : {},
|
|
@@ -31668,6 +31790,9 @@ function resolveUserStateRoot(env) {
|
|
|
31668
31790
|
function hubBindingFile(env = process.env) {
|
|
31669
31791
|
return join19(resolveUserStateRoot(env), "hub-binding.json");
|
|
31670
31792
|
}
|
|
31793
|
+
function hubBindingCloudFile(env = process.env) {
|
|
31794
|
+
return join19(resolveUserStateRoot(env), "hub-binding.cloud.json");
|
|
31795
|
+
}
|
|
31671
31796
|
function validateHubUrl(raw) {
|
|
31672
31797
|
let parsed;
|
|
31673
31798
|
try {
|
|
@@ -31777,33 +31902,37 @@ function isIsoTimestamp(value) {
|
|
|
31777
31902
|
if (Number.isNaN(Date.parse(value))) return false;
|
|
31778
31903
|
return value === new Date(value).toISOString();
|
|
31779
31904
|
}
|
|
31780
|
-
|
|
31781
|
-
|
|
31782
|
-
|
|
31905
|
+
var LEGACY_BINDING_KEYS = ["schema", "url", "boundAt"];
|
|
31906
|
+
var BINDING_KEYS = /* @__PURE__ */ new Set([...LEGACY_BINDING_KEYS, "cloud", "tokenEnv", "tokenCommand"]);
|
|
31907
|
+
function bindingFallbackWarning(file, reason) {
|
|
31908
|
+
return `hub binding at ${file} ${reason}; falling back to the local hub. Restart the Runtime supervisor, the hub, and workers after upgrading and before a cloud bind.`;
|
|
31909
|
+
}
|
|
31910
|
+
function writeBindingJson(file, value) {
|
|
31911
|
+
mkdirSync7(dirname9(file), { recursive: true, mode: 448 });
|
|
31912
|
+
const temporary = join19(dirname9(file), `.${file.endsWith("cloud.json") ? "hub-binding-cloud" : "hub-binding"}-${process.pid}.tmp`);
|
|
31913
|
+
try {
|
|
31914
|
+
writeFileSync8(temporary, `${JSON.stringify(value, null, 2)}
|
|
31915
|
+
`, { encoding: "utf8", mode: 384 });
|
|
31916
|
+
renameSync2(temporary, file);
|
|
31917
|
+
} finally {
|
|
31918
|
+
rmSync2(temporary, { force: true });
|
|
31919
|
+
}
|
|
31920
|
+
}
|
|
31921
|
+
function parseBindingObject(file) {
|
|
31922
|
+
if (!existsSync16(file)) return new HubBindingError(`missing hub binding at ${file}`);
|
|
31783
31923
|
let parsed;
|
|
31784
31924
|
try {
|
|
31785
31925
|
parsed = JSON.parse(readFileSync14(file, "utf8"));
|
|
31786
31926
|
} catch {
|
|
31787
|
-
|
|
31927
|
+
return new HubBindingError(`malformed hub binding at ${file}`);
|
|
31788
31928
|
}
|
|
31789
31929
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
31790
|
-
|
|
31791
|
-
}
|
|
31792
|
-
const row = parsed;
|
|
31793
|
-
const keys = Object.keys(row);
|
|
31794
|
-
const allowed = /* @__PURE__ */ new Set(["schema", "url", "boundAt", "cloud", "tokenEnv", "tokenCommand"]);
|
|
31795
|
-
const cloudish = keys.some((key) => key === "cloud" || key === "tokenEnv" || key === "tokenCommand");
|
|
31796
|
-
if (keys.some((key) => !allowed.has(key)) || !cloudish && keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
|
|
31797
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31930
|
+
return new HubBindingError(`malformed hub binding at ${file}`);
|
|
31798
31931
|
}
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31804
|
-
}
|
|
31805
|
-
if (!cloudish) return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
|
|
31806
|
-
if (row.cloud !== true) throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31932
|
+
return parsed;
|
|
31933
|
+
}
|
|
31934
|
+
function cloudRecordFromRow(file, row, url, boundAt) {
|
|
31935
|
+
if (row.cloud !== true) return void 0;
|
|
31807
31936
|
let tokenEnv;
|
|
31808
31937
|
let tokenCommand;
|
|
31809
31938
|
if (row.tokenEnv !== void 0) {
|
|
@@ -31825,12 +31954,65 @@ function readHubBinding(env = process.env) {
|
|
|
31825
31954
|
return {
|
|
31826
31955
|
schema: HUB_BINDING_SCHEMA,
|
|
31827
31956
|
url,
|
|
31828
|
-
boundAt
|
|
31957
|
+
boundAt,
|
|
31829
31958
|
cloud: true,
|
|
31830
31959
|
...tokenEnv ? { tokenEnv } : {},
|
|
31831
31960
|
...tokenCommand ? { tokenCommand } : {}
|
|
31832
31961
|
};
|
|
31833
31962
|
}
|
|
31963
|
+
function interpretBindingFile(file) {
|
|
31964
|
+
if (!existsSync16(file)) return {};
|
|
31965
|
+
const parsed = parseBindingObject(file);
|
|
31966
|
+
if (parsed instanceof HubBindingError) throw parsed;
|
|
31967
|
+
const keys = Object.keys(parsed);
|
|
31968
|
+
const unknown = keys.filter((key) => !BINDING_KEYS.has(key));
|
|
31969
|
+
if (parsed.schema !== HUB_BINDING_SCHEMA || unknown.length > 0) {
|
|
31970
|
+
return { warning: bindingFallbackWarning(file, `is newer than this build understands (${String(parsed.schema)})`) };
|
|
31971
|
+
}
|
|
31972
|
+
if (typeof parsed.url !== "string" || typeof parsed.boundAt !== "string" || !isIsoTimestamp(parsed.boundAt)) {
|
|
31973
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31974
|
+
}
|
|
31975
|
+
let url;
|
|
31976
|
+
try {
|
|
31977
|
+
url = validateHubUrl(parsed.url);
|
|
31978
|
+
} catch {
|
|
31979
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31980
|
+
}
|
|
31981
|
+
const cloudish = keys.some((key) => key === "cloud" || key === "tokenEnv" || key === "tokenCommand");
|
|
31982
|
+
if (!cloudish) {
|
|
31983
|
+
if (keys.length !== 3) throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31984
|
+
return { record: { schema: HUB_BINDING_SCHEMA, url, boundAt: parsed.boundAt } };
|
|
31985
|
+
}
|
|
31986
|
+
const record2 = cloudRecordFromRow(file, parsed, url, parsed.boundAt);
|
|
31987
|
+
if (!record2) throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31988
|
+
return { record: record2, legacyCloud: true };
|
|
31989
|
+
}
|
|
31990
|
+
function loadHubBinding(env = process.env) {
|
|
31991
|
+
const cloudFile = hubBindingCloudFile(env);
|
|
31992
|
+
const cloud = interpretBindingFile(cloudFile);
|
|
31993
|
+
if (cloud.warning && !cloud.record) {
|
|
31994
|
+
const local = existsSync16(hubBindingFile(env)) ? interpretBindingFile(hubBindingFile(env)) : {};
|
|
31995
|
+
if (local.record && !local.record.cloud) return { record: local.record, warning: cloud.warning };
|
|
31996
|
+
return { warning: cloud.warning };
|
|
31997
|
+
}
|
|
31998
|
+
if (cloud.record?.cloud) return { record: cloud.record, ...cloud.warning ? { warning: cloud.warning } : {} };
|
|
31999
|
+
const main = interpretBindingFile(hubBindingFile(env));
|
|
32000
|
+
if (main.legacyCloud && main.record) {
|
|
32001
|
+
writeBindingJson(cloudFile, main.record);
|
|
32002
|
+
rmSync2(hubBindingFile(env), { force: true });
|
|
32003
|
+
return {
|
|
32004
|
+
record: main.record,
|
|
32005
|
+
warning: bindingFallbackWarning(
|
|
32006
|
+
hubBindingFile(env),
|
|
32007
|
+
"stored a cloud bind in the pre-0.7.160 file"
|
|
32008
|
+
)
|
|
32009
|
+
};
|
|
32010
|
+
}
|
|
32011
|
+
return { ...main.record ? { record: main.record } : {}, ...main.warning ? { warning: main.warning } : {} };
|
|
32012
|
+
}
|
|
32013
|
+
function readHubBinding(env = process.env) {
|
|
32014
|
+
return loadHubBinding(env).record;
|
|
32015
|
+
}
|
|
31834
32016
|
|
|
31835
32017
|
// plugins/kxm/src/hub-env.ts
|
|
31836
32018
|
import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
@@ -31906,15 +32088,6 @@ function resolveProjectIdentity(cwd, env = process.env, explicit) {
|
|
|
31906
32088
|
if (fromPackage) return { project: fromPackage, source: "package.json", sourceLabel: "package.json name" };
|
|
31907
32089
|
return { project: basename4(cwd), source: "directory", sourceLabel: "directory name" };
|
|
31908
32090
|
}
|
|
31909
|
-
function opReferenceFromTokenCommand(command) {
|
|
31910
|
-
try {
|
|
31911
|
-
const args = splitTokenCommand(command);
|
|
31912
|
-
if (args[0] === "op" && args[1] === "read" && args[2] && isHubOpReference(args[2])) return args[2];
|
|
31913
|
-
} catch {
|
|
31914
|
-
return void 0;
|
|
31915
|
-
}
|
|
31916
|
-
return void 0;
|
|
31917
|
-
}
|
|
31918
32091
|
function defaultOpRead(reference, env) {
|
|
31919
32092
|
if (!isHubOpReference(reference)) {
|
|
31920
32093
|
throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
|
|
@@ -31959,18 +32132,24 @@ function resolveKeyReference(ref, env, opRead = defaultOpRead) {
|
|
|
31959
32132
|
}
|
|
31960
32133
|
function keySourceFor(input) {
|
|
31961
32134
|
const ref = input.endpoint?.key;
|
|
31962
|
-
|
|
31963
|
-
|
|
31964
|
-
|
|
31965
|
-
|
|
32135
|
+
const envName = (name) => {
|
|
32136
|
+
if (!name) return void 0;
|
|
32137
|
+
return input.env[name]?.trim() ? `env:${name}` : void 0;
|
|
32138
|
+
};
|
|
31966
32139
|
if (input.mode === "cloud") {
|
|
31967
|
-
|
|
31968
|
-
if (
|
|
31969
|
-
|
|
31970
|
-
|
|
31971
|
-
|
|
32140
|
+
const fromEnv2 = envName(ref?.env);
|
|
32141
|
+
if (fromEnv2) return fromEnv2;
|
|
32142
|
+
if (ref?.op) return `op:${ref.op}`;
|
|
32143
|
+
const fromBindingEnv = envName(input.binding?.tokenEnv);
|
|
32144
|
+
if (fromBindingEnv) return fromBindingEnv;
|
|
32145
|
+
if (input.binding?.tokenCommand) return "token-command";
|
|
31972
32146
|
return "missing";
|
|
31973
32147
|
}
|
|
32148
|
+
const fromAuth = envName("KXM_AUTH_TOKEN");
|
|
32149
|
+
if (fromAuth) return fromAuth;
|
|
32150
|
+
const fromEnv = envName(ref?.env);
|
|
32151
|
+
if (fromEnv) return fromEnv;
|
|
32152
|
+
if (ref?.op) return `op:${ref.op}`;
|
|
31974
32153
|
if (input.hasSavedProjectToken?.(input.project)) return "hub-env";
|
|
31975
32154
|
return "missing";
|
|
31976
32155
|
}
|
|
@@ -32631,8 +32810,17 @@ async function syncKxmOutbox(eventStore, client, options = {}) {
|
|
|
32631
32810
|
}
|
|
32632
32811
|
}
|
|
32633
32812
|
}
|
|
32634
|
-
function runtimeHubClientFor(context, env) {
|
|
32635
|
-
|
|
32813
|
+
function runtimeHubClientFor(context, env, onBindingWarning) {
|
|
32814
|
+
let recordUrl;
|
|
32815
|
+
try {
|
|
32816
|
+
const loaded = loadHubBinding(env);
|
|
32817
|
+
if (loaded.warning) onBindingWarning?.(loaded.warning);
|
|
32818
|
+
recordUrl = loaded.record?.url;
|
|
32819
|
+
} catch (error) {
|
|
32820
|
+
if (!(error instanceof HubBindingError)) throw error;
|
|
32821
|
+
onBindingWarning?.(`${error.message}; falling back to the local hub so sync can continue`);
|
|
32822
|
+
}
|
|
32823
|
+
const serverUrl = env.KXM_SERVER_URL?.trim() || recordUrl;
|
|
32636
32824
|
if (!serverUrl) return void 0;
|
|
32637
32825
|
const project = context.projectId;
|
|
32638
32826
|
const authToken = resolveClientHubAuthToken(env, project);
|
|
@@ -33160,6 +33348,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
33160
33348
|
else logger.info(entry);
|
|
33161
33349
|
};
|
|
33162
33350
|
let syncing = false;
|
|
33351
|
+
let bindingWarning;
|
|
33163
33352
|
const syncTimer = setInterval(() => {
|
|
33164
33353
|
if (syncing || stopping) return;
|
|
33165
33354
|
syncing = true;
|
|
@@ -33177,7 +33366,11 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
33177
33366
|
consecutiveFailures: 0
|
|
33178
33367
|
};
|
|
33179
33368
|
try {
|
|
33180
|
-
const client = runtimeHubClientFor(context, env)
|
|
33369
|
+
const client = runtimeHubClientFor(context, env, (message) => {
|
|
33370
|
+
if (bindingWarning === message) return;
|
|
33371
|
+
bindingWarning = message;
|
|
33372
|
+
logger.warn({ event: "hub_binding_fallback", message });
|
|
33373
|
+
});
|
|
33181
33374
|
if (!client) {
|
|
33182
33375
|
recordSyncStatus(context, { ...base, state: "no_hub" });
|
|
33183
33376
|
continue;
|