@devframes/plugin-terminals 0.0.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.
@@ -0,0 +1,247 @@
1
+ import { defineRpcFunction } from "devframe";
2
+ import * as v from "valibot";
3
+ import { colors } from "devframe/utils/colors";
4
+ import { defineDiagnostics } from "nostics";
5
+ import { ansiFormatter } from "nostics/formatters/ansi";
6
+ //#region src/node/diagnostics.ts
7
+ const formatAnsi = ansiFormatter(colors);
8
+ function reporter(d, { method = "warn" } = {}) {
9
+ console[method](formatAnsi(d));
10
+ }
11
+ /**
12
+ * Structured diagnostics for the terminals plugin. Uses the plugin's own
13
+ * `DP_TERMINALS_` prefix per the built-in plugin convention, keeping it
14
+ * collision-free with devframe core (`DF`) and the hub (`DF8xxx`).
15
+ */
16
+ const diagnostics = defineDiagnostics({
17
+ docsBase: "https://devfra.me/errors",
18
+ reporters: [reporter],
19
+ codes: {
20
+ DP_TERMINALS_0001: {
21
+ why: (p) => `Terminal session "${p.id}" does not exist`,
22
+ fix: "Spawn a session first, or refresh the session list."
23
+ },
24
+ DP_TERMINALS_0002: {
25
+ why: (p) => `Spawning the arbitrary command "${p.command}" is not allowed`,
26
+ fix: "Add it to `presets`, or pass `allowArbitraryCommands: true` to createTerminalsDevframe()."
27
+ },
28
+ DP_TERMINALS_0003: {
29
+ why: (p) => `Cannot write to read-only terminal session "${p.id}"`,
30
+ fix: "Spawn the session with `mode: \"interactive\"` to accept input."
31
+ },
32
+ DP_TERMINALS_0004: { why: (p) => `Failed to spawn "${p.command}": ${p.reason}` },
33
+ DP_TERMINALS_0005: {
34
+ why: "Native PTY bindings (zigpty) are unavailable; interactive sessions fall back to pipe-based terminal emulation. Full-screen TUIs may not render correctly.",
35
+ fix: "Check that this platform is covered by zigpty's prebuilds (Linux/macOS/Windows, x64/arm64, glibc/musl) and that the dependency installed intact."
36
+ },
37
+ DP_TERMINALS_0006: { why: (p) => `Unknown terminal preset "${p.id}"` },
38
+ DP_TERMINALS_0007: {
39
+ why: "Terminals manager is not initialised on this context",
40
+ fix: "Call setupTerminals(ctx) (or use createTerminalsDevframe) before invoking terminal RPCs."
41
+ }
42
+ }
43
+ });
44
+ //#endregion
45
+ //#region src/node/context.ts
46
+ const managers = /* @__PURE__ */ new WeakMap();
47
+ function setTerminalManager(ctx, manager) {
48
+ managers.set(ctx, manager);
49
+ }
50
+ function getTerminalManager(ctx) {
51
+ const manager = managers.get(ctx);
52
+ if (!manager) throw diagnostics.DP_TERMINALS_0007({});
53
+ return manager;
54
+ }
55
+ //#endregion
56
+ //#region src/rpc/schemas.ts
57
+ const terminalModeSchema = v.picklist(["interactive", "readonly"]);
58
+ const spawnRequestSchema = v.object({
59
+ presetId: v.optional(v.string()),
60
+ command: v.optional(v.string()),
61
+ args: v.optional(v.array(v.string())),
62
+ cwd: v.optional(v.string()),
63
+ mode: v.optional(terminalModeSchema),
64
+ title: v.optional(v.string()),
65
+ cols: v.optional(v.number()),
66
+ rows: v.optional(v.number()),
67
+ env: v.optional(v.record(v.string(), v.string()))
68
+ });
69
+ const sessionInfoSchema = v.object({
70
+ id: v.string(),
71
+ title: v.string(),
72
+ processName: v.optional(v.string()),
73
+ customTitle: v.optional(v.string()),
74
+ mode: terminalModeSchema,
75
+ status: v.picklist([
76
+ "running",
77
+ "exited",
78
+ "error"
79
+ ]),
80
+ backend: v.picklist(["pty", "pipe"]),
81
+ command: v.string(),
82
+ args: v.array(v.string()),
83
+ cwd: v.string(),
84
+ cols: v.number(),
85
+ rows: v.number(),
86
+ pid: v.optional(v.number()),
87
+ exitCode: v.optional(v.number()),
88
+ icon: v.optional(v.string()),
89
+ channel: v.optional(v.string()),
90
+ presetId: v.optional(v.string()),
91
+ createdAt: v.number()
92
+ });
93
+ const presetSchema = v.object({
94
+ id: v.string(),
95
+ title: v.string(),
96
+ command: v.string(),
97
+ args: v.array(v.string()),
98
+ mode: terminalModeSchema,
99
+ icon: v.optional(v.string())
100
+ });
101
+ //#endregion
102
+ //#region src/rpc/functions/list.ts
103
+ const list = defineRpcFunction({
104
+ name: "devframes-plugin-terminals:list",
105
+ type: "query",
106
+ jsonSerializable: true,
107
+ snapshot: true,
108
+ args: [],
109
+ returns: v.array(sessionInfoSchema),
110
+ agent: {
111
+ description: "List the current terminal sessions with their status, mode, and command.",
112
+ safety: "read"
113
+ },
114
+ setup: (ctx) => ({ handler: () => getTerminalManager(ctx).list() })
115
+ });
116
+ //#endregion
117
+ //#region src/rpc/functions/presets.ts
118
+ const presets = defineRpcFunction({
119
+ name: "devframes-plugin-terminals:presets",
120
+ type: "query",
121
+ jsonSerializable: true,
122
+ snapshot: true,
123
+ args: [],
124
+ returns: v.array(presetSchema),
125
+ setup: (ctx) => ({ handler: () => getTerminalManager(ctx).getPresets().map((p) => ({
126
+ id: p.id,
127
+ title: p.title,
128
+ command: p.command,
129
+ args: p.args ?? [],
130
+ mode: p.mode ?? "readonly",
131
+ icon: p.icon
132
+ })) })
133
+ });
134
+ //#endregion
135
+ //#region src/rpc/functions/remove.ts
136
+ const remove = defineRpcFunction({
137
+ name: "devframes-plugin-terminals:remove",
138
+ type: "action",
139
+ jsonSerializable: true,
140
+ args: [v.object({ id: v.string() })],
141
+ returns: v.void(),
142
+ agent: {
143
+ description: "Kill a terminal session and discard it (process, stream, and scrollback).",
144
+ safety: "destructive"
145
+ },
146
+ setup: (ctx) => ({ handler: ({ id }) => {
147
+ getTerminalManager(ctx).remove(id);
148
+ } })
149
+ });
150
+ //#endregion
151
+ //#region src/rpc/functions/rename.ts
152
+ const rename = defineRpcFunction({
153
+ name: "devframes-plugin-terminals:rename",
154
+ type: "action",
155
+ jsonSerializable: true,
156
+ args: [v.object({
157
+ id: v.string(),
158
+ title: v.string()
159
+ })],
160
+ returns: v.void(),
161
+ setup: (ctx) => ({ handler: ({ id, title }) => {
162
+ getTerminalManager(ctx).rename(id, title);
163
+ } })
164
+ });
165
+ //#endregion
166
+ //#region src/rpc/functions/resize.ts
167
+ const resize = defineRpcFunction({
168
+ name: "devframes-plugin-terminals:resize",
169
+ type: "action",
170
+ jsonSerializable: true,
171
+ args: [v.object({
172
+ id: v.string(),
173
+ cols: v.pipe(v.number(), v.integer(), v.minValue(1)),
174
+ rows: v.pipe(v.number(), v.integer(), v.minValue(1))
175
+ })],
176
+ returns: v.void(),
177
+ setup: (ctx) => ({ handler: ({ id, cols, rows }) => {
178
+ getTerminalManager(ctx).resize(id, cols, rows);
179
+ } })
180
+ });
181
+ //#endregion
182
+ //#region src/rpc/functions/restart.ts
183
+ const restart = defineRpcFunction({
184
+ name: "devframes-plugin-terminals:restart",
185
+ type: "action",
186
+ jsonSerializable: true,
187
+ args: [v.object({ id: v.string() })],
188
+ returns: sessionInfoSchema,
189
+ setup: (ctx) => ({ handler: ({ id }) => getTerminalManager(ctx).restart(id) })
190
+ });
191
+ //#endregion
192
+ //#region src/rpc/functions/spawn.ts
193
+ const spawn = defineRpcFunction({
194
+ name: "devframes-plugin-terminals:spawn",
195
+ type: "action",
196
+ jsonSerializable: true,
197
+ args: [spawnRequestSchema],
198
+ returns: sessionInfoSchema,
199
+ agent: {
200
+ description: "Spawn a new terminal session. Pass a preset id, or a command + mode. Interactive sessions accept input; readonly sessions only stream output.",
201
+ safety: "action"
202
+ },
203
+ setup: (ctx) => ({ handler: (req) => getTerminalManager(ctx).spawn(req ?? {}) })
204
+ });
205
+ //#endregion
206
+ //#region src/rpc/functions/terminate.ts
207
+ const terminate = defineRpcFunction({
208
+ name: "devframes-plugin-terminals:terminate",
209
+ type: "action",
210
+ jsonSerializable: true,
211
+ args: [v.object({ id: v.string() })],
212
+ returns: v.void(),
213
+ agent: {
214
+ description: "Terminate a terminal session's running process. The session and its scrollback are kept; use restart to run it again.",
215
+ safety: "destructive"
216
+ },
217
+ setup: (ctx) => ({ handler: ({ id }) => {
218
+ getTerminalManager(ctx).terminate(id);
219
+ } })
220
+ });
221
+ //#endregion
222
+ //#region src/rpc/index.ts
223
+ const serverFunctions = [
224
+ list,
225
+ presets,
226
+ spawn,
227
+ defineRpcFunction({
228
+ name: "devframes-plugin-terminals:write",
229
+ type: "action",
230
+ jsonSerializable: true,
231
+ args: [v.object({
232
+ id: v.string(),
233
+ data: v.string()
234
+ })],
235
+ returns: v.void(),
236
+ setup: (ctx) => ({ handler: ({ id, data }) => {
237
+ getTerminalManager(ctx).write(id, data);
238
+ } })
239
+ }),
240
+ resize,
241
+ terminate,
242
+ restart,
243
+ rename,
244
+ remove
245
+ ];
246
+ //#endregion
247
+ export { diagnostics as i, getTerminalManager as n, setTerminalManager as r, serverFunctions as t };