@modootoday/pmcp 0.15.0 → 0.16.1
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/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +22 -11
- package/dist/cli.js +472 -78
- package/dist/{embed-CVCEz2FH.d.ts → embed-DRROSD3F.d.ts} +1 -1
- package/dist/http.d.ts +5 -4
- package/dist/http.js +4 -2
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/{server-DAcT89iv.d.ts → server-VWSY3Shw.d.ts} +2 -3
- package/dist/stdio.d.ts +1 -1
- package/dist/stdio.js +2 -2
- package/dist/tui-worker.js +271 -12
- package/docs/hosting.md +13 -0
- package/docs/terminal-workspace.md +102 -5
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugins/pmcp/.claude-plugin/plugin.json +1 -1
- package/plugins/pmcp/gemini-extension.json +1 -1
- package/plugins/pmcp/plugin.json +1 -1
package/dist/http.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { S as SkillServerOptions, E as Embedder, a as SkillEntry } from './server-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { S as SkillServerOptions, E as Embedder, a as SkillEntry } from './server-VWSY3Shw.js';
|
|
2
|
+
export { C as CatalogOptions, R as Runtime, r as readCatalog, v as verifiedRuntimesOf } from './server-VWSY3Shw.js';
|
|
3
|
+
import { L as LoadEmbedderOptions } from './embed-DRROSD3F.js';
|
|
4
|
+
export { l as loadEmbedder } from './embed-DRROSD3F.js';
|
|
4
5
|
import { M as MailboxService } from './service-qpCsOXkQ.js';
|
|
5
6
|
export { D as DEFAULT_MODEL_ID } from './encoder-DGRy9skC.js';
|
|
6
7
|
import 'zod';
|
|
@@ -80,4 +81,4 @@ interface SkillHttpHandler {
|
|
|
80
81
|
}
|
|
81
82
|
declare function createSkillHttpHandler(options: SkillHttpOptions): SkillHttpHandler;
|
|
82
83
|
|
|
83
|
-
export { DEFAULT_MAX_REQUEST_BYTES, DEFAULT_TIERS, Embedder, type RateLimitOptions, type SkillCaller, type SkillHttpHandler, type SkillHttpOptions, type SkillHttpSemanticOptions, createSkillHttpHandler, visibleTo };
|
|
84
|
+
export { DEFAULT_MAX_REQUEST_BYTES, DEFAULT_TIERS, Embedder, type RateLimitOptions, type SkillCaller, SkillEntry, type SkillHttpHandler, type SkillHttpOptions, type SkillHttpSemanticOptions, createSkillHttpHandler, visibleTo };
|
package/dist/http.js
CHANGED
|
@@ -694,7 +694,7 @@ function withoutBody({
|
|
|
694
694
|
|
|
695
695
|
// src/intent.ts
|
|
696
696
|
function normaliseIntent(intent) {
|
|
697
|
-
return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
697
|
+
return intent.normalize("NFC").toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
698
698
|
}
|
|
699
699
|
|
|
700
700
|
// src/embed.ts
|
|
@@ -2294,7 +2294,7 @@ function registerMailboxTools(server, service) {
|
|
|
2294
2294
|
|
|
2295
2295
|
// src/mcp.ts
|
|
2296
2296
|
var SERVER_NAME = "pmcp";
|
|
2297
|
-
var SERVER_VERSION = "0.
|
|
2297
|
+
var SERVER_VERSION = "0.16.1";
|
|
2298
2298
|
function registerSkillTools(server, tools) {
|
|
2299
2299
|
registerCatalogTool(server, tools);
|
|
2300
2300
|
registerFindTool(server, tools);
|
|
@@ -2426,6 +2426,8 @@ export {
|
|
|
2426
2426
|
DEFAULT_TIERS,
|
|
2427
2427
|
createSkillHttpHandler,
|
|
2428
2428
|
loadEmbedder,
|
|
2429
|
+
readCatalog,
|
|
2430
|
+
verifiedRuntimesOf,
|
|
2429
2431
|
visibleTo
|
|
2430
2432
|
};
|
|
2431
2433
|
//# sourceMappingURL=http.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I as IntentCache,
|
|
2
|
-
export { A as ASSET_KINDS,
|
|
3
|
-
import { L as LoadEmbedderOptions } from './embed-
|
|
4
|
-
export { E as EntryVectorStore, e as encodeEntries, l as loadEmbedder, o as openEntryVectors } from './embed-
|
|
1
|
+
import { I as IntentCache, b as Ranking, S as SkillServerOptions, E as Embedder, C as CatalogOptions, a as SkillEntry, M as MetadataValue, c as SkillTools } from './server-VWSY3Shw.js';
|
|
2
|
+
export { A as ASSET_KINDS, d as AssetKind, B as BundleFile, e as CATALOG_PAGE_MAX, f as CATALOG_PAGE_SIZE, g as COMPATIBILITY_MAX, h as CallResponse, i as CatalogItem, j as CatalogResponse, k as Classified, D as DEFAULT_FIND_LIMIT, l as DESCRIBE_FILE_LIMIT, m as DESCRIPTION_MAX, F as FileKind, n as FindOptions, o as FindResult, p as IntentCacheStats, K as KindFilter, q as MAX_SKILL_DEPTH, s as Match, t as MetadataLeaf, N as NAME_MAX, u as READ_BUDGET_BYTES, w as RUNTIMES, x as ReadResult, R as Runtime, y as SKIPPED_DIRS, z as SkillBundle, G as SkillFile, H as SkillFileContent, J as SkillFileListing, L as SkillFilters, O as bundleResponseSchema, P as bundleSkill, Q as catalogResponseSchema, T as classify, U as cosine, V as createSkillTools, W as describeResponseSchema, X as detectRuntime, Y as displayPath, Z as find, _ as findResultSchema, $ as isUtf8Text, a0 as kindOf, a1 as lexicalScore, a2 as listSkillFiles, a3 as mimeOf, a4 as normaliseIntent, a5 as parseSkillFrontmatter, a6 as readBody, r as readCatalog, a7 as readFrontmatter, a8 as readSkillFile, a9 as runtimeOfClient, aa as specProblems, v as verifiedRuntimesOf, ab as withoutBody } from './server-VWSY3Shw.js';
|
|
3
|
+
import { L as LoadEmbedderOptions } from './embed-DRROSD3F.js';
|
|
4
|
+
export { E as EntryVectorStore, e as encodeEntries, l as loadEmbedder, o as openEntryVectors } from './embed-DRROSD3F.js';
|
|
5
5
|
export { D as DEFAULT_MODEL_ID, a as EmbeddingModel, b as EncoderDtype, E as EncoderOptions, c as EncoderSpec, R as ResolvedEncoder, e as embeddingModels, d as encoderSpec, r as resolveEncoder } from './encoder-DGRy9skC.js';
|
|
6
6
|
import { P as ProjectConfig } from './config-BkSpjUG6.js';
|
|
7
7
|
export { C as CONFIG_FILE, a as CatalogSection, b as ConfigError, E as EmbeddingSection, f as findConfig, r as readConfig } from './config-BkSpjUG6.js';
|
|
@@ -275,7 +275,7 @@ declare function resolveSkillFileUri(uri: string, entries: readonly SkillEntry[]
|
|
|
275
275
|
|
|
276
276
|
/** Reported to the host on connect. */
|
|
277
277
|
declare const SERVER_NAME = "pmcp";
|
|
278
|
-
declare const SERVER_VERSION = "0.
|
|
278
|
+
declare const SERVER_VERSION = "0.16.1";
|
|
279
279
|
/**
|
|
280
280
|
* Registers the skill_* tools on a server.
|
|
281
281
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/intent.ts
|
|
2
2
|
function normaliseIntent(intent) {
|
|
3
|
-
return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
3
|
+
return intent.normalize("NFC").toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/storage/sqlite.ts
|
|
@@ -4679,7 +4679,7 @@ function registerMailboxTools(server, service) {
|
|
|
4679
4679
|
|
|
4680
4680
|
// src/mcp.ts
|
|
4681
4681
|
var SERVER_NAME = "pmcp";
|
|
4682
|
-
var SERVER_VERSION = "0.
|
|
4682
|
+
var SERVER_VERSION = "0.16.1";
|
|
4683
4683
|
function registerSkillTools(server, tools) {
|
|
4684
4684
|
registerCatalogTool(server, tools);
|
|
4685
4685
|
registerFindTool(server, tools);
|
|
@@ -144,8 +144,7 @@ interface IntentCache {
|
|
|
144
144
|
close(): void;
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
148
|
-
* key has to survive a caller writing "How do I wire X?" and "how do i wire x".
|
|
147
|
+
* Canonically equivalent Unicode must share the same query and cache key.
|
|
149
148
|
*/
|
|
150
149
|
declare function normaliseIntent(intent: string): string;
|
|
151
150
|
|
|
@@ -541,4 +540,4 @@ declare function createSkillTools(options: SkillServerOptions): {
|
|
|
541
540
|
};
|
|
542
541
|
type SkillTools = ReturnType<typeof createSkillTools>;
|
|
543
542
|
|
|
544
|
-
export {
|
|
543
|
+
export { isUtf8Text as $, ASSET_KINDS as A, type BundleFile as B, type CatalogOptions as C, DEFAULT_FIND_LIMIT as D, type Embedder as E, type FileKind as F, type SkillFile as G, type SkillFileContent as H, type IntentCache as I, type SkillFileListing as J, type KindFilter as K, type SkillFilters as L, type MetadataValue as M, NAME_MAX as N, bundleResponseSchema as O, bundleSkill as P, catalogResponseSchema as Q, type Runtime as R, type SkillServerOptions as S, classify as T, cosine as U, createSkillTools as V, describeResponseSchema as W, detectRuntime as X, displayPath as Y, find as Z, findResultSchema as _, type SkillEntry as a, kindOf as a0, lexicalScore as a1, listSkillFiles as a2, mimeOf as a3, normaliseIntent as a4, frontmatterObject as a5, readBody as a6, readFrontmatter as a7, readSkillFile as a8, runtimeOfClient as a9, specProblems as aa, withoutBody as ab, type Ranking as b, type SkillTools as c, type AssetKind as d, CATALOG_PAGE_MAX as e, CATALOG_PAGE_SIZE as f, COMPATIBILITY_MAX as g, type CallResponse as h, type CatalogItem as i, type CatalogResponse as j, type Classified as k, DESCRIBE_FILE_LIMIT as l, DESCRIPTION_MAX as m, type FindOptions as n, type FindResult as o, type IntentCacheStats as p, MAX_SKILL_DEPTH as q, readCatalog as r, type Match as s, type MetadataLeaf as t, READ_BUDGET_BYTES as u, verifiedRuntimesOf as v, RUNTIMES as w, type ReadResult as x, SKIPPED_DIRS as y, type SkillBundle as z };
|
package/dist/stdio.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
3
|
-
import { S as SkillServerOptions, C as CatalogOptions } from './server-
|
|
3
|
+
import { S as SkillServerOptions, C as CatalogOptions } from './server-VWSY3Shw.js';
|
|
4
4
|
import { M as MailboxService } from './service-qpCsOXkQ.js';
|
|
5
5
|
import { M as MailboxAttachmentOptions } from './attachment-tU5FZrgZ.js';
|
|
6
6
|
import 'zod';
|
package/dist/stdio.js
CHANGED
|
@@ -1050,7 +1050,7 @@ import { createHash as createHash2 } from "crypto";
|
|
|
1050
1050
|
|
|
1051
1051
|
// src/intent.ts
|
|
1052
1052
|
function normaliseIntent(intent) {
|
|
1053
|
-
return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
1053
|
+
return intent.normalize("NFC").toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
|
|
1054
1054
|
}
|
|
1055
1055
|
|
|
1056
1056
|
// src/find.ts
|
|
@@ -1941,7 +1941,7 @@ function registerMailboxTools(server, service) {
|
|
|
1941
1941
|
|
|
1942
1942
|
// src/mcp.ts
|
|
1943
1943
|
var SERVER_NAME = "pmcp";
|
|
1944
|
-
var SERVER_VERSION = "0.
|
|
1944
|
+
var SERVER_VERSION = "0.16.1";
|
|
1945
1945
|
function registerSkillTools(server, tools) {
|
|
1946
1946
|
registerCatalogTool(server, tools);
|
|
1947
1947
|
registerFindTool(server, tools);
|
package/dist/tui-worker.js
CHANGED
|
@@ -64,6 +64,11 @@ var tuiOptions = [
|
|
|
64
64
|
},
|
|
65
65
|
{ name: "rows", describe: "Terminal rows (12\u20131000)", placeholder: "<count>" },
|
|
66
66
|
{ name: "index", describe: "Worker dock index", placeholder: "<count>" },
|
|
67
|
+
{
|
|
68
|
+
name: "memory-mb",
|
|
69
|
+
describe: "Worker memory reservation",
|
|
70
|
+
placeholder: "<count>"
|
|
71
|
+
},
|
|
67
72
|
{
|
|
68
73
|
name: "confirm-session",
|
|
69
74
|
describe: "Exact target ID for stop",
|
|
@@ -76,7 +81,7 @@ var tuiOptions = [
|
|
|
76
81
|
},
|
|
77
82
|
{
|
|
78
83
|
name: "runtime",
|
|
79
|
-
describe: "
|
|
84
|
+
describe: "Native runtime ID for main launch or worker start",
|
|
80
85
|
placeholder: "<runtime>"
|
|
81
86
|
},
|
|
82
87
|
{
|
|
@@ -98,6 +103,8 @@ var tuiOptions = [
|
|
|
98
103
|
var allowed = {
|
|
99
104
|
launch: ["config", "runtime", "yes", "new"],
|
|
100
105
|
plan: ["config", "runtime"],
|
|
106
|
+
recovery: ["config"],
|
|
107
|
+
"start-worker": ["config", "view", "runtime", "memory-mb"],
|
|
101
108
|
doctor: [],
|
|
102
109
|
create: ["group-file", "columns", "rows", "config", "actor-file"],
|
|
103
110
|
inspect: ["view"],
|
|
@@ -115,6 +122,7 @@ var allowed = {
|
|
|
115
122
|
close: ["view"],
|
|
116
123
|
dismiss: ["view"],
|
|
117
124
|
management: ["view"],
|
|
125
|
+
"worker-menu": ["view"],
|
|
118
126
|
check: ["view"]
|
|
119
127
|
};
|
|
120
128
|
function parseTui(args) {
|
|
@@ -139,7 +147,23 @@ function parseTui(args) {
|
|
|
139
147
|
return Number(value);
|
|
140
148
|
};
|
|
141
149
|
if (action === "doctor") return { action, input: {} };
|
|
142
|
-
if (action === "
|
|
150
|
+
if (action === "start-worker") {
|
|
151
|
+
const config = one(args, "config");
|
|
152
|
+
const viewFile = one(args, "view");
|
|
153
|
+
if (config && viewFile)
|
|
154
|
+
throw new ArgumentError("Choose --config or --view for worker start");
|
|
155
|
+
return {
|
|
156
|
+
action,
|
|
157
|
+
viewFile,
|
|
158
|
+
input: {},
|
|
159
|
+
startup: {
|
|
160
|
+
config,
|
|
161
|
+
runtime: required("runtime"),
|
|
162
|
+
memoryMb: number("memory-mb")
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (action === "launch" || action === "plan" || action === "recovery")
|
|
143
167
|
return {
|
|
144
168
|
action,
|
|
145
169
|
input: {},
|
|
@@ -3427,13 +3451,19 @@ var CommonHarness = class {
|
|
|
3427
3451
|
// src/tui/application/feedback.ts
|
|
3428
3452
|
import { join as join20 } from "path";
|
|
3429
3453
|
var hints = {
|
|
3430
|
-
startup_requires_recovery: "
|
|
3454
|
+
startup_requires_recovery: "Run pmcp tui recovery for saved ownership and exact inspection commands. No runtime was restarted.",
|
|
3431
3455
|
startup_group_paused: "The saved group is paused. Resume it explicitly through the harness CLI before reopening.",
|
|
3432
3456
|
startup_main_unavailable: "Inspect the saved main and recovery state. Stop the old group explicitly before using pmcp tui --new.",
|
|
3433
3457
|
startup_busy: "Another workspace launch is running. Wait and retry.",
|
|
3434
3458
|
startup_runtime_conflict: "The saved main uses another runtime. Stop the old group before creating a new workspace.",
|
|
3435
3459
|
workspace_already_running: "Reopen the existing workspace, or stop its exact owned group before using --new.",
|
|
3436
3460
|
runtime_executable_unavailable: "Install the selected native CLI on PATH, or choose an installed runtime with --runtime.",
|
|
3461
|
+
view_client_unavailable: "Open the view before using the worker menu, or run pmcp tui start-worker with --view and --runtime.",
|
|
3462
|
+
runtime_not_granted: "Choose a runtime granted by this owned group. Inspect the group before creating another workspace.",
|
|
3463
|
+
runtime_capacity_exceeded: "Stop an exact owned worker before adding another, or use the main session alone.",
|
|
3464
|
+
aggregate_memory_budget_exceeded: "Use fewer sessions or start a worker with an explicit smaller --memory-mb reservation.",
|
|
3465
|
+
group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
|
|
3466
|
+
startup_group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
|
|
3437
3467
|
control_connection_lost: "The native writer or lease disappeared. Inspect and release control before reconnecting.",
|
|
3438
3468
|
control_busy: "Another controller holds the lease. Release it explicitly before retrying.",
|
|
3439
3469
|
native_writer_active: "A native writer is still attached. Detach that writer before retrying.",
|
|
@@ -3489,6 +3519,14 @@ function phaseLabel(phase2) {
|
|
|
3489
3519
|
return labels[phase2];
|
|
3490
3520
|
}
|
|
3491
3521
|
|
|
3522
|
+
// src/tui/presentation/target.ts
|
|
3523
|
+
function targetStatus(view) {
|
|
3524
|
+
const role2 = view.target === view.mainId ? "MAIN" : "WORKER";
|
|
3525
|
+
const phase2 = view.phase === "read-only" ? "OBSERVE" : phaseLabel(view.phase);
|
|
3526
|
+
const input = view.phase === "controlled" ? "INPUT ON" : "INPUT OFF";
|
|
3527
|
+
return `${role2} ${view.target.slice(0, 8)} | ${phase2} | ${input}`;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3492
3530
|
// src/tui/adapters/tmux/connection.ts
|
|
3493
3531
|
import { spawn as spawn2 } from "child_process";
|
|
3494
3532
|
import { join as join21 } from "path";
|
|
@@ -3642,7 +3680,8 @@ function bindKeys(connection, view, file) {
|
|
|
3642
3680
|
["b", "mailbox"],
|
|
3643
3681
|
["d", "detach"],
|
|
3644
3682
|
["!", "dismiss"],
|
|
3645
|
-
["g", "management"]
|
|
3683
|
+
["g", "management"],
|
|
3684
|
+
["n", "worker-menu"]
|
|
3646
3685
|
])
|
|
3647
3686
|
connection.command([
|
|
3648
3687
|
"bind-key",
|
|
@@ -3663,7 +3702,7 @@ function bindKeys(connection, view, file) {
|
|
|
3663
3702
|
"bind-key",
|
|
3664
3703
|
"?",
|
|
3665
3704
|
"display-message",
|
|
3666
|
-
"m main; w/1-4 worker; c control; r release; h history; b mailbox; d detach; ! dismiss error; double Ctrl-g forwards native Ctrl-g"
|
|
3705
|
+
"m main; n add worker; w/1-4 worker; c control; r release; h history; b mailbox; d detach; ! dismiss error; double Ctrl-g forwards native Ctrl-g"
|
|
3667
3706
|
]);
|
|
3668
3707
|
connection.command([
|
|
3669
3708
|
"set-hook",
|
|
@@ -3863,7 +3902,7 @@ var TmuxView = class {
|
|
|
3863
3902
|
"set-option",
|
|
3864
3903
|
"-g",
|
|
3865
3904
|
"status-right",
|
|
3866
|
-
`${
|
|
3905
|
+
`${targetStatus(view)}${failure2} | Ctrl-g ?`
|
|
3867
3906
|
]);
|
|
3868
3907
|
}
|
|
3869
3908
|
input(pane, enabled) {
|
|
@@ -3886,6 +3925,32 @@ var TmuxView = class {
|
|
|
3886
3925
|
clients() {
|
|
3887
3926
|
return this.connection.command(["list-clients", "-F", "#{client_pid}"]);
|
|
3888
3927
|
}
|
|
3928
|
+
workerMenu(view) {
|
|
3929
|
+
const pane = view.workerPane ?? view.mainPane;
|
|
3930
|
+
this.connection.assertOwned(pane);
|
|
3931
|
+
const popup = [
|
|
3932
|
+
"tmux",
|
|
3933
|
+
"-S",
|
|
3934
|
+
this.connection.socket,
|
|
3935
|
+
"display-popup",
|
|
3936
|
+
"-E",
|
|
3937
|
+
"-t",
|
|
3938
|
+
pane,
|
|
3939
|
+
"-w",
|
|
3940
|
+
"90%",
|
|
3941
|
+
"-h",
|
|
3942
|
+
"80%",
|
|
3943
|
+
this.shell([
|
|
3944
|
+
view.node,
|
|
3945
|
+
view.worker,
|
|
3946
|
+
"worker-menu",
|
|
3947
|
+
"--view",
|
|
3948
|
+
this.stateFile,
|
|
3949
|
+
"--callback"
|
|
3950
|
+
])
|
|
3951
|
+
];
|
|
3952
|
+
this.connection.command(["run-shell", "-b", popup.map(quote).join(" ")]);
|
|
3953
|
+
}
|
|
3889
3954
|
copy(pane) {
|
|
3890
3955
|
this.connection.assertOwned(pane);
|
|
3891
3956
|
this.connection.command(["copy-mode", "-t", pane]);
|
|
@@ -3952,6 +4017,7 @@ function dockLines(snapshot) {
|
|
|
3952
4017
|
),
|
|
3953
4018
|
"",
|
|
3954
4019
|
"Ctrl-g w: observe worker",
|
|
4020
|
+
"Ctrl-g n: add worker",
|
|
3955
4021
|
"Ctrl-g m: return to main",
|
|
3956
4022
|
"Ctrl-g c: acquire control",
|
|
3957
4023
|
"Ctrl-g r: release control",
|
|
@@ -3963,7 +4029,7 @@ function dockLines(snapshot) {
|
|
|
3963
4029
|
];
|
|
3964
4030
|
}
|
|
3965
4031
|
function coordinationLines(snapshot, view) {
|
|
3966
|
-
const target = `TARGET ${
|
|
4032
|
+
const target = `TARGET ${safe(targetStatus(view))}`;
|
|
3967
4033
|
if (view.failure)
|
|
3968
4034
|
return [
|
|
3969
4035
|
target,
|
|
@@ -5717,6 +5783,11 @@ var handlers = {
|
|
|
5717
5783
|
close: closeView,
|
|
5718
5784
|
check: checkControl,
|
|
5719
5785
|
dismiss: ({ view, file }) => dismissFailure(file, view),
|
|
5786
|
+
"worker-menu": async (context) => {
|
|
5787
|
+
if (!context.renderer.clients()) throw new Error("view_client_unavailable");
|
|
5788
|
+
await releaseControl(context);
|
|
5789
|
+
context.renderer.workerMenu(context.view);
|
|
5790
|
+
},
|
|
5720
5791
|
management: async (context) => {
|
|
5721
5792
|
await releaseControl(context);
|
|
5722
5793
|
if (context.view.managementWindow) {
|
|
@@ -5850,6 +5921,70 @@ function runtime() {
|
|
|
5850
5921
|
return { node: realpathSync10(available.node), worker: bundled };
|
|
5851
5922
|
}
|
|
5852
5923
|
|
|
5924
|
+
// src/tui/startup/diagnostics.ts
|
|
5925
|
+
function remediation(tools, userManagerAvailable) {
|
|
5926
|
+
const steps = [];
|
|
5927
|
+
if (tools.platform !== "linux")
|
|
5928
|
+
steps.push(
|
|
5929
|
+
"Use a qualified Linux host for the terminal workspace. Other PMCP CLI features do not require this host profile."
|
|
5930
|
+
);
|
|
5931
|
+
const missing = ["tmux", "flock", "systemctl", "systemdRun"].filter(
|
|
5932
|
+
(name) => !tools[name]
|
|
5933
|
+
);
|
|
5934
|
+
if (missing.length)
|
|
5935
|
+
steps.push(
|
|
5936
|
+
`Install the missing tmux, util-linux or systemd tools for your distribution and add them to PATH: ${missing.join(", ")}.`
|
|
5937
|
+
);
|
|
5938
|
+
if (!tools.nodeSupported)
|
|
5939
|
+
steps.push(
|
|
5940
|
+
"Select Node.js 22 or later through your existing version manager and check node --version in this terminal."
|
|
5941
|
+
);
|
|
5942
|
+
if (tools.supported && !userManagerAvailable)
|
|
5943
|
+
steps.push(
|
|
5944
|
+
"Check systemctl --user status in your login session and restore access to its user manager before launching. Doctor does not start or repair services."
|
|
5945
|
+
);
|
|
5946
|
+
return steps;
|
|
5947
|
+
}
|
|
5948
|
+
function inspectTerminalHost() {
|
|
5949
|
+
const tools = dependencies();
|
|
5950
|
+
let userManagerAvailable = false;
|
|
5951
|
+
let reason;
|
|
5952
|
+
try {
|
|
5953
|
+
if (!tools.supported) throw new Error("tui_dependencies_unavailable");
|
|
5954
|
+
assertNativeHost();
|
|
5955
|
+
userManagerAvailable = true;
|
|
5956
|
+
} catch (error) {
|
|
5957
|
+
reason = error instanceof Error ? error.message : "native_host_unavailable";
|
|
5958
|
+
}
|
|
5959
|
+
const runtimes = Object.entries(runtimeExecutables).map(
|
|
5960
|
+
([id2, executable]) => {
|
|
5961
|
+
const path = locateExecutable(executable);
|
|
5962
|
+
return {
|
|
5963
|
+
id: id2,
|
|
5964
|
+
executable,
|
|
5965
|
+
installed: Boolean(path),
|
|
5966
|
+
...path ? { path } : {},
|
|
5967
|
+
nativeAcceptance: "not-assessed",
|
|
5968
|
+
authentication: "not-inspected"
|
|
5969
|
+
};
|
|
5970
|
+
}
|
|
5971
|
+
);
|
|
5972
|
+
return {
|
|
5973
|
+
schemaVersion: 1,
|
|
5974
|
+
ok: true,
|
|
5975
|
+
...tools,
|
|
5976
|
+
supported: tools.supported && userManagerAvailable,
|
|
5977
|
+
userManagerAvailable,
|
|
5978
|
+
...reason ? { reason } : {},
|
|
5979
|
+
runtimes,
|
|
5980
|
+
qualification: "Prerequisite availability only; native behavior and cgroup delegation require separate qualification.",
|
|
5981
|
+
remediation: remediation(tools, userManagerAvailable),
|
|
5982
|
+
nextCommands: ["pmcp tui plan", "pmcp tui recovery"],
|
|
5983
|
+
providerInvoked: false,
|
|
5984
|
+
authenticationInspected: false
|
|
5985
|
+
};
|
|
5986
|
+
}
|
|
5987
|
+
|
|
5853
5988
|
// src/tui/application/create.ts
|
|
5854
5989
|
import { join as join29 } from "path";
|
|
5855
5990
|
import { realpathSync as realpathSync11 } from "fs";
|
|
@@ -5928,10 +6063,9 @@ async function createView(input) {
|
|
|
5928
6063
|
|
|
5929
6064
|
// src/tui/application/invoke.ts
|
|
5930
6065
|
async function invokeTui(invocation) {
|
|
5931
|
-
if (
|
|
6066
|
+
if (["launch", "plan", "recovery", "start-worker"].includes(invocation.action))
|
|
5932
6067
|
throw new Error("startup_requires_cli_context");
|
|
5933
|
-
if (invocation.action === "doctor")
|
|
5934
|
-
return { schemaVersion: 1, ok: true, ...dependencies() };
|
|
6068
|
+
if (invocation.action === "doctor") return inspectTerminalHost();
|
|
5935
6069
|
if (invocation.action === "create") return createView(invocation.create);
|
|
5936
6070
|
if (invocation.action === "inspect")
|
|
5937
6071
|
return operate("inspect", invocation.viewFile);
|
|
@@ -6206,6 +6340,115 @@ function assertGroup(group, projectRoot, generation) {
|
|
|
6206
6340
|
throw new Error("stale_group_generation");
|
|
6207
6341
|
}
|
|
6208
6342
|
|
|
6343
|
+
// src/tui/application/worker-start.ts
|
|
6344
|
+
async function startViewWorker(file, runtime2, memoryMb) {
|
|
6345
|
+
const { view, common } = openView(file);
|
|
6346
|
+
if (view.state !== "open") throw new Error("view_closed");
|
|
6347
|
+
const observed = await common.snapshot();
|
|
6348
|
+
if (observed.group.state !== "ready") throw new Error("group_not_ready");
|
|
6349
|
+
const main = observed.sessions.find(
|
|
6350
|
+
(session) => session.id === view.mainId && session.role === "main"
|
|
6351
|
+
);
|
|
6352
|
+
if (!main?.alive) throw new Error("startup_main_unavailable");
|
|
6353
|
+
return common.call("session", "start", {
|
|
6354
|
+
input: { role: "worker", runtime: runtime2, memoryMb: memoryMb ?? main.memoryMb }
|
|
6355
|
+
});
|
|
6356
|
+
}
|
|
6357
|
+
|
|
6358
|
+
// src/cli/entry.ts
|
|
6359
|
+
function interactiveEntry(terminal) {
|
|
6360
|
+
if (!terminal.stdin || !terminal.stdout) return false;
|
|
6361
|
+
if (!terminal.term || terminal.term === "dumb") return false;
|
|
6362
|
+
return !terminal.ci || terminal.ci === "false" || terminal.ci === "0";
|
|
6363
|
+
}
|
|
6364
|
+
|
|
6365
|
+
// src/tui/startup/host.ts
|
|
6366
|
+
function terminalSupport() {
|
|
6367
|
+
try {
|
|
6368
|
+
if (!dependencies().supported)
|
|
6369
|
+
throw new Error("tui_dependencies_unavailable");
|
|
6370
|
+
assertNativeHost();
|
|
6371
|
+
planLayout(process.stdout.columns ?? 0, process.stdout.rows ?? 0);
|
|
6372
|
+
return { supported: true };
|
|
6373
|
+
} catch (error) {
|
|
6374
|
+
return {
|
|
6375
|
+
supported: false,
|
|
6376
|
+
reason: error instanceof Error ? error.message : "terminal_unavailable"
|
|
6377
|
+
};
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
function requireTerminal() {
|
|
6381
|
+
if (!interactiveEntry({
|
|
6382
|
+
stdin: process.stdin.isTTY === true,
|
|
6383
|
+
stdout: process.stdout.isTTY === true,
|
|
6384
|
+
term: process.env.TERM,
|
|
6385
|
+
ci: process.env.CI
|
|
6386
|
+
}))
|
|
6387
|
+
throw new Error("terminal_required");
|
|
6388
|
+
const support = terminalSupport();
|
|
6389
|
+
if (!support.supported) throw new Error(support.reason);
|
|
6390
|
+
}
|
|
6391
|
+
|
|
6392
|
+
// src/tui/presentation/runtime-choice.ts
|
|
6393
|
+
import { createInterface } from "readline/promises";
|
|
6394
|
+
async function chooseRuntime(prompt, choices, preferred) {
|
|
6395
|
+
if (!choices.length) throw new Error("runtime_executable_unavailable");
|
|
6396
|
+
const fallback = Math.max(choices.indexOf(preferred), 0);
|
|
6397
|
+
process.stderr.write(
|
|
6398
|
+
`${choices.map((runtime2, index) => `${index + 1}. ${runtime2}`).join("\n")}
|
|
6399
|
+
`
|
|
6400
|
+
);
|
|
6401
|
+
while (true) {
|
|
6402
|
+
const answer = (await prompt.question(
|
|
6403
|
+
`Runtime [1-${choices.length}, Enter=${fallback + 1}, q=cancel] `
|
|
6404
|
+
)).trim();
|
|
6405
|
+
if (answer === "q") return void 0;
|
|
6406
|
+
if (answer === "") return choices[fallback];
|
|
6407
|
+
if (!/^\d+$/.test(answer)) continue;
|
|
6408
|
+
const index = Number(answer) - 1;
|
|
6409
|
+
if (Number.isSafeInteger(index) && index >= 0 && index < choices.length)
|
|
6410
|
+
return choices[index];
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
async function confirmRuntimeStart(prompt, runtime2) {
|
|
6414
|
+
const answer = await prompt.question(
|
|
6415
|
+
`Start ${runtime2}? [Enter=start, q=cancel] `
|
|
6416
|
+
);
|
|
6417
|
+
return answer.trim() === "";
|
|
6418
|
+
}
|
|
6419
|
+
function terminalPrompt() {
|
|
6420
|
+
return createInterface({ input: process.stdin, output: process.stderr });
|
|
6421
|
+
}
|
|
6422
|
+
|
|
6423
|
+
// src/tui/presentation/worker-menu.ts
|
|
6424
|
+
async function runWorkerMenu(file) {
|
|
6425
|
+
requireTerminal();
|
|
6426
|
+
const { view, common } = openView(file);
|
|
6427
|
+
if (view.state !== "open") throw new Error("view_closed");
|
|
6428
|
+
const observed = await common.snapshot();
|
|
6429
|
+
const group = observed.group;
|
|
6430
|
+
if (group.state !== "ready") throw new Error("startup_group_not_ready");
|
|
6431
|
+
const choices = group.allowedRuntimes.filter((runtime2) => {
|
|
6432
|
+
const executable = runtimeExecutables[runtime2];
|
|
6433
|
+
if (!executable) return false;
|
|
6434
|
+
if (group.profile === "docker")
|
|
6435
|
+
return Boolean(group.dockerPolicy?.commands[runtime2]);
|
|
6436
|
+
return Boolean(locateExecutable(executable, group.projectRoot));
|
|
6437
|
+
});
|
|
6438
|
+
const main = observed.sessions.find((session) => session.id === view.mainId);
|
|
6439
|
+
if (!main?.alive) throw new Error("startup_main_unavailable");
|
|
6440
|
+
process.stderr.write(`PMCP WORKER | ${group.projectRoot}
|
|
6441
|
+
`);
|
|
6442
|
+
const prompt = terminalPrompt();
|
|
6443
|
+
try {
|
|
6444
|
+
const runtime2 = await chooseRuntime(prompt, choices, main.runtime);
|
|
6445
|
+
if (!runtime2 || !await confirmRuntimeStart(prompt, runtime2)) return;
|
|
6446
|
+
await startViewWorker(file, runtime2);
|
|
6447
|
+
} finally {
|
|
6448
|
+
prompt.close();
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
|
|
6209
6452
|
// src/tui/application/worker.ts
|
|
6210
6453
|
var [role, ...argv] = process.argv.slice(2);
|
|
6211
6454
|
var callback2 = argv.includes("--callback");
|
|
@@ -6222,6 +6465,14 @@ try {
|
|
|
6222
6465
|
const result = await prepareWorkspace(request2);
|
|
6223
6466
|
process.stdout.write(`${JSON.stringify(result)}
|
|
6224
6467
|
`);
|
|
6468
|
+
} else if (role === "worker-menu") {
|
|
6469
|
+
const invocation = parseTui(
|
|
6470
|
+
parseArgs(
|
|
6471
|
+
["worker-menu", ...argv.filter((arg) => arg !== "--callback")],
|
|
6472
|
+
tuiOptions
|
|
6473
|
+
)
|
|
6474
|
+
);
|
|
6475
|
+
await runWorkerMenu(invocation.viewFile);
|
|
6225
6476
|
} else if (role === "attach") {
|
|
6226
6477
|
const [file, sessionId, leaseFile] = argv;
|
|
6227
6478
|
if (!file || !sessionId) throw new Error("invalid_attachment_arguments");
|
|
@@ -6254,7 +6505,14 @@ try {
|
|
|
6254
6505
|
tuiOptions
|
|
6255
6506
|
)
|
|
6256
6507
|
);
|
|
6257
|
-
if ([
|
|
6508
|
+
if ([
|
|
6509
|
+
"create",
|
|
6510
|
+
"doctor",
|
|
6511
|
+
"launch",
|
|
6512
|
+
"plan",
|
|
6513
|
+
"recovery",
|
|
6514
|
+
"start-worker"
|
|
6515
|
+
].includes(invocation.action))
|
|
6258
6516
|
throw new Error("unsupported_worker_operation");
|
|
6259
6517
|
const result = role === "locked" ? await operate(
|
|
6260
6518
|
invocation.action,
|
|
@@ -6273,7 +6531,8 @@ try {
|
|
|
6273
6531
|
const file = argv[index + 1];
|
|
6274
6532
|
if (index >= 0 && file) {
|
|
6275
6533
|
try {
|
|
6276
|
-
|
|
6534
|
+
const action = role === "worker-menu" ? role : argv[0] ?? "callback";
|
|
6535
|
+
recordCallbackFailure(file, action, error);
|
|
6277
6536
|
const { view } = readView(file);
|
|
6278
6537
|
if (view.state === "open") new TmuxView(view, file).status(view);
|
|
6279
6538
|
} catch {
|
package/docs/hosting.md
CHANGED
|
@@ -26,6 +26,19 @@ const skills = createSkillHttpHandler({
|
|
|
26
26
|
export default { fetch: (request: Request) => skills.fetch(request) };
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Import `readCatalog` and `verifiedRuntimesOf` from the same `/http` entry when
|
|
30
|
+
the host also displays the catalog. `verifiedRuntimesOf(metadata)` accepts an
|
|
31
|
+
array or a comma/whitespace-separated string, drops unknown names and removes
|
|
32
|
+
duplicates while preserving declaration order. Hosts that need a fixed display
|
|
33
|
+
order can sort the result themselves.
|
|
34
|
+
|
|
35
|
+
Pass `onReject: ({ path, reason }) => reportRejectedCatalog(path, reason)` to
|
|
36
|
+
observe rejected marketplace entries during startup and `reload()`. This callback
|
|
37
|
+
is synchronous and reports only the path and reason. A custom `loadCatalog` must
|
|
38
|
+
forward its options to `readCatalog` to retain rejection reporting. If the host
|
|
39
|
+
loads a separate site catalog, pass the callback to that load as well. Deduplicate
|
|
40
|
+
repeated warnings in the host and keep local paths out of public responses.
|
|
41
|
+
|
|
29
42
|
The handler checks requests before they reach MCP:
|
|
30
43
|
|
|
31
44
|
| Option | Default | Effect |
|