@kontextmind/kxm 0.7.115 → 0.7.119
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/package.json +1 -1
- package/packages/core/tui/dist/index.js +478 -0
- package/packages/core/tui/src/adapters/claude.ts +131 -0
- package/packages/core/tui/src/adapters/historyExport.ts +35 -0
- package/packages/core/tui/src/adapters/omp.ts +174 -0
- package/packages/core/tui/src/adapters/pi.ts +34 -0
- package/packages/core/tui/src/exports/index.ts +8 -0
- package/packages/core/tui/src/tui/history.ts +106 -0
- package/packages/core/tui/src/tui/modelSelector.ts +43 -0
- package/packages/core/tui/src/tui/optimizer.ts +154 -0
- package/packages/core/tui/src/tui/queue.ts +90 -0
- package/packages/core/tui/src/tui/roleBudget.ts +104 -0
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +220 -22
- package/plugins/kxm/dist/extension.js +24 -8
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +36 -4
- package/plugins/kxm/dist/runtime.js +36 -4
- package/plugins/kxm/dist/server.js +22 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +6 -0
- package/plugins/kxm/src/autocomplete.ts +3 -0
- package/plugins/kxm/src/cli/plugins.ts +201 -0
- package/plugins/kxm/src/cli.ts +14 -0
- package/plugins/kxm/src/harness.ts +36 -4
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/providers/claude-bridge/register.ts +25 -9
- package/scripts/demo-overlay.mjs +175 -0
|
@@ -15624,6 +15624,7 @@ var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
|
15624
15624
|
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
15625
15625
|
// is not enough: extensions and hooks can still run with their own permissions.
|
|
15626
15626
|
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15627
|
+
omp: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15627
15628
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15628
15629
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15629
15630
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -15635,6 +15636,7 @@ function oneShotReadOnlyArgs(harness) {
|
|
|
15635
15636
|
}
|
|
15636
15637
|
var WRITER_ONESHOT_ARGS = Object.freeze({
|
|
15637
15638
|
pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15639
|
+
omp: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15638
15640
|
grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
|
|
15639
15641
|
});
|
|
15640
15642
|
function oneShotWriterArgs(harness) {
|
|
@@ -15663,6 +15665,26 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15663
15665
|
usageParser: parsePiOneShotUsage
|
|
15664
15666
|
}
|
|
15665
15667
|
},
|
|
15668
|
+
{
|
|
15669
|
+
id: "omp",
|
|
15670
|
+
label: "Oh My Pi",
|
|
15671
|
+
default: false,
|
|
15672
|
+
mode: "headless",
|
|
15673
|
+
commands: ["omp"],
|
|
15674
|
+
versionArgs: ["--version"],
|
|
15675
|
+
authArgs: ["models", "--json"],
|
|
15676
|
+
update: {
|
|
15677
|
+
self: ["update"],
|
|
15678
|
+
extensions: ["plugin", "upgrade"],
|
|
15679
|
+
models: ["models", "refresh"]
|
|
15680
|
+
},
|
|
15681
|
+
oneShot: {
|
|
15682
|
+
argv: ["-p", ...oneShotReadOnlyArgs("omp"), "--mode", "json"],
|
|
15683
|
+
promptVia: "arg",
|
|
15684
|
+
outputFormat: "json",
|
|
15685
|
+
usageParser: parsePiOneShotUsage
|
|
15686
|
+
}
|
|
15687
|
+
},
|
|
15666
15688
|
{
|
|
15667
15689
|
id: "claude",
|
|
15668
15690
|
label: "Claude Code",
|
|
@@ -15911,6 +15933,16 @@ function interpretAuth(id, result) {
|
|
|
15911
15933
|
if (commandSucceeded(result) && lines.some((line) => line === GROK_LOGIN_LINE)) return { authenticated: true, issues: [] };
|
|
15912
15934
|
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
15913
15935
|
}
|
|
15936
|
+
if (id === "omp") {
|
|
15937
|
+
if (commandSucceeded(result)) {
|
|
15938
|
+
const payload = parseJsonObject(result.stdout);
|
|
15939
|
+
if (payload && Array.isArray(payload.models) && payload.models.length > 0) {
|
|
15940
|
+
return { authenticated: true, issues: [] };
|
|
15941
|
+
}
|
|
15942
|
+
if (commandSucceeded(result)) return { authenticated: true, issues: [] };
|
|
15943
|
+
}
|
|
15944
|
+
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
15945
|
+
}
|
|
15914
15946
|
if (id === "agy") {
|
|
15915
15947
|
const text = `${result.stdout}
|
|
15916
15948
|
${result.stderr}`;
|
|
@@ -16104,7 +16136,7 @@ function validateHarnessModelPair(harnessId, modelSpec) {
|
|
|
16104
16136
|
}
|
|
16105
16137
|
return { valid: true };
|
|
16106
16138
|
}
|
|
16107
|
-
if (harnessId === "pi") {
|
|
16139
|
+
if (harnessId === "pi" || harnessId === "omp") {
|
|
16108
16140
|
if (!provider && model?.includes("/")) {
|
|
16109
16141
|
const idx = model.indexOf("/");
|
|
16110
16142
|
provider = model.slice(0, idx).toLowerCase();
|
|
@@ -16113,17 +16145,17 @@ function validateHarnessModelPair(harnessId, modelSpec) {
|
|
|
16113
16145
|
if (!provider) return { valid: true };
|
|
16114
16146
|
const blocked = (message) => ({ valid: false, issue: "pi_native_impersonation_blocked", message });
|
|
16115
16147
|
if (PI_NATIVE_BRAKE_PROVIDERS.includes(provider)) {
|
|
16116
|
-
return blocked(
|
|
16148
|
+
return blocked(`${harnessId} must not impersonate native provider ${provider}; use the native harness`);
|
|
16117
16149
|
}
|
|
16118
16150
|
if (provider === "antigravity" && model && PI_ANTIGRAVITY_MODEL_ID.test(model)) return { valid: true };
|
|
16119
16151
|
const owned = Object.hasOwn(PI_VENDOR_OWNED_PROVIDERS, provider) ? PI_VENDOR_OWNED_PROVIDERS[provider] : void 0;
|
|
16120
16152
|
if (owned) {
|
|
16121
|
-
return blocked(
|
|
16153
|
+
return blocked(`${harnessId} provider ${provider} bills native vendor ${owned} for ${model ?? "this model"}; use the native harness`);
|
|
16122
16154
|
}
|
|
16123
16155
|
const segment = model?.includes("/") ? model.slice(0, model.indexOf("/")).toLowerCase() : void 0;
|
|
16124
16156
|
const vendor = segment && Object.hasOwn(PI_VENDOR_SEGMENT_ALIASES, segment) ? PI_VENDOR_SEGMENT_ALIASES[segment] : segment;
|
|
16125
16157
|
if (vendor && PI_NATIVE_BRAKE_PROVIDERS.includes(vendor)) {
|
|
16126
|
-
return blocked(
|
|
16158
|
+
return blocked(`${harnessId} must not bill native vendor ${vendor} through ${provider}; use the native harness`);
|
|
16127
16159
|
}
|
|
16128
16160
|
return { valid: true };
|
|
16129
16161
|
}
|
|
@@ -15621,6 +15621,7 @@ var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
|
15621
15621
|
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
15622
15622
|
// is not enough: extensions and hooks can still run with their own permissions.
|
|
15623
15623
|
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15624
|
+
omp: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15624
15625
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15625
15626
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15626
15627
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -15632,6 +15633,7 @@ function oneShotReadOnlyArgs(harness) {
|
|
|
15632
15633
|
}
|
|
15633
15634
|
var WRITER_ONESHOT_ARGS = Object.freeze({
|
|
15634
15635
|
pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15636
|
+
omp: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15635
15637
|
grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
|
|
15636
15638
|
});
|
|
15637
15639
|
function oneShotWriterArgs(harness) {
|
|
@@ -15660,6 +15662,26 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15660
15662
|
usageParser: parsePiOneShotUsage
|
|
15661
15663
|
}
|
|
15662
15664
|
},
|
|
15665
|
+
{
|
|
15666
|
+
id: "omp",
|
|
15667
|
+
label: "Oh My Pi",
|
|
15668
|
+
default: false,
|
|
15669
|
+
mode: "headless",
|
|
15670
|
+
commands: ["omp"],
|
|
15671
|
+
versionArgs: ["--version"],
|
|
15672
|
+
authArgs: ["models", "--json"],
|
|
15673
|
+
update: {
|
|
15674
|
+
self: ["update"],
|
|
15675
|
+
extensions: ["plugin", "upgrade"],
|
|
15676
|
+
models: ["models", "refresh"]
|
|
15677
|
+
},
|
|
15678
|
+
oneShot: {
|
|
15679
|
+
argv: ["-p", ...oneShotReadOnlyArgs("omp"), "--mode", "json"],
|
|
15680
|
+
promptVia: "arg",
|
|
15681
|
+
outputFormat: "json",
|
|
15682
|
+
usageParser: parsePiOneShotUsage
|
|
15683
|
+
}
|
|
15684
|
+
},
|
|
15663
15685
|
{
|
|
15664
15686
|
id: "claude",
|
|
15665
15687
|
label: "Claude Code",
|
|
@@ -15913,6 +15935,16 @@ function interpretAuth(id, result) {
|
|
|
15913
15935
|
if (commandSucceeded(result) && lines.some((line) => line === GROK_LOGIN_LINE)) return { authenticated: true, issues: [] };
|
|
15914
15936
|
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
15915
15937
|
}
|
|
15938
|
+
if (id === "omp") {
|
|
15939
|
+
if (commandSucceeded(result)) {
|
|
15940
|
+
const payload = parseJsonObject(result.stdout);
|
|
15941
|
+
if (payload && Array.isArray(payload.models) && payload.models.length > 0) {
|
|
15942
|
+
return { authenticated: true, issues: [] };
|
|
15943
|
+
}
|
|
15944
|
+
if (commandSucceeded(result)) return { authenticated: true, issues: [] };
|
|
15945
|
+
}
|
|
15946
|
+
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
15947
|
+
}
|
|
15916
15948
|
if (id === "agy") {
|
|
15917
15949
|
const text = `${result.stdout}
|
|
15918
15950
|
${result.stderr}`;
|
|
@@ -16127,7 +16159,7 @@ function validateHarnessModelPair(harnessId, modelSpec) {
|
|
|
16127
16159
|
}
|
|
16128
16160
|
return { valid: true };
|
|
16129
16161
|
}
|
|
16130
|
-
if (harnessId === "pi") {
|
|
16162
|
+
if (harnessId === "pi" || harnessId === "omp") {
|
|
16131
16163
|
if (!provider && model?.includes("/")) {
|
|
16132
16164
|
const idx = model.indexOf("/");
|
|
16133
16165
|
provider = model.slice(0, idx).toLowerCase();
|
|
@@ -16136,17 +16168,17 @@ function validateHarnessModelPair(harnessId, modelSpec) {
|
|
|
16136
16168
|
if (!provider) return { valid: true };
|
|
16137
16169
|
const blocked = (message) => ({ valid: false, issue: "pi_native_impersonation_blocked", message });
|
|
16138
16170
|
if (PI_NATIVE_BRAKE_PROVIDERS.includes(provider)) {
|
|
16139
|
-
return blocked(
|
|
16171
|
+
return blocked(`${harnessId} must not impersonate native provider ${provider}; use the native harness`);
|
|
16140
16172
|
}
|
|
16141
16173
|
if (provider === "antigravity" && model && PI_ANTIGRAVITY_MODEL_ID.test(model)) return { valid: true };
|
|
16142
16174
|
const owned = Object.hasOwn(PI_VENDOR_OWNED_PROVIDERS, provider) ? PI_VENDOR_OWNED_PROVIDERS[provider] : void 0;
|
|
16143
16175
|
if (owned) {
|
|
16144
|
-
return blocked(
|
|
16176
|
+
return blocked(`${harnessId} provider ${provider} bills native vendor ${owned} for ${model ?? "this model"}; use the native harness`);
|
|
16145
16177
|
}
|
|
16146
16178
|
const segment = model?.includes("/") ? model.slice(0, model.indexOf("/")).toLowerCase() : void 0;
|
|
16147
16179
|
const vendor = segment && Object.hasOwn(PI_VENDOR_SEGMENT_ALIASES, segment) ? PI_VENDOR_SEGMENT_ALIASES[segment] : segment;
|
|
16148
16180
|
if (vendor && PI_NATIVE_BRAKE_PROVIDERS.includes(vendor)) {
|
|
16149
|
-
return blocked(
|
|
16181
|
+
return blocked(`${harnessId} must not bill native vendor ${vendor} through ${provider}; use the native harness`);
|
|
16150
16182
|
}
|
|
16151
16183
|
return { valid: true };
|
|
16152
16184
|
}
|
|
@@ -15262,6 +15262,7 @@ var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
|
15262
15262
|
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
15263
15263
|
// is not enough: extensions and hooks can still run with their own permissions.
|
|
15264
15264
|
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15265
|
+
omp: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15265
15266
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15266
15267
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15267
15268
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -15273,6 +15274,7 @@ function oneShotReadOnlyArgs(harness) {
|
|
|
15273
15274
|
}
|
|
15274
15275
|
var WRITER_ONESHOT_ARGS = Object.freeze({
|
|
15275
15276
|
pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15277
|
+
omp: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
15276
15278
|
grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
|
|
15277
15279
|
});
|
|
15278
15280
|
var BUILTIN_HARNESSES = Object.freeze([
|
|
@@ -15295,6 +15297,26 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15295
15297
|
usageParser: parsePiOneShotUsage
|
|
15296
15298
|
}
|
|
15297
15299
|
},
|
|
15300
|
+
{
|
|
15301
|
+
id: "omp",
|
|
15302
|
+
label: "Oh My Pi",
|
|
15303
|
+
default: false,
|
|
15304
|
+
mode: "headless",
|
|
15305
|
+
commands: ["omp"],
|
|
15306
|
+
versionArgs: ["--version"],
|
|
15307
|
+
authArgs: ["models", "--json"],
|
|
15308
|
+
update: {
|
|
15309
|
+
self: ["update"],
|
|
15310
|
+
extensions: ["plugin", "upgrade"],
|
|
15311
|
+
models: ["models", "refresh"]
|
|
15312
|
+
},
|
|
15313
|
+
oneShot: {
|
|
15314
|
+
argv: ["-p", ...oneShotReadOnlyArgs("omp"), "--mode", "json"],
|
|
15315
|
+
promptVia: "arg",
|
|
15316
|
+
outputFormat: "json",
|
|
15317
|
+
usageParser: parsePiOneShotUsage
|
|
15318
|
+
}
|
|
15319
|
+
},
|
|
15298
15320
|
{
|
|
15299
15321
|
id: "claude",
|
|
15300
15322
|
label: "Claude Code",
|
package/plugins/kxm/package.json
CHANGED
|
@@ -23,6 +23,12 @@ Issuing a token is an operator action. When kxm_* tools fail with
|
|
|
23
23
|
`No active session token found in env or disk` for such a file even though it
|
|
24
24
|
still blocks the tools.
|
|
25
25
|
|
|
26
|
+
## Plugins
|
|
27
|
+
|
|
28
|
+
| Command | Purpose | Options |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `kxm plugin install` | Install KXM plugin for discovered or specified harnesses | `--all`, `--claude`, `--omp`, `--pi`, `--dry-run`, `--json` |
|
|
31
|
+
|
|
26
32
|
## Updates
|
|
27
33
|
|
|
28
34
|
| Command | Purpose |
|
|
@@ -8,6 +8,7 @@ const TOP_LEVEL_COMMANDS = [
|
|
|
8
8
|
"runs",
|
|
9
9
|
"tenant",
|
|
10
10
|
"harness",
|
|
11
|
+
"plugin",
|
|
11
12
|
"update",
|
|
12
13
|
"runtime",
|
|
13
14
|
"trust",
|
|
@@ -38,6 +39,7 @@ const SUBCOMMANDS: Record<string, string[]> = {
|
|
|
38
39
|
runs: ["status", "cancel", "list"],
|
|
39
40
|
tenant: ["status"],
|
|
40
41
|
harness: ["list"],
|
|
42
|
+
plugin: ["install"],
|
|
41
43
|
runtime: ["start", "status", "stop"],
|
|
42
44
|
trust: ["diff", "check"],
|
|
43
45
|
agent: ["worker"],
|
|
@@ -137,6 +139,7 @@ _kxm() {
|
|
|
137
139
|
'run:Create a KXM workflow run'
|
|
138
140
|
'runs:Inspect KXM runs'
|
|
139
141
|
'harness:Detect coding-agent harnesses and auth'
|
|
142
|
+
'plugin:Install or manage KXM harness plugins'
|
|
140
143
|
'update:Update kxm, harness CLIs, and model catalogs'
|
|
141
144
|
'runtime:Manage the KXM Runtime supervisor'
|
|
142
145
|
'trust:Permission-diff trust review'
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { findKxmRepoRoot } from "../repo-root.ts";
|
|
3
|
+
import { probeHarnesses } from "../harness.ts";
|
|
4
|
+
import { print, type Runtime } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
export type PluginHarnessId = "pi" | "omp" | "claude";
|
|
7
|
+
|
|
8
|
+
export const PLUGIN_SUPPORTED_HARNESSES: readonly PluginHarnessId[] = Object.freeze(["pi", "omp", "claude"]);
|
|
9
|
+
|
|
10
|
+
export interface PluginInstallOptions {
|
|
11
|
+
all?: boolean | undefined;
|
|
12
|
+
claude?: boolean | undefined;
|
|
13
|
+
omp?: boolean | undefined;
|
|
14
|
+
pi?: boolean | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PluginInstallHarnessResult {
|
|
18
|
+
harness: string;
|
|
19
|
+
status: "passed" | "failed" | "would" | "skipped";
|
|
20
|
+
command?: string | undefined;
|
|
21
|
+
args?: readonly string[] | undefined;
|
|
22
|
+
detail?: string | undefined;
|
|
23
|
+
error?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function cmdPluginInstall(
|
|
27
|
+
runtime: Runtime,
|
|
28
|
+
options: PluginInstallOptions,
|
|
29
|
+
): Promise<number> {
|
|
30
|
+
const repoRoot = findKxmRepoRoot(import.meta.url);
|
|
31
|
+
const env = runtime.env.PATH ? runtime.env : { ...process.env, ...runtime.env };
|
|
32
|
+
const inventory = probeHarnesses({ env });
|
|
33
|
+
|
|
34
|
+
const isExplicit = Boolean(options.claude || options.omp || options.pi);
|
|
35
|
+
const requestedHarnesses: PluginHarnessId[] = [];
|
|
36
|
+
if (options.pi) requestedHarnesses.push("pi");
|
|
37
|
+
if (options.omp) requestedHarnesses.push("omp");
|
|
38
|
+
if (options.claude) requestedHarnesses.push("claude");
|
|
39
|
+
|
|
40
|
+
const targetHarnesses: readonly PluginHarnessId[] = isExplicit
|
|
41
|
+
? requestedHarnesses
|
|
42
|
+
: PLUGIN_SUPPORTED_HARNESSES;
|
|
43
|
+
|
|
44
|
+
const results: PluginInstallHarnessResult[] = [];
|
|
45
|
+
|
|
46
|
+
for (const harnessId of targetHarnesses) {
|
|
47
|
+
const status = inventory.harnesses.find((h) => h.id === harnessId);
|
|
48
|
+
const detected = Boolean(status?.detected && status?.command);
|
|
49
|
+
|
|
50
|
+
if (!detected) {
|
|
51
|
+
if (isExplicit) {
|
|
52
|
+
results.push({
|
|
53
|
+
harness: harnessId,
|
|
54
|
+
status: "failed",
|
|
55
|
+
error: "harness_not_detected",
|
|
56
|
+
detail: `${harnessId} harness is not installed or not found on PATH`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const command = status!.command!;
|
|
63
|
+
|
|
64
|
+
if (harnessId === "pi") {
|
|
65
|
+
const args = ["install", repoRoot, "-a"];
|
|
66
|
+
if (runtime.dryRun) {
|
|
67
|
+
results.push({
|
|
68
|
+
harness: "pi",
|
|
69
|
+
status: "would",
|
|
70
|
+
command,
|
|
71
|
+
args,
|
|
72
|
+
detail: `would install kxm plugin into pi: ${command} ${args.join(" ")}`,
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
const run = runtime.io.spawnSync
|
|
76
|
+
? runtime.io.spawnSync(command, args)
|
|
77
|
+
: spawnSync(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
|
|
78
|
+
if (run.error || (run.status !== 0 && run.status !== null)) {
|
|
79
|
+
results.push({
|
|
80
|
+
harness: "pi",
|
|
81
|
+
status: "failed",
|
|
82
|
+
command,
|
|
83
|
+
args,
|
|
84
|
+
error: run.error?.message || "install_failed",
|
|
85
|
+
detail: (run.stderr || run.stdout || "pi install exited with error").trim(),
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
results.push({
|
|
89
|
+
harness: "pi",
|
|
90
|
+
status: "passed",
|
|
91
|
+
command,
|
|
92
|
+
args,
|
|
93
|
+
detail: "installed kxm plugin into pi",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} else if (harnessId === "omp") {
|
|
98
|
+
const args = ["plugin", "install", repoRoot];
|
|
99
|
+
if (runtime.dryRun) {
|
|
100
|
+
results.push({
|
|
101
|
+
harness: "omp",
|
|
102
|
+
status: "would",
|
|
103
|
+
command,
|
|
104
|
+
args,
|
|
105
|
+
detail: `would install kxm plugin into omp: ${command} ${args.join(" ")}`,
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
const run = runtime.io.spawnSync
|
|
109
|
+
? runtime.io.spawnSync(command, args)
|
|
110
|
+
: spawnSync(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
|
|
111
|
+
if (run.error || (run.status !== 0 && run.status !== null)) {
|
|
112
|
+
results.push({
|
|
113
|
+
harness: "omp",
|
|
114
|
+
status: "failed",
|
|
115
|
+
command,
|
|
116
|
+
args,
|
|
117
|
+
error: run.error?.message || "install_failed",
|
|
118
|
+
detail: (run.stderr || run.stdout || "omp plugin install exited with error").trim(),
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
results.push({
|
|
122
|
+
harness: "omp",
|
|
123
|
+
status: "passed",
|
|
124
|
+
command,
|
|
125
|
+
args,
|
|
126
|
+
detail: "installed kxm plugin into omp",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else if (harnessId === "claude") {
|
|
131
|
+
const marketplaceArgs = ["plugin", "marketplace", "add", repoRoot];
|
|
132
|
+
const installArgs = ["plugin", "install", "kxm", "-y"];
|
|
133
|
+
if (runtime.dryRun) {
|
|
134
|
+
results.push({
|
|
135
|
+
harness: "claude",
|
|
136
|
+
status: "would",
|
|
137
|
+
command,
|
|
138
|
+
args: installArgs,
|
|
139
|
+
detail: `would add marketplace and install kxm plugin into claude: ${command} ${installArgs.join(" ")}`,
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
if (runtime.io.spawnSync) {
|
|
143
|
+
runtime.io.spawnSync(command, marketplaceArgs);
|
|
144
|
+
} else {
|
|
145
|
+
spawnSync(command, marketplaceArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
|
|
146
|
+
}
|
|
147
|
+
const run = runtime.io.spawnSync
|
|
148
|
+
? runtime.io.spawnSync(command, installArgs)
|
|
149
|
+
: spawnSync(command, installArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
|
|
150
|
+
if (run.error || (run.status !== 0 && run.status !== null)) {
|
|
151
|
+
results.push({
|
|
152
|
+
harness: "claude",
|
|
153
|
+
status: "failed",
|
|
154
|
+
command,
|
|
155
|
+
args: installArgs,
|
|
156
|
+
error: run.error?.message || "install_failed",
|
|
157
|
+
detail: (run.stderr || run.stdout || "claude plugin install exited with error").trim(),
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
results.push({
|
|
161
|
+
harness: "claude",
|
|
162
|
+
status: "passed",
|
|
163
|
+
command,
|
|
164
|
+
args: installArgs,
|
|
165
|
+
detail: "installed kxm plugin into claude",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (results.length === 0) {
|
|
173
|
+
const errorMsg = "no supported harnesses (pi, omp, claude) detected on PATH";
|
|
174
|
+
print(
|
|
175
|
+
runtime.io,
|
|
176
|
+
runtime.json,
|
|
177
|
+
{ ok: false, command: "plugin install", error: "no_harnesses_detected", results: [] },
|
|
178
|
+
errorMsg,
|
|
179
|
+
);
|
|
180
|
+
return 1;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const ok = results.every((r) => r.status === "passed" || r.status === "would");
|
|
184
|
+
const summary = results
|
|
185
|
+
.map((r) => `${r.harness}: ${r.status}${r.detail ? ` (${r.detail})` : ""}`)
|
|
186
|
+
.join("\n");
|
|
187
|
+
|
|
188
|
+
print(
|
|
189
|
+
runtime.io,
|
|
190
|
+
runtime.json,
|
|
191
|
+
{
|
|
192
|
+
ok,
|
|
193
|
+
command: "plugin install",
|
|
194
|
+
...(runtime.dryRun ? { dryRun: true } : {}),
|
|
195
|
+
results,
|
|
196
|
+
},
|
|
197
|
+
summary,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return ok ? 0 : 1;
|
|
201
|
+
}
|
package/plugins/kxm/src/cli.ts
CHANGED
|
@@ -120,6 +120,8 @@ cmdBackup,
|
|
|
120
120
|
cmdKxmRuntime,
|
|
121
121
|
} from "./cli/project.ts";
|
|
122
122
|
|
|
123
|
+
import { cmdPluginInstall } from "./cli/plugins.ts";
|
|
124
|
+
|
|
123
125
|
import {
|
|
124
126
|
cmdStatus,
|
|
125
127
|
cmdDash,
|
|
@@ -185,6 +187,7 @@ const USAGE_ERROR_CODES = new Set([
|
|
|
185
187
|
*/
|
|
186
188
|
const DRY_RUN_COMMANDS: ReadonlySet<string> = new Set([
|
|
187
189
|
"init", "backup", "restore", "run", "explain", "suggest", "update", "dash", "completion", "completion install",
|
|
190
|
+
"plugin install", "plugin",
|
|
188
191
|
"runs status", "runs drive", "runs receipt", "runs cancel", "runs list",
|
|
189
192
|
"tenant status", "models inventory-refresh", "harness list", "auth token",
|
|
190
193
|
"routes list", "routes count", "routes admit", "routes disable",
|
|
@@ -506,6 +509,17 @@ function createProgram(ctx: CliContext, result: { code: number }): Command {
|
|
|
506
509
|
harnessCmd.helpCommand("help", "Show harness help");
|
|
507
510
|
addGlobalOptions(harnessCmd.command("list").description("Show installed harnesses, auth, and native updaters")).action(bind(cmdHarnessList));
|
|
508
511
|
|
|
512
|
+
const pluginCmd = addGlobalOptions(program.command("plugin").description("Install or manage KXM harness plugins"));
|
|
513
|
+
pluginCmd.helpCommand("help", "Show plugin help");
|
|
514
|
+
addGlobalOptions(pluginCmd.command("install", { isDefault: true }).description("Install the KXM plugin into discovered or specified harnesses"))
|
|
515
|
+
.option("--all", "Install plugin for all discovered harnesses (default)")
|
|
516
|
+
.option("--claude", "Install plugin for Claude Code")
|
|
517
|
+
.option("--omp", "Install plugin for Oh My Pi (OMP)")
|
|
518
|
+
.option("--pi", "Install plugin for Pi")
|
|
519
|
+
.action(async function pluginInstallAction(this: Command, options: { all?: boolean; claude?: boolean; omp?: boolean; pi?: boolean }) {
|
|
520
|
+
result.code = await cmdPluginInstall(runtimeFrom(ctx, this), options);
|
|
521
|
+
});
|
|
522
|
+
|
|
509
523
|
const authCmd = addGlobalOptions(program.command("auth").description("Manage credentials, tokens, and authorization"));
|
|
510
524
|
authCmd.helpCommand("help", "Show auth help");
|
|
511
525
|
addGlobalOptions(authCmd.command("token").description("Inspect, issue, or clear local disk session tokens"))
|
|
@@ -508,6 +508,7 @@ const READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
|
508
508
|
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
509
509
|
// is not enough: extensions and hooks can still run with their own permissions.
|
|
510
510
|
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
511
|
+
omp: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
511
512
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
512
513
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
513
514
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -537,6 +538,7 @@ export function oneShotReadOnlyArgs(harness: string): readonly string[] | undefi
|
|
|
537
538
|
*/
|
|
538
539
|
const WRITER_ONESHOT_ARGS = Object.freeze({
|
|
539
540
|
pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
541
|
+
omp: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
540
542
|
grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"]),
|
|
541
543
|
});
|
|
542
544
|
|
|
@@ -568,6 +570,26 @@ export const BUILTIN_HARNESSES: readonly HarnessCatalogEntry[] = Object.freeze([
|
|
|
568
570
|
usageParser: parsePiOneShotUsage,
|
|
569
571
|
},
|
|
570
572
|
},
|
|
573
|
+
{
|
|
574
|
+
id: "omp",
|
|
575
|
+
label: "Oh My Pi",
|
|
576
|
+
default: false,
|
|
577
|
+
mode: "headless",
|
|
578
|
+
commands: ["omp"],
|
|
579
|
+
versionArgs: ["--version"],
|
|
580
|
+
authArgs: ["models", "--json"],
|
|
581
|
+
update: {
|
|
582
|
+
self: ["update"],
|
|
583
|
+
extensions: ["plugin", "upgrade"],
|
|
584
|
+
models: ["models", "refresh"],
|
|
585
|
+
},
|
|
586
|
+
oneShot: {
|
|
587
|
+
argv: ["-p", ...oneShotReadOnlyArgs("omp")!, "--mode", "json"],
|
|
588
|
+
promptVia: "arg",
|
|
589
|
+
outputFormat: "json",
|
|
590
|
+
usageParser: parsePiOneShotUsage,
|
|
591
|
+
},
|
|
592
|
+
},
|
|
571
593
|
{
|
|
572
594
|
id: "claude",
|
|
573
595
|
label: "Claude Code",
|
|
@@ -857,6 +879,16 @@ function interpretAuth(id: string, result: HarnessCommandResult): Pick<HarnessSt
|
|
|
857
879
|
if (commandSucceeded(result) && lines.some((line) => line === GROK_LOGIN_LINE)) return { authenticated: true, issues: [] };
|
|
858
880
|
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
859
881
|
}
|
|
882
|
+
if (id === "omp") {
|
|
883
|
+
if (commandSucceeded(result)) {
|
|
884
|
+
const payload = parseJsonObject(result.stdout);
|
|
885
|
+
if (payload && Array.isArray(payload.models) && payload.models.length > 0) {
|
|
886
|
+
return { authenticated: true, issues: [] };
|
|
887
|
+
}
|
|
888
|
+
if (commandSucceeded(result)) return { authenticated: true, issues: [] };
|
|
889
|
+
}
|
|
890
|
+
return { authenticated: null, issues: ["auth_unparsed"] };
|
|
891
|
+
}
|
|
860
892
|
if (id === "agy") {
|
|
861
893
|
const text = `${result.stdout}\n${result.stderr}`;
|
|
862
894
|
if (!commandSucceeded(result) || !text.trim()) {
|
|
@@ -1118,7 +1150,7 @@ export function validateHarnessModelPair(
|
|
|
1118
1150
|
return { valid: true };
|
|
1119
1151
|
}
|
|
1120
1152
|
|
|
1121
|
-
if (harnessId === "pi") {
|
|
1153
|
+
if (harnessId === "pi" || harnessId === "omp") {
|
|
1122
1154
|
if (!provider && model?.includes("/")) {
|
|
1123
1155
|
const idx = model.indexOf("/");
|
|
1124
1156
|
provider = model.slice(0, idx).toLowerCase();
|
|
@@ -1127,17 +1159,17 @@ export function validateHarnessModelPair(
|
|
|
1127
1159
|
if (!provider) return { valid: true };
|
|
1128
1160
|
const blocked = (message: string): HarnessModelValidation => ({ valid: false, issue: "pi_native_impersonation_blocked", message });
|
|
1129
1161
|
if (PI_NATIVE_BRAKE_PROVIDERS.includes(provider)) {
|
|
1130
|
-
return blocked(
|
|
1162
|
+
return blocked(`${harnessId} must not impersonate native provider ${provider}; use the native harness`);
|
|
1131
1163
|
}
|
|
1132
1164
|
if (provider === "antigravity" && model && PI_ANTIGRAVITY_MODEL_ID.test(model)) return { valid: true };
|
|
1133
1165
|
const owned = Object.hasOwn(PI_VENDOR_OWNED_PROVIDERS, provider) ? PI_VENDOR_OWNED_PROVIDERS[provider] : undefined;
|
|
1134
1166
|
if (owned) {
|
|
1135
|
-
return blocked(
|
|
1167
|
+
return blocked(`${harnessId} provider ${provider} bills native vendor ${owned} for ${model ?? "this model"}; use the native harness`);
|
|
1136
1168
|
}
|
|
1137
1169
|
const segment = model?.includes("/") ? model.slice(0, model.indexOf("/")).toLowerCase() : undefined;
|
|
1138
1170
|
const vendor = segment && Object.hasOwn(PI_VENDOR_SEGMENT_ALIASES, segment) ? PI_VENDOR_SEGMENT_ALIASES[segment] : segment;
|
|
1139
1171
|
if (vendor && PI_NATIVE_BRAKE_PROVIDERS.includes(vendor)) {
|
|
1140
|
-
return blocked(
|
|
1172
|
+
return blocked(`${harnessId} must not bill native vendor ${vendor} through ${provider}; use the native harness`);
|
|
1141
1173
|
}
|
|
1142
1174
|
return { valid: true };
|
|
1143
1175
|
}
|
|
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
|
|
|
11
11
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
12
12
|
import { sessionTokenFixHint } from "./session-token-hint.ts";
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.7.
|
|
14
|
+
const VERSION = "0.7.119";
|
|
15
15
|
const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
16
16
|
const inbox = new Map<string, MessageRecord>();
|
|
17
17
|
const notifiedInbox = new Set<string>();
|
|
@@ -92,6 +92,12 @@ export function claudeBridgeRegistrationNotice(
|
|
|
92
92
|
report: ClaudeBridgeRegistration | undefined,
|
|
93
93
|
pi?: object,
|
|
94
94
|
): { message: string; type: "warning" } | undefined {
|
|
95
|
+
if (report?.warning) {
|
|
96
|
+
return {
|
|
97
|
+
message: boundedRegistrationWarning(report.warning),
|
|
98
|
+
type: "warning",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
95
101
|
const conflict = Boolean(report?.conflict) || (pi ? claudeBridgeStandaloneToolsPresent(pi) : false);
|
|
96
102
|
if (!conflict) return undefined;
|
|
97
103
|
return {
|
|
@@ -112,13 +118,23 @@ export function registerClaudeBridgeProvider(pi: ExtensionAPI): ClaudeBridgeRegi
|
|
|
112
118
|
return { registered: false, conflict: false };
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
try {
|
|
122
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
123
|
+
name: PROVIDER_NAME,
|
|
124
|
+
baseUrl: PROVIDER_ID,
|
|
125
|
+
apiKey: PROVIDER_ID,
|
|
126
|
+
api: CLAUDE_BRIDGE_API,
|
|
127
|
+
models: registeredClaudeBridgeModels(),
|
|
128
|
+
streamSimple: streamClaudeBridge,
|
|
129
|
+
} as unknown as Parameters<ExtensionAPI["registerProvider"]>[1]);
|
|
130
|
+
|
|
131
|
+
return { registered: true, conflict: false };
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
134
|
+
return {
|
|
135
|
+
registered: false,
|
|
136
|
+
conflict: false,
|
|
137
|
+
warning: `kxm: claude-bridge provider registration failed (${reason}). Configure apiKey or ensure provider auth is valid.`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
124
140
|
}
|