@rigkit/provider-cmux 0.2.4 → 0.2.6
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/README.md +6 -1
- package/package.json +3 -3
- package/src/capabilities.ts +22 -6
- package/src/host.ts +101 -37
- package/src/index.test.ts +117 -19
- package/src/index.ts +23 -0
- package/src/provider.ts +24 -4
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -52,11 +52,16 @@ export default workflow("site", {
|
|
|
52
52
|
sshOptions: ["ServerAliveInterval=15"],
|
|
53
53
|
},
|
|
54
54
|
cwd: "/workspace/site",
|
|
55
|
-
|
|
55
|
+
surfaceLayout: "tabs",
|
|
56
|
+
terminals: [{ command: "pnpm dev" }],
|
|
56
57
|
url: "http://localhost:3000",
|
|
57
58
|
});
|
|
58
59
|
},
|
|
59
60
|
});
|
|
60
61
|
```
|
|
61
62
|
|
|
63
|
+
Set `surfaceLayout: "tabs"` to open terminals and the optional browser URL as
|
|
64
|
+
tabs in the same cmux pane. Omit it, or set `"splits"`, to keep the default
|
|
65
|
+
split-pane behavior.
|
|
66
|
+
|
|
62
67
|
Local hosts can import `@rigkit/provider-cmux/host` to register the trusted `cmux.open` handler. The Rigkit CLI registers this handler automatically.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigkit/provider-cmux",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@rigkit/sdk": "0.2.
|
|
21
|
-
"@rigkit/engine": "0.2.
|
|
20
|
+
"@rigkit/sdk": "0.2.6",
|
|
21
|
+
"@rigkit/engine": "0.2.6"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/bun": "latest",
|
package/src/capabilities.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const CMUX_OPEN_CAPABILITY_ID = "cmux.open";
|
|
2
2
|
|
|
3
3
|
export const CMUX_OPEN_SCHEMA_HASH =
|
|
4
|
-
"sha256:
|
|
4
|
+
"sha256:3a2975cfe53089c6a607da751b55575dc8806bd90132242d4b7e9065a26ae3af";
|
|
5
5
|
|
|
6
6
|
export const CMUX_OPEN_CAPABILITY = {
|
|
7
7
|
id: CMUX_OPEN_CAPABILITY_ID,
|
|
@@ -29,24 +29,40 @@ export type CmuxRemoteReadyOptions = {
|
|
|
29
29
|
requireProxy?: boolean;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
export type CmuxOpenTerminalDirection = "left" | "right" | "up" | "down";
|
|
33
|
+
export type CmuxOpenSurfaceLayout = "splits" | "tabs";
|
|
34
|
+
|
|
35
|
+
export type CmuxOpenTerminalInput = {
|
|
36
|
+
command: string;
|
|
37
|
+
cwd?: string;
|
|
38
|
+
direction?: CmuxOpenTerminalDirection;
|
|
39
|
+
focus?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
32
42
|
export type CmuxOpenInput = {
|
|
33
43
|
name: string;
|
|
34
44
|
ssh?: CmuxOpenSshInput;
|
|
35
45
|
cwd?: string;
|
|
36
|
-
|
|
46
|
+
surfaceLayout?: CmuxOpenSurfaceLayout;
|
|
47
|
+
terminals?: readonly CmuxOpenTerminalInput[];
|
|
37
48
|
url?: string;
|
|
38
49
|
focus?: boolean;
|
|
39
50
|
waitForRemoteReady?: boolean | CmuxRemoteReadyOptions;
|
|
40
51
|
};
|
|
41
52
|
|
|
53
|
+
export type CmuxOpenPaneResult = {
|
|
54
|
+
paneId?: string;
|
|
55
|
+
paneRef?: string;
|
|
56
|
+
surfaceId?: string;
|
|
57
|
+
surfaceRef?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
42
60
|
export type CmuxOpenResult = {
|
|
43
61
|
sessionId: string;
|
|
44
62
|
workspaceId: string;
|
|
45
63
|
workspaceRef?: string;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
browserPaneId?: string;
|
|
49
|
-
browserSurfaceId?: string;
|
|
64
|
+
terminalPanes: CmuxOpenPaneResult[];
|
|
65
|
+
browserPane?: CmuxOpenPaneResult;
|
|
50
66
|
};
|
|
51
67
|
|
|
52
68
|
export type CmuxOpenSession = CmuxOpenResult & {
|
package/src/host.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createCmuxClient,
|
|
3
3
|
formatShellCommand,
|
|
4
|
-
type CmuxBrowserOpenOptions,
|
|
5
4
|
type CmuxClient,
|
|
6
5
|
type CmuxClientOptions,
|
|
7
|
-
type CmuxNewPaneOptions,
|
|
8
6
|
type CmuxNewWorkspaceOptions,
|
|
9
7
|
type CmuxPane,
|
|
10
8
|
type CmuxPortsKickOptions,
|
|
@@ -21,8 +19,11 @@ import {
|
|
|
21
19
|
import {
|
|
22
20
|
CMUX_OPEN_CAPABILITY,
|
|
23
21
|
type CmuxOpenInput,
|
|
22
|
+
type CmuxOpenPaneResult,
|
|
24
23
|
type CmuxOpenResult,
|
|
25
24
|
type CmuxOpenSshInput,
|
|
25
|
+
type CmuxOpenTerminalDirection,
|
|
26
|
+
type CmuxOpenTerminalInput,
|
|
26
27
|
type CmuxRemoteReadyOptions,
|
|
27
28
|
} from "./capabilities.ts";
|
|
28
29
|
|
|
@@ -33,6 +34,7 @@ export type CmuxOpenClient = Pick<
|
|
|
33
34
|
| "newWorkspace"
|
|
34
35
|
| "ssh"
|
|
35
36
|
| "newPane"
|
|
37
|
+
| "newSurface"
|
|
36
38
|
| "send"
|
|
37
39
|
| "portsKick"
|
|
38
40
|
| "browserOpen"
|
|
@@ -72,9 +74,8 @@ export async function openCmux(
|
|
|
72
74
|
...(options.logger ? { logger: options.logger } : {}),
|
|
73
75
|
printCommands: options.clientOptions?.printCommands ?? false,
|
|
74
76
|
});
|
|
75
|
-
const command = commandForInput(input);
|
|
76
77
|
let workspace: CmuxWorkspace;
|
|
77
|
-
|
|
78
|
+
const terminalPanes: CmuxPane[] = [];
|
|
78
79
|
|
|
79
80
|
logger?.(`cmux: opening ${input.name}`);
|
|
80
81
|
if (input.ssh) {
|
|
@@ -89,27 +90,34 @@ export async function openCmux(
|
|
|
89
90
|
const workspaceOptions: CmuxNewWorkspaceOptions = {
|
|
90
91
|
name: input.name,
|
|
91
92
|
cwd: input.cwd,
|
|
92
|
-
command: input.command,
|
|
93
93
|
focus: input.focus,
|
|
94
94
|
};
|
|
95
95
|
workspace = await cmux.newWorkspace(workspaceOptions);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
const workspaceId = workspace.id ?? workspace.handle;
|
|
99
|
+
const useTabLayout = input.surfaceLayout === "tabs";
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
for (const terminal of input.terminals ?? []) {
|
|
102
|
+
const cwd = terminal.cwd ?? input.cwd;
|
|
103
|
+
logger?.(cwd ? `cmux: starting terminal in ${cwd}` : "cmux: starting terminal");
|
|
104
|
+
const terminalPane = useTabLayout
|
|
105
|
+
? await cmux.newSurface({
|
|
106
|
+
workspace: workspaceId,
|
|
107
|
+
type: "terminal",
|
|
108
|
+
focus: terminal.focus ?? true,
|
|
109
|
+
})
|
|
110
|
+
: await cmux.newPane({
|
|
111
|
+
workspace: workspaceId,
|
|
112
|
+
type: "terminal",
|
|
113
|
+
direction: terminal.direction ?? "down",
|
|
114
|
+
focus: terminal.focus ?? true,
|
|
115
|
+
});
|
|
116
|
+
terminalPanes.push(terminalPane);
|
|
109
117
|
const sendOptions: CmuxSendOptions = {
|
|
110
118
|
workspace: workspaceId,
|
|
111
119
|
surface: terminalPane.surface,
|
|
112
|
-
text:
|
|
120
|
+
text: commandForTerminal(terminal, input),
|
|
113
121
|
};
|
|
114
122
|
await cmux.send(sendOptions);
|
|
115
123
|
}
|
|
@@ -120,25 +128,34 @@ export async function openCmux(
|
|
|
120
128
|
await cmux.waitForRemoteReady(workspaceId, waitOptions);
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
if (input.ssh &&
|
|
131
|
+
if (input.ssh && terminalPanes.some((pane) => pane.surface)) {
|
|
124
132
|
logger?.("cmux: refreshing remote ports");
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
for (const pane of terminalPanes) {
|
|
134
|
+
if (!pane.surface) continue;
|
|
135
|
+
const kickOptions: CmuxPortsKickOptions = {
|
|
136
|
+
workspace: workspaceId,
|
|
137
|
+
surface: pane.surface,
|
|
138
|
+
reason: "command",
|
|
139
|
+
};
|
|
140
|
+
await cmux.portsKick(kickOptions);
|
|
141
|
+
}
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
let browserPane: CmuxPane | undefined;
|
|
134
145
|
if (input.url) {
|
|
135
146
|
logger?.(`cmux: opening ${input.url}`);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
browserPane = useTabLayout
|
|
148
|
+
? await cmux.newSurface({
|
|
149
|
+
workspace: workspaceId,
|
|
150
|
+
type: "browser",
|
|
151
|
+
url: input.url,
|
|
152
|
+
focus: input.focus !== false,
|
|
153
|
+
})
|
|
154
|
+
: await cmux.browserOpen({
|
|
155
|
+
workspace: workspaceId,
|
|
156
|
+
url: input.url,
|
|
157
|
+
focus: input.focus !== false,
|
|
158
|
+
});
|
|
142
159
|
}
|
|
143
160
|
|
|
144
161
|
if (input.focus !== false) {
|
|
@@ -151,10 +168,8 @@ export async function openCmux(
|
|
|
151
168
|
sessionId: workspaceId,
|
|
152
169
|
workspaceId,
|
|
153
170
|
...(workspace.ref ? { workspaceRef: workspace.ref } : {}),
|
|
154
|
-
|
|
155
|
-
...(
|
|
156
|
-
...(browserPane?.pane ? { browserPaneId: browserPane.pane } : {}),
|
|
157
|
-
...(browserPane?.surface ? { browserSurfaceId: browserPane.surface } : {}),
|
|
171
|
+
terminalPanes: terminalPanes.map(paneResultForCmuxPane),
|
|
172
|
+
...(browserPane ? { browserPane: paneResultForCmuxPane(browserPane) } : {}),
|
|
158
173
|
};
|
|
159
174
|
}
|
|
160
175
|
|
|
@@ -174,7 +189,8 @@ export function parseCmuxOpenInput(value: unknown): CmuxOpenInput {
|
|
|
174
189
|
name,
|
|
175
190
|
...(value.ssh !== undefined ? { ssh: parseSshInput(value.ssh) } : {}),
|
|
176
191
|
...optionalStringField(value, "cwd"),
|
|
177
|
-
...
|
|
192
|
+
...optionalSurfaceLayoutField(value, "surfaceLayout"),
|
|
193
|
+
...(value.terminals !== undefined ? { terminals: parseTerminalInputs(value.terminals) } : {}),
|
|
178
194
|
...optionalStringField(value, "url"),
|
|
179
195
|
...optionalBooleanField(value, "focus"),
|
|
180
196
|
...(value.waitForRemoteReady !== undefined
|
|
@@ -237,10 +253,34 @@ function sshDestination(ssh: Extract<CmuxOpenSshInput, object>): string {
|
|
|
237
253
|
return `${ssh.username}@${ssh.host}`;
|
|
238
254
|
}
|
|
239
255
|
|
|
240
|
-
function
|
|
241
|
-
if (!
|
|
242
|
-
|
|
243
|
-
|
|
256
|
+
function parseTerminalInputs(value: unknown): CmuxOpenTerminalInput[] {
|
|
257
|
+
if (!Array.isArray(value)) throw new Error(`cmux.open terminals must be an array`);
|
|
258
|
+
return value.map((item, index) => parseTerminalInput(item, index));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function parseTerminalInput(value: unknown, index: number): CmuxOpenTerminalInput {
|
|
262
|
+
if (!isRecord(value)) throw new Error(`cmux.open terminals[${index}] must be an object`);
|
|
263
|
+
return {
|
|
264
|
+
command: requiredString(value, "command"),
|
|
265
|
+
...optionalStringField(value, "cwd"),
|
|
266
|
+
...optionalTerminalDirectionField(value, "direction"),
|
|
267
|
+
...optionalBooleanField(value, "focus"),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function commandForTerminal(terminal: CmuxOpenTerminalInput, input: CmuxOpenInput): string {
|
|
272
|
+
const cwd = terminal.cwd ?? input.cwd;
|
|
273
|
+
const prefix = cwd ? `${formatShellCommand(["cd", cwd])} && ` : "";
|
|
274
|
+
return `${prefix}${terminal.command}\n`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function paneResultForCmuxPane(pane: CmuxPane): CmuxOpenPaneResult {
|
|
278
|
+
return {
|
|
279
|
+
...(pane.pane ? { paneId: pane.pane } : {}),
|
|
280
|
+
...(pane.paneRef ? { paneRef: pane.paneRef } : {}),
|
|
281
|
+
...(pane.surface ? { surfaceId: pane.surface } : {}),
|
|
282
|
+
...(pane.surfaceRef ? { surfaceRef: pane.surfaceRef } : {}),
|
|
283
|
+
};
|
|
244
284
|
}
|
|
245
285
|
|
|
246
286
|
function remoteReadyOptionsForInput(input: CmuxOpenInput): CmuxWaitForRemoteOptions | false {
|
|
@@ -301,6 +341,30 @@ function optionalBooleanField(record: Record<string, unknown>, key: string): Rec
|
|
|
301
341
|
return { [key]: value };
|
|
302
342
|
}
|
|
303
343
|
|
|
344
|
+
function optionalTerminalDirectionField(
|
|
345
|
+
record: Record<string, unknown>,
|
|
346
|
+
key: string,
|
|
347
|
+
): Record<string, CmuxOpenTerminalDirection> {
|
|
348
|
+
const value = record[key];
|
|
349
|
+
if (value === undefined) return {};
|
|
350
|
+
if (value !== "left" && value !== "right" && value !== "up" && value !== "down") {
|
|
351
|
+
throw new Error(`cmux.open ${key} must be "left", "right", "up", or "down"`);
|
|
352
|
+
}
|
|
353
|
+
return { [key]: value };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function optionalSurfaceLayoutField(
|
|
357
|
+
record: Record<string, unknown>,
|
|
358
|
+
key: string,
|
|
359
|
+
): Record<string, "splits" | "tabs"> {
|
|
360
|
+
const value = record[key];
|
|
361
|
+
if (value === undefined) return {};
|
|
362
|
+
if (value !== "splits" && value !== "tabs") {
|
|
363
|
+
throw new Error(`cmux.open ${key} must be "splits" or "tabs"`);
|
|
364
|
+
}
|
|
365
|
+
return { [key]: value };
|
|
366
|
+
}
|
|
367
|
+
|
|
304
368
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
305
369
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
306
370
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -135,7 +135,7 @@ describe("cmux sdk", () => {
|
|
|
135
135
|
]);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
test("creates panes, opens browsers, and sends terminal text", async () => {
|
|
138
|
+
test("creates panes, surfaces, opens browsers, and sends terminal text", async () => {
|
|
139
139
|
const calls: Array<{ method: string; params: CmuxRpcParams }> = [];
|
|
140
140
|
const cmux = createCmuxClient({
|
|
141
141
|
printCommands: false,
|
|
@@ -161,6 +161,16 @@ describe("cmux sdk", () => {
|
|
|
161
161
|
pane_ref: "pane:11",
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
+
if (method === "surface.create") {
|
|
165
|
+
return {
|
|
166
|
+
workspace_id: "00000000-0000-0000-0000-000000000009",
|
|
167
|
+
workspace_ref: "workspace:9",
|
|
168
|
+
surface_id: "00000000-0000-0000-0000-000000000012",
|
|
169
|
+
surface_ref: "surface:12",
|
|
170
|
+
pane_id: "00000000-0000-0000-0000-000000000008",
|
|
171
|
+
pane_ref: "pane:8",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
164
174
|
return {};
|
|
165
175
|
},
|
|
166
176
|
});
|
|
@@ -181,6 +191,17 @@ describe("cmux sdk", () => {
|
|
|
181
191
|
surface: pane.surface,
|
|
182
192
|
reason: "refresh",
|
|
183
193
|
});
|
|
194
|
+
const surface = await cmux.newSurface({
|
|
195
|
+
workspace: "00000000-0000-0000-0000-000000000009",
|
|
196
|
+
pane: pane.pane,
|
|
197
|
+
type: "terminal",
|
|
198
|
+
focus: false,
|
|
199
|
+
});
|
|
200
|
+
await cmux.send({
|
|
201
|
+
workspace: "00000000-0000-0000-0000-000000000009",
|
|
202
|
+
surface: surface.surface,
|
|
203
|
+
text: "codex\\n",
|
|
204
|
+
});
|
|
184
205
|
await cmux.browserOpen({
|
|
185
206
|
workspace: "00000000-0000-0000-0000-000000000009",
|
|
186
207
|
url: "http://localhost:3000",
|
|
@@ -188,6 +209,7 @@ describe("cmux sdk", () => {
|
|
|
188
209
|
});
|
|
189
210
|
|
|
190
211
|
expect(pane.surface).toBe("00000000-0000-0000-0000-000000000007");
|
|
212
|
+
expect(surface.surface).toBe("00000000-0000-0000-0000-000000000012");
|
|
191
213
|
expect(calls).toEqual([
|
|
192
214
|
{
|
|
193
215
|
method: "pane.create",
|
|
@@ -214,6 +236,23 @@ describe("cmux sdk", () => {
|
|
|
214
236
|
reason: "refresh",
|
|
215
237
|
},
|
|
216
238
|
},
|
|
239
|
+
{
|
|
240
|
+
method: "surface.create",
|
|
241
|
+
params: {
|
|
242
|
+
type: "terminal",
|
|
243
|
+
pane_id: "00000000-0000-0000-0000-000000000008",
|
|
244
|
+
workspace_id: "00000000-0000-0000-0000-000000000009",
|
|
245
|
+
focus: false,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
method: "surface.send_text",
|
|
250
|
+
params: {
|
|
251
|
+
workspace_id: "00000000-0000-0000-0000-000000000009",
|
|
252
|
+
surface_id: "00000000-0000-0000-0000-000000000012",
|
|
253
|
+
text: "codex\\n",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
217
256
|
{
|
|
218
257
|
method: "browser.open_split",
|
|
219
258
|
params: {
|
|
@@ -400,7 +439,7 @@ describe("cmux sdk", () => {
|
|
|
400
439
|
expect(() => cmux.run(["bad"])).toThrow(CmuxCommandError);
|
|
401
440
|
});
|
|
402
441
|
|
|
403
|
-
test("handles cmux.open host capability for an ssh workspace", async () => {
|
|
442
|
+
test("handles cmux.open host capability for an ssh workspace with tabs", async () => {
|
|
404
443
|
const calls: Array<{ method: string; params: unknown }> = [];
|
|
405
444
|
const logs: string[] = [];
|
|
406
445
|
const client = fakeOpenClient(calls);
|
|
@@ -413,7 +452,11 @@ describe("cmux sdk", () => {
|
|
|
413
452
|
sshOptions: ["ServerAliveInterval=15"],
|
|
414
453
|
},
|
|
415
454
|
cwd: "/workspace/site",
|
|
416
|
-
|
|
455
|
+
surfaceLayout: "tabs",
|
|
456
|
+
terminals: [
|
|
457
|
+
{ command: "pnpm dev" },
|
|
458
|
+
{ command: "codex", focus: false },
|
|
459
|
+
],
|
|
417
460
|
url: "http://localhost:4321",
|
|
418
461
|
}, { client, logger: (message) => logs.push(message) });
|
|
419
462
|
|
|
@@ -421,10 +464,26 @@ describe("cmux sdk", () => {
|
|
|
421
464
|
sessionId: "workspace-1",
|
|
422
465
|
workspaceId: "workspace-1",
|
|
423
466
|
workspaceRef: "workspace:1",
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
467
|
+
terminalPanes: [
|
|
468
|
+
{
|
|
469
|
+
paneId: "pane-1",
|
|
470
|
+
paneRef: "pane:1",
|
|
471
|
+
surfaceId: "surface-1",
|
|
472
|
+
surfaceRef: "surface:1",
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
paneId: "pane-2",
|
|
476
|
+
paneRef: "pane:2",
|
|
477
|
+
surfaceId: "surface-2",
|
|
478
|
+
surfaceRef: "surface:2",
|
|
479
|
+
},
|
|
480
|
+
],
|
|
481
|
+
browserPane: {
|
|
482
|
+
paneId: "pane-3",
|
|
483
|
+
paneRef: "pane:3",
|
|
484
|
+
surfaceId: "surface-3",
|
|
485
|
+
surfaceRef: "surface:3",
|
|
486
|
+
},
|
|
428
487
|
});
|
|
429
488
|
expect(calls).toEqual([
|
|
430
489
|
{
|
|
@@ -436,11 +495,10 @@ describe("cmux sdk", () => {
|
|
|
436
495
|
}),
|
|
437
496
|
},
|
|
438
497
|
{
|
|
439
|
-
method: "
|
|
498
|
+
method: "newSurface",
|
|
440
499
|
params: {
|
|
441
500
|
workspace: "workspace-1",
|
|
442
501
|
type: "terminal",
|
|
443
|
-
direction: "down",
|
|
444
502
|
focus: true,
|
|
445
503
|
},
|
|
446
504
|
},
|
|
@@ -452,6 +510,22 @@ describe("cmux sdk", () => {
|
|
|
452
510
|
text: "cd /workspace/site && pnpm dev\n",
|
|
453
511
|
},
|
|
454
512
|
},
|
|
513
|
+
{
|
|
514
|
+
method: "newSurface",
|
|
515
|
+
params: {
|
|
516
|
+
workspace: "workspace-1",
|
|
517
|
+
type: "terminal",
|
|
518
|
+
focus: false,
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
method: "send",
|
|
523
|
+
params: {
|
|
524
|
+
workspace: "workspace-1",
|
|
525
|
+
surface: "surface-2",
|
|
526
|
+
text: "cd /workspace/site && codex\n",
|
|
527
|
+
},
|
|
528
|
+
},
|
|
455
529
|
{
|
|
456
530
|
method: "waitForRemoteReady",
|
|
457
531
|
params: {
|
|
@@ -468,9 +542,18 @@ describe("cmux sdk", () => {
|
|
|
468
542
|
},
|
|
469
543
|
},
|
|
470
544
|
{
|
|
471
|
-
method: "
|
|
545
|
+
method: "portsKick",
|
|
546
|
+
params: {
|
|
547
|
+
workspace: "workspace-1",
|
|
548
|
+
surface: "surface-2",
|
|
549
|
+
reason: "command",
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
method: "newSurface",
|
|
472
554
|
params: {
|
|
473
555
|
workspace: "workspace-1",
|
|
556
|
+
type: "browser",
|
|
474
557
|
url: "http://localhost:4321",
|
|
475
558
|
focus: true,
|
|
476
559
|
},
|
|
@@ -484,7 +567,8 @@ describe("cmux sdk", () => {
|
|
|
484
567
|
expect(logs).toEqual([
|
|
485
568
|
"cmux: opening website",
|
|
486
569
|
"cmux: connecting remote workspace",
|
|
487
|
-
"cmux: starting
|
|
570
|
+
"cmux: starting terminal in /workspace/site",
|
|
571
|
+
"cmux: starting terminal in /workspace/site",
|
|
488
572
|
"cmux: waiting for remote ports",
|
|
489
573
|
"cmux: refreshing remote ports",
|
|
490
574
|
"cmux: opening http://localhost:4321",
|
|
@@ -611,6 +695,7 @@ describe("cmux sdk", () => {
|
|
|
611
695
|
});
|
|
612
696
|
|
|
613
697
|
function fakeOpenClient(calls: Array<{ method: string; params: unknown }>): CmuxOpenClient {
|
|
698
|
+
let terminalPaneIndex = 0;
|
|
614
699
|
return {
|
|
615
700
|
async newWorkspace(params) {
|
|
616
701
|
calls.push({ method: "newWorkspace", params });
|
|
@@ -622,13 +707,26 @@ function fakeOpenClient(calls: Array<{ method: string; params: unknown }>): Cmux
|
|
|
622
707
|
},
|
|
623
708
|
async newPane(params) {
|
|
624
709
|
calls.push({ method: "newPane", params });
|
|
710
|
+
terminalPaneIndex += 1;
|
|
711
|
+
return {
|
|
712
|
+
workspace: "workspace-1",
|
|
713
|
+
workspaceRef: "workspace:1",
|
|
714
|
+
pane: `pane-${terminalPaneIndex}`,
|
|
715
|
+
paneRef: `pane:${terminalPaneIndex}`,
|
|
716
|
+
surface: `surface-${terminalPaneIndex}`,
|
|
717
|
+
surfaceRef: `surface:${terminalPaneIndex}`,
|
|
718
|
+
};
|
|
719
|
+
},
|
|
720
|
+
async newSurface(params) {
|
|
721
|
+
calls.push({ method: "newSurface", params });
|
|
722
|
+
terminalPaneIndex += 1;
|
|
625
723
|
return {
|
|
626
724
|
workspace: "workspace-1",
|
|
627
725
|
workspaceRef: "workspace:1",
|
|
628
|
-
pane:
|
|
629
|
-
paneRef:
|
|
630
|
-
surface:
|
|
631
|
-
surfaceRef:
|
|
726
|
+
pane: `pane-${terminalPaneIndex}`,
|
|
727
|
+
paneRef: `pane:${terminalPaneIndex}`,
|
|
728
|
+
surface: `surface-${terminalPaneIndex}`,
|
|
729
|
+
surfaceRef: `surface:${terminalPaneIndex}`,
|
|
632
730
|
};
|
|
633
731
|
},
|
|
634
732
|
async send(params) {
|
|
@@ -644,10 +742,10 @@ function fakeOpenClient(calls: Array<{ method: string; params: unknown }>): Cmux
|
|
|
644
742
|
return {
|
|
645
743
|
workspace: "workspace-1",
|
|
646
744
|
workspaceRef: "workspace:1",
|
|
647
|
-
pane: "pane-
|
|
648
|
-
paneRef: "pane:
|
|
649
|
-
surface: "surface-
|
|
650
|
-
surfaceRef: "surface:
|
|
745
|
+
pane: "browser-pane-1",
|
|
746
|
+
paneRef: "pane:browser-1",
|
|
747
|
+
surface: "browser-surface-1",
|
|
748
|
+
surfaceRef: "surface:browser-1",
|
|
651
749
|
};
|
|
652
750
|
},
|
|
653
751
|
async selectWorkspace(workspace) {
|
package/src/index.ts
CHANGED
|
@@ -103,6 +103,14 @@ export type CmuxNewPaneOptions = {
|
|
|
103
103
|
focus?: boolean;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
+
export type CmuxNewSurfaceOptions = {
|
|
107
|
+
workspace?: string;
|
|
108
|
+
pane?: string;
|
|
109
|
+
type?: "terminal" | "browser";
|
|
110
|
+
url?: string;
|
|
111
|
+
focus?: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
106
114
|
export type CmuxPane = {
|
|
107
115
|
workspace?: string;
|
|
108
116
|
workspaceRef?: string;
|
|
@@ -277,6 +285,17 @@ export class CmuxClient {
|
|
|
277
285
|
return paneFromResult(await this.rpc("pane.create", params));
|
|
278
286
|
}
|
|
279
287
|
|
|
288
|
+
async newSurface(options: CmuxNewSurfaceOptions = {}): Promise<CmuxPane> {
|
|
289
|
+
const params: CmuxRpcParams = {};
|
|
290
|
+
if (options.type) params.type = options.type;
|
|
291
|
+
if (options.pane) params.pane_id = options.pane;
|
|
292
|
+
if (options.workspace) params.workspace_id = options.workspace;
|
|
293
|
+
if (options.url) params.url = options.url;
|
|
294
|
+
if (options.focus !== undefined) params.focus = options.focus;
|
|
295
|
+
|
|
296
|
+
return paneFromResult(await this.rpc("surface.create", params));
|
|
297
|
+
}
|
|
298
|
+
|
|
280
299
|
async listWorkspaces(): Promise<CmuxWorkspaceStatus[]> {
|
|
281
300
|
const result = await this.rpc("workspace.list");
|
|
282
301
|
const workspaces = Array.isArray(result.workspaces)
|
|
@@ -477,9 +496,13 @@ export {
|
|
|
477
496
|
CMUX_OPEN_CAPABILITY_ID,
|
|
478
497
|
CMUX_OPEN_SCHEMA_HASH,
|
|
479
498
|
type CmuxOpenInput,
|
|
499
|
+
type CmuxOpenPaneResult,
|
|
480
500
|
type CmuxOpenResult,
|
|
481
501
|
type CmuxOpenSession,
|
|
502
|
+
type CmuxOpenSurfaceLayout,
|
|
482
503
|
type CmuxOpenSshInput,
|
|
504
|
+
type CmuxOpenTerminalDirection,
|
|
505
|
+
type CmuxOpenTerminalInput,
|
|
483
506
|
type CmuxRemoteReadyOptions,
|
|
484
507
|
} from "./capabilities.ts";
|
|
485
508
|
export {
|
package/src/provider.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { BaseProviderPlugin, WorkflowProviderController } from "@rigkit/eng
|
|
|
7
7
|
import {
|
|
8
8
|
CMUX_OPEN_CAPABILITY_ID,
|
|
9
9
|
type CmuxOpenInput,
|
|
10
|
+
type CmuxOpenPaneResult,
|
|
10
11
|
type CmuxOpenResult,
|
|
11
12
|
type CmuxOpenSession,
|
|
12
13
|
} from "./capabilities.ts";
|
|
@@ -82,13 +83,32 @@ export function parseCmuxOpenResult(value: unknown): CmuxOpenResult {
|
|
|
82
83
|
sessionId,
|
|
83
84
|
workspaceId,
|
|
84
85
|
...optionalStringField(value, "workspaceRef"),
|
|
85
|
-
|
|
86
|
-
...
|
|
87
|
-
...optionalStringField(value, "browserPaneId"),
|
|
88
|
-
...optionalStringField(value, "browserSurfaceId"),
|
|
86
|
+
terminalPanes: arrayField(value, "terminalPanes", parseCmuxOpenPaneResult),
|
|
87
|
+
...(value.browserPane !== undefined ? { browserPane: parseCmuxOpenPaneResult(value.browserPane) } : {}),
|
|
89
88
|
};
|
|
90
89
|
}
|
|
91
90
|
|
|
91
|
+
function parseCmuxOpenPaneResult(value: unknown): CmuxOpenPaneResult {
|
|
92
|
+
if (!isRecord(value)) throw new Error(`cmux.open returned a non-object pane result`);
|
|
93
|
+
return {
|
|
94
|
+
...optionalStringField(value, "paneId"),
|
|
95
|
+
...optionalStringField(value, "paneRef"),
|
|
96
|
+
...optionalStringField(value, "surfaceId"),
|
|
97
|
+
...optionalStringField(value, "surfaceRef"),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function arrayField<Item>(
|
|
102
|
+
record: Record<string, unknown>,
|
|
103
|
+
key: string,
|
|
104
|
+
parseItem: (value: unknown) => Item,
|
|
105
|
+
): Item[] {
|
|
106
|
+
const value = record[key];
|
|
107
|
+
if (value === undefined) return [];
|
|
108
|
+
if (!Array.isArray(value)) throw new Error(`cmux.open result ${key} must be an array`);
|
|
109
|
+
return value.map(parseItem);
|
|
110
|
+
}
|
|
111
|
+
|
|
92
112
|
function optionalStringField(record: Record<string, unknown>, key: string): Record<string, string> {
|
|
93
113
|
const value = stringField(record, key);
|
|
94
114
|
return value ? { [key]: value } : {};
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const RIGKIT_PROVIDER_CMUX_VERSION = "0.2.
|
|
1
|
+
export const RIGKIT_PROVIDER_CMUX_VERSION = "0.2.6";
|