@modootoday/pmcp 0.15.0 → 0.16.0
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 +471 -89
- package/dist/harness-launch-gate.js +0 -3
- package/dist/harness-worker.js +0 -4
- package/dist/http.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mailbox/index.js +0 -3
- package/dist/stdio.js +1 -3
- package/dist/tui-worker.js +271 -24
- 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
|
@@ -546,7 +546,6 @@ var Connection = class {
|
|
|
546
546
|
constructor(records3) {
|
|
547
547
|
this.records = records3;
|
|
548
548
|
}
|
|
549
|
-
records;
|
|
550
549
|
tmux(args, options = {}) {
|
|
551
550
|
return command(
|
|
552
551
|
"tmux",
|
|
@@ -693,8 +692,6 @@ var LaunchJournal = class {
|
|
|
693
692
|
this.id = id2;
|
|
694
693
|
runtimeUnit(id2);
|
|
695
694
|
}
|
|
696
|
-
records;
|
|
697
|
-
id;
|
|
698
695
|
path(kind) {
|
|
699
696
|
if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind))
|
|
700
697
|
throw new Error("invalid_launch_artifact");
|
package/dist/harness-worker.js
CHANGED
|
@@ -453,7 +453,6 @@ var Connection = class {
|
|
|
453
453
|
constructor(records2) {
|
|
454
454
|
this.records = records2;
|
|
455
455
|
}
|
|
456
|
-
records;
|
|
457
456
|
tmux(args, options = {}) {
|
|
458
457
|
return command(
|
|
459
458
|
"tmux",
|
|
@@ -706,8 +705,6 @@ var LaunchJournal = class {
|
|
|
706
705
|
this.id = id2;
|
|
707
706
|
runtimeUnit(id2);
|
|
708
707
|
}
|
|
709
|
-
records;
|
|
710
|
-
id;
|
|
711
708
|
path(kind) {
|
|
712
709
|
if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind))
|
|
713
710
|
throw new Error("invalid_launch_artifact");
|
|
@@ -1794,7 +1791,6 @@ var Backend = class {
|
|
|
1794
1791
|
this.runDirectory = recordStore.directory;
|
|
1795
1792
|
this.metadataPath = recordStore.metadataPath;
|
|
1796
1793
|
}
|
|
1797
|
-
recordStore;
|
|
1798
1794
|
connection;
|
|
1799
1795
|
owner;
|
|
1800
1796
|
socket;
|
package/dist/http.js
CHANGED
|
@@ -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.0";
|
|
2298
2298
|
function registerSkillTools(server, tools) {
|
|
2299
2299
|
registerCatalogTool(server, tools);
|
|
2300
2300
|
registerFindTool(server, tools);
|
package/dist/index.d.ts
CHANGED
|
@@ -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.0";
|
|
279
279
|
/**
|
|
280
280
|
* Registers the skill_* tools on a server.
|
|
281
281
|
*
|
package/dist/index.js
CHANGED
|
@@ -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.0";
|
|
4683
4683
|
function registerSkillTools(server, tools) {
|
|
4684
4684
|
registerCatalogTool(server, tools);
|
|
4685
4685
|
registerFindTool(server, tools);
|
package/dist/mailbox/index.js
CHANGED
|
@@ -144,8 +144,6 @@ var MailboxError = class extends Error {
|
|
|
144
144
|
this.retryable = retryable;
|
|
145
145
|
this.name = "MailboxError";
|
|
146
146
|
}
|
|
147
|
-
code;
|
|
148
|
-
retryable;
|
|
149
147
|
};
|
|
150
148
|
function failure(error) {
|
|
151
149
|
if (error instanceof MailboxError) {
|
|
@@ -1091,7 +1089,6 @@ var NotificationOutbox = class {
|
|
|
1091
1089
|
constructor(store) {
|
|
1092
1090
|
this.store = store;
|
|
1093
1091
|
}
|
|
1094
|
-
store;
|
|
1095
1092
|
discover(binding, nativeTarget) {
|
|
1096
1093
|
return this.store.run(() => {
|
|
1097
1094
|
this.store.assertBinding(binding);
|
package/dist/stdio.js
CHANGED
|
@@ -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.0";
|
|
1945
1945
|
function registerSkillTools(server, tools) {
|
|
1946
1946
|
registerCatalogTool(server, tools);
|
|
1947
1947
|
registerFindTool(server, tools);
|
|
@@ -2003,8 +2003,6 @@ var MailboxError = class extends Error {
|
|
|
2003
2003
|
this.retryable = retryable;
|
|
2004
2004
|
this.name = "MailboxError";
|
|
2005
2005
|
}
|
|
2006
|
-
code;
|
|
2007
|
-
retryable;
|
|
2008
2006
|
};
|
|
2009
2007
|
function failure(error) {
|
|
2010
2008
|
if (error instanceof MailboxError) {
|
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: {},
|
|
@@ -887,7 +911,6 @@ var Connection = class {
|
|
|
887
911
|
constructor(records2) {
|
|
888
912
|
this.records = records2;
|
|
889
913
|
}
|
|
890
|
-
records;
|
|
891
914
|
tmux(args, options = {}) {
|
|
892
915
|
return command(
|
|
893
916
|
"tmux",
|
|
@@ -1124,8 +1147,6 @@ var LaunchJournal = class {
|
|
|
1124
1147
|
this.id = id2;
|
|
1125
1148
|
runtimeUnit(id2);
|
|
1126
1149
|
}
|
|
1127
|
-
records;
|
|
1128
|
-
id;
|
|
1129
1150
|
path(kind) {
|
|
1130
1151
|
if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind))
|
|
1131
1152
|
throw new Error("invalid_launch_artifact");
|
|
@@ -2254,7 +2275,6 @@ var Backend = class {
|
|
|
2254
2275
|
this.runDirectory = recordStore.directory;
|
|
2255
2276
|
this.metadataPath = recordStore.metadataPath;
|
|
2256
2277
|
}
|
|
2257
|
-
recordStore;
|
|
2258
2278
|
connection;
|
|
2259
2279
|
owner;
|
|
2260
2280
|
socket;
|
|
@@ -3393,7 +3413,6 @@ var CommonHarness = class {
|
|
|
3393
3413
|
constructor(view) {
|
|
3394
3414
|
this.view = view;
|
|
3395
3415
|
}
|
|
3396
|
-
view;
|
|
3397
3416
|
async call(family, action, input = {}) {
|
|
3398
3417
|
const group = new GroupStore(this.view.groupFile).state.group;
|
|
3399
3418
|
if (group.owner !== this.view.groupOwner) throw new Error("foreign_group");
|
|
@@ -3427,13 +3446,19 @@ var CommonHarness = class {
|
|
|
3427
3446
|
// src/tui/application/feedback.ts
|
|
3428
3447
|
import { join as join20 } from "path";
|
|
3429
3448
|
var hints = {
|
|
3430
|
-
startup_requires_recovery: "
|
|
3449
|
+
startup_requires_recovery: "Run pmcp tui recovery for saved ownership and exact inspection commands. No runtime was restarted.",
|
|
3431
3450
|
startup_group_paused: "The saved group is paused. Resume it explicitly through the harness CLI before reopening.",
|
|
3432
3451
|
startup_main_unavailable: "Inspect the saved main and recovery state. Stop the old group explicitly before using pmcp tui --new.",
|
|
3433
3452
|
startup_busy: "Another workspace launch is running. Wait and retry.",
|
|
3434
3453
|
startup_runtime_conflict: "The saved main uses another runtime. Stop the old group before creating a new workspace.",
|
|
3435
3454
|
workspace_already_running: "Reopen the existing workspace, or stop its exact owned group before using --new.",
|
|
3436
3455
|
runtime_executable_unavailable: "Install the selected native CLI on PATH, or choose an installed runtime with --runtime.",
|
|
3456
|
+
view_client_unavailable: "Open the view before using the worker menu, or run pmcp tui start-worker with --view and --runtime.",
|
|
3457
|
+
runtime_not_granted: "Choose a runtime granted by this owned group. Inspect the group before creating another workspace.",
|
|
3458
|
+
runtime_capacity_exceeded: "Stop an exact owned worker before adding another, or use the main session alone.",
|
|
3459
|
+
aggregate_memory_budget_exceeded: "Use fewer sessions or start a worker with an explicit smaller --memory-mb reservation.",
|
|
3460
|
+
group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
|
|
3461
|
+
startup_group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
|
|
3437
3462
|
control_connection_lost: "The native writer or lease disappeared. Inspect and release control before reconnecting.",
|
|
3438
3463
|
control_busy: "Another controller holds the lease. Release it explicitly before retrying.",
|
|
3439
3464
|
native_writer_active: "A native writer is still attached. Detach that writer before retrying.",
|
|
@@ -3489,6 +3514,14 @@ function phaseLabel(phase2) {
|
|
|
3489
3514
|
return labels[phase2];
|
|
3490
3515
|
}
|
|
3491
3516
|
|
|
3517
|
+
// src/tui/presentation/target.ts
|
|
3518
|
+
function targetStatus(view) {
|
|
3519
|
+
const role2 = view.target === view.mainId ? "MAIN" : "WORKER";
|
|
3520
|
+
const phase2 = view.phase === "read-only" ? "OBSERVE" : phaseLabel(view.phase);
|
|
3521
|
+
const input = view.phase === "controlled" ? "INPUT ON" : "INPUT OFF";
|
|
3522
|
+
return `${role2} ${view.target.slice(0, 8)} | ${phase2} | ${input}`;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3492
3525
|
// src/tui/adapters/tmux/connection.ts
|
|
3493
3526
|
import { spawn as spawn2 } from "child_process";
|
|
3494
3527
|
import { join as join21 } from "path";
|
|
@@ -3537,9 +3570,6 @@ var ViewConnection = class {
|
|
|
3537
3570
|
this.identity = identity;
|
|
3538
3571
|
this.socket = join21(directory, "s");
|
|
3539
3572
|
}
|
|
3540
|
-
directory;
|
|
3541
|
-
owner;
|
|
3542
|
-
identity;
|
|
3543
3573
|
socket;
|
|
3544
3574
|
raw(args) {
|
|
3545
3575
|
const env = { ...process.env };
|
|
@@ -3642,7 +3672,8 @@ function bindKeys(connection, view, file) {
|
|
|
3642
3672
|
["b", "mailbox"],
|
|
3643
3673
|
["d", "detach"],
|
|
3644
3674
|
["!", "dismiss"],
|
|
3645
|
-
["g", "management"]
|
|
3675
|
+
["g", "management"],
|
|
3676
|
+
["n", "worker-menu"]
|
|
3646
3677
|
])
|
|
3647
3678
|
connection.command([
|
|
3648
3679
|
"bind-key",
|
|
@@ -3663,7 +3694,7 @@ function bindKeys(connection, view, file) {
|
|
|
3663
3694
|
"bind-key",
|
|
3664
3695
|
"?",
|
|
3665
3696
|
"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"
|
|
3697
|
+
"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
3698
|
]);
|
|
3668
3699
|
connection.command([
|
|
3669
3700
|
"set-hook",
|
|
@@ -3690,8 +3721,6 @@ var TmuxView = class {
|
|
|
3690
3721
|
view.serverIdentity
|
|
3691
3722
|
);
|
|
3692
3723
|
}
|
|
3693
|
-
view;
|
|
3694
|
-
stateFile;
|
|
3695
3724
|
connection;
|
|
3696
3725
|
shell(argv2) {
|
|
3697
3726
|
return `exec ${argv2.map(quote).join(" ")}`;
|
|
@@ -3863,7 +3892,7 @@ var TmuxView = class {
|
|
|
3863
3892
|
"set-option",
|
|
3864
3893
|
"-g",
|
|
3865
3894
|
"status-right",
|
|
3866
|
-
`${
|
|
3895
|
+
`${targetStatus(view)}${failure2} | Ctrl-g ?`
|
|
3867
3896
|
]);
|
|
3868
3897
|
}
|
|
3869
3898
|
input(pane, enabled) {
|
|
@@ -3886,6 +3915,32 @@ var TmuxView = class {
|
|
|
3886
3915
|
clients() {
|
|
3887
3916
|
return this.connection.command(["list-clients", "-F", "#{client_pid}"]);
|
|
3888
3917
|
}
|
|
3918
|
+
workerMenu(view) {
|
|
3919
|
+
const pane = view.workerPane ?? view.mainPane;
|
|
3920
|
+
this.connection.assertOwned(pane);
|
|
3921
|
+
const popup = [
|
|
3922
|
+
"tmux",
|
|
3923
|
+
"-S",
|
|
3924
|
+
this.connection.socket,
|
|
3925
|
+
"display-popup",
|
|
3926
|
+
"-E",
|
|
3927
|
+
"-t",
|
|
3928
|
+
pane,
|
|
3929
|
+
"-w",
|
|
3930
|
+
"90%",
|
|
3931
|
+
"-h",
|
|
3932
|
+
"80%",
|
|
3933
|
+
this.shell([
|
|
3934
|
+
view.node,
|
|
3935
|
+
view.worker,
|
|
3936
|
+
"worker-menu",
|
|
3937
|
+
"--view",
|
|
3938
|
+
this.stateFile,
|
|
3939
|
+
"--callback"
|
|
3940
|
+
])
|
|
3941
|
+
];
|
|
3942
|
+
this.connection.command(["run-shell", "-b", popup.map(quote).join(" ")]);
|
|
3943
|
+
}
|
|
3889
3944
|
copy(pane) {
|
|
3890
3945
|
this.connection.assertOwned(pane);
|
|
3891
3946
|
this.connection.command(["copy-mode", "-t", pane]);
|
|
@@ -3952,6 +4007,7 @@ function dockLines(snapshot) {
|
|
|
3952
4007
|
),
|
|
3953
4008
|
"",
|
|
3954
4009
|
"Ctrl-g w: observe worker",
|
|
4010
|
+
"Ctrl-g n: add worker",
|
|
3955
4011
|
"Ctrl-g m: return to main",
|
|
3956
4012
|
"Ctrl-g c: acquire control",
|
|
3957
4013
|
"Ctrl-g r: release control",
|
|
@@ -3963,7 +4019,7 @@ function dockLines(snapshot) {
|
|
|
3963
4019
|
];
|
|
3964
4020
|
}
|
|
3965
4021
|
function coordinationLines(snapshot, view) {
|
|
3966
|
-
const target = `TARGET ${
|
|
4022
|
+
const target = `TARGET ${safe(targetStatus(view))}`;
|
|
3967
4023
|
if (view.failure)
|
|
3968
4024
|
return [
|
|
3969
4025
|
target,
|
|
@@ -4352,8 +4408,6 @@ var MailboxError = class extends Error {
|
|
|
4352
4408
|
this.retryable = retryable;
|
|
4353
4409
|
this.name = "MailboxError";
|
|
4354
4410
|
}
|
|
4355
|
-
code;
|
|
4356
|
-
retryable;
|
|
4357
4411
|
};
|
|
4358
4412
|
function failure(error) {
|
|
4359
4413
|
if (error instanceof MailboxError) {
|
|
@@ -5717,6 +5771,11 @@ var handlers = {
|
|
|
5717
5771
|
close: closeView,
|
|
5718
5772
|
check: checkControl,
|
|
5719
5773
|
dismiss: ({ view, file }) => dismissFailure(file, view),
|
|
5774
|
+
"worker-menu": async (context) => {
|
|
5775
|
+
if (!context.renderer.clients()) throw new Error("view_client_unavailable");
|
|
5776
|
+
await releaseControl(context);
|
|
5777
|
+
context.renderer.workerMenu(context.view);
|
|
5778
|
+
},
|
|
5720
5779
|
management: async (context) => {
|
|
5721
5780
|
await releaseControl(context);
|
|
5722
5781
|
if (context.view.managementWindow) {
|
|
@@ -5850,6 +5909,70 @@ function runtime() {
|
|
|
5850
5909
|
return { node: realpathSync10(available.node), worker: bundled };
|
|
5851
5910
|
}
|
|
5852
5911
|
|
|
5912
|
+
// src/tui/startup/diagnostics.ts
|
|
5913
|
+
function remediation(tools, userManagerAvailable) {
|
|
5914
|
+
const steps = [];
|
|
5915
|
+
if (tools.platform !== "linux")
|
|
5916
|
+
steps.push(
|
|
5917
|
+
"Use a qualified Linux host for the terminal workspace. Other PMCP CLI features do not require this host profile."
|
|
5918
|
+
);
|
|
5919
|
+
const missing = ["tmux", "flock", "systemctl", "systemdRun"].filter(
|
|
5920
|
+
(name) => !tools[name]
|
|
5921
|
+
);
|
|
5922
|
+
if (missing.length)
|
|
5923
|
+
steps.push(
|
|
5924
|
+
`Install the missing tmux, util-linux or systemd tools for your distribution and add them to PATH: ${missing.join(", ")}.`
|
|
5925
|
+
);
|
|
5926
|
+
if (!tools.nodeSupported)
|
|
5927
|
+
steps.push(
|
|
5928
|
+
"Select Node.js 22 or later through your existing version manager and check node --version in this terminal."
|
|
5929
|
+
);
|
|
5930
|
+
if (tools.supported && !userManagerAvailable)
|
|
5931
|
+
steps.push(
|
|
5932
|
+
"Check systemctl --user status in your login session and restore access to its user manager before launching. Doctor does not start or repair services."
|
|
5933
|
+
);
|
|
5934
|
+
return steps;
|
|
5935
|
+
}
|
|
5936
|
+
function inspectTerminalHost() {
|
|
5937
|
+
const tools = dependencies();
|
|
5938
|
+
let userManagerAvailable = false;
|
|
5939
|
+
let reason;
|
|
5940
|
+
try {
|
|
5941
|
+
if (!tools.supported) throw new Error("tui_dependencies_unavailable");
|
|
5942
|
+
assertNativeHost();
|
|
5943
|
+
userManagerAvailable = true;
|
|
5944
|
+
} catch (error) {
|
|
5945
|
+
reason = error instanceof Error ? error.message : "native_host_unavailable";
|
|
5946
|
+
}
|
|
5947
|
+
const runtimes = Object.entries(runtimeExecutables).map(
|
|
5948
|
+
([id2, executable]) => {
|
|
5949
|
+
const path = locateExecutable(executable);
|
|
5950
|
+
return {
|
|
5951
|
+
id: id2,
|
|
5952
|
+
executable,
|
|
5953
|
+
installed: Boolean(path),
|
|
5954
|
+
...path ? { path } : {},
|
|
5955
|
+
nativeAcceptance: "not-assessed",
|
|
5956
|
+
authentication: "not-inspected"
|
|
5957
|
+
};
|
|
5958
|
+
}
|
|
5959
|
+
);
|
|
5960
|
+
return {
|
|
5961
|
+
schemaVersion: 1,
|
|
5962
|
+
ok: true,
|
|
5963
|
+
...tools,
|
|
5964
|
+
supported: tools.supported && userManagerAvailable,
|
|
5965
|
+
userManagerAvailable,
|
|
5966
|
+
...reason ? { reason } : {},
|
|
5967
|
+
runtimes,
|
|
5968
|
+
qualification: "Prerequisite availability only; native behavior and cgroup delegation require separate qualification.",
|
|
5969
|
+
remediation: remediation(tools, userManagerAvailable),
|
|
5970
|
+
nextCommands: ["pmcp tui plan", "pmcp tui recovery"],
|
|
5971
|
+
providerInvoked: false,
|
|
5972
|
+
authenticationInspected: false
|
|
5973
|
+
};
|
|
5974
|
+
}
|
|
5975
|
+
|
|
5853
5976
|
// src/tui/application/create.ts
|
|
5854
5977
|
import { join as join29 } from "path";
|
|
5855
5978
|
import { realpathSync as realpathSync11 } from "fs";
|
|
@@ -5928,10 +6051,9 @@ async function createView(input) {
|
|
|
5928
6051
|
|
|
5929
6052
|
// src/tui/application/invoke.ts
|
|
5930
6053
|
async function invokeTui(invocation) {
|
|
5931
|
-
if (
|
|
6054
|
+
if (["launch", "plan", "recovery", "start-worker"].includes(invocation.action))
|
|
5932
6055
|
throw new Error("startup_requires_cli_context");
|
|
5933
|
-
if (invocation.action === "doctor")
|
|
5934
|
-
return { schemaVersion: 1, ok: true, ...dependencies() };
|
|
6056
|
+
if (invocation.action === "doctor") return inspectTerminalHost();
|
|
5935
6057
|
if (invocation.action === "create") return createView(invocation.create);
|
|
5936
6058
|
if (invocation.action === "inspect")
|
|
5937
6059
|
return operate("inspect", invocation.viewFile);
|
|
@@ -6206,6 +6328,115 @@ function assertGroup(group, projectRoot, generation) {
|
|
|
6206
6328
|
throw new Error("stale_group_generation");
|
|
6207
6329
|
}
|
|
6208
6330
|
|
|
6331
|
+
// src/tui/application/worker-start.ts
|
|
6332
|
+
async function startViewWorker(file, runtime2, memoryMb) {
|
|
6333
|
+
const { view, common } = openView(file);
|
|
6334
|
+
if (view.state !== "open") throw new Error("view_closed");
|
|
6335
|
+
const observed = await common.snapshot();
|
|
6336
|
+
if (observed.group.state !== "ready") throw new Error("group_not_ready");
|
|
6337
|
+
const main = observed.sessions.find(
|
|
6338
|
+
(session) => session.id === view.mainId && session.role === "main"
|
|
6339
|
+
);
|
|
6340
|
+
if (!main?.alive) throw new Error("startup_main_unavailable");
|
|
6341
|
+
return common.call("session", "start", {
|
|
6342
|
+
input: { role: "worker", runtime: runtime2, memoryMb: memoryMb ?? main.memoryMb }
|
|
6343
|
+
});
|
|
6344
|
+
}
|
|
6345
|
+
|
|
6346
|
+
// src/cli/entry.ts
|
|
6347
|
+
function interactiveEntry(terminal) {
|
|
6348
|
+
if (!terminal.stdin || !terminal.stdout) return false;
|
|
6349
|
+
if (!terminal.term || terminal.term === "dumb") return false;
|
|
6350
|
+
return !terminal.ci || terminal.ci === "false" || terminal.ci === "0";
|
|
6351
|
+
}
|
|
6352
|
+
|
|
6353
|
+
// src/tui/startup/host.ts
|
|
6354
|
+
function terminalSupport() {
|
|
6355
|
+
try {
|
|
6356
|
+
if (!dependencies().supported)
|
|
6357
|
+
throw new Error("tui_dependencies_unavailable");
|
|
6358
|
+
assertNativeHost();
|
|
6359
|
+
planLayout(process.stdout.columns ?? 0, process.stdout.rows ?? 0);
|
|
6360
|
+
return { supported: true };
|
|
6361
|
+
} catch (error) {
|
|
6362
|
+
return {
|
|
6363
|
+
supported: false,
|
|
6364
|
+
reason: error instanceof Error ? error.message : "terminal_unavailable"
|
|
6365
|
+
};
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
6368
|
+
function requireTerminal() {
|
|
6369
|
+
if (!interactiveEntry({
|
|
6370
|
+
stdin: process.stdin.isTTY === true,
|
|
6371
|
+
stdout: process.stdout.isTTY === true,
|
|
6372
|
+
term: process.env.TERM,
|
|
6373
|
+
ci: process.env.CI
|
|
6374
|
+
}))
|
|
6375
|
+
throw new Error("terminal_required");
|
|
6376
|
+
const support = terminalSupport();
|
|
6377
|
+
if (!support.supported) throw new Error(support.reason);
|
|
6378
|
+
}
|
|
6379
|
+
|
|
6380
|
+
// src/tui/presentation/runtime-choice.ts
|
|
6381
|
+
import { createInterface } from "readline/promises";
|
|
6382
|
+
async function chooseRuntime(prompt, choices, preferred) {
|
|
6383
|
+
if (!choices.length) throw new Error("runtime_executable_unavailable");
|
|
6384
|
+
const fallback = Math.max(choices.indexOf(preferred), 0);
|
|
6385
|
+
process.stderr.write(
|
|
6386
|
+
`${choices.map((runtime2, index) => `${index + 1}. ${runtime2}`).join("\n")}
|
|
6387
|
+
`
|
|
6388
|
+
);
|
|
6389
|
+
while (true) {
|
|
6390
|
+
const answer = (await prompt.question(
|
|
6391
|
+
`Runtime [1-${choices.length}, Enter=${fallback + 1}, q=cancel] `
|
|
6392
|
+
)).trim();
|
|
6393
|
+
if (answer === "q") return void 0;
|
|
6394
|
+
if (answer === "") return choices[fallback];
|
|
6395
|
+
if (!/^\d+$/.test(answer)) continue;
|
|
6396
|
+
const index = Number(answer) - 1;
|
|
6397
|
+
if (Number.isSafeInteger(index) && index >= 0 && index < choices.length)
|
|
6398
|
+
return choices[index];
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
6401
|
+
async function confirmRuntimeStart(prompt, runtime2) {
|
|
6402
|
+
const answer = await prompt.question(
|
|
6403
|
+
`Start ${runtime2}? [Enter=start, q=cancel] `
|
|
6404
|
+
);
|
|
6405
|
+
return answer.trim() === "";
|
|
6406
|
+
}
|
|
6407
|
+
function terminalPrompt() {
|
|
6408
|
+
return createInterface({ input: process.stdin, output: process.stderr });
|
|
6409
|
+
}
|
|
6410
|
+
|
|
6411
|
+
// src/tui/presentation/worker-menu.ts
|
|
6412
|
+
async function runWorkerMenu(file) {
|
|
6413
|
+
requireTerminal();
|
|
6414
|
+
const { view, common } = openView(file);
|
|
6415
|
+
if (view.state !== "open") throw new Error("view_closed");
|
|
6416
|
+
const observed = await common.snapshot();
|
|
6417
|
+
const group = observed.group;
|
|
6418
|
+
if (group.state !== "ready") throw new Error("startup_group_not_ready");
|
|
6419
|
+
const choices = group.allowedRuntimes.filter((runtime2) => {
|
|
6420
|
+
const executable = runtimeExecutables[runtime2];
|
|
6421
|
+
if (!executable) return false;
|
|
6422
|
+
if (group.profile === "docker")
|
|
6423
|
+
return Boolean(group.dockerPolicy?.commands[runtime2]);
|
|
6424
|
+
return Boolean(locateExecutable(executable, group.projectRoot));
|
|
6425
|
+
});
|
|
6426
|
+
const main = observed.sessions.find((session) => session.id === view.mainId);
|
|
6427
|
+
if (!main?.alive) throw new Error("startup_main_unavailable");
|
|
6428
|
+
process.stderr.write(`PMCP WORKER | ${group.projectRoot}
|
|
6429
|
+
`);
|
|
6430
|
+
const prompt = terminalPrompt();
|
|
6431
|
+
try {
|
|
6432
|
+
const runtime2 = await chooseRuntime(prompt, choices, main.runtime);
|
|
6433
|
+
if (!runtime2 || !await confirmRuntimeStart(prompt, runtime2)) return;
|
|
6434
|
+
await startViewWorker(file, runtime2);
|
|
6435
|
+
} finally {
|
|
6436
|
+
prompt.close();
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
|
|
6209
6440
|
// src/tui/application/worker.ts
|
|
6210
6441
|
var [role, ...argv] = process.argv.slice(2);
|
|
6211
6442
|
var callback2 = argv.includes("--callback");
|
|
@@ -6222,6 +6453,14 @@ try {
|
|
|
6222
6453
|
const result = await prepareWorkspace(request2);
|
|
6223
6454
|
process.stdout.write(`${JSON.stringify(result)}
|
|
6224
6455
|
`);
|
|
6456
|
+
} else if (role === "worker-menu") {
|
|
6457
|
+
const invocation = parseTui(
|
|
6458
|
+
parseArgs(
|
|
6459
|
+
["worker-menu", ...argv.filter((arg) => arg !== "--callback")],
|
|
6460
|
+
tuiOptions
|
|
6461
|
+
)
|
|
6462
|
+
);
|
|
6463
|
+
await runWorkerMenu(invocation.viewFile);
|
|
6225
6464
|
} else if (role === "attach") {
|
|
6226
6465
|
const [file, sessionId, leaseFile] = argv;
|
|
6227
6466
|
if (!file || !sessionId) throw new Error("invalid_attachment_arguments");
|
|
@@ -6254,7 +6493,14 @@ try {
|
|
|
6254
6493
|
tuiOptions
|
|
6255
6494
|
)
|
|
6256
6495
|
);
|
|
6257
|
-
if ([
|
|
6496
|
+
if ([
|
|
6497
|
+
"create",
|
|
6498
|
+
"doctor",
|
|
6499
|
+
"launch",
|
|
6500
|
+
"plan",
|
|
6501
|
+
"recovery",
|
|
6502
|
+
"start-worker"
|
|
6503
|
+
].includes(invocation.action))
|
|
6258
6504
|
throw new Error("unsupported_worker_operation");
|
|
6259
6505
|
const result = role === "locked" ? await operate(
|
|
6260
6506
|
invocation.action,
|
|
@@ -6273,7 +6519,8 @@ try {
|
|
|
6273
6519
|
const file = argv[index + 1];
|
|
6274
6520
|
if (index >= 0 && file) {
|
|
6275
6521
|
try {
|
|
6276
|
-
|
|
6522
|
+
const action = role === "worker-menu" ? role : argv[0] ?? "callback";
|
|
6523
|
+
recordCallbackFailure(file, action, error);
|
|
6277
6524
|
const { view } = readView(file);
|
|
6278
6525
|
if (view.state === "open") new TmuxView(view, file).status(view);
|
|
6279
6526
|
} catch {
|