@kontextmind/kxm 0.7.165 → 0.7.167
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 +16 -0
- package/docs/README.md +1 -0
- package/docs/architecture/hosted-direction.md +9 -4
- package/docs/architecture/inventory.md +1 -0
- package/docs/concepts/trust-model.md +1 -1
- package/docs/contributing/decisions.md +153 -0
- package/docs/contributing/learnings.md +54 -53
- package/docs/contributing/operating-rules.md +19 -6
- package/docs/glossary.md +2 -2
- package/docs/operations/troubleshooting.md +2 -2
- package/docs/operations.md +62 -8
- package/docs/reference/cli-reference.md +11 -11
- package/docs/reference/config-reference.md +36 -11
- package/docs/reference/configuration.md +2 -2
- package/docs/reference/tools.md +1 -1
- package/docs/start/quickstart-claude-code.md +2 -2
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +3 -3
- package/plugins/kxm/dist/claude-hook.js +417 -66
- package/plugins/kxm/dist/cli.js +1399 -900
- package/plugins/kxm/dist/extension.js +399 -64
- package/plugins/kxm/dist/mcp-server.js +622 -135
- package/plugins/kxm/dist/runtime-supervisor.js +668 -166
- package/plugins/kxm/dist/runtime.js +713 -211
- package/plugins/kxm/dist/server.js +466 -58
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +15 -7
- package/plugins/kxm/src/cli/hub.ts +80 -21
- package/plugins/kxm/src/cli/types.ts +8 -1
- package/plugins/kxm/src/cli.ts +41 -7
- package/plugins/kxm/src/config.ts +246 -3
- package/plugins/kxm/src/extension.ts +8 -7
- package/plugins/kxm/src/hub-autostart.ts +20 -0
- package/plugins/kxm/src/hub-env.ts +83 -31
- package/plugins/kxm/src/hub-identity.ts +337 -0
- package/plugins/kxm/src/mcp-server.ts +17 -15
- package/plugins/kxm/src/project-name.ts +6 -47
- package/plugins/kxm/src/server.ts +22 -4
- package/scripts/kxm-hub.mjs +5 -0
|
@@ -14523,7 +14523,7 @@ var require_dist2 = __commonJS({
|
|
|
14523
14523
|
});
|
|
14524
14524
|
|
|
14525
14525
|
// plugins/kxm/src/extension.ts
|
|
14526
|
-
import { spawnSync as
|
|
14526
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
14527
14527
|
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
14528
14528
|
import { dirname as dirname8, join as join15 } from "node:path";
|
|
14529
14529
|
|
|
@@ -15995,9 +15995,25 @@ function enforceToolPolicy(commandName, env = process.env, options) {
|
|
|
15995
15995
|
// plugins/kxm/src/config.ts
|
|
15996
15996
|
var import_yaml = __toESM(require_dist(), 1);
|
|
15997
15997
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
15998
|
-
import { homedir as homedir2 } from "node:os";
|
|
15998
|
+
import { tmpdir, homedir as homedir2 } from "node:os";
|
|
15999
15999
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
|
|
16000
16000
|
var KXM_CONFIG_SCHEMA = "kxm.config.v1";
|
|
16001
|
+
var KxmHubConfigError = class extends Error {
|
|
16002
|
+
code = "hub_config_invalid";
|
|
16003
|
+
constructor(message) {
|
|
16004
|
+
super(message);
|
|
16005
|
+
this.name = "KxmHubConfigError";
|
|
16006
|
+
}
|
|
16007
|
+
};
|
|
16008
|
+
var HUB_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
16009
|
+
var HUB_OP_REF = /^op:\/\/\S+\/\S+\/\S+$/;
|
|
16010
|
+
var LITERAL_SECRET_KEY = /^(token|secret|password|authToken|auth_token|apiKey|api_key|api_token|bearer|credential|access_token)$/i;
|
|
16011
|
+
function isHubKeyEnvName(value) {
|
|
16012
|
+
return HUB_ENV_NAME.test(value);
|
|
16013
|
+
}
|
|
16014
|
+
function isHubOpReference(value) {
|
|
16015
|
+
return HUB_OP_REF.test(value);
|
|
16016
|
+
}
|
|
16001
16017
|
var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
|
|
16002
16018
|
var DEFAULT_KXM_CONFIG = {
|
|
16003
16019
|
schema: KXM_CONFIG_SCHEMA,
|
|
@@ -16068,9 +16084,151 @@ function deepMerge(target, source) {
|
|
|
16068
16084
|
}
|
|
16069
16085
|
return result2;
|
|
16070
16086
|
}
|
|
16087
|
+
function literalSecretMessage(path) {
|
|
16088
|
+
return `refusing literal hub token at ${path}: store an op:// reference (key.op) or an environment variable name (key.env), never the token`;
|
|
16089
|
+
}
|
|
16090
|
+
function rejectLiteralHubSecrets(value, path) {
|
|
16091
|
+
if (Array.isArray(value)) {
|
|
16092
|
+
value.forEach((item, index) => rejectLiteralHubSecrets(item, `${path}[${index}]`));
|
|
16093
|
+
return;
|
|
16094
|
+
}
|
|
16095
|
+
if (!value || typeof value !== "object") return;
|
|
16096
|
+
for (const [key, child] of Object.entries(value)) {
|
|
16097
|
+
const childPath = `${path}.${key}`;
|
|
16098
|
+
if (typeof child === "string" && (key === "key" || LITERAL_SECRET_KEY.test(key))) {
|
|
16099
|
+
throw new KxmHubConfigError(literalSecretMessage(childPath));
|
|
16100
|
+
}
|
|
16101
|
+
if (child && typeof child === "object") rejectLiteralHubSecrets(child, childPath);
|
|
16102
|
+
}
|
|
16103
|
+
}
|
|
16104
|
+
function parseKeyRef(raw, path) {
|
|
16105
|
+
if (raw === void 0) return void 0;
|
|
16106
|
+
if (typeof raw === "string") throw new KxmHubConfigError(literalSecretMessage(path));
|
|
16107
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
16108
|
+
throw new KxmHubConfigError(`${path} must be a key reference with op and/or env, never a token`);
|
|
16109
|
+
}
|
|
16110
|
+
const row = raw;
|
|
16111
|
+
for (const key of Object.keys(row)) {
|
|
16112
|
+
if (key !== "op" && key !== "env") {
|
|
16113
|
+
throw new KxmHubConfigError(literalSecretMessage(`${path}.${key}`));
|
|
16114
|
+
}
|
|
16115
|
+
}
|
|
16116
|
+
const ref = {};
|
|
16117
|
+
if (row.op !== void 0) {
|
|
16118
|
+
if (typeof row.op !== "string" || !isHubOpReference(row.op)) {
|
|
16119
|
+
throw new KxmHubConfigError(`${path}.op must be an op://vault/item/field reference, not a token`);
|
|
16120
|
+
}
|
|
16121
|
+
ref.op = row.op;
|
|
16122
|
+
}
|
|
16123
|
+
if (row.env !== void 0) {
|
|
16124
|
+
if (typeof row.env !== "string" || !isHubKeyEnvName(row.env)) {
|
|
16125
|
+
throw new KxmHubConfigError(`${path}.env must be an environment variable name, not a token`);
|
|
16126
|
+
}
|
|
16127
|
+
ref.env = row.env;
|
|
16128
|
+
}
|
|
16129
|
+
if (!ref.op && !ref.env) throw new KxmHubConfigError(`${path} needs key.op, key.env, or both`);
|
|
16130
|
+
return ref;
|
|
16131
|
+
}
|
|
16132
|
+
function parseEndpoint(raw, path) {
|
|
16133
|
+
if (raw === void 0) return void 0;
|
|
16134
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
16135
|
+
throw new KxmHubConfigError(`${path} must be an object with url, project, and key`);
|
|
16136
|
+
}
|
|
16137
|
+
const row = raw;
|
|
16138
|
+
const endpoint = {};
|
|
16139
|
+
if (row.url !== void 0) {
|
|
16140
|
+
if (typeof row.url !== "string" || !isHubUrl(row.url)) {
|
|
16141
|
+
throw new KxmHubConfigError(`${path}.url must be an http or https URL without credentials, query, or fragment`);
|
|
16142
|
+
}
|
|
16143
|
+
endpoint.url = row.url.replace(/\/$/, "");
|
|
16144
|
+
}
|
|
16145
|
+
if (row.project !== void 0) {
|
|
16146
|
+
if (typeof row.project !== "string" || !isHubProjectId(row.project)) {
|
|
16147
|
+
throw new KxmHubConfigError(`${path}.project must be a single-line project id, not a token`);
|
|
16148
|
+
}
|
|
16149
|
+
endpoint.project = row.project.trim();
|
|
16150
|
+
}
|
|
16151
|
+
const key = parseKeyRef(row.key, `${path}.key`);
|
|
16152
|
+
if (key) endpoint.key = key;
|
|
16153
|
+
for (const name of Object.keys(row)) {
|
|
16154
|
+
if (name !== "url" && name !== "project" && name !== "key") {
|
|
16155
|
+
if (typeof row[name] === "string") throw new KxmHubConfigError(literalSecretMessage(`${path}.${name}`));
|
|
16156
|
+
throw new KxmHubConfigError(`${path}.${name} is not a hub endpoint field`);
|
|
16157
|
+
}
|
|
16158
|
+
}
|
|
16159
|
+
return endpoint;
|
|
16160
|
+
}
|
|
16161
|
+
function isHubUrl(raw) {
|
|
16162
|
+
try {
|
|
16163
|
+
const parsed = new URL(raw);
|
|
16164
|
+
return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && !raw.includes("?") && !raw.includes("#");
|
|
16165
|
+
} catch {
|
|
16166
|
+
return false;
|
|
16167
|
+
}
|
|
16168
|
+
}
|
|
16169
|
+
function isHubProjectId(raw) {
|
|
16170
|
+
const value = raw.trim();
|
|
16171
|
+
return value.length > 0 && value.length <= 200 && !/[\s\r\n]/.test(value);
|
|
16172
|
+
}
|
|
16173
|
+
function parseProjectMap(raw) {
|
|
16174
|
+
if (raw === void 0) return void 0;
|
|
16175
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
16176
|
+
throw new KxmHubConfigError("hub.projects must be a map of project id to key reference, never a token");
|
|
16177
|
+
}
|
|
16178
|
+
const projects = {};
|
|
16179
|
+
for (const [project, value] of Object.entries(raw)) {
|
|
16180
|
+
if (!isHubProjectId(project)) {
|
|
16181
|
+
throw new KxmHubConfigError(`hub.projects key ${JSON.stringify(project)} must be a project id, not a token`);
|
|
16182
|
+
}
|
|
16183
|
+
const ref = parseKeyRef(value, `hub.projects.${project}`);
|
|
16184
|
+
if (!ref) throw new KxmHubConfigError(`hub.projects.${project} needs an op:// reference or an environment variable name`);
|
|
16185
|
+
projects[project.trim()] = ref;
|
|
16186
|
+
}
|
|
16187
|
+
return projects;
|
|
16188
|
+
}
|
|
16071
16189
|
function normalizeHubConfig(raw) {
|
|
16072
16190
|
const autoStart = raw?.autoStart;
|
|
16073
|
-
|
|
16191
|
+
const hub = {
|
|
16192
|
+
autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart
|
|
16193
|
+
};
|
|
16194
|
+
if (raw === void 0) return hub;
|
|
16195
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
16196
|
+
throw new KxmHubConfigError("hub must be an object");
|
|
16197
|
+
}
|
|
16198
|
+
rejectLiteralHubSecrets(raw, "hub");
|
|
16199
|
+
const row = raw;
|
|
16200
|
+
if (row.mode !== void 0) {
|
|
16201
|
+
if (row.mode !== "local" && row.mode !== "cloud") {
|
|
16202
|
+
throw new KxmHubConfigError("hub.mode must be local or cloud");
|
|
16203
|
+
}
|
|
16204
|
+
hub.mode = row.mode;
|
|
16205
|
+
}
|
|
16206
|
+
const local = parseEndpoint(row.local, "hub.local");
|
|
16207
|
+
const cloud = parseEndpoint(row.cloud, "hub.cloud");
|
|
16208
|
+
const projects = parseProjectMap(row.projects);
|
|
16209
|
+
if (local) hub.local = local;
|
|
16210
|
+
if (cloud) hub.cloud = cloud;
|
|
16211
|
+
if (projects && Object.keys(projects).length > 0) hub.projects = projects;
|
|
16212
|
+
return hub;
|
|
16213
|
+
}
|
|
16214
|
+
function findConfigRoot(start) {
|
|
16215
|
+
let dir = resolve2(start);
|
|
16216
|
+
for (; ; ) {
|
|
16217
|
+
if (existsSync2(join2(dir, ".kxm", "project.yaml")) || existsSync2(join2(dir, ".kxm", "config.yaml"))) return dir;
|
|
16218
|
+
const parent = dirname2(dir);
|
|
16219
|
+
if (parent === dir) return resolve2(start);
|
|
16220
|
+
dir = parent;
|
|
16221
|
+
}
|
|
16222
|
+
}
|
|
16223
|
+
function userConfigDirForEnv(env) {
|
|
16224
|
+
const explicit = env.KXM_USER_CONFIG_DIR?.trim();
|
|
16225
|
+
if (explicit) return explicit;
|
|
16226
|
+
if (env === process.env) return void 0;
|
|
16227
|
+
return join2(tmpdir(), "kxm-no-user-config");
|
|
16228
|
+
}
|
|
16229
|
+
function loadHubSettings(start, env = process.env) {
|
|
16230
|
+
const userConfigDir = userConfigDirForEnv(env);
|
|
16231
|
+
return loadKxmConfig(findConfigRoot(start), userConfigDir === void 0 ? {} : { userConfigDir }).hub;
|
|
16074
16232
|
}
|
|
16075
16233
|
function recordOf(raw) {
|
|
16076
16234
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
@@ -16244,17 +16402,6 @@ function splitTokenCommand(raw) {
|
|
|
16244
16402
|
function isCloudTokenEnvName(value) {
|
|
16245
16403
|
return TOKEN_ENV_NAME.test(value);
|
|
16246
16404
|
}
|
|
16247
|
-
function resolveHubServerUrl(env = process.env) {
|
|
16248
|
-
const fromEnv = env.KXM_SERVER_URL?.trim();
|
|
16249
|
-
if (fromEnv) return fromEnv.replace(/\/$/, "");
|
|
16250
|
-
try {
|
|
16251
|
-
const binding = readHubBinding(env);
|
|
16252
|
-
if (binding?.url) return binding.url;
|
|
16253
|
-
} catch (error2) {
|
|
16254
|
-
if (!(error2 instanceof HubBindingError)) throw error2;
|
|
16255
|
-
}
|
|
16256
|
-
return "http://127.0.0.1:7331";
|
|
16257
|
-
}
|
|
16258
16405
|
function resolveCloudHubToken(binding, env = process.env) {
|
|
16259
16406
|
if (!binding.cloud) {
|
|
16260
16407
|
throw new CloudTokenError("cloud_token_missing", "cloud hub token is missing; the local hub-env token was not used");
|
|
@@ -16296,6 +16443,17 @@ function runTokenCommand(command, env) {
|
|
|
16296
16443
|
}
|
|
16297
16444
|
return stdout.trim();
|
|
16298
16445
|
}
|
|
16446
|
+
function hubBindingScope(url) {
|
|
16447
|
+
let host;
|
|
16448
|
+
try {
|
|
16449
|
+
host = new URL(url).hostname.toLowerCase();
|
|
16450
|
+
} catch {
|
|
16451
|
+
return "remote";
|
|
16452
|
+
}
|
|
16453
|
+
if (host === "localhost" || host === "::1" || host === "[::1]" || host.endsWith(".localhost")) return "loopback";
|
|
16454
|
+
const v4 = /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(host);
|
|
16455
|
+
return v4 && [v4[1], v4[2], v4[3]].every((part) => Number(part) <= 255) ? "loopback" : "remote";
|
|
16456
|
+
}
|
|
16299
16457
|
function isIsoTimestamp(value) {
|
|
16300
16458
|
if (Number.isNaN(Date.parse(value))) return false;
|
|
16301
16459
|
return value === new Date(value).toISOString();
|
|
@@ -16383,31 +16541,21 @@ async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
|
|
|
16383
16541
|
}
|
|
16384
16542
|
}
|
|
16385
16543
|
|
|
16386
|
-
// plugins/kxm/src/hub-
|
|
16387
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
16388
|
-
import { homedir as homedir4 } from "node:os";
|
|
16389
|
-
import { randomBytes } from "node:crypto";
|
|
16390
|
-
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join5, resolve as resolve5 } from "node:path";
|
|
16391
|
-
|
|
16392
|
-
// plugins/kxm/src/project-name.ts
|
|
16544
|
+
// plugins/kxm/src/hub-identity.ts
|
|
16393
16545
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
16546
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16394
16547
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
16395
16548
|
import { basename, dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
const fromEnv = env.KXM_PROJECT?.trim();
|
|
16400
|
-
if (fromEnv) return fromEnv;
|
|
16401
|
-
const fromProject = readProjectYamlId(cwd);
|
|
16402
|
-
if (fromProject) return fromProject;
|
|
16549
|
+
var OP_TIMEOUT_MS = 15e3;
|
|
16550
|
+
var OP_MAX_BUFFER = 4096;
|
|
16551
|
+
function normalizeUrl(raw) {
|
|
16403
16552
|
try {
|
|
16404
|
-
|
|
16405
|
-
if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
|
|
16553
|
+
return validateHubUrl(raw);
|
|
16406
16554
|
} catch {
|
|
16555
|
+
return void 0;
|
|
16407
16556
|
}
|
|
16408
|
-
return basename(cwd);
|
|
16409
16557
|
}
|
|
16410
|
-
function
|
|
16558
|
+
function projectYamlId(start) {
|
|
16411
16559
|
let dir = resolve4(start);
|
|
16412
16560
|
for (; ; ) {
|
|
16413
16561
|
const file = join4(dir, ".kxm", "project.yaml");
|
|
@@ -16426,8 +16574,174 @@ function readProjectYamlId(start) {
|
|
|
16426
16574
|
dir = parent;
|
|
16427
16575
|
}
|
|
16428
16576
|
}
|
|
16577
|
+
function packageName(cwd) {
|
|
16578
|
+
try {
|
|
16579
|
+
const pkg = JSON.parse(readFileSync4(join4(cwd, "package.json"), "utf8"));
|
|
16580
|
+
if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
|
|
16581
|
+
} catch {
|
|
16582
|
+
}
|
|
16583
|
+
return void 0;
|
|
16584
|
+
}
|
|
16585
|
+
function activeBinding(env) {
|
|
16586
|
+
try {
|
|
16587
|
+
return readHubBinding(env);
|
|
16588
|
+
} catch (error2) {
|
|
16589
|
+
if (error2 instanceof HubBindingError) return void 0;
|
|
16590
|
+
throw error2;
|
|
16591
|
+
}
|
|
16592
|
+
}
|
|
16593
|
+
function endpointFor(mode, settings) {
|
|
16594
|
+
return mode === "cloud" ? settings.cloud : settings.local;
|
|
16595
|
+
}
|
|
16596
|
+
function resolveProjectIdentity(cwd, env = process.env, explicit) {
|
|
16597
|
+
const fromExplicit = explicit?.trim();
|
|
16598
|
+
if (fromExplicit) return { project: fromExplicit, source: "flag", sourceLabel: "--project" };
|
|
16599
|
+
const fromEnv = env.KXM_PROJECT?.trim();
|
|
16600
|
+
if (fromEnv) return { project: fromEnv, source: "env", sourceLabel: "KXM_PROJECT" };
|
|
16601
|
+
const settings = loadHubSettings(cwd, env);
|
|
16602
|
+
const mode = settings.mode ?? (activeBinding(env)?.cloud ? "cloud" : "local");
|
|
16603
|
+
const fromConfig = endpointFor(mode, settings)?.project?.trim();
|
|
16604
|
+
if (fromConfig) {
|
|
16605
|
+
return { project: fromConfig, source: "config", sourceLabel: `.kxm/config.yaml hub.${mode}.project` };
|
|
16606
|
+
}
|
|
16607
|
+
const fromProject = projectYamlId(cwd);
|
|
16608
|
+
if (fromProject) return { project: fromProject, source: "project.yaml", sourceLabel: ".kxm/project.yaml id" };
|
|
16609
|
+
const fromPackage = packageName(cwd);
|
|
16610
|
+
if (fromPackage) return { project: fromPackage, source: "package.json", sourceLabel: "package.json name" };
|
|
16611
|
+
return { project: basename(cwd), source: "directory", sourceLabel: "directory name" };
|
|
16612
|
+
}
|
|
16613
|
+
function opReferenceFromTokenCommand(command) {
|
|
16614
|
+
try {
|
|
16615
|
+
const args = splitTokenCommand(command);
|
|
16616
|
+
if (args[0] === "op" && args[1] === "read" && args[2] && isHubOpReference(args[2])) return args[2];
|
|
16617
|
+
} catch {
|
|
16618
|
+
return void 0;
|
|
16619
|
+
}
|
|
16620
|
+
return void 0;
|
|
16621
|
+
}
|
|
16622
|
+
function defaultOpRead(reference, env) {
|
|
16623
|
+
if (!isHubOpReference(reference)) {
|
|
16624
|
+
throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
|
|
16625
|
+
}
|
|
16626
|
+
const result2 = spawnSync2("op", ["read", reference], {
|
|
16627
|
+
encoding: "utf8",
|
|
16628
|
+
shell: false,
|
|
16629
|
+
timeout: OP_TIMEOUT_MS,
|
|
16630
|
+
maxBuffer: OP_MAX_BUFFER,
|
|
16631
|
+
env,
|
|
16632
|
+
windowsHide: true
|
|
16633
|
+
});
|
|
16634
|
+
if (result2.error || result2.status !== 0) {
|
|
16635
|
+
throw new CloudTokenError(
|
|
16636
|
+
"cloud_token_command_failed",
|
|
16637
|
+
`op read failed for ${reference}; the token was not saved`
|
|
16638
|
+
);
|
|
16639
|
+
}
|
|
16640
|
+
const line = (result2.stdout ?? "").trim();
|
|
16641
|
+
if (!line || line.split(/\r?\n/).length !== 1) {
|
|
16642
|
+
throw new CloudTokenError(
|
|
16643
|
+
"cloud_token_command_failed",
|
|
16644
|
+
`op read did not print a single token for ${reference}; the token was not saved`
|
|
16645
|
+
);
|
|
16646
|
+
}
|
|
16647
|
+
return line;
|
|
16648
|
+
}
|
|
16649
|
+
function resolveKeyReference(ref, env, opRead = defaultOpRead) {
|
|
16650
|
+
if (!ref) return void 0;
|
|
16651
|
+
if (ref.env) {
|
|
16652
|
+
const value = env[ref.env]?.trim();
|
|
16653
|
+
if (value) return { value, source: `env:${ref.env}` };
|
|
16654
|
+
}
|
|
16655
|
+
if (ref.op) {
|
|
16656
|
+
const value = opRead(ref.op, env).trim();
|
|
16657
|
+
if (!value || value.split(/\r?\n/).length !== 1) {
|
|
16658
|
+
throw new CloudTokenError("cloud_token_command_failed", `op read did not print a single token for ${ref.op}`);
|
|
16659
|
+
}
|
|
16660
|
+
return { value, source: `op:${ref.op}` };
|
|
16661
|
+
}
|
|
16662
|
+
return void 0;
|
|
16663
|
+
}
|
|
16664
|
+
function keySourceFor(input) {
|
|
16665
|
+
const ref = input.endpoint?.key;
|
|
16666
|
+
if (input.mode === "local" && input.env.KXM_AUTH_TOKEN?.trim()) return "env:KXM_AUTH_TOKEN";
|
|
16667
|
+
if (ref?.env && input.env[ref.env]?.trim()) return `env:${ref.env}`;
|
|
16668
|
+
if (ref?.op) return `op:${ref.op}`;
|
|
16669
|
+
if (ref?.env) return `env:${ref.env}`;
|
|
16670
|
+
if (input.mode === "cloud") {
|
|
16671
|
+
if (input.binding?.tokenEnv) return `env:${input.binding.tokenEnv}`;
|
|
16672
|
+
if (input.binding?.tokenCommand) {
|
|
16673
|
+
const op = opReferenceFromTokenCommand(input.binding.tokenCommand);
|
|
16674
|
+
return op ? `op:${op}` : "command";
|
|
16675
|
+
}
|
|
16676
|
+
return "missing";
|
|
16677
|
+
}
|
|
16678
|
+
if (input.hasSavedProjectToken?.(input.project)) return "hub-env";
|
|
16679
|
+
return "missing";
|
|
16680
|
+
}
|
|
16681
|
+
function describeHubConnection(cwd, env = process.env, hooks = {}) {
|
|
16682
|
+
const settings = loadHubSettings(cwd, env);
|
|
16683
|
+
const binding = activeBinding(env);
|
|
16684
|
+
const mode = settings.mode ?? (binding?.cloud ? "cloud" : "local");
|
|
16685
|
+
const modeSource = settings.mode ? "config" : binding?.cloud ? "binding" : "default";
|
|
16686
|
+
const endpoint = endpointFor(mode, settings);
|
|
16687
|
+
const fromEnv = env.KXM_SERVER_URL?.trim();
|
|
16688
|
+
const fromConfig = endpoint?.url?.trim();
|
|
16689
|
+
const fromBinding = binding?.url;
|
|
16690
|
+
let url = "http://127.0.0.1:7331";
|
|
16691
|
+
let urlSource = "default";
|
|
16692
|
+
if (fromEnv) {
|
|
16693
|
+
url = fromEnv.replace(/\/$/, "");
|
|
16694
|
+
urlSource = "env";
|
|
16695
|
+
} else if (fromConfig) {
|
|
16696
|
+
url = fromConfig.replace(/\/$/, "");
|
|
16697
|
+
urlSource = "config";
|
|
16698
|
+
} else if (fromBinding) {
|
|
16699
|
+
url = fromBinding;
|
|
16700
|
+
urlSource = "binding";
|
|
16701
|
+
}
|
|
16702
|
+
const identity = resolveProjectIdentity(cwd, env, hooks.explicitProject);
|
|
16703
|
+
const configuredCloudUrl = settings.cloud?.url ?? (binding?.cloud ? binding.url : void 0);
|
|
16704
|
+
let scope;
|
|
16705
|
+
if (mode === "cloud") {
|
|
16706
|
+
const override = fromEnv ? normalizeUrl(fromEnv) : void 0;
|
|
16707
|
+
const named = configuredCloudUrl ? normalizeUrl(configuredCloudUrl) : void 0;
|
|
16708
|
+
scope = override && named && override !== named ? hubBindingScope(url) : "remote";
|
|
16709
|
+
} else {
|
|
16710
|
+
scope = hubBindingScope(url);
|
|
16711
|
+
}
|
|
16712
|
+
return {
|
|
16713
|
+
...identity,
|
|
16714
|
+
mode,
|
|
16715
|
+
modeSource,
|
|
16716
|
+
url,
|
|
16717
|
+
urlSource,
|
|
16718
|
+
scope,
|
|
16719
|
+
...endpoint ? { endpoint } : {},
|
|
16720
|
+
...binding ? { binding } : {},
|
|
16721
|
+
keySource: keySourceFor({
|
|
16722
|
+
mode,
|
|
16723
|
+
env,
|
|
16724
|
+
...endpoint ? { endpoint } : {},
|
|
16725
|
+
...binding ? { binding } : {},
|
|
16726
|
+
project: identity.project,
|
|
16727
|
+
...hooks.hasSavedProjectToken ? { hasSavedProjectToken: hooks.hasSavedProjectToken } : {}
|
|
16728
|
+
})
|
|
16729
|
+
};
|
|
16730
|
+
}
|
|
16731
|
+
function projectTokenMissingMessage(identity) {
|
|
16732
|
+
return [
|
|
16733
|
+
`kxm has no project token for project ${identity.project} (from ${identity.sourceLabel}).`,
|
|
16734
|
+
`Ask the user to set this project's token. Set KXM_AUTH_TOKEN, or set hub.local.project or hub.cloud.project in .kxm/config.yaml to the project key the hub already knows, or add a key reference under hub.projects.${identity.project} (key.op is an op:// reference, key.env is an environment variable name, never the token) and restart the hub.`,
|
|
16735
|
+
"KXM_PROJECT or --project overrides the id for one command.",
|
|
16736
|
+
"An agent never uses the hub admin token."
|
|
16737
|
+
].join(" ");
|
|
16738
|
+
}
|
|
16429
16739
|
|
|
16430
16740
|
// plugins/kxm/src/hub-env.ts
|
|
16741
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
16742
|
+
import { homedir as homedir4 } from "node:os";
|
|
16743
|
+
import { randomBytes } from "node:crypto";
|
|
16744
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join5, resolve as resolve5 } from "node:path";
|
|
16431
16745
|
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
16432
16746
|
var HubEnvError = class extends Error {
|
|
16433
16747
|
constructor(message) {
|
|
@@ -16567,24 +16881,24 @@ function resolveHubCredentials(options = {}) {
|
|
|
16567
16881
|
written: needsPersist === true
|
|
16568
16882
|
};
|
|
16569
16883
|
}
|
|
16570
|
-
function
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
|
|
16576
|
-
|
|
16577
|
-
|
|
16578
|
-
|
|
16579
|
-
|
|
16580
|
-
throw error2;
|
|
16581
|
-
}
|
|
16884
|
+
function cloudModeToken(cwd, env, explicitProject, opRead) {
|
|
16885
|
+
const connection = describeHubConnection(cwd, env, explicitProject === void 0 ? {} : { explicitProject });
|
|
16886
|
+
if (connection.mode !== "cloud") return void 0;
|
|
16887
|
+
const fromConfig = resolveKeyReference(connection.endpoint?.key, env, opRead);
|
|
16888
|
+
if (fromConfig) return fromConfig.value;
|
|
16889
|
+
if (connection.binding?.cloud) return resolveCloudHubToken(connection.binding, env);
|
|
16890
|
+
throw new CloudTokenError(
|
|
16891
|
+
"cloud_token_missing",
|
|
16892
|
+
"cloud hub token is missing; the local hub-env token was not used"
|
|
16893
|
+
);
|
|
16582
16894
|
}
|
|
16583
|
-
function resolveAgentHubAuthToken(env, project) {
|
|
16584
|
-
const cloud =
|
|
16585
|
-
if (cloud) return
|
|
16895
|
+
function resolveAgentHubAuthToken(env, project, cwd = process.cwd(), opRead) {
|
|
16896
|
+
const cloud = cloudModeToken(cwd, env, void 0, opRead);
|
|
16897
|
+
if (cloud) return cloud;
|
|
16586
16898
|
const envToken = env.KXM_AUTH_TOKEN?.trim();
|
|
16587
16899
|
if (envToken) return envToken;
|
|
16900
|
+
const fromConfig = resolveKeyReference(describeHubConnection(cwd, env).endpoint?.key, env, opRead);
|
|
16901
|
+
if (fromConfig) return fromConfig.value;
|
|
16588
16902
|
const tokens = readHubEnvRecord(env)?.projectTokens;
|
|
16589
16903
|
if (!tokens || !Object.hasOwn(tokens, project)) return void 0;
|
|
16590
16904
|
return tokens[project]?.trim() || void 0;
|
|
@@ -16592,12 +16906,16 @@ function resolveAgentHubAuthToken(env, project) {
|
|
|
16592
16906
|
var AgentProjectTokenMissingError = class extends Error {
|
|
16593
16907
|
code = "project_token_missing";
|
|
16594
16908
|
project;
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
|
|
16909
|
+
projectSource;
|
|
16910
|
+
hint;
|
|
16911
|
+
constructor(projectOrIdentity) {
|
|
16912
|
+
const identity = typeof projectOrIdentity === "string" ? { project: projectOrIdentity, source: "env", sourceLabel: "the project id sent to the hub" } : projectOrIdentity;
|
|
16913
|
+
const message = projectTokenMissingMessage(identity);
|
|
16914
|
+
super(message);
|
|
16599
16915
|
this.name = "AgentProjectTokenMissingError";
|
|
16600
|
-
this.project = project;
|
|
16916
|
+
this.project = identity.project;
|
|
16917
|
+
this.projectSource = typeof projectOrIdentity === "string" ? "unknown" : identity.source;
|
|
16918
|
+
this.hint = message;
|
|
16601
16919
|
}
|
|
16602
16920
|
};
|
|
16603
16921
|
|
|
@@ -16683,6 +17001,22 @@ async function ensureHubRunning(options) {
|
|
|
16683
17001
|
if (health === "on") return { status: "bound-healthy", url: binding.url };
|
|
16684
17002
|
if (binding.cloud) return { status: "failed", reason: "cloud_hub_unreachable", logPath: "" };
|
|
16685
17003
|
}
|
|
17004
|
+
if (!binding?.cloud) {
|
|
17005
|
+
try {
|
|
17006
|
+
const connection = describeHubConnection(options.cwd, env);
|
|
17007
|
+
if (connection.mode === "cloud") {
|
|
17008
|
+
const { health } = await probeHubHealth(connection.url, fetchImpl);
|
|
17009
|
+
if (health === "on") return { status: "bound-healthy", url: connection.url };
|
|
17010
|
+
return { status: "failed", reason: "cloud_hub_unreachable", logPath: "" };
|
|
17011
|
+
}
|
|
17012
|
+
} catch (error2) {
|
|
17013
|
+
return {
|
|
17014
|
+
status: "failed",
|
|
17015
|
+
reason: error2 instanceof Error ? error2.message : "hub_config_invalid",
|
|
17016
|
+
logPath: ""
|
|
17017
|
+
};
|
|
17018
|
+
}
|
|
17019
|
+
}
|
|
16686
17020
|
const workdir = resolve6(env.KXM_WORKDIR?.trim() || options.cwd);
|
|
16687
17021
|
const workspaceDir = resolve6(workdir, env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
|
|
16688
17022
|
const stateDir = resolve6(workdir, env.KXM_STATE_DIR?.trim() || join7(workspaceDir, "state"));
|
|
@@ -36777,7 +37111,7 @@ async function consumeWorkerRecoveryEnvelope(client, stateDir, agentName, projec
|
|
|
36777
37111
|
}
|
|
36778
37112
|
|
|
36779
37113
|
// plugins/kxm/src/session-work.ts
|
|
36780
|
-
import { spawnSync as
|
|
37114
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
36781
37115
|
import { randomUUID } from "node:crypto";
|
|
36782
37116
|
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
36783
37117
|
import { join as join13 } from "node:path";
|
|
@@ -37275,10 +37609,10 @@ function formatShipLine(ship) {
|
|
|
37275
37609
|
}
|
|
37276
37610
|
function readGitShip(cwd) {
|
|
37277
37611
|
try {
|
|
37278
|
-
const dirty =
|
|
37612
|
+
const dirty = spawnSync3("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
|
|
37279
37613
|
if (dirty.status !== 0) return void 0;
|
|
37280
37614
|
const isDirty2 = dirty.stdout.trim().length > 0;
|
|
37281
|
-
const upstream =
|
|
37615
|
+
const upstream = spawnSync3("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
|
|
37282
37616
|
if (upstream.status === 0) {
|
|
37283
37617
|
return {
|
|
37284
37618
|
dirty: isDirty2,
|
|
@@ -37286,9 +37620,9 @@ function readGitShip(cwd) {
|
|
|
37286
37620
|
};
|
|
37287
37621
|
}
|
|
37288
37622
|
for (const baseRef of ["origin/HEAD", "main", "origin/main", "master", "origin/master"]) {
|
|
37289
|
-
const mb =
|
|
37623
|
+
const mb = spawnSync3("git", ["-C", cwd, "merge-base", baseRef, "HEAD"], { encoding: "utf8", windowsHide: true });
|
|
37290
37624
|
if (mb.status === 0 && mb.stdout.trim()) {
|
|
37291
|
-
const count =
|
|
37625
|
+
const count = spawnSync3("git", ["-C", cwd, "rev-list", "--count", `${mb.stdout.trim()}..HEAD`], { encoding: "utf8", windowsHide: true });
|
|
37292
37626
|
if (count.status === 0) {
|
|
37293
37627
|
return {
|
|
37294
37628
|
dirty: isDirty2,
|
|
@@ -37994,7 +38328,7 @@ function piMeshExtension(pi) {
|
|
|
37994
38328
|
ctx2.ui.setEditorText?.(item.prompt);
|
|
37995
38329
|
}
|
|
37996
38330
|
async function showKxmHub(ctx2) {
|
|
37997
|
-
const url =
|
|
38331
|
+
const url = describeHubConnection(process.cwd(), process.env).url;
|
|
37998
38332
|
let health = "unreachable";
|
|
37999
38333
|
try {
|
|
38000
38334
|
const response = await fetch(`${url}/health`);
|
|
@@ -38284,8 +38618,9 @@ function piMeshExtension(pi) {
|
|
|
38284
38618
|
return;
|
|
38285
38619
|
}
|
|
38286
38620
|
currentPiSessionId = ctx2.sessionManager?.getSessionId();
|
|
38287
|
-
const
|
|
38288
|
-
const
|
|
38621
|
+
const identity = resolveProjectIdentity(ctx2.cwd, process.env);
|
|
38622
|
+
const serverUrl = describeHubConnection(ctx2.cwd, process.env).url;
|
|
38623
|
+
const project = identity.project;
|
|
38289
38624
|
const name = process.env.KXM_AGENT_NAME ?? pi.getSessionName() ?? `pi-${process.pid}`;
|
|
38290
38625
|
agentName = name;
|
|
38291
38626
|
projectName = project;
|
|
@@ -38311,7 +38646,7 @@ function piMeshExtension(pi) {
|
|
|
38311
38646
|
}
|
|
38312
38647
|
let hubAuthToken;
|
|
38313
38648
|
try {
|
|
38314
|
-
hubAuthToken = resolveAgentHubAuthToken(process.env, project);
|
|
38649
|
+
hubAuthToken = resolveAgentHubAuthToken(process.env, project, ctx2.cwd);
|
|
38315
38650
|
} catch (error2) {
|
|
38316
38651
|
const detail = error2 instanceof CloudTokenError ? error2.message : `kxm could not read persisted hub credentials: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
38317
38652
|
ctx2.ui.notify(detail, "error");
|
|
@@ -38319,7 +38654,7 @@ function piMeshExtension(pi) {
|
|
|
38319
38654
|
return;
|
|
38320
38655
|
}
|
|
38321
38656
|
if (!hubAuthToken) {
|
|
38322
|
-
ctx2.ui.notify(new AgentProjectTokenMissingError(
|
|
38657
|
+
ctx2.ui.notify(new AgentProjectTokenMissingError(identity).message, "error");
|
|
38323
38658
|
await applySessionChrome(ctx2, event, true);
|
|
38324
38659
|
return;
|
|
38325
38660
|
}
|
|
@@ -38533,12 +38868,12 @@ function piMeshExtension(pi) {
|
|
|
38533
38868
|
if (command === "memory") {
|
|
38534
38869
|
const cwd = typeof ctx2.cwd === "string" ? ctx2.cwd : process.cwd();
|
|
38535
38870
|
const kxmBin = process.env.KXM_BIN || "kxm";
|
|
38536
|
-
const res =
|
|
38871
|
+
const res = spawnSync4(kxmBin, ["memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
|
|
38537
38872
|
let memText = res.status === 0 && res.stdout ? res.stdout.trim() : "";
|
|
38538
38873
|
if (!memText) {
|
|
38539
38874
|
const script = join15(cwd, "scripts", "kxm.mjs");
|
|
38540
38875
|
if (existsSync13(script)) {
|
|
38541
|
-
const scriptRes =
|
|
38876
|
+
const scriptRes = spawnSync4(process.execPath, [script, "memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
|
|
38542
38877
|
if (scriptRes.status === 0 && scriptRes.stdout) memText = scriptRes.stdout.trim();
|
|
38543
38878
|
}
|
|
38544
38879
|
}
|