@kontextmind/kxm 0.7.71 → 0.7.73
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 +22 -0
- package/docs/contracts/synchronization.md +6 -3
- package/docs/operations.md +68 -5
- package/docs/test-matrix.md +1 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +346 -234
- package/plugins/kxm/dist/client.js +100 -35
- package/plugins/kxm/dist/core.js +10 -0
- package/plugins/kxm/dist/extension.js +73 -36
- package/plugins/kxm/dist/mcp-server.js +73 -36
- package/plugins/kxm/dist/runtime-supervisor.js +917 -141
- package/plugins/kxm/dist/runtime.js +977 -235
- package/plugins/kxm/dist/server.js +18584 -10365
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/references/protocol.md +18 -0
- package/plugins/kxm/src/client.ts +124 -4
- package/plugins/kxm/src/database.ts +8 -4
- package/plugins/kxm/src/external-effects.ts +390 -66
- package/plugins/kxm/src/hub.ts +358 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +15 -0
- package/plugins/kxm/src/protocol.ts +52 -0
- package/plugins/kxm/src/runtime-store.ts +109 -1
- package/plugins/kxm/src/runtime-supervisor.ts +150 -0
- package/plugins/kxm/src/store.ts +416 -5
- package/plugins/kxm/src/sync-transform.ts +380 -0
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -18360,6 +18360,8 @@ var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
|
18360
18360
|
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
18361
18361
|
var MAX_BODY_BYTES = 256 * 1024;
|
|
18362
18362
|
var MAX_AGENT_HOST_CHARS = 64;
|
|
18363
|
+
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
18364
|
+
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
18363
18365
|
var ProtocolError = class extends Error {
|
|
18364
18366
|
statusCode;
|
|
18365
18367
|
code;
|
|
@@ -19396,6 +19398,37 @@ var HubClient = class {
|
|
|
19396
19398
|
);
|
|
19397
19399
|
return result.message;
|
|
19398
19400
|
}
|
|
19401
|
+
// ----- Fenced leases over shared resources (P3) -----
|
|
19402
|
+
/**
|
|
19403
|
+
* Take or extend the lease over `resource` inside this client's project.
|
|
19404
|
+
*
|
|
19405
|
+
* The returned `fencingToken` is the whole point: hold it, present it on every
|
|
19406
|
+
* renewal, and present it again before committing anything shared. A hub that
|
|
19407
|
+
* has moved past it refuses, and the caller must stop rather than retry —
|
|
19408
|
+
* another holder owns the resource now. Rejects `HubHttpError` with code
|
|
19409
|
+
* `lease_held` when a live holder has it.
|
|
19410
|
+
*/
|
|
19411
|
+
async acquireLease(resource, ttlMs) {
|
|
19412
|
+
return await this.request(`/v1/leases/${encodeURIComponent(resource)}/acquire`, {
|
|
19413
|
+
method: "POST",
|
|
19414
|
+
body: JSON.stringify(ttlMs === void 0 ? {} : { ttlMs })
|
|
19415
|
+
});
|
|
19416
|
+
}
|
|
19417
|
+
/** Extend a lease this client holds. The token never changes on renewal; a
|
|
19418
|
+
* `lease_superseded` or `lease_expired` refusal means it is gone. */
|
|
19419
|
+
async renewLease(resource, fencingToken, ttlMs) {
|
|
19420
|
+
return await this.request(`/v1/leases/${encodeURIComponent(resource)}/renew`, {
|
|
19421
|
+
method: "POST",
|
|
19422
|
+
body: JSON.stringify(ttlMs === void 0 ? { fencingToken } : { fencingToken, ttlMs })
|
|
19423
|
+
});
|
|
19424
|
+
}
|
|
19425
|
+
/** Give the resource back. */
|
|
19426
|
+
async releaseLease(resource, fencingToken) {
|
|
19427
|
+
return await this.request(`/v1/leases/${encodeURIComponent(resource)}/release`, {
|
|
19428
|
+
method: "POST",
|
|
19429
|
+
body: JSON.stringify({ fencingToken })
|
|
19430
|
+
});
|
|
19431
|
+
}
|
|
19399
19432
|
async listWorkflows() {
|
|
19400
19433
|
const result = await this.request("/v1/workflows");
|
|
19401
19434
|
return result.runs;
|
|
@@ -19561,45 +19594,49 @@ var HubClient = class {
|
|
|
19561
19594
|
await this.register();
|
|
19562
19595
|
}
|
|
19563
19596
|
async request(path4, init = {}, includeIdentity = true) {
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19597
|
+
return await hubJsonRequest(this.options, path4, init, this.headers(includeIdentity));
|
|
19598
|
+
}
|
|
19599
|
+
};
|
|
19600
|
+
async function hubJsonRequest(options, path4, init, headers) {
|
|
19601
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 15e3;
|
|
19602
|
+
const timeoutSignal = AbortSignal.timeout(requestTimeoutMs);
|
|
19603
|
+
const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
|
|
19604
|
+
let response;
|
|
19605
|
+
try {
|
|
19606
|
+
response = await (options.fetchImpl ?? fetch)(`${options.serverUrl.replace(/\/$/, "")}${path4}`, {
|
|
19607
|
+
...init,
|
|
19608
|
+
signal,
|
|
19609
|
+
headers: { ...headers, ...init.headers ?? {} }
|
|
19610
|
+
});
|
|
19611
|
+
} catch (error) {
|
|
19612
|
+
if (timeoutSignal.aborted) throw new Error(`request timed out after ${requestTimeoutMs}ms`);
|
|
19613
|
+
throw error;
|
|
19614
|
+
}
|
|
19615
|
+
const text = await response.text();
|
|
19616
|
+
let body = {};
|
|
19617
|
+
if (text) {
|
|
19568
19618
|
try {
|
|
19569
|
-
|
|
19570
|
-
|
|
19571
|
-
|
|
19572
|
-
headers: { ...this.headers(includeIdentity), ...init.headers ?? {} }
|
|
19573
|
-
});
|
|
19574
|
-
} catch (error) {
|
|
19575
|
-
if (timeoutSignal.aborted) throw new Error(`request timed out after ${requestTimeoutMs}ms`);
|
|
19576
|
-
throw error;
|
|
19577
|
-
}
|
|
19578
|
-
const text = await response.text();
|
|
19579
|
-
let body = {};
|
|
19580
|
-
if (text) {
|
|
19581
|
-
try {
|
|
19582
|
-
body = JSON.parse(text);
|
|
19583
|
-
} catch {
|
|
19584
|
-
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
|
|
19585
|
-
}
|
|
19586
|
-
}
|
|
19587
|
-
if (!response.ok) {
|
|
19588
|
-
const extras = {};
|
|
19589
|
-
for (const key of ["operation", "nextAction", "assignedCoordinatorName"]) {
|
|
19590
|
-
if (typeof body[key] === "string") extras[key] = body[key];
|
|
19591
|
-
}
|
|
19592
|
-
throw new HubHttpError(
|
|
19593
|
-
response.status,
|
|
19594
|
-
String(body.error ?? `HTTP ${response.status}`),
|
|
19595
|
-
typeof body.code === "string" ? body.code : void 0,
|
|
19596
|
-
response.headers.get("x-request-id") ?? void 0,
|
|
19597
|
-
Object.keys(extras).length > 0 ? extras : void 0
|
|
19598
|
-
);
|
|
19619
|
+
body = JSON.parse(text);
|
|
19620
|
+
} catch {
|
|
19621
|
+
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
|
|
19599
19622
|
}
|
|
19600
|
-
return body;
|
|
19601
19623
|
}
|
|
19602
|
-
|
|
19624
|
+
if (!response.ok) {
|
|
19625
|
+
const extras = {};
|
|
19626
|
+
for (const key of ["operation", "nextAction", "assignedCoordinatorName"]) {
|
|
19627
|
+
if (typeof body[key] === "string") extras[key] = body[key];
|
|
19628
|
+
}
|
|
19629
|
+
if (body.lease && typeof body.lease === "object") extras.lease = body.lease;
|
|
19630
|
+
throw new HubHttpError(
|
|
19631
|
+
response.status,
|
|
19632
|
+
String(body.error ?? `HTTP ${response.status}`),
|
|
19633
|
+
typeof body.code === "string" ? body.code : void 0,
|
|
19634
|
+
response.headers.get("x-request-id") ?? void 0,
|
|
19635
|
+
Object.keys(extras).length > 0 ? extras : void 0
|
|
19636
|
+
);
|
|
19637
|
+
}
|
|
19638
|
+
return body;
|
|
19639
|
+
}
|
|
19603
19640
|
|
|
19604
19641
|
// plugins/kxm/src/hub-env.ts
|
|
19605
19642
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -22133,6 +22170,7 @@ var KxmSchemaRegistry = class {
|
|
|
22133
22170
|
driveReceiptValidator;
|
|
22134
22171
|
coordinatorValidator;
|
|
22135
22172
|
intakeMessageValidator;
|
|
22173
|
+
syncEventValidator;
|
|
22136
22174
|
constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
|
|
22137
22175
|
this.schemasDir = resolve3(schemasDir);
|
|
22138
22176
|
this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
@@ -22149,6 +22187,7 @@ var KxmSchemaRegistry = class {
|
|
|
22149
22187
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
22150
22188
|
const coordinatorFile = "coordinator.schema.json";
|
|
22151
22189
|
const intakeMessageFile = "intake-message.schema.json";
|
|
22190
|
+
const syncEventFile = "sync-event.schema.json";
|
|
22152
22191
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, localBindingsFile)));
|
|
22153
22192
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, templateProvenanceFile)));
|
|
22154
22193
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, initOperationFile)));
|
|
@@ -22157,6 +22196,7 @@ var KxmSchemaRegistry = class {
|
|
|
22157
22196
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, driveReceiptFile)));
|
|
22158
22197
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, coordinatorFile)));
|
|
22159
22198
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, intakeMessageFile)));
|
|
22199
|
+
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, syncEventFile)));
|
|
22160
22200
|
for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
|
|
22161
22201
|
const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
|
|
22162
22202
|
if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
|
|
@@ -22170,6 +22210,7 @@ var KxmSchemaRegistry = class {
|
|
|
22170
22210
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
22171
22211
|
const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
|
|
22172
22212
|
const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
|
|
22213
|
+
const syncEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${syncEventFile}`);
|
|
22173
22214
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
22174
22215
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
22175
22216
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
@@ -22178,6 +22219,7 @@ var KxmSchemaRegistry = class {
|
|
|
22178
22219
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
22179
22220
|
if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
|
|
22180
22221
|
if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
|
|
22222
|
+
if (!syncEventValidator) throw new Error(`schema did not compile: ${syncEventFile}`);
|
|
22181
22223
|
this.localBindingsValidator = localBindingsValidator;
|
|
22182
22224
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
22183
22225
|
this.initOperationValidator = initOperationValidator;
|
|
@@ -22186,6 +22228,7 @@ var KxmSchemaRegistry = class {
|
|
|
22186
22228
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
22187
22229
|
this.coordinatorValidator = coordinatorValidator;
|
|
22188
22230
|
this.intakeMessageValidator = intakeMessageValidator;
|
|
22231
|
+
this.syncEventValidator = syncEventValidator;
|
|
22189
22232
|
}
|
|
22190
22233
|
validate(kind, value, file) {
|
|
22191
22234
|
const definition = RESOURCE_SCHEMA[kind];
|
|
@@ -23224,12 +23267,12 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
|
|
|
23224
23267
|
|
|
23225
23268
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
23226
23269
|
import { spawn as spawn2 } from "node:child_process";
|
|
23227
|
-
import { createHash as
|
|
23228
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
23229
|
-
import { dirname as
|
|
23270
|
+
import { createHash as createHash12, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
23271
|
+
import { chmodSync as chmodSync4, existsSync as existsSync13, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
23272
|
+
import { dirname as dirname9, isAbsolute as isAbsolute6, join as join13 } from "node:path";
|
|
23230
23273
|
|
|
23231
23274
|
// plugins/kxm/src/runtime-store.ts
|
|
23232
|
-
import { createHash as
|
|
23275
|
+
import { createHash as createHash10, randomUUID as randomUUID4 } from "node:crypto";
|
|
23233
23276
|
import { existsSync as existsSync9, lstatSync as lstatSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23234
23277
|
import { dirname as dirname7, join as join9, resolve as resolve6 } from "node:path";
|
|
23235
23278
|
|
|
@@ -23586,8 +23629,88 @@ function writeKxmLocalBindings(projectRoot, projectId, repositories, options = {
|
|
|
23586
23629
|
return { file, written: true, record };
|
|
23587
23630
|
}
|
|
23588
23631
|
|
|
23632
|
+
// plugins/kxm/src/sync-transform.ts
|
|
23633
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
23634
|
+
|
|
23635
|
+
// plugins/kxm/src/redact.ts
|
|
23636
|
+
var SECRET_PATTERNS = [
|
|
23637
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
23638
|
+
/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
|
|
23639
|
+
/\bghp_[A-Za-z0-9_]{20,}\b/g,
|
|
23640
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
23641
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
|
|
23642
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
|
|
23643
|
+
/\bya29\.[A-Za-z0-9._~+/-]+=*/g,
|
|
23644
|
+
/\b1\/\/[A-Za-z0-9_-]+/g,
|
|
23645
|
+
/\b1\/[A-Za-z0-9_-]{20,}/g,
|
|
23646
|
+
/("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
23647
|
+
/\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
|
|
23648
|
+
/\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
|
|
23649
|
+
/\b[A-Fa-f0-9]{64}\b/g
|
|
23650
|
+
];
|
|
23651
|
+
function redactSecrets(value) {
|
|
23652
|
+
let result = value;
|
|
23653
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
23654
|
+
result = result.replace(pattern, "[redacted]");
|
|
23655
|
+
}
|
|
23656
|
+
return result;
|
|
23657
|
+
}
|
|
23658
|
+
function redactStringList(values, maxItems = 32) {
|
|
23659
|
+
return values.slice(0, maxItems).map((value) => redactSecrets(value).slice(0, 500));
|
|
23660
|
+
}
|
|
23661
|
+
|
|
23662
|
+
// plugins/kxm/src/sync-transform.ts
|
|
23663
|
+
var KXM_DEFAULT_SYNC_POLICY = Object.freeze({
|
|
23664
|
+
prompts: "title-only",
|
|
23665
|
+
results: "bounded-summary",
|
|
23666
|
+
evidence: "references",
|
|
23667
|
+
artifacts: "metadata",
|
|
23668
|
+
fileChanges: "paths-only",
|
|
23669
|
+
rawLogs: false,
|
|
23670
|
+
diffs: false,
|
|
23671
|
+
environmentValues: false
|
|
23672
|
+
});
|
|
23673
|
+
var KXM_DEFAULT_SYNC_POLICY_REVISION = `sha256:${createHash8("sha256").update(kxmCanonicalJson(KXM_DEFAULT_SYNC_POLICY), "utf8").digest("hex")}`;
|
|
23674
|
+
var SCALAR_FIELDS = [
|
|
23675
|
+
"workflowId",
|
|
23676
|
+
"promptHash",
|
|
23677
|
+
"status",
|
|
23678
|
+
"previousStatus",
|
|
23679
|
+
"outcome",
|
|
23680
|
+
"stepId",
|
|
23681
|
+
"stepAttempt",
|
|
23682
|
+
"fromStepId",
|
|
23683
|
+
"toStepId",
|
|
23684
|
+
"assignmentId",
|
|
23685
|
+
"attemptId",
|
|
23686
|
+
"agentId",
|
|
23687
|
+
"instanceNo",
|
|
23688
|
+
"scopeEpoch",
|
|
23689
|
+
"resolutionAction"
|
|
23690
|
+
];
|
|
23691
|
+
var CARRIED_FIELDS = /* @__PURE__ */ new Set([
|
|
23692
|
+
...SCALAR_FIELDS,
|
|
23693
|
+
"displayTitle",
|
|
23694
|
+
"summary",
|
|
23695
|
+
"reason",
|
|
23696
|
+
"executor",
|
|
23697
|
+
"model",
|
|
23698
|
+
"timing",
|
|
23699
|
+
"usage",
|
|
23700
|
+
"evidenceRefs",
|
|
23701
|
+
"artifacts",
|
|
23702
|
+
"receipt",
|
|
23703
|
+
"error",
|
|
23704
|
+
"effect",
|
|
23705
|
+
"lease",
|
|
23706
|
+
"actor",
|
|
23707
|
+
"resolvedBy",
|
|
23708
|
+
"suppliedRefs",
|
|
23709
|
+
"counts"
|
|
23710
|
+
]);
|
|
23711
|
+
|
|
23589
23712
|
// plugins/kxm/src/database.ts
|
|
23590
|
-
import { createHash as
|
|
23713
|
+
import { createHash as createHash9, randomBytes } from "node:crypto";
|
|
23591
23714
|
import {
|
|
23592
23715
|
chmodSync as chmodSync3,
|
|
23593
23716
|
copyFileSync,
|
|
@@ -23740,7 +23863,7 @@ function checkIntegrity(database) {
|
|
|
23740
23863
|
}
|
|
23741
23864
|
function fileSha256(filePath) {
|
|
23742
23865
|
const bytes = readFileSync7(filePath);
|
|
23743
|
-
return `sha256:${
|
|
23866
|
+
return `sha256:${createHash9("sha256").update(bytes).digest("hex")}`;
|
|
23744
23867
|
}
|
|
23745
23868
|
function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
23746
23869
|
const resolvedSource = resolve5(sourcePath);
|
|
@@ -23862,7 +23985,7 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
23862
23985
|
const stores = [];
|
|
23863
23986
|
const hubPath = options.hubDataPath ? resolve5(options.hubDataPath) : join8(root, ".kxm", "state", "kxm.db");
|
|
23864
23987
|
if (existsSync8(hubPath)) {
|
|
23865
|
-
stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion:
|
|
23988
|
+
stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion: 5 });
|
|
23866
23989
|
}
|
|
23867
23990
|
const registryPath = join8(root, ".kxm", "runtime", "registry.db");
|
|
23868
23991
|
if (existsSync8(registryPath)) {
|
|
@@ -23881,7 +24004,7 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
23881
24004
|
stores.push({
|
|
23882
24005
|
storeId: `events:${key}`,
|
|
23883
24006
|
sourcePath: join8(eventsDir, entry.name),
|
|
23884
|
-
maxSupportedVersion:
|
|
24007
|
+
maxSupportedVersion: 6
|
|
23885
24008
|
});
|
|
23886
24009
|
}
|
|
23887
24010
|
}
|
|
@@ -23924,7 +24047,7 @@ function createBackup(options = {}) {
|
|
|
23924
24047
|
stores: backedUpStores
|
|
23925
24048
|
};
|
|
23926
24049
|
const manifestJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
23927
|
-
const manifestSha256 = `sha256:${
|
|
24050
|
+
const manifestSha256 = `sha256:${createHash9("sha256").update(manifestJson, "utf8").digest("hex")}`;
|
|
23928
24051
|
manifest.manifestSha256 = manifestSha256;
|
|
23929
24052
|
const finalJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
23930
24053
|
const manifestPath = join8(outDir, "manifest.json");
|
|
@@ -23968,11 +24091,11 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
23968
24091
|
`backup file ${store.backupFile} sha256 ${actualSha256} does not match manifest hash ${store.sha256}`
|
|
23969
24092
|
);
|
|
23970
24093
|
}
|
|
23971
|
-
let maxSupported =
|
|
24094
|
+
let maxSupported = 5;
|
|
23972
24095
|
if (store.storeId === "registry" || store.storeId === "binding-store") {
|
|
23973
24096
|
maxSupported = 1;
|
|
23974
24097
|
} else if (store.storeId.startsWith("events:")) {
|
|
23975
|
-
maxSupported =
|
|
24098
|
+
maxSupported = 6;
|
|
23976
24099
|
}
|
|
23977
24100
|
let targetPath = store.sourcePath;
|
|
23978
24101
|
if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
|
|
@@ -24020,7 +24143,7 @@ function projectRuntimeKey(projectRoot) {
|
|
|
24020
24143
|
canonical2 = resolve6(projectRoot);
|
|
24021
24144
|
}
|
|
24022
24145
|
const folded = process.platform === "win32" ? canonical2.toLocaleLowerCase("en-US") : canonical2;
|
|
24023
|
-
return
|
|
24146
|
+
return createHash10("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
24024
24147
|
}
|
|
24025
24148
|
var KXM_REGISTRY_SCHEMA_VERSION = 1;
|
|
24026
24149
|
var REGISTRY_TABLES = {
|
|
@@ -24140,6 +24263,15 @@ var KxmRuntimeRegistry = class {
|
|
|
24140
24263
|
return this.readSupervisorRow();
|
|
24141
24264
|
}
|
|
24142
24265
|
/** Register or revalidate a project's home binding. Home Runtime is immutable. */
|
|
24266
|
+
/** All projects registered to this Runtime, for restart recovery: the
|
|
24267
|
+
* supervisor needs to reopen their contexts so pending outbox rows resume
|
|
24268
|
+
* syncing and presence keeps beating. */
|
|
24269
|
+
projectsForRuntime(homeRuntimeId) {
|
|
24270
|
+
const rows = this.database.prepare(
|
|
24271
|
+
"SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
|
|
24272
|
+
).all(homeRuntimeId);
|
|
24273
|
+
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
24274
|
+
}
|
|
24143
24275
|
registerProject(registration) {
|
|
24144
24276
|
const projectRoot = resolve6(registration.projectRoot);
|
|
24145
24277
|
const projectKey = projectRuntimeKey(projectRoot);
|
|
@@ -24316,7 +24448,8 @@ var EVENT_STORE_TABLES = {
|
|
|
24316
24448
|
"schema",
|
|
24317
24449
|
"record"
|
|
24318
24450
|
],
|
|
24319
|
-
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
|
|
24451
|
+
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"],
|
|
24452
|
+
outbox: ["seq", "run_id", "sequence", "sync_event", "attempted_at", "acked_at"]
|
|
24320
24453
|
};
|
|
24321
24454
|
var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
|
|
24322
24455
|
|
|
@@ -24833,7 +24966,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
|
24833
24966
|
|
|
24834
24967
|
// plugins/kxm/src/prices.ts
|
|
24835
24968
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
24836
|
-
import { createHash as
|
|
24969
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
24837
24970
|
import { existsSync as existsSync10, readFileSync as readFileSync9, statSync } from "node:fs";
|
|
24838
24971
|
import { join as join10 } from "node:path";
|
|
24839
24972
|
|
|
@@ -24896,7 +25029,7 @@ function hashPriceCatalog(catalog) {
|
|
|
24896
25029
|
}))
|
|
24897
25030
|
})).sort((a, b2) => a.id.localeCompare(b2.id))
|
|
24898
25031
|
};
|
|
24899
|
-
return
|
|
25032
|
+
return createHash11("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
|
|
24900
25033
|
}
|
|
24901
25034
|
function parsePriceCatalog(text) {
|
|
24902
25035
|
const parsed = (0, import_yaml3.parse)(text);
|
|
@@ -25702,9 +25835,144 @@ function formatRoutingReport(report2, options = {}) {
|
|
|
25702
25835
|
return lines.join("\n");
|
|
25703
25836
|
}
|
|
25704
25837
|
|
|
25838
|
+
// plugins/kxm/src/hub-binding.ts
|
|
25839
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
25840
|
+
import { homedir as homedir4 } from "node:os";
|
|
25841
|
+
import { dirname as dirname8, isAbsolute as isAbsolute5, join as join12, resolve as resolve8 } from "node:path";
|
|
25842
|
+
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
25843
|
+
var HUB_HEALTH_PROBE_MS = 300;
|
|
25844
|
+
var HubBindingError = class extends Error {
|
|
25845
|
+
constructor(message) {
|
|
25846
|
+
super(message);
|
|
25847
|
+
this.name = "HubBindingError";
|
|
25848
|
+
}
|
|
25849
|
+
};
|
|
25850
|
+
function resolveUserStateRoot2(env) {
|
|
25851
|
+
const explicit = env.KXM_STATE_HOME?.trim();
|
|
25852
|
+
if (explicit) {
|
|
25853
|
+
if (!isAbsolute5(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
25854
|
+
return resolve8(explicit);
|
|
25855
|
+
}
|
|
25856
|
+
if (process.platform === "win32") {
|
|
25857
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
25858
|
+
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join12(homedir4(), "AppData", "Local");
|
|
25859
|
+
return resolve8(base2, "KXM");
|
|
25860
|
+
}
|
|
25861
|
+
if (process.platform === "darwin") return resolve8(homedir4(), "Library", "Application Support", "KXM");
|
|
25862
|
+
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
25863
|
+
const base = xdgState && isAbsolute5(xdgState) ? xdgState : join12(homedir4(), ".local", "state");
|
|
25864
|
+
return resolve8(base, "kxm");
|
|
25865
|
+
}
|
|
25866
|
+
function hubBindingFile(env = process.env) {
|
|
25867
|
+
return join12(resolveUserStateRoot2(env), "hub-binding.json");
|
|
25868
|
+
}
|
|
25869
|
+
function validateHubUrl(raw) {
|
|
25870
|
+
let parsed;
|
|
25871
|
+
try {
|
|
25872
|
+
parsed = new URL(raw);
|
|
25873
|
+
} catch {
|
|
25874
|
+
throw new HubBindingError("hub_url_invalid");
|
|
25875
|
+
}
|
|
25876
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || raw.includes("?") || raw.includes("#")) {
|
|
25877
|
+
throw new HubBindingError("hub_url_invalid");
|
|
25878
|
+
}
|
|
25879
|
+
return parsed.href.replace(/\/$/, "");
|
|
25880
|
+
}
|
|
25881
|
+
function hubBindingScope(url) {
|
|
25882
|
+
let host;
|
|
25883
|
+
try {
|
|
25884
|
+
host = new URL(url).hostname.toLowerCase();
|
|
25885
|
+
} catch {
|
|
25886
|
+
return "remote";
|
|
25887
|
+
}
|
|
25888
|
+
if (host === "localhost" || host === "::1" || host === "[::1]" || host.endsWith(".localhost")) return "loopback";
|
|
25889
|
+
const v4 = /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(host);
|
|
25890
|
+
return v4 && [v4[1], v4[2], v4[3]].every((part) => Number(part) <= 255) ? "loopback" : "remote";
|
|
25891
|
+
}
|
|
25892
|
+
function isIsoTimestamp(value) {
|
|
25893
|
+
if (Number.isNaN(Date.parse(value))) return false;
|
|
25894
|
+
return value === new Date(value).toISOString();
|
|
25895
|
+
}
|
|
25896
|
+
function isAbortError(error) {
|
|
25897
|
+
return Boolean(
|
|
25898
|
+
error && typeof error === "object" && ("name" in error && error.name === "AbortError" || "code" in error && error.code === "ABORT_ERR")
|
|
25899
|
+
);
|
|
25900
|
+
}
|
|
25901
|
+
function readHubBinding(env = process.env) {
|
|
25902
|
+
const file = hubBindingFile(env);
|
|
25903
|
+
if (!existsSync12(file)) return void 0;
|
|
25904
|
+
let parsed;
|
|
25905
|
+
try {
|
|
25906
|
+
parsed = JSON.parse(readFileSync11(file, "utf8"));
|
|
25907
|
+
} catch {
|
|
25908
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
25909
|
+
}
|
|
25910
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
25911
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
25912
|
+
}
|
|
25913
|
+
const row = parsed;
|
|
25914
|
+
const keys = Object.keys(row);
|
|
25915
|
+
if (keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
|
|
25916
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
25917
|
+
}
|
|
25918
|
+
let url;
|
|
25919
|
+
try {
|
|
25920
|
+
url = validateHubUrl(row.url);
|
|
25921
|
+
} catch {
|
|
25922
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
25923
|
+
}
|
|
25924
|
+
return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
|
|
25925
|
+
}
|
|
25926
|
+
function writeHubBinding(record, env = process.env) {
|
|
25927
|
+
const file = hubBindingFile(env);
|
|
25928
|
+
mkdirSync8(dirname8(file), { recursive: true, mode: 448 });
|
|
25929
|
+
const temporary = join12(dirname8(file), `.hub-binding-${process.pid}.tmp`);
|
|
25930
|
+
try {
|
|
25931
|
+
writeFileSync8(temporary, `${JSON.stringify(record, null, 2)}
|
|
25932
|
+
`, { encoding: "utf8", mode: 384 });
|
|
25933
|
+
renameSync3(temporary, file);
|
|
25934
|
+
} finally {
|
|
25935
|
+
rmSync3(temporary, { force: true });
|
|
25936
|
+
}
|
|
25937
|
+
return file;
|
|
25938
|
+
}
|
|
25939
|
+
function removeHubBinding(env = process.env) {
|
|
25940
|
+
const file = hubBindingFile(env);
|
|
25941
|
+
try {
|
|
25942
|
+
rmSync3(file);
|
|
25943
|
+
return true;
|
|
25944
|
+
} catch (error) {
|
|
25945
|
+
if (error.code === "ENOENT") return false;
|
|
25946
|
+
throw error;
|
|
25947
|
+
}
|
|
25948
|
+
}
|
|
25949
|
+
async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
|
|
25950
|
+
const started = Date.now();
|
|
25951
|
+
const controller = new AbortController();
|
|
25952
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
25953
|
+
try {
|
|
25954
|
+
const response = await fetchImpl(`${url}/health`, { signal: controller.signal });
|
|
25955
|
+
if (!response.ok) return { health: "unknown", probeMs: Date.now() - started };
|
|
25956
|
+
let body;
|
|
25957
|
+
try {
|
|
25958
|
+
body = await response.json();
|
|
25959
|
+
} catch {
|
|
25960
|
+
return { health: "unknown", probeMs: Date.now() - started };
|
|
25961
|
+
}
|
|
25962
|
+
if (body && typeof body === "object" && body.ok === true) {
|
|
25963
|
+
return { health: "on", probeMs: Date.now() - started };
|
|
25964
|
+
}
|
|
25965
|
+
return { health: "unknown", probeMs: Date.now() - started };
|
|
25966
|
+
} catch (error) {
|
|
25967
|
+
return { health: isAbortError(error) ? "unknown" : "off", probeMs: Date.now() - started };
|
|
25968
|
+
} finally {
|
|
25969
|
+
clearTimeout(timer);
|
|
25970
|
+
}
|
|
25971
|
+
}
|
|
25972
|
+
|
|
25705
25973
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
25706
25974
|
function kxmSupervisorTokenFile(paths) {
|
|
25707
|
-
return
|
|
25975
|
+
return join13(paths.runtimeDir, "supervisor.token");
|
|
25708
25976
|
}
|
|
25709
25977
|
function readKxmSupervisorToken(paths) {
|
|
25710
25978
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -25713,7 +25981,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
25713
25981
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
25714
25982
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
25715
25983
|
}
|
|
25716
|
-
const token =
|
|
25984
|
+
const token = readFileSync12(file, "utf8").trim();
|
|
25717
25985
|
return token.length >= 32 ? token : void 0;
|
|
25718
25986
|
}
|
|
25719
25987
|
function processAlive(pid) {
|
|
@@ -25727,11 +25995,11 @@ function processAlive(pid) {
|
|
|
25727
25995
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
25728
25996
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
25729
25997
|
function supervisorErrorFile(paths) {
|
|
25730
|
-
return
|
|
25998
|
+
return join13(paths.runtimeDir, "supervisor.error");
|
|
25731
25999
|
}
|
|
25732
26000
|
function clearSupervisorError(paths) {
|
|
25733
26001
|
const file = supervisorErrorFile(paths);
|
|
25734
|
-
if (
|
|
26002
|
+
if (existsSync13(file)) rmSync4(file, { force: true });
|
|
25735
26003
|
}
|
|
25736
26004
|
function readRecentSupervisorError(paths) {
|
|
25737
26005
|
const file = supervisorErrorFile(paths);
|
|
@@ -25740,13 +26008,13 @@ function readRecentSupervisorError(paths) {
|
|
|
25740
26008
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
25741
26009
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
25742
26010
|
try {
|
|
25743
|
-
return
|
|
26011
|
+
return readFileSync12(file, "utf8").trim();
|
|
25744
26012
|
} catch {
|
|
25745
26013
|
return void 0;
|
|
25746
26014
|
}
|
|
25747
26015
|
}
|
|
25748
26016
|
function kxmSupervisorStatus(paths) {
|
|
25749
|
-
if (!
|
|
26017
|
+
if (!existsSync13(paths.registryDb)) return { running: false };
|
|
25750
26018
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
25751
26019
|
try {
|
|
25752
26020
|
const record = registry.supervisor();
|
|
@@ -25816,7 +26084,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
25816
26084
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
25817
26085
|
}
|
|
25818
26086
|
clearSupervisorError(paths);
|
|
25819
|
-
const scriptPath =
|
|
26087
|
+
const scriptPath = join13(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
25820
26088
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
25821
26089
|
const child = spawn2(process.execPath, [script], {
|
|
25822
26090
|
detached: true,
|
|
@@ -25871,36 +26139,9 @@ async function kxmRuntimeRequest(handle, method, path4, body) {
|
|
|
25871
26139
|
import { spawn as spawn3 } from "node:child_process";
|
|
25872
26140
|
import { join as join15, resolve as resolve9 } from "node:path";
|
|
25873
26141
|
|
|
25874
|
-
// plugins/kxm/src/redact.ts
|
|
25875
|
-
var SECRET_PATTERNS = [
|
|
25876
|
-
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
25877
|
-
/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
|
|
25878
|
-
/\bghp_[A-Za-z0-9_]{20,}\b/g,
|
|
25879
|
-
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
25880
|
-
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
|
|
25881
|
-
/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
|
|
25882
|
-
/\bya29\.[A-Za-z0-9._~+/-]+=*/g,
|
|
25883
|
-
/\b1\/\/[A-Za-z0-9_-]+/g,
|
|
25884
|
-
/\b1\/[A-Za-z0-9_-]{20,}/g,
|
|
25885
|
-
/("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
25886
|
-
/\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
|
|
25887
|
-
/\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
|
|
25888
|
-
/\b[A-Fa-f0-9]{64}\b/g
|
|
25889
|
-
];
|
|
25890
|
-
function redactSecrets(value) {
|
|
25891
|
-
let result = value;
|
|
25892
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
25893
|
-
result = result.replace(pattern, "[redacted]");
|
|
25894
|
-
}
|
|
25895
|
-
return result;
|
|
25896
|
-
}
|
|
25897
|
-
function redactStringList(values, maxItems = 32) {
|
|
25898
|
-
return values.slice(0, maxItems).map((value) => redactSecrets(value).slice(0, 500));
|
|
25899
|
-
}
|
|
25900
|
-
|
|
25901
26142
|
// plugins/kxm/src/telemetry.ts
|
|
25902
|
-
import { appendFileSync, mkdirSync as
|
|
25903
|
-
import { dirname as
|
|
26143
|
+
import { appendFileSync, mkdirSync as mkdirSync10, readFileSync as readFileSync13 } from "node:fs";
|
|
26144
|
+
import { dirname as dirname10, join as join14 } from "node:path";
|
|
25904
26145
|
var TELEMETRY_SCHEMA = "kxm.telemetry.v1";
|
|
25905
26146
|
function inferImprovementTarget(input) {
|
|
25906
26147
|
const explicit = input.env?.KXM_IMPROVE_TARGET?.trim();
|
|
@@ -25910,12 +26151,12 @@ function inferImprovementTarget(input) {
|
|
|
25910
26151
|
return project || workflowId ? "project" : "cli";
|
|
25911
26152
|
}
|
|
25912
26153
|
function appendTelemetry(path4, event) {
|
|
25913
|
-
|
|
26154
|
+
mkdirSync10(dirname10(path4), { recursive: true });
|
|
25914
26155
|
appendFileSync(path4, `${JSON.stringify(event)}
|
|
25915
26156
|
`, { encoding: "utf8", mode: 384 });
|
|
25916
26157
|
}
|
|
25917
26158
|
function telemetryPath(logsDir) {
|
|
25918
|
-
return
|
|
26159
|
+
return join14(logsDir, "telemetry.jsonl");
|
|
25919
26160
|
}
|
|
25920
26161
|
function makeTelemetryEvent(input) {
|
|
25921
26162
|
return {
|
|
@@ -25930,7 +26171,7 @@ function makeTelemetryEvent(input) {
|
|
|
25930
26171
|
function readRoutingRecords(path4) {
|
|
25931
26172
|
const records = [];
|
|
25932
26173
|
try {
|
|
25933
|
-
const raw =
|
|
26174
|
+
const raw = readFileSync13(path4, "utf8");
|
|
25934
26175
|
for (const line of raw.split(/\r?\n/)) {
|
|
25935
26176
|
if (!line.trim()) continue;
|
|
25936
26177
|
try {
|
|
@@ -26006,141 +26247,6 @@ function workerResult(worker, payload) {
|
|
|
26006
26247
|
};
|
|
26007
26248
|
}
|
|
26008
26249
|
|
|
26009
|
-
// plugins/kxm/src/hub-binding.ts
|
|
26010
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
26011
|
-
import { homedir as homedir4 } from "node:os";
|
|
26012
|
-
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join14, resolve as resolve8 } from "node:path";
|
|
26013
|
-
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
26014
|
-
var HUB_HEALTH_PROBE_MS = 300;
|
|
26015
|
-
var HubBindingError = class extends Error {
|
|
26016
|
-
constructor(message) {
|
|
26017
|
-
super(message);
|
|
26018
|
-
this.name = "HubBindingError";
|
|
26019
|
-
}
|
|
26020
|
-
};
|
|
26021
|
-
function resolveUserStateRoot2(env) {
|
|
26022
|
-
const explicit = env.KXM_STATE_HOME?.trim();
|
|
26023
|
-
if (explicit) {
|
|
26024
|
-
if (!isAbsolute6(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
26025
|
-
return resolve8(explicit);
|
|
26026
|
-
}
|
|
26027
|
-
if (process.platform === "win32") {
|
|
26028
|
-
const localAppData = env.LOCALAPPDATA?.trim();
|
|
26029
|
-
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join14(homedir4(), "AppData", "Local");
|
|
26030
|
-
return resolve8(base2, "KXM");
|
|
26031
|
-
}
|
|
26032
|
-
if (process.platform === "darwin") return resolve8(homedir4(), "Library", "Application Support", "KXM");
|
|
26033
|
-
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
26034
|
-
const base = xdgState && isAbsolute6(xdgState) ? xdgState : join14(homedir4(), ".local", "state");
|
|
26035
|
-
return resolve8(base, "kxm");
|
|
26036
|
-
}
|
|
26037
|
-
function hubBindingFile(env = process.env) {
|
|
26038
|
-
return join14(resolveUserStateRoot2(env), "hub-binding.json");
|
|
26039
|
-
}
|
|
26040
|
-
function validateHubUrl(raw) {
|
|
26041
|
-
let parsed;
|
|
26042
|
-
try {
|
|
26043
|
-
parsed = new URL(raw);
|
|
26044
|
-
} catch {
|
|
26045
|
-
throw new HubBindingError("hub_url_invalid");
|
|
26046
|
-
}
|
|
26047
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || raw.includes("?") || raw.includes("#")) {
|
|
26048
|
-
throw new HubBindingError("hub_url_invalid");
|
|
26049
|
-
}
|
|
26050
|
-
return parsed.href.replace(/\/$/, "");
|
|
26051
|
-
}
|
|
26052
|
-
function hubBindingScope(url) {
|
|
26053
|
-
let host;
|
|
26054
|
-
try {
|
|
26055
|
-
host = new URL(url).hostname.toLowerCase();
|
|
26056
|
-
} catch {
|
|
26057
|
-
return "remote";
|
|
26058
|
-
}
|
|
26059
|
-
if (host === "localhost" || host === "::1" || host === "[::1]" || host.endsWith(".localhost")) return "loopback";
|
|
26060
|
-
const v4 = /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(host);
|
|
26061
|
-
return v4 && [v4[1], v4[2], v4[3]].every((part) => Number(part) <= 255) ? "loopback" : "remote";
|
|
26062
|
-
}
|
|
26063
|
-
function isIsoTimestamp(value) {
|
|
26064
|
-
if (Number.isNaN(Date.parse(value))) return false;
|
|
26065
|
-
return value === new Date(value).toISOString();
|
|
26066
|
-
}
|
|
26067
|
-
function isAbortError(error) {
|
|
26068
|
-
return Boolean(
|
|
26069
|
-
error && typeof error === "object" && ("name" in error && error.name === "AbortError" || "code" in error && error.code === "ABORT_ERR")
|
|
26070
|
-
);
|
|
26071
|
-
}
|
|
26072
|
-
function readHubBinding(env = process.env) {
|
|
26073
|
-
const file = hubBindingFile(env);
|
|
26074
|
-
if (!existsSync13(file)) return void 0;
|
|
26075
|
-
let parsed;
|
|
26076
|
-
try {
|
|
26077
|
-
parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
26078
|
-
} catch {
|
|
26079
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
26080
|
-
}
|
|
26081
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
26082
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
26083
|
-
}
|
|
26084
|
-
const row = parsed;
|
|
26085
|
-
const keys = Object.keys(row);
|
|
26086
|
-
if (keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
|
|
26087
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
26088
|
-
}
|
|
26089
|
-
let url;
|
|
26090
|
-
try {
|
|
26091
|
-
url = validateHubUrl(row.url);
|
|
26092
|
-
} catch {
|
|
26093
|
-
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
26094
|
-
}
|
|
26095
|
-
return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
|
|
26096
|
-
}
|
|
26097
|
-
function writeHubBinding(record, env = process.env) {
|
|
26098
|
-
const file = hubBindingFile(env);
|
|
26099
|
-
mkdirSync10(dirname10(file), { recursive: true, mode: 448 });
|
|
26100
|
-
const temporary = join14(dirname10(file), `.hub-binding-${process.pid}.tmp`);
|
|
26101
|
-
try {
|
|
26102
|
-
writeFileSync9(temporary, `${JSON.stringify(record, null, 2)}
|
|
26103
|
-
`, { encoding: "utf8", mode: 384 });
|
|
26104
|
-
renameSync4(temporary, file);
|
|
26105
|
-
} finally {
|
|
26106
|
-
rmSync4(temporary, { force: true });
|
|
26107
|
-
}
|
|
26108
|
-
return file;
|
|
26109
|
-
}
|
|
26110
|
-
function removeHubBinding(env = process.env) {
|
|
26111
|
-
const file = hubBindingFile(env);
|
|
26112
|
-
try {
|
|
26113
|
-
rmSync4(file);
|
|
26114
|
-
return true;
|
|
26115
|
-
} catch (error) {
|
|
26116
|
-
if (error.code === "ENOENT") return false;
|
|
26117
|
-
throw error;
|
|
26118
|
-
}
|
|
26119
|
-
}
|
|
26120
|
-
async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
|
|
26121
|
-
const started = Date.now();
|
|
26122
|
-
const controller = new AbortController();
|
|
26123
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
26124
|
-
try {
|
|
26125
|
-
const response = await fetchImpl(`${url}/health`, { signal: controller.signal });
|
|
26126
|
-
if (!response.ok) return { health: "unknown", probeMs: Date.now() - started };
|
|
26127
|
-
let body;
|
|
26128
|
-
try {
|
|
26129
|
-
body = await response.json();
|
|
26130
|
-
} catch {
|
|
26131
|
-
return { health: "unknown", probeMs: Date.now() - started };
|
|
26132
|
-
}
|
|
26133
|
-
if (body && typeof body === "object" && body.ok === true) {
|
|
26134
|
-
return { health: "on", probeMs: Date.now() - started };
|
|
26135
|
-
}
|
|
26136
|
-
return { health: "unknown", probeMs: Date.now() - started };
|
|
26137
|
-
} catch (error) {
|
|
26138
|
-
return { health: isAbortError(error) ? "unknown" : "off", probeMs: Date.now() - started };
|
|
26139
|
-
} finally {
|
|
26140
|
-
clearTimeout(timer);
|
|
26141
|
-
}
|
|
26142
|
-
}
|
|
26143
|
-
|
|
26144
26250
|
// plugins/kxm/src/cli/types.ts
|
|
26145
26251
|
function spawnScript(scriptName, extraEnv = {}) {
|
|
26146
26252
|
return new Promise((resolveExit) => {
|
|
@@ -29922,7 +30028,7 @@ import { dirname as dirname15, isAbsolute as isAbsolute7, join as join24, relati
|
|
|
29922
30028
|
|
|
29923
30029
|
// plugins/kxm/src/permission.ts
|
|
29924
30030
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
29925
|
-
import { createHash as
|
|
30031
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
29926
30032
|
import { existsSync as existsSync20, mkdtempSync, mkdirSync as mkdirSync17, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
29927
30033
|
import { tmpdir } from "node:os";
|
|
29928
30034
|
import { dirname as dirname14, join as join23, resolve as resolve17, sep as sep3 } from "node:path";
|
|
@@ -30128,7 +30234,7 @@ function kxmProseEntries(resource) {
|
|
|
30128
30234
|
return entries;
|
|
30129
30235
|
}
|
|
30130
30236
|
function valueHash(canonicalValue) {
|
|
30131
|
-
return `sha256:${
|
|
30237
|
+
return `sha256:${createHash13("sha256").update(canonicalValue, "utf8").digest("hex")}`;
|
|
30132
30238
|
}
|
|
30133
30239
|
function rankDirection(rank, baseValue, candidateValue) {
|
|
30134
30240
|
const base = JSON.parse(baseValue);
|
|
@@ -30448,7 +30554,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30448
30554
|
const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
30449
30555
|
const shadow = mkdtempSync(join23(tmpdir(), "kxm-permission-base-"));
|
|
30450
30556
|
const shadowResolved = resolve17(shadow);
|
|
30451
|
-
const shadowMembersDir = `.kxm-shadow-members-${
|
|
30557
|
+
const shadowMembersDir = `.kxm-shadow-members-${createHash13("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
|
|
30452
30558
|
try {
|
|
30453
30559
|
for (const path4 of paths) {
|
|
30454
30560
|
const segments = path4.split("/");
|
|
@@ -33345,7 +33451,7 @@ remains Grok.
|
|
|
33345
33451
|
|
|
33346
33452
|
// plugins/kxm/src/skills.ts
|
|
33347
33453
|
var import_yaml14 = __toESM(require_dist(), 1);
|
|
33348
|
-
import { createHash as
|
|
33454
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
33349
33455
|
import { existsSync as existsSync25, mkdirSync as mkdirSync20, readdirSync as readdirSync10, readFileSync as readFileSync24, renameSync as renameSync7, rmSync as rmSync9, statSync as statSync3, writeFileSync as writeFileSync19 } from "node:fs";
|
|
33350
33456
|
import { join as join29 } from "node:path";
|
|
33351
33457
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
@@ -33370,7 +33476,7 @@ var SkillLifecycleError = class extends Error {
|
|
|
33370
33476
|
}
|
|
33371
33477
|
};
|
|
33372
33478
|
function skillContentSha256(content) {
|
|
33373
|
-
return
|
|
33479
|
+
return createHash14("sha256").update(content, "utf8").digest("hex");
|
|
33374
33480
|
}
|
|
33375
33481
|
function skillIdFor(name, contentSha256) {
|
|
33376
33482
|
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -42603,6 +42709,12 @@ function slugifyBranchPart(text, maxLength = 40) {
|
|
|
42603
42709
|
if (cleaned.length <= maxLength) return cleaned;
|
|
42604
42710
|
return cleaned.slice(0, maxLength).replace(/-+$/, "");
|
|
42605
42711
|
}
|
|
42712
|
+
var SHARED_EFFECT_KINDS = Object.freeze([
|
|
42713
|
+
"git-push",
|
|
42714
|
+
"pr-create",
|
|
42715
|
+
"tracker-issue",
|
|
42716
|
+
"webhook"
|
|
42717
|
+
]);
|
|
42606
42718
|
|
|
42607
42719
|
// plugins/kxm/src/local-snapshot.ts
|
|
42608
42720
|
import { existsSync as existsSync26, readdirSync as readdirSync11, readFileSync as readFileSync26 } from "node:fs";
|
|
@@ -45004,14 +45116,14 @@ async function cmdSessionStart(runtime, options) {
|
|
|
45004
45116
|
|
|
45005
45117
|
// plugins/kxm/src/cli/system.ts
|
|
45006
45118
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
45007
|
-
import { createHash as
|
|
45119
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
45008
45120
|
import { existsSync as existsSync37, mkdtempSync as mkdtempSync2, readFileSync as readFileSync36, rmSync as rmSync12 } from "node:fs";
|
|
45009
45121
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
45010
45122
|
import { join as join47, resolve as resolve28 } from "node:path";
|
|
45011
45123
|
import { createInterface as createInterface3 } from "node:readline";
|
|
45012
45124
|
|
|
45013
45125
|
// plugins/kxm/src/improve.ts
|
|
45014
|
-
import { createHash as
|
|
45126
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
45015
45127
|
import { existsSync as existsSync32, mkdirSync as mkdirSync26, writeFileSync as writeFileSync24 } from "node:fs";
|
|
45016
45128
|
import { join as join42, relative as relative6, resolve as resolve25 } from "node:path";
|
|
45017
45129
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
@@ -45159,7 +45271,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
45159
45271
|
const isCandidate = recurrence >= minRecurrence && verifyPassRate >= minPassRate;
|
|
45160
45272
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
45161
45273
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
45162
|
-
const keyHash =
|
|
45274
|
+
const keyHash = createHash15("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
45163
45275
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
45164
45276
|
rows.push({
|
|
45165
45277
|
workflowHash: group.workflowHash,
|
|
@@ -46834,7 +46946,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
46834
46946
|
for (const step of planned) {
|
|
46835
46947
|
if (step.kind === "verify") {
|
|
46836
46948
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
46837
|
-
const actual = existsSync37(step.path) ?
|
|
46949
|
+
const actual = existsSync37(step.path) ? createHash16("sha256").update(readFileSync36(step.path)).digest("hex") : "missing";
|
|
46838
46950
|
return {
|
|
46839
46951
|
ok: false,
|
|
46840
46952
|
error: "release_digest_mismatch",
|