@libtmux/workspace-cli 0.1.0-alpha.11
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/LICENSE +21 -0
- package/README.md +330 -0
- package/THIRD-PARTY-NOTICES.md +183 -0
- package/dist/app-w7cbtf0n.js +747 -0
- package/dist/completions-2a9fqwq3.js +282 -0
- package/dist/extensions-8v6mfszj.js +268 -0
- package/dist/index-0j386v40.js +7112 -0
- package/dist/main-1de529jm.js +37 -0
- package/dist/main-bfnr2nkj.js +3106 -0
- package/dist/main-c8atq2pj.js +470 -0
- package/dist/main-gh3pafqb.js +369 -0
- package/dist/main.js +35 -0
- package/dist/progress-n08zapg2.js +1649 -0
- package/dist/reference-ebfxh6yp.js +90 -0
- package/dist/tmux-wa2zn8z1.js +893 -0
- package/docs/command-reference.md +227 -0
- package/docs/commands.json +1125 -0
- package/docs/completions/_tmux-workspace +239 -0
- package/docs/completions/tmux-workspace.bash +257 -0
- package/docs/completions/tmux-workspace.fish +430 -0
- package/package.json +49 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliError,
|
|
3
|
+
expandPath,
|
|
4
|
+
mapping,
|
|
5
|
+
scalarText,
|
|
6
|
+
sequence
|
|
7
|
+
} from "./main-gh3pafqb.js";
|
|
8
|
+
|
|
9
|
+
// src/process.ts
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { open } from "node:fs/promises";
|
|
12
|
+
import { Readable } from "node:stream";
|
|
13
|
+
import { pipeline } from "node:stream/promises";
|
|
14
|
+
import { StringDecoder } from "node:string_decoder";
|
|
15
|
+
import { isatty } from "node:tty";
|
|
16
|
+
async function openTerminal(options) {
|
|
17
|
+
const fd = [0, 1, 2].find(isatty);
|
|
18
|
+
if (fd !== undefined)
|
|
19
|
+
return open(`/dev/fd/${fd}`, "r+");
|
|
20
|
+
const query = await processRun(["ps", "-p", String(process.pid), "-o", "tty="], {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
env: options.env,
|
|
23
|
+
signal: AbortSignal.any([
|
|
24
|
+
...options.signal ? [options.signal] : [],
|
|
25
|
+
AbortSignal.timeout(1000)
|
|
26
|
+
])
|
|
27
|
+
});
|
|
28
|
+
const name = query.stdout.trim();
|
|
29
|
+
if (query.code !== 0 || !/^(?:\/dev\/)?[a-zA-Z0-9][a-zA-Z0-9/_-]*$/.test(name))
|
|
30
|
+
throw new Error("No controlling terminal");
|
|
31
|
+
return open(name.startsWith("/dev/") ? name : `/dev/${name}`, "r+");
|
|
32
|
+
}
|
|
33
|
+
function tokenize(value) {
|
|
34
|
+
const args = [];
|
|
35
|
+
let word = "";
|
|
36
|
+
let quote = "";
|
|
37
|
+
let active = false;
|
|
38
|
+
for (let index = 0;index < value.length; index++) {
|
|
39
|
+
const character = value[index];
|
|
40
|
+
if (character === "\\" && quote !== "'") {
|
|
41
|
+
const next = value[++index];
|
|
42
|
+
if (next === undefined)
|
|
43
|
+
throw new Error("Incomplete escape in command");
|
|
44
|
+
if (quote === '"' && !['"', "\\"].includes(next))
|
|
45
|
+
word += "\\";
|
|
46
|
+
word += next;
|
|
47
|
+
active = true;
|
|
48
|
+
} else if (quote) {
|
|
49
|
+
if (character === quote)
|
|
50
|
+
quote = "";
|
|
51
|
+
else
|
|
52
|
+
word += character;
|
|
53
|
+
} else if (character === '"' || character === "'") {
|
|
54
|
+
quote = character;
|
|
55
|
+
active = true;
|
|
56
|
+
} else if (/\s/u.test(character)) {
|
|
57
|
+
if (active)
|
|
58
|
+
args.push(word);
|
|
59
|
+
word = "";
|
|
60
|
+
active = false;
|
|
61
|
+
} else {
|
|
62
|
+
word += character;
|
|
63
|
+
active = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (quote)
|
|
67
|
+
throw new Error("Unterminated quote in command");
|
|
68
|
+
if (active)
|
|
69
|
+
args.push(word);
|
|
70
|
+
if (!args[0])
|
|
71
|
+
throw new Error("Command needs an executable");
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
async function processRun(argv, options) {
|
|
75
|
+
options.signal?.throwIfAborted();
|
|
76
|
+
if (!argv[0])
|
|
77
|
+
throw new Error("Command needs an executable");
|
|
78
|
+
if (options.terminal && options.input !== undefined)
|
|
79
|
+
throw new Error("Terminal processes cannot also receive captured input");
|
|
80
|
+
const grouped = process.platform !== "win32";
|
|
81
|
+
const terminal = options.terminal ? await openTerminal(options) : undefined;
|
|
82
|
+
let child;
|
|
83
|
+
try {
|
|
84
|
+
options.signal?.throwIfAborted();
|
|
85
|
+
child = spawn(argv[0], argv.slice(1), {
|
|
86
|
+
cwd: options.cwd,
|
|
87
|
+
env: options.env,
|
|
88
|
+
stdio: terminal ? options.terminal === "input" ? [terminal.fd, "pipe", "pipe"] : [terminal.fd, terminal.fd, terminal.fd] : [options.input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
89
|
+
detached: grouped && !terminal
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
await terminal?.close();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
let failure;
|
|
96
|
+
let escalation;
|
|
97
|
+
let drainDeadline;
|
|
98
|
+
const forcedClose = { stdout: false, stderr: false };
|
|
99
|
+
let escalationDone;
|
|
100
|
+
let finishEscalation;
|
|
101
|
+
const kill = (signal) => {
|
|
102
|
+
try {
|
|
103
|
+
if (grouped && !terminal && child.pid !== undefined) {
|
|
104
|
+
process.kill(-child.pid, signal);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return child.kill(signal);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error.code === "ESRCH")
|
|
110
|
+
return false;
|
|
111
|
+
failure ??= error;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const stop = () => {
|
|
116
|
+
if (escalationDone)
|
|
117
|
+
return;
|
|
118
|
+
if (!kill("SIGTERM")) {
|
|
119
|
+
escalationDone = Promise.resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
escalationDone = new Promise((resolve) => {
|
|
123
|
+
finishEscalation = resolve;
|
|
124
|
+
escalation = setTimeout(() => {
|
|
125
|
+
kill("SIGKILL");
|
|
126
|
+
resolve();
|
|
127
|
+
}, 500);
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const closed = new Promise((resolve) => {
|
|
131
|
+
child.once("error", (error) => {
|
|
132
|
+
failure ??= error;
|
|
133
|
+
});
|
|
134
|
+
child.once("close", (code, signal) => {
|
|
135
|
+
clearTimeout(drainDeadline);
|
|
136
|
+
if (escalationDone && (!grouped || terminal || !kill(0))) {
|
|
137
|
+
clearTimeout(escalation);
|
|
138
|
+
finishEscalation?.();
|
|
139
|
+
}
|
|
140
|
+
resolve(options.signal?.aborted ? 130 : code ?? (signal ? 1 : 0));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
if (!terminal)
|
|
144
|
+
child.once("exit", () => {
|
|
145
|
+
drainDeadline = setTimeout(() => {
|
|
146
|
+
stop();
|
|
147
|
+
escalationDone?.then(() => {
|
|
148
|
+
for (const name of ["stdout", "stderr"]) {
|
|
149
|
+
const stream = child[name];
|
|
150
|
+
if (stream && !stream.readableEnded) {
|
|
151
|
+
forcedClose[name] = true;
|
|
152
|
+
stream.destroy();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}, 100);
|
|
157
|
+
});
|
|
158
|
+
options.signal?.addEventListener("abort", stop, { once: true });
|
|
159
|
+
if (options.signal?.aborted)
|
|
160
|
+
stop();
|
|
161
|
+
const input = child.stdin ? pipeline(Readable.from([options.input]), child.stdin, options.signal ? { signal: options.signal } : {}).catch((error) => {
|
|
162
|
+
if (!options.signal?.aborted)
|
|
163
|
+
failure ??= error;
|
|
164
|
+
stop();
|
|
165
|
+
}) : Promise.resolve();
|
|
166
|
+
const drain = async (stream, name) => {
|
|
167
|
+
if (!stream)
|
|
168
|
+
return { text: "", truncated: false };
|
|
169
|
+
const decoder = new StringDecoder("utf8");
|
|
170
|
+
const chunks = [];
|
|
171
|
+
let bytes = 0;
|
|
172
|
+
let total = 0;
|
|
173
|
+
try {
|
|
174
|
+
for await (const chunk of stream) {
|
|
175
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
176
|
+
total += buffer.length;
|
|
177
|
+
const retained = buffer.subarray(0, Math.max(0, 65536 - bytes));
|
|
178
|
+
if (retained.length)
|
|
179
|
+
chunks.push(Buffer.from(retained));
|
|
180
|
+
bytes += retained.length;
|
|
181
|
+
const text = decoder.write(buffer);
|
|
182
|
+
if (text)
|
|
183
|
+
await options.output?.(name, text);
|
|
184
|
+
}
|
|
185
|
+
const final = decoder.end();
|
|
186
|
+
if (final)
|
|
187
|
+
await options.output?.(name, final);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
const cancelled = options.signal?.aborted && error === options.signal.reason;
|
|
190
|
+
const stopped = forcedClose[name] && error.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
191
|
+
if (!cancelled && !stopped)
|
|
192
|
+
failure ??= error;
|
|
193
|
+
stop();
|
|
194
|
+
stream.destroy();
|
|
195
|
+
}
|
|
196
|
+
const truncated = total > bytes || forcedClose[name];
|
|
197
|
+
return {
|
|
198
|
+
text: new TextDecoder().decode(Buffer.concat(chunks), { stream: truncated }),
|
|
199
|
+
truncated
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
try {
|
|
203
|
+
const [code, stdout, stderr] = await Promise.all([
|
|
204
|
+
closed,
|
|
205
|
+
drain(child.stdout, "stdout"),
|
|
206
|
+
drain(child.stderr, "stderr"),
|
|
207
|
+
input
|
|
208
|
+
]);
|
|
209
|
+
if (failure)
|
|
210
|
+
throw failure;
|
|
211
|
+
return {
|
|
212
|
+
code,
|
|
213
|
+
stdout: stdout.text,
|
|
214
|
+
stderr: stderr.text,
|
|
215
|
+
truncated: { stdout: stdout.truncated, stderr: stderr.truncated }
|
|
216
|
+
};
|
|
217
|
+
} finally {
|
|
218
|
+
options.signal?.removeEventListener("abort", stop);
|
|
219
|
+
await escalationDone;
|
|
220
|
+
clearTimeout(escalation);
|
|
221
|
+
clearTimeout(drainDeadline);
|
|
222
|
+
await terminal?.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/normalize.ts
|
|
227
|
+
import { isTmuxName } from "libtmux";
|
|
228
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
229
|
+
var fields = {
|
|
230
|
+
workspace: [
|
|
231
|
+
"session_name",
|
|
232
|
+
"windows",
|
|
233
|
+
"start_directory",
|
|
234
|
+
"environment",
|
|
235
|
+
"shell_command_before",
|
|
236
|
+
"suppress_history",
|
|
237
|
+
"options",
|
|
238
|
+
"global_options",
|
|
239
|
+
"before_script",
|
|
240
|
+
"workspace_builder_options",
|
|
241
|
+
"plugins",
|
|
242
|
+
"workspace_builder",
|
|
243
|
+
"workspace_builder_paths",
|
|
244
|
+
"description"
|
|
245
|
+
],
|
|
246
|
+
window: [
|
|
247
|
+
"window_name",
|
|
248
|
+
"window_index",
|
|
249
|
+
"panes",
|
|
250
|
+
"start_directory",
|
|
251
|
+
"environment",
|
|
252
|
+
"shell_command_before",
|
|
253
|
+
"suppress_history",
|
|
254
|
+
"options",
|
|
255
|
+
"options_after",
|
|
256
|
+
"layout",
|
|
257
|
+
"window_shell",
|
|
258
|
+
"focus",
|
|
259
|
+
"description"
|
|
260
|
+
],
|
|
261
|
+
pane: [
|
|
262
|
+
"shell_command",
|
|
263
|
+
"shell_command_before",
|
|
264
|
+
"start_directory",
|
|
265
|
+
"environment",
|
|
266
|
+
"suppress_history",
|
|
267
|
+
"shell",
|
|
268
|
+
"focus",
|
|
269
|
+
"enter",
|
|
270
|
+
"sleep_before",
|
|
271
|
+
"sleep_after",
|
|
272
|
+
"description"
|
|
273
|
+
],
|
|
274
|
+
command: ["cmd", "enter", "sleep_before", "sleep_after"],
|
|
275
|
+
readiness: ["pane_readiness"]
|
|
276
|
+
};
|
|
277
|
+
function validateFields(data, scope, path, allowExtensionFields) {
|
|
278
|
+
if (allowExtensionFields)
|
|
279
|
+
return;
|
|
280
|
+
for (const key of Object.keys(data)) {
|
|
281
|
+
if (key.startsWith("x-"))
|
|
282
|
+
continue;
|
|
283
|
+
if (!fields[scope].includes(key))
|
|
284
|
+
throw new CliError("unsupported_key", `Unsupported field: ${path}.${key} (prefix a custom key with "x-" to have it ignored)`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function expand(value, context) {
|
|
288
|
+
return expandPath(value, context).replace(/\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z_0-9]*)/g, (original, braced, bare) => context.env[braced ?? bare] ?? original);
|
|
289
|
+
}
|
|
290
|
+
function optionalString(value, name) {
|
|
291
|
+
if (value === undefined || value === null)
|
|
292
|
+
return;
|
|
293
|
+
if (typeof value !== "string")
|
|
294
|
+
throw new Error(`${name} must be a string`);
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
function behavior(data, base, context) {
|
|
298
|
+
if (data.focus === "true" || data.focus === "false")
|
|
299
|
+
data.focus = data.focus === "true";
|
|
300
|
+
for (const name of ["focus", "suppress_history", "enter"]) {
|
|
301
|
+
if (data[name] !== undefined && typeof data[name] !== "boolean")
|
|
302
|
+
throw new Error(`${name} must be boolean`);
|
|
303
|
+
}
|
|
304
|
+
for (const name of ["options", "global_options", "options_after"])
|
|
305
|
+
if (data[name] !== undefined) {
|
|
306
|
+
const options = mapping(data[name], name);
|
|
307
|
+
for (const [key, value] of Object.entries(options)) {
|
|
308
|
+
scalarText(value);
|
|
309
|
+
if (typeof value === "string") {
|
|
310
|
+
const expanded = expand(value, context);
|
|
311
|
+
options[key] = expanded.startsWith(".") ? resolve(base, expanded) : expanded;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
for (const name of ["layout", "window_shell", "shell"])
|
|
316
|
+
optionalString(data[name], name);
|
|
317
|
+
}
|
|
318
|
+
function readiness(data, allowExtensionFields, warnings) {
|
|
319
|
+
const catalog = data.workspace_builder_options == null ? {} : mapping(data.workspace_builder_options, "workspace_builder_options");
|
|
320
|
+
if (!allowExtensionFields) {
|
|
321
|
+
for (const key of Object.keys(catalog))
|
|
322
|
+
if (!key.startsWith("x-") && !fields.readiness.includes(key))
|
|
323
|
+
warnings.push(`Ignoring unknown workspace_builder_options key: ${key}`);
|
|
324
|
+
}
|
|
325
|
+
const value = catalog.pane_readiness == null ? "auto" : scalarText(catalog.pane_readiness).trim().toLowerCase();
|
|
326
|
+
if (value === "auto")
|
|
327
|
+
return "auto";
|
|
328
|
+
if (["always", "true", "on", "yes", "1"].includes(value))
|
|
329
|
+
return "always";
|
|
330
|
+
if (["never", "false", "off", "no", "0"].includes(value))
|
|
331
|
+
return "never";
|
|
332
|
+
throw new Error("pane_readiness must be auto, always, never, or a boolean");
|
|
333
|
+
}
|
|
334
|
+
function directory(value, parent, base, context) {
|
|
335
|
+
const path = optionalString(value, "start_directory");
|
|
336
|
+
if (path === undefined)
|
|
337
|
+
return parent;
|
|
338
|
+
const expanded = expand(path, context);
|
|
339
|
+
return isAbsolute(expanded) ? expanded : resolve(parent ?? base, expanded);
|
|
340
|
+
}
|
|
341
|
+
function environment(value, base, context) {
|
|
342
|
+
if (value === undefined)
|
|
343
|
+
return {};
|
|
344
|
+
return Object.fromEntries(Object.entries(mapping(value, "environment")).map(([key, value2]) => {
|
|
345
|
+
if (!key || key.includes("=") || key.includes("\x00"))
|
|
346
|
+
throw new Error(`Invalid environment name: ${key}`);
|
|
347
|
+
const expanded = expand(optionalString(value2, `environment.${key}`) ?? "", context);
|
|
348
|
+
if (expanded.includes("\x00"))
|
|
349
|
+
throw new Error(`Invalid environment value: ${key}`);
|
|
350
|
+
return [key, expanded.startsWith(".") ? resolve(base, expanded) : expanded];
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
function delays(data) {
|
|
354
|
+
for (const key of ["sleep_before", "sleep_after"]) {
|
|
355
|
+
const delay = data[key];
|
|
356
|
+
if (delay !== undefined && delay !== null && (typeof delay !== "number" || delay < 0 || !Number.isFinite(delay)))
|
|
357
|
+
throw new Error(`${key} must be a nonnegative number or null`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function commands(value, context, path, allowExtensionFields) {
|
|
361
|
+
if (value === undefined || value === null || value === "blank" || value === "pane")
|
|
362
|
+
return [];
|
|
363
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
364
|
+
if (entries.length === 1 && [null, "blank", "pane"].includes(entries[0]))
|
|
365
|
+
return [];
|
|
366
|
+
return entries.map((value2, index) => {
|
|
367
|
+
const item = typeof value2 === "string" ? { cmd: value2 } : mapping(value2, "command");
|
|
368
|
+
validateFields(item, "command", `${path}[${index}]`, allowExtensionFields);
|
|
369
|
+
if (typeof item.cmd !== "string")
|
|
370
|
+
throw new Error("A command needs a string cmd");
|
|
371
|
+
if (item.enter !== undefined && typeof item.enter !== "boolean")
|
|
372
|
+
throw new Error("enter must be boolean");
|
|
373
|
+
delays(item);
|
|
374
|
+
return { ...item, cmd: expand(item.cmd, context) };
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function workspaceName(document, context, override) {
|
|
378
|
+
const name = expand(override ?? optionalString(document.session_name, "session_name") ?? "", context);
|
|
379
|
+
if (!isTmuxName(name))
|
|
380
|
+
throw new Error("session_name must be a valid tmux name");
|
|
381
|
+
return name;
|
|
382
|
+
}
|
|
383
|
+
function normalize(document, path, context, override, options = {}) {
|
|
384
|
+
const data = structuredClone(document);
|
|
385
|
+
const warnings = [];
|
|
386
|
+
const allowExtensionFields = options.allowExtensionFields ?? false;
|
|
387
|
+
validateFields(data, "workspace", "workspace", allowExtensionFields);
|
|
388
|
+
const name = workspaceName(data, context, override);
|
|
389
|
+
const base = dirname(path);
|
|
390
|
+
behavior(data, base, context);
|
|
391
|
+
const policy = readiness(data, allowExtensionFields, warnings);
|
|
392
|
+
const cwd = directory(data.start_directory, undefined, base, context);
|
|
393
|
+
const sessionEnvironment = environment(data.environment, base, context);
|
|
394
|
+
const sessionBefore = commands(data.shell_command_before, context, "shell_command_before", allowExtensionFields);
|
|
395
|
+
const windows = sequence(data.windows, "windows");
|
|
396
|
+
if (!windows.length)
|
|
397
|
+
throw new Error("Workspace must have at least one window");
|
|
398
|
+
const indexes = new Set;
|
|
399
|
+
const normalized = windows.map((value, ordinal) => {
|
|
400
|
+
const windowPath = `windows[${ordinal}]`;
|
|
401
|
+
const window = mapping(value, windowPath);
|
|
402
|
+
validateFields(window, "window", windowPath, allowExtensionFields);
|
|
403
|
+
behavior(window, base, context);
|
|
404
|
+
const windowName = optionalString(window.window_name, "window_name");
|
|
405
|
+
const name2 = windowName === undefined ? undefined : expand(windowName, context);
|
|
406
|
+
if (name2 !== undefined && !isTmuxName(name2))
|
|
407
|
+
throw new Error("window_name must be a valid tmux name");
|
|
408
|
+
const rawIndex = window.window_index;
|
|
409
|
+
const index = rawIndex == null ? undefined : typeof rawIndex === "number" ? rawIndex : typeof rawIndex === "string" && /^\d+$/.test(rawIndex) ? Number(rawIndex) : Number.NaN;
|
|
410
|
+
if (index !== undefined && (!Number.isSafeInteger(index) || index < 0 || index > 2147483647 || indexes.has(index)))
|
|
411
|
+
throw new Error("window_index must be a distinct integer from 0 through 2147483647");
|
|
412
|
+
if (index !== undefined)
|
|
413
|
+
indexes.add(index);
|
|
414
|
+
const windowDirectory = directory(window.start_directory, cwd, base, context);
|
|
415
|
+
const windowEnvironment = environment(window.environment, base, context);
|
|
416
|
+
const before = [
|
|
417
|
+
...sessionBefore,
|
|
418
|
+
...commands(window.shell_command_before, context, `${windowPath}.shell_command_before`, allowExtensionFields)
|
|
419
|
+
];
|
|
420
|
+
const declared = sequence(window.panes ?? [null], "panes");
|
|
421
|
+
const panes = declared.length === 0 ? [null] : declared;
|
|
422
|
+
return {
|
|
423
|
+
data: window,
|
|
424
|
+
name: name2,
|
|
425
|
+
index,
|
|
426
|
+
panes: panes.map((value2, paneIndex) => {
|
|
427
|
+
const panePath = `${windowPath}.panes[${paneIndex}]`;
|
|
428
|
+
const pane = typeof value2 === "string" || Array.isArray(value2) || value2 === null ? { shell_command: value2 } : mapping(value2, "pane");
|
|
429
|
+
validateFields(pane, "pane", panePath, allowExtensionFields);
|
|
430
|
+
const own = commands(pane.shell_command, context, `${panePath}.shell_command`, allowExtensionFields);
|
|
431
|
+
behavior(pane, base, context);
|
|
432
|
+
delays(pane);
|
|
433
|
+
return {
|
|
434
|
+
data: pane,
|
|
435
|
+
commands: [
|
|
436
|
+
...before,
|
|
437
|
+
...commands(pane.shell_command_before, context, `${panePath}.shell_command_before`, allowExtensionFields),
|
|
438
|
+
...own
|
|
439
|
+
],
|
|
440
|
+
directory: directory(pane.start_directory, windowDirectory, base, context),
|
|
441
|
+
environment: {
|
|
442
|
+
...sessionEnvironment,
|
|
443
|
+
...pane.environment !== undefined ? environment(pane.environment, base, context) : windowEnvironment
|
|
444
|
+
},
|
|
445
|
+
shell: optionalString(pane.shell ?? window.window_shell, "shell"),
|
|
446
|
+
suppress: Boolean(pane.suppress_history ?? window.suppress_history ?? data.suppress_history ?? true)
|
|
447
|
+
};
|
|
448
|
+
})
|
|
449
|
+
};
|
|
450
|
+
});
|
|
451
|
+
const beforeScript = optionalString(data.before_script, "before_script");
|
|
452
|
+
let bootstrap;
|
|
453
|
+
if (beforeScript !== undefined) {
|
|
454
|
+
bootstrap = tokenize(expand(beforeScript, context));
|
|
455
|
+
if (bootstrap[0].startsWith("."))
|
|
456
|
+
bootstrap[0] = resolve(base, bootstrap[0]);
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
data,
|
|
460
|
+
name,
|
|
461
|
+
directory: cwd,
|
|
462
|
+
environment: sessionEnvironment,
|
|
463
|
+
windows: normalized,
|
|
464
|
+
bootstrap,
|
|
465
|
+
readiness: policy,
|
|
466
|
+
warnings
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export { openTerminal, tokenize, processRun, expand, workspaceName, normalize };
|