@agimon-ai/po-runner 0.0.0-alpha.21 → 0.0.0-alpha.23
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/dist/apiContracts.cjs +8 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/apiContracts.mjs +8 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/{bashRunService-Ci0cGdYO.d.mts → bashRunService-BL4JIUi2.d.mts} +6 -1
- package/dist/bashRunService-BL4JIUi2.d.mts.map +1 -0
- package/dist/{bashRunService-DjOH5kye.cjs → bashRunService-CJhMNrPz.cjs} +8 -4
- package/dist/bashRunService-CJhMNrPz.cjs.map +1 -0
- package/dist/{bashRunService-Cl3pYccr.mjs → bashRunService-DbYsj1zc.mjs} +8 -4
- package/dist/bashRunService-DbYsj1zc.mjs.map +1 -0
- package/dist/bashRunService.cjs +1 -1
- package/dist/bashRunService.d.mts +1 -1
- package/dist/bashRunService.d.mts.map +1 -1
- package/dist/bashRunService.mjs +1 -1
- package/dist/bashTool.cjs +1 -1
- package/dist/bashTool.d.mts +2 -2
- package/dist/bashTool.mjs +1 -1
- package/dist/bin/cli.cjs +1 -1
- package/dist/bin/cli.mjs +1 -1
- package/dist/extensions/mcp.cjs +1 -0
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.mjs +1 -0
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/extensions/native.mjs +533 -256
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +24 -6
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +23 -5
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +459 -517
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/{headless-Dko_yBiy.cjs → headless-BqYKFHwy.cjs} +31 -6
- package/dist/headless-BqYKFHwy.cjs.map +1 -0
- package/dist/{headless-DOlCixyf.mjs → headless-CV-eb5rz.mjs} +31 -6
- package/dist/headless-CV-eb5rz.mjs.map +1 -0
- package/dist/{index-BzeXJk6V.d.mts → index-BciAgV5f.d.mts} +11 -4
- package/dist/index-BciAgV5f.d.mts.map +1 -0
- package/dist/{index--xF7DvsE.d.mts → index-CXbcmf-G.d.mts} +2 -2
- package/dist/{index--xF7DvsE.d.mts.map → index-CXbcmf-G.d.mts.map} +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/responseEnvelope.d.mts +1 -1
- package/dist/{rmuxBackend-Dh0CYVAL.cjs → rmuxBackend-Cod2H85V.cjs} +2 -2
- package/dist/{rmuxBackend-Dh0CYVAL.cjs.map → rmuxBackend-Cod2H85V.cjs.map} +1 -1
- package/dist/rmuxBackend.cjs +1 -1
- package/dist/{rtkProcessor-DSGbM-Cm.cjs → rtkProcessor-xwARIxhe.cjs} +2 -2
- package/dist/{rtkProcessor-DSGbM-Cm.cjs.map → rtkProcessor-xwARIxhe.cjs.map} +1 -1
- package/dist/rtkProcessor.cjs +1 -1
- package/dist/{runnerDependencies-7h_cxOva.mjs → runnerDependencies-CamKwphq.mjs} +2 -2
- package/dist/{runnerDependencies-7h_cxOva.mjs.map → runnerDependencies-CamKwphq.mjs.map} +1 -1
- package/dist/{runnerDependencies-DSun3xO-.cjs → runnerDependencies-HIqMQp9b.cjs} +4 -4
- package/dist/{runnerDependencies-DSun3xO-.cjs.map → runnerDependencies-HIqMQp9b.cjs.map} +1 -1
- package/dist/runnerDependencies.cjs +1 -1
- package/dist/runnerDependencies.d.mts +1 -1
- package/dist/runnerDependencies.mjs +1 -1
- package/dist/{type-Do0YxwVQ.d.mts → type-BQ2shEB5.d.mts} +2 -2
- package/dist/{type-Do0YxwVQ.d.mts.map → type-BQ2shEB5.d.mts.map} +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +17 -17
- package/dist/bashRunService-Ci0cGdYO.d.mts.map +0 -1
- package/dist/bashRunService-Cl3pYccr.mjs.map +0 -1
- package/dist/bashRunService-DjOH5kye.cjs.map +0 -1
- package/dist/headless-DOlCixyf.mjs.map +0 -1
- package/dist/headless-Dko_yBiy.cjs.map +0 -1
- package/dist/index-BzeXJk6V.d.mts.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, defineNativePlugin, defineSessionStore, nativeApiTransport, sessionApiAddress } from "@agimon-ai/po-core/native";
|
|
2
|
+
import { ArrowRightIcon, Button, Dot, FolderIcon, Input, ListRow, PlayCircleIcon, SearchIcon, Sheet, SheetBody, SheetContent, SheetFooter, SheetHeader, SheetTitle, StatusBadge, fontOf } from "@agimon-ai/po-native-components";
|
|
3
|
+
import { createElement, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
4
|
import { View } from "react-native-unistyles/components/native/View";
|
|
3
5
|
import { Text } from "react-native-unistyles/components/native/Text";
|
|
4
6
|
import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
|
|
5
|
-
import {
|
|
6
|
-
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
7
|
+
import { Pressable } from "react-native-unistyles/components/native/Pressable";
|
|
7
8
|
import { StyleSheet } from "react-native-unistyles";
|
|
8
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/extensions/workspaces/sessions/(frontend)/_lib/format.ts
|
|
@@ -51,10 +52,55 @@ function tailLineNumbers(lastLineNumber, count) {
|
|
|
51
52
|
const first = Math.max(1, lastLineNumber - count + 1);
|
|
52
53
|
return Array.from({ length: count }, (_, index) => first + index);
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* A folder as the command bar names it: the session directory by its last
|
|
57
|
+
* segment, a folder inside it relative to it, anything else as written.
|
|
58
|
+
*/
|
|
59
|
+
function runnerFolderLabel(folder, sessionDirectory) {
|
|
60
|
+
if (folder === "" || folder === sessionDirectory) return sessionDirectory === void 0 ? "session directory" : sessionDirectory.split("/").pop() ?? sessionDirectory;
|
|
61
|
+
if (sessionDirectory !== void 0 && folder.startsWith(`${sessionDirectory}/`)) return folder.slice(sessionDirectory.length + 1);
|
|
62
|
+
return folder;
|
|
63
|
+
}
|
|
64
|
+
/** The folders the picker offers past the session directory: those in use, then the one in effect. */
|
|
65
|
+
function runnerFolders(inUse, current, sessionDirectory) {
|
|
66
|
+
const folders = /* @__PURE__ */ new Set([...inUse, current]);
|
|
67
|
+
folders.delete("");
|
|
68
|
+
if (sessionDirectory !== void 0) folders.delete(sessionDirectory);
|
|
69
|
+
return [...folders];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Undoes iOS smart punctuation, which React Native cannot switch off: a shell
|
|
73
|
+
* reads ‘hi’ as literal quotes and a long dash before port as no flag at all.
|
|
74
|
+
*/
|
|
75
|
+
function plainPunctuation(text) {
|
|
76
|
+
return text.replace(/[‘’]/g, "'").replace(/[“”]/g, "\"").replace(/\u2014/g, "--");
|
|
77
|
+
}
|
|
78
|
+
/** The badge a run wears: its state while running, then how it ended. */
|
|
79
|
+
function runnerBadge(run) {
|
|
80
|
+
if (run === void 0) return {
|
|
81
|
+
tone: "neutral",
|
|
82
|
+
label: "gone"
|
|
83
|
+
};
|
|
84
|
+
if (run.state === "running") return {
|
|
85
|
+
tone: "running",
|
|
86
|
+
label: "running"
|
|
87
|
+
};
|
|
88
|
+
if (run.exit === void 0 || run.exit.reason === "stopped") return {
|
|
89
|
+
tone: "neutral",
|
|
90
|
+
label: "stopped"
|
|
91
|
+
};
|
|
92
|
+
return run.exit.reason === "completed" && (run.exit.code === 0 || run.exit.code === null) ? {
|
|
93
|
+
tone: "ok",
|
|
94
|
+
label: "done"
|
|
95
|
+
} : {
|
|
96
|
+
tone: "error",
|
|
97
|
+
label: run.exit.reason.replace("_", " ")
|
|
98
|
+
};
|
|
99
|
+
}
|
|
54
100
|
//#endregion
|
|
55
101
|
//#region src/extensions/workspaces/sessions/(frontend)/_lib/launchLine.ts
|
|
56
102
|
/**
|
|
57
|
-
* The `/runners start` line the
|
|
103
|
+
* The `/runners start` line the runners panel's command bar sends.
|
|
58
104
|
*
|
|
59
105
|
* WHY THE BUILDER LIVES HERE AND THE PARSER DOES NOT:
|
|
60
106
|
* Cockpit code is compiled into the host bundle and may import only this
|
|
@@ -70,6 +116,21 @@ const RUNNER_SHELL_REQUEST = {
|
|
|
70
116
|
name: "shell",
|
|
71
117
|
interactive: true
|
|
72
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* What the runners panel's Run sends: the typed command on a terminal when
|
|
121
|
+
* one is available, or a login shell when nothing was typed.
|
|
122
|
+
*/
|
|
123
|
+
function runnerRunRequest(command, cwd) {
|
|
124
|
+
const where = cwd.trim() === "" ? {} : { cwd };
|
|
125
|
+
return command.trim() === "" ? {
|
|
126
|
+
...RUNNER_SHELL_REQUEST,
|
|
127
|
+
...where
|
|
128
|
+
} : {
|
|
129
|
+
command,
|
|
130
|
+
...where,
|
|
131
|
+
preferInteractive: true
|
|
132
|
+
};
|
|
133
|
+
}
|
|
73
134
|
/** Quotes a value that carries whitespace, which is how a value survives tokenizing. */
|
|
74
135
|
function quote(value) {
|
|
75
136
|
return /\s/u.test(value) ? `"${value}"` : value;
|
|
@@ -80,7 +141,7 @@ function runnerLaunchLine(request) {
|
|
|
80
141
|
const pairs = [
|
|
81
142
|
...name === "" ? [] : [`name=${quote(name)}`],
|
|
82
143
|
...cwd === "" ? [] : [`cwd=${quote(cwd)}`],
|
|
83
|
-
...request.interactive === true ? ["interactive=true"] : []
|
|
144
|
+
...request.interactive === true ? ["interactive=true"] : request.preferInteractive === true ? ["interactive=prefer"] : []
|
|
84
145
|
];
|
|
85
146
|
return [
|
|
86
147
|
RUNNERS_START_LINE,
|
|
@@ -89,12 +150,6 @@ function runnerLaunchLine(request) {
|
|
|
89
150
|
request.command.trim()
|
|
90
151
|
].join(" ");
|
|
91
152
|
}
|
|
92
|
-
/** What the form will not let a reader send, worded the way the session would answer. */
|
|
93
|
-
function launchProblems(request) {
|
|
94
|
-
const problems = [];
|
|
95
|
-
if (request.command.trim() === "") problems.push("A runner needs a command to run.");
|
|
96
|
-
return problems;
|
|
97
|
-
}
|
|
98
153
|
//#endregion
|
|
99
154
|
//#region src/constants/webRunners.ts
|
|
100
155
|
const RUNNER_RUNS_TYPE = "runner_runs";
|
|
@@ -153,122 +208,142 @@ const runnerRunsChannel = runners.channel({
|
|
|
153
208
|
channel: RUNNER_RUNS_TYPE,
|
|
154
209
|
parse(input) {
|
|
155
210
|
if (!isRecord$1(input) || !Array.isArray(input.runs)) return null;
|
|
156
|
-
|
|
211
|
+
const failure = input.startFailure;
|
|
212
|
+
return {
|
|
213
|
+
runs: input.runs.filter(isRecord$1),
|
|
214
|
+
...typeof input.sessionDirectory === "string" ? { sessionDirectory: input.sessionDirectory } : {},
|
|
215
|
+
...isRecord$1(failure) && typeof failure.id === "string" && typeof failure.message === "string" ? { startFailure: {
|
|
216
|
+
id: failure.id,
|
|
217
|
+
message: failure.message
|
|
218
|
+
} } : {}
|
|
219
|
+
};
|
|
157
220
|
},
|
|
158
|
-
reduce(current,
|
|
159
|
-
const stillRunning = new Set(runs.filter((run) => run.state === "running").map((run) => run.id));
|
|
221
|
+
reduce(current, payload) {
|
|
222
|
+
const stillRunning = new Set(payload.runs.filter((run) => run.state === "running").map((run) => run.id));
|
|
160
223
|
return {
|
|
161
|
-
|
|
224
|
+
...payload,
|
|
162
225
|
stopRequested: current.stopRequested.filter((id) => stillRunning.has(id))
|
|
163
226
|
};
|
|
164
227
|
}
|
|
165
228
|
});
|
|
166
229
|
//#endregion
|
|
167
|
-
//#region src/extensions/workspaces/sessions/(frontend)/_components/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
230
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_components/RunnerCommandBar.native.tsx
|
|
231
|
+
/**
|
|
232
|
+
* The phone's command bar: the command and Run on one line, the folder it runs
|
|
233
|
+
* in on the row under it, chosen from a bottom sheet.
|
|
234
|
+
*/
|
|
235
|
+
function RunnerCommandBar({ command, onCommandChange, cwd, onCwdChange, sessionDirectory, folders, problem, onLaunch, defaultFolderOpen = false }) {
|
|
236
|
+
const [folderOpen, setFolderOpen] = useState(defaultFolderOpen);
|
|
237
|
+
const [path, setPath] = useState("");
|
|
238
|
+
const pick = (next) => {
|
|
239
|
+
onCwdChange(next);
|
|
240
|
+
setPath("");
|
|
241
|
+
setFolderOpen(false);
|
|
178
242
|
};
|
|
179
|
-
const
|
|
180
|
-
return /* @__PURE__ */
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
testID: "runner-launch-interactive",
|
|
212
|
-
variant: "outline",
|
|
213
|
-
accessibilityState: { checked: interactive },
|
|
214
|
-
onPress: () => setInteractive((value) => !value),
|
|
215
|
-
children: interactive ? "Terminal enabled" : "Terminal off"
|
|
216
|
-
}),
|
|
217
|
-
/* @__PURE__ */ jsx(Label, { children: "Sent to the session" }),
|
|
218
|
-
/* @__PURE__ */ jsx(Text, {
|
|
219
|
-
selectable: true,
|
|
220
|
-
testID: "runner-launch-line",
|
|
221
|
-
style: styles$3.line,
|
|
222
|
-
children: runnerLaunchLine(request)
|
|
223
|
-
}),
|
|
224
|
-
problems.length > 0 ? /* @__PURE__ */ jsx(Text, {
|
|
225
|
-
style: styles$3.error,
|
|
226
|
-
children: problems.join(" ")
|
|
227
|
-
}) : null,
|
|
228
|
-
/* @__PURE__ */ jsx(Button, {
|
|
229
|
-
variant: "outline",
|
|
230
|
-
onPress: onClose,
|
|
231
|
-
children: "Cancel"
|
|
232
|
-
}),
|
|
233
|
-
/* @__PURE__ */ jsx(Button, {
|
|
234
|
-
testID: "runner-launch-submit",
|
|
235
|
-
disabled: problems.length > 0,
|
|
236
|
-
onPress: () => {
|
|
237
|
-
if (problems.length > 0) return;
|
|
238
|
-
requestRunnerStart(send, sessionId, request);
|
|
239
|
-
onClose();
|
|
240
|
-
},
|
|
241
|
-
children: "Launch"
|
|
243
|
+
const choices = ["", ...runnerFolders(folders, cwd, sessionDirectory)];
|
|
244
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
245
|
+
style: styles$3.bar,
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsxs(View, {
|
|
248
|
+
style: styles$3.line,
|
|
249
|
+
children: [/* @__PURE__ */ jsx(View, {
|
|
250
|
+
style: styles$3.grow,
|
|
251
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
252
|
+
testID: "runners-panel-command",
|
|
253
|
+
accessibilityLabel: "Command to run",
|
|
254
|
+
size: "lg",
|
|
255
|
+
value: command,
|
|
256
|
+
onChangeText: (text) => onCommandChange(plainPunctuation(text)),
|
|
257
|
+
onSubmitEditing: onLaunch,
|
|
258
|
+
placeholder: "a command, like pnpm dev",
|
|
259
|
+
autoCapitalize: "none",
|
|
260
|
+
autoCorrect: false,
|
|
261
|
+
returnKeyType: "go"
|
|
262
|
+
})
|
|
263
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
264
|
+
testID: "runners-panel-run",
|
|
265
|
+
variant: "primary",
|
|
266
|
+
size: "lg",
|
|
267
|
+
onPress: onLaunch,
|
|
268
|
+
children: command.trim() === "" ? "Open shell" : "Run"
|
|
269
|
+
})]
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ jsx(ListRow, {
|
|
272
|
+
leading: /* @__PURE__ */ jsx(FolderIcon, {
|
|
273
|
+
size: 14,
|
|
274
|
+
color: problem === void 0 ? "faint" : "red"
|
|
242
275
|
}),
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
276
|
+
title: runnerFolderLabel(cwd, sessionDirectory),
|
|
277
|
+
description: "Runs in",
|
|
278
|
+
disclosure: true,
|
|
279
|
+
accessibilityLabel: `Runs in ${runnerFolderLabel(cwd, sessionDirectory)}`,
|
|
280
|
+
onPress: () => setFolderOpen(true)
|
|
281
|
+
}),
|
|
282
|
+
problem === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
283
|
+
style: styles$3.meta,
|
|
284
|
+
children: "Leave the command empty for a plain shell."
|
|
285
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
286
|
+
accessibilityRole: "alert",
|
|
287
|
+
testID: "runners-panel-problem",
|
|
288
|
+
style: styles$3.problem,
|
|
289
|
+
children: problem
|
|
290
|
+
}),
|
|
291
|
+
/* @__PURE__ */ jsx(Sheet, {
|
|
292
|
+
open: folderOpen,
|
|
293
|
+
onOpenChange: setFolderOpen,
|
|
294
|
+
children: /* @__PURE__ */ jsxs(SheetContent, { children: [
|
|
295
|
+
/* @__PURE__ */ jsx(SheetHeader, {
|
|
296
|
+
closeLabel: "Close folders",
|
|
297
|
+
children: /* @__PURE__ */ jsx(SheetTitle, { children: "Runs in" })
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ jsxs(SheetBody, { children: [/* @__PURE__ */ jsx(View, { children: choices.map((folder) => /* @__PURE__ */ jsx(ListRow, {
|
|
300
|
+
title: folder === "" ? "Session directory" : runnerFolderLabel(folder, sessionDirectory),
|
|
301
|
+
description: folder === "" ? sessionDirectory ?? "Where this session started" : folder,
|
|
302
|
+
selected: folder === cwd,
|
|
303
|
+
onPress: () => pick(folder)
|
|
304
|
+
}, folder)) }), /* @__PURE__ */ jsx(Input, {
|
|
305
|
+
testID: "runners-panel-cwd-path",
|
|
306
|
+
accessibilityLabel: "Another folder",
|
|
307
|
+
value: path,
|
|
308
|
+
onChangeText: (text) => setPath(plainPunctuation(text)),
|
|
309
|
+
onSubmitEditing: () => {
|
|
310
|
+
if (path.trim() !== "") pick(path.trim());
|
|
311
|
+
},
|
|
312
|
+
placeholder: "another folder, like packages/web",
|
|
313
|
+
autoCapitalize: "none",
|
|
314
|
+
autoCorrect: false,
|
|
315
|
+
returnKeyType: "done"
|
|
316
|
+
})] }),
|
|
317
|
+
/* @__PURE__ */ jsx(SheetFooter, { children: "A relative folder starts from the session directory." })
|
|
318
|
+
] })
|
|
319
|
+
})
|
|
320
|
+
]
|
|
249
321
|
});
|
|
250
322
|
}
|
|
251
323
|
const styles$3 = StyleSheet.create((theme) => ({
|
|
252
|
-
|
|
253
|
-
gap: theme.spacing(
|
|
324
|
+
bar: {
|
|
325
|
+
gap: theme.spacing(1.5),
|
|
254
326
|
uni__dependencies: [0]
|
|
255
327
|
},
|
|
256
328
|
line: {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
329
|
+
flexDirection: "row",
|
|
330
|
+
alignItems: "center",
|
|
331
|
+
gap: theme.spacing(2),
|
|
260
332
|
uni__dependencies: [0]
|
|
261
333
|
},
|
|
262
|
-
|
|
263
|
-
|
|
334
|
+
grow: { flex: 1 },
|
|
335
|
+
meta: {
|
|
336
|
+
color: theme.colors.dim,
|
|
264
337
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
265
|
-
fontSize: theme.fontSize.
|
|
338
|
+
fontSize: theme.fontSize.sm,
|
|
339
|
+
lineHeight: theme.lineHeight.sm,
|
|
266
340
|
uni__dependencies: [0]
|
|
267
341
|
},
|
|
268
|
-
|
|
269
|
-
color: theme.colors.
|
|
342
|
+
problem: {
|
|
343
|
+
color: theme.colors.red,
|
|
270
344
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
271
345
|
fontSize: theme.fontSize.sm,
|
|
346
|
+
lineHeight: theme.lineHeight.sm,
|
|
272
347
|
uni__dependencies: [0]
|
|
273
348
|
}
|
|
274
349
|
}));
|
|
@@ -437,6 +512,43 @@ function useRunnerTail(sessionId, runId, running) {
|
|
|
437
512
|
}
|
|
438
513
|
//#endregion
|
|
439
514
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/RunnerLogPanel.native.tsx
|
|
515
|
+
/** The top of both runner screens: name, outcome, an action, and the command in its folder. */
|
|
516
|
+
function RunnerRunHeader({ run, name, sessionDirectory, action }) {
|
|
517
|
+
const badge = runnerBadge(run);
|
|
518
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(View, {
|
|
519
|
+
style: styles$2.header,
|
|
520
|
+
children: [
|
|
521
|
+
/* @__PURE__ */ jsx(Text, {
|
|
522
|
+
numberOfLines: 1,
|
|
523
|
+
style: styles$2.title,
|
|
524
|
+
children: name
|
|
525
|
+
}),
|
|
526
|
+
/* @__PURE__ */ jsx(StatusBadge, {
|
|
527
|
+
tone: badge.tone,
|
|
528
|
+
children: badge.label
|
|
529
|
+
}),
|
|
530
|
+
/* @__PURE__ */ jsx(View, { style: styles$2.grow }),
|
|
531
|
+
action
|
|
532
|
+
]
|
|
533
|
+
}), run ? /* @__PURE__ */ jsxs(View, {
|
|
534
|
+
style: styles$2.commandBox,
|
|
535
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
536
|
+
selectable: true,
|
|
537
|
+
style: styles$2.command,
|
|
538
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
539
|
+
style: styles$2.prompt,
|
|
540
|
+
children: "$ "
|
|
541
|
+
}), run.command]
|
|
542
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
543
|
+
numberOfLines: 1,
|
|
544
|
+
style: styles$2.folder,
|
|
545
|
+
children: ["in ", runnerFolderLabel(run.cwd, sessionDirectory)]
|
|
546
|
+
})]
|
|
547
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
548
|
+
style: styles$2.meta,
|
|
549
|
+
children: "No longer listed; its log stays readable."
|
|
550
|
+
})] });
|
|
551
|
+
}
|
|
440
552
|
function runnerLogTab(run) {
|
|
441
553
|
return {
|
|
442
554
|
id: `runner-log-${run.id}`,
|
|
@@ -501,74 +613,66 @@ function RunnerLogPanel({ sessionId, runId, sendSessionFrame }) {
|
|
|
501
613
|
]);
|
|
502
614
|
const lines = logViewLines(slice?.text ?? "", [], 2e3).lines;
|
|
503
615
|
const numbers = query === "" ? tailLineNumbers(slice?.totalLines ?? 0, lines.length) : slice?.lineNumbers ?? [];
|
|
616
|
+
const gutter = String(numbers.at(-1) ?? 0).length;
|
|
504
617
|
const stopping = session.stopRequested.includes(runId);
|
|
618
|
+
const live = following && query === "" && slice?.running === true;
|
|
505
619
|
return /* @__PURE__ */ jsxs(View, {
|
|
506
620
|
testID: "runner-log-panel",
|
|
507
621
|
style: styles$2.panel,
|
|
508
622
|
children: [
|
|
509
|
-
/* @__PURE__ */ jsx(
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
623
|
+
/* @__PURE__ */ jsx(RunnerRunHeader, {
|
|
624
|
+
run,
|
|
625
|
+
name: run?.name ?? runId,
|
|
626
|
+
sessionDirectory: session.sessionDirectory,
|
|
627
|
+
action: run?.state === "running" && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
628
|
+
variant: stopping ? "outline" : "danger-outline",
|
|
629
|
+
size: "sm",
|
|
630
|
+
disabled: stopping,
|
|
631
|
+
onPress: () => requestRunnerStop(sendSessionFrame, sessionId, runId),
|
|
632
|
+
children: stopping ? "Stopping…" : "Stop"
|
|
633
|
+
}) : null
|
|
516
634
|
}),
|
|
517
|
-
run ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
|
|
518
|
-
selectable: true,
|
|
519
|
-
style: styles$2.meta,
|
|
520
|
-
children: run.command
|
|
521
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
522
|
-
selectable: true,
|
|
523
|
-
style: styles$2.meta,
|
|
524
|
-
children: run.cwd
|
|
525
|
-
})] }) : null,
|
|
526
|
-
run?.state === "running" && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
527
|
-
variant: "danger-outline",
|
|
528
|
-
disabled: stopping,
|
|
529
|
-
onPress: () => requestRunnerStop(sendSessionFrame, sessionId, runId),
|
|
530
|
-
children: stopping ? "Stopping…" : "Stop"
|
|
531
|
-
}) : null,
|
|
532
|
-
/* @__PURE__ */ jsx(Label, { children: "Search this log" }),
|
|
533
635
|
/* @__PURE__ */ jsx(Input, {
|
|
534
636
|
testID: "runner-log-search",
|
|
637
|
+
accessibilityLabel: "Search this log",
|
|
638
|
+
placeholder: "Search output",
|
|
535
639
|
value: typed,
|
|
536
|
-
onChangeText: setTyped
|
|
640
|
+
onChangeText: setTyped,
|
|
641
|
+
autoCapitalize: "none",
|
|
642
|
+
returnKeyType: "search"
|
|
537
643
|
}),
|
|
538
644
|
/* @__PURE__ */ jsxs(View, {
|
|
539
645
|
style: styles$2.actions,
|
|
540
646
|
children: [
|
|
541
647
|
/* @__PURE__ */ jsx(Button, {
|
|
542
|
-
variant: "outline",
|
|
648
|
+
variant: ignoreCase ? "subtle" : "outline",
|
|
649
|
+
size: "sm",
|
|
543
650
|
accessibilityState: { selected: ignoreCase },
|
|
544
651
|
onPress: () => setIgnoreCase((value) => !value),
|
|
545
|
-
children: "
|
|
652
|
+
children: ignoreCase ? "Any case" : "Match case"
|
|
546
653
|
}),
|
|
547
|
-
/* @__PURE__ */
|
|
548
|
-
variant: "outline",
|
|
549
|
-
|
|
654
|
+
query === "" ? null : /* @__PURE__ */ jsx(Button, {
|
|
655
|
+
variant: context > 0 ? "subtle" : "outline",
|
|
656
|
+
size: "sm",
|
|
550
657
|
onPress: () => setContext((value) => value === 0 ? 2 : 0),
|
|
551
|
-
children:
|
|
552
|
-
"± ",
|
|
553
|
-
context,
|
|
554
|
-
" lines"
|
|
555
|
-
]
|
|
658
|
+
children: `± ${String(context)} lines`
|
|
556
659
|
}),
|
|
557
|
-
/* @__PURE__ */ jsx(Button, {
|
|
558
|
-
variant: "outline",
|
|
559
|
-
|
|
560
|
-
accessibilityState: { selected:
|
|
660
|
+
query === "" && slice?.running === true ? /* @__PURE__ */ jsx(Button, {
|
|
661
|
+
variant: live ? "subtle" : "outline",
|
|
662
|
+
size: "sm",
|
|
663
|
+
accessibilityState: { selected: live },
|
|
561
664
|
onPress: () => setFollowing((value) => !value),
|
|
562
|
-
children:
|
|
563
|
-
})
|
|
665
|
+
children: live ? "Following" : "Follow"
|
|
666
|
+
}) : null
|
|
564
667
|
]
|
|
565
668
|
}),
|
|
566
669
|
/* @__PURE__ */ jsx(Text, {
|
|
567
670
|
style: styles$2.meta,
|
|
568
|
-
children: slice === void 0 ? "Reading…" : query !== "" ? `${slice.lineCount} matching of ${slice.totalLines} lines` : `${
|
|
671
|
+
children: slice === void 0 ? error === "" ? "Reading…" : "" : query !== "" ? `${slice.lineCount} matching of ${slice.totalLines} lines` : `${live ? "Tailing" : "Showing"} last ${lines.length} of ${slice.totalLines} lines`
|
|
569
672
|
}),
|
|
570
673
|
/* @__PURE__ */ jsx(ScrollView, {
|
|
571
674
|
style: styles$2.log,
|
|
675
|
+
contentContainerStyle: styles$2.logBody,
|
|
572
676
|
children: error !== "" ? /* @__PURE__ */ jsx(Text, {
|
|
573
677
|
accessibilityRole: "alert",
|
|
574
678
|
style: styles$2.error,
|
|
@@ -581,17 +685,18 @@ function RunnerLogPanel({ sessionId, runId, sendSessionFrame }) {
|
|
|
581
685
|
children: query === "" ? "The log is empty." : "Nothing matched."
|
|
582
686
|
}) : lines.map((line, index) => /* @__PURE__ */ jsxs(Text, {
|
|
583
687
|
selectable: true,
|
|
584
|
-
style: styles$2.
|
|
585
|
-
children: [
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
]
|
|
688
|
+
style: styles$2.line,
|
|
689
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
690
|
+
style: styles$2.number,
|
|
691
|
+
children: `${String(numbers[index] ?? "").padStart(gutter)} `
|
|
692
|
+
}), line]
|
|
590
693
|
}, `${numbers[index] ?? index}`))
|
|
591
694
|
}),
|
|
592
695
|
/* @__PURE__ */ jsx(Text, {
|
|
593
696
|
selectable: true,
|
|
594
|
-
|
|
697
|
+
numberOfLines: 1,
|
|
698
|
+
ellipsizeMode: "middle",
|
|
699
|
+
style: styles$2.path,
|
|
595
700
|
children: run?.logPath ?? slice?.path ?? ""
|
|
596
701
|
})
|
|
597
702
|
]
|
|
@@ -605,13 +710,46 @@ const styles$2 = StyleSheet.create((theme) => ({
|
|
|
605
710
|
backgroundColor: theme.colors.bg,
|
|
606
711
|
uni__dependencies: [0]
|
|
607
712
|
},
|
|
713
|
+
header: {
|
|
714
|
+
flexDirection: "row",
|
|
715
|
+
alignItems: "center",
|
|
716
|
+
gap: theme.spacing(2),
|
|
717
|
+
minHeight: 32,
|
|
718
|
+
uni__dependencies: [0]
|
|
719
|
+
},
|
|
720
|
+
grow: { flex: 1 },
|
|
608
721
|
title: {
|
|
722
|
+
flexShrink: 1,
|
|
609
723
|
color: theme.colors.hi,
|
|
610
724
|
fontWeight: "600",
|
|
611
725
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
612
726
|
fontSize: theme.fontSize.base,
|
|
613
727
|
uni__dependencies: [0]
|
|
614
728
|
},
|
|
729
|
+
commandBox: {
|
|
730
|
+
gap: theme.spacing(1),
|
|
731
|
+
paddingHorizontal: theme.spacing(3),
|
|
732
|
+
paddingVertical: theme.spacing(2),
|
|
733
|
+
borderRadius: theme.borderRadius.md,
|
|
734
|
+
backgroundColor: theme.colors.deep,
|
|
735
|
+
uni__dependencies: [0]
|
|
736
|
+
},
|
|
737
|
+
command: {
|
|
738
|
+
color: theme.colors.hi,
|
|
739
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
740
|
+
fontSize: theme.fontSize.sm,
|
|
741
|
+
uni__dependencies: [0]
|
|
742
|
+
},
|
|
743
|
+
prompt: {
|
|
744
|
+
color: theme.colors.faint,
|
|
745
|
+
uni__dependencies: [0]
|
|
746
|
+
},
|
|
747
|
+
folder: {
|
|
748
|
+
color: theme.colors.dim,
|
|
749
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
750
|
+
fontSize: theme.fontSize.xs,
|
|
751
|
+
uni__dependencies: [0]
|
|
752
|
+
},
|
|
615
753
|
meta: {
|
|
616
754
|
color: theme.colors.dim,
|
|
617
755
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
@@ -622,7 +760,7 @@ const styles$2 = StyleSheet.create((theme) => ({
|
|
|
622
760
|
error: {
|
|
623
761
|
color: theme.colors.red,
|
|
624
762
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
625
|
-
fontSize: theme.fontSize.
|
|
763
|
+
fontSize: theme.fontSize.sm,
|
|
626
764
|
uni__dependencies: [0]
|
|
627
765
|
},
|
|
628
766
|
actions: {
|
|
@@ -633,8 +771,30 @@ const styles$2 = StyleSheet.create((theme) => ({
|
|
|
633
771
|
},
|
|
634
772
|
log: {
|
|
635
773
|
flex: 1,
|
|
774
|
+
borderRadius: theme.borderRadius.md,
|
|
636
775
|
backgroundColor: theme.colors.deep,
|
|
637
776
|
uni__dependencies: [0]
|
|
777
|
+
},
|
|
778
|
+
logBody: {
|
|
779
|
+
padding: theme.spacing(3),
|
|
780
|
+
uni__dependencies: [0]
|
|
781
|
+
},
|
|
782
|
+
line: {
|
|
783
|
+
color: theme.colors.text,
|
|
784
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
785
|
+
fontSize: theme.fontSize.xs,
|
|
786
|
+
lineHeight: theme.lineHeight.sm,
|
|
787
|
+
uni__dependencies: [0]
|
|
788
|
+
},
|
|
789
|
+
number: {
|
|
790
|
+
color: theme.colors.faint,
|
|
791
|
+
uni__dependencies: [0]
|
|
792
|
+
},
|
|
793
|
+
path: {
|
|
794
|
+
color: theme.colors.faint,
|
|
795
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
796
|
+
fontSize: theme.fontSize.xs,
|
|
797
|
+
uni__dependencies: [0]
|
|
638
798
|
}
|
|
639
799
|
}));
|
|
640
800
|
//#endregion
|
|
@@ -672,26 +832,78 @@ function runnerShellTab(run) {
|
|
|
672
832
|
})
|
|
673
833
|
};
|
|
674
834
|
}
|
|
675
|
-
|
|
835
|
+
/**
|
|
836
|
+
* One terminal runner, full screen. A phone keyboard has no Ctrl key, so
|
|
837
|
+
* Ctrl-C is a button; Search opens the runner's log, which the sink keeps
|
|
838
|
+
* scrubbed beside the raw bytes for exactly that.
|
|
839
|
+
*/
|
|
840
|
+
function RunnerShellPanel({ sessionId, runId, renderRunnerTerminal, sendSessionFrame, openTransientTab }) {
|
|
841
|
+
const session = useRunners(sessionId);
|
|
842
|
+
const run = session.runs.find((candidate) => candidate.id === runId);
|
|
843
|
+
const live = run?.state === "running";
|
|
844
|
+
const stopping = session.stopRequested.includes(runId);
|
|
676
845
|
const request = useMemo(() => sessionId === null ? void 0 : {
|
|
677
846
|
key: `${sessionId}:${runId}`,
|
|
678
847
|
read: (from, generation, signal) => readRunnerScreen(sessionId, runId, from, generation, signal),
|
|
679
848
|
write: (text) => sendRunnerInput(sessionId, runId, text)
|
|
680
849
|
}, [sessionId, runId]);
|
|
681
|
-
return /* @__PURE__ */
|
|
850
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
682
851
|
style: styles$1.panel,
|
|
683
|
-
children:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
852
|
+
children: [
|
|
853
|
+
/* @__PURE__ */ jsx(RunnerRunHeader, {
|
|
854
|
+
run,
|
|
855
|
+
name: run?.name ?? runId,
|
|
856
|
+
sessionDirectory: session.sessionDirectory,
|
|
857
|
+
action: live && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
858
|
+
testID: "runner-shell-stop",
|
|
859
|
+
variant: stopping ? "outline" : "danger-outline",
|
|
860
|
+
size: "sm",
|
|
861
|
+
disabled: stopping,
|
|
862
|
+
onPress: () => requestRunnerStop(sendSessionFrame, sessionId, runId),
|
|
863
|
+
children: stopping ? "Stopping…" : "Stop"
|
|
864
|
+
}) : null
|
|
865
|
+
}),
|
|
866
|
+
/* @__PURE__ */ jsx(View, {
|
|
867
|
+
style: styles$1.terminal,
|
|
868
|
+
children: request === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
869
|
+
style: styles$1.meta,
|
|
870
|
+
children: "Select a session to attach to this runner."
|
|
871
|
+
}) : renderRunnerTerminal?.(request)
|
|
872
|
+
}),
|
|
873
|
+
/* @__PURE__ */ jsxs(View, {
|
|
874
|
+
style: styles$1.actions,
|
|
875
|
+
children: [run ? /* @__PURE__ */ jsxs(Button, {
|
|
876
|
+
testID: "runner-shell-search",
|
|
877
|
+
variant: "outline",
|
|
878
|
+
onPress: () => openTransientTab(runnerLogTab(run)),
|
|
879
|
+
style: styles$1.action,
|
|
880
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, { size: 14 }), "Search output"]
|
|
881
|
+
}) : null, live && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
882
|
+
testID: "runner-shell-interrupt",
|
|
883
|
+
variant: "outline",
|
|
884
|
+
onPress: () => void sendRunnerInput(sessionId, runId, ""),
|
|
885
|
+
style: styles$1.action,
|
|
886
|
+
children: "Ctrl-C"
|
|
887
|
+
}) : null]
|
|
888
|
+
})
|
|
889
|
+
]
|
|
687
890
|
});
|
|
688
891
|
}
|
|
689
892
|
const styles$1 = StyleSheet.create((theme) => ({
|
|
690
893
|
panel: {
|
|
691
894
|
flex: 1,
|
|
895
|
+
gap: theme.spacing(2),
|
|
896
|
+
padding: theme.spacing(3),
|
|
692
897
|
backgroundColor: theme.colors.bg,
|
|
693
898
|
uni__dependencies: [0]
|
|
694
899
|
},
|
|
900
|
+
actions: {
|
|
901
|
+
flexDirection: "row",
|
|
902
|
+
gap: theme.spacing(2),
|
|
903
|
+
uni__dependencies: [0]
|
|
904
|
+
},
|
|
905
|
+
action: { flex: 1 },
|
|
906
|
+
terminal: { flex: 1 },
|
|
695
907
|
meta: {
|
|
696
908
|
color: theme.colors.dim,
|
|
697
909
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
@@ -701,6 +913,10 @@ const styles$1 = StyleSheet.create((theme) => ({
|
|
|
701
913
|
}));
|
|
702
914
|
//#endregion
|
|
703
915
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/RunnersPanel.native.tsx
|
|
916
|
+
/** A runner's own screen: its terminal when it has one, its log when it does not. */
|
|
917
|
+
function runnerTab(run) {
|
|
918
|
+
return run.interactive ? runnerShellTab(run) : runnerLogTab(run);
|
|
919
|
+
}
|
|
704
920
|
function runnersTab() {
|
|
705
921
|
return {
|
|
706
922
|
id: "runner-runs",
|
|
@@ -708,60 +924,46 @@ function runnersTab() {
|
|
|
708
924
|
panel: RunnersPanel
|
|
709
925
|
};
|
|
710
926
|
}
|
|
711
|
-
function RunnerRow({ run, props, stopping, now
|
|
927
|
+
function RunnerRow({ run, props, stopping, now }) {
|
|
712
928
|
const tail = useRunnerTail(props.sessionId, run.id, run.state === "running");
|
|
713
|
-
return /* @__PURE__ */ jsxs(
|
|
929
|
+
return /* @__PURE__ */ jsxs(Pressable, {
|
|
714
930
|
testID: `activity-runner-${run.id}`,
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
931
|
+
accessibilityRole: "button",
|
|
932
|
+
accessibilityLabel: `Open ${run.name}`,
|
|
933
|
+
onPress: () => props.openTransientTab(runnerTab(run)),
|
|
934
|
+
style: ({ pressed }) => [styles.row, pressed && styles.pressed],
|
|
935
|
+
children: [/* @__PURE__ */ jsxs(View, {
|
|
936
|
+
style: styles.line,
|
|
937
|
+
children: [
|
|
938
|
+
/* @__PURE__ */ jsx(Dot, {
|
|
719
939
|
tone: "yellow",
|
|
720
940
|
pulse: true
|
|
721
941
|
}),
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}) : null,
|
|
748
|
-
detail && run.interactive && props.sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
749
|
-
variant: "outline",
|
|
750
|
-
onPress: () => props.openTransientTab(runnerShellTab(run)),
|
|
751
|
-
children: "Shell"
|
|
752
|
-
}) : null,
|
|
753
|
-
props.sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
754
|
-
testID: `activity-runner-stop-${run.id}`,
|
|
755
|
-
variant: stopping ? "outline" : "danger-outline",
|
|
756
|
-
disabled: stopping,
|
|
757
|
-
onPress: () => {
|
|
758
|
-
if (props.sessionId !== null) requestRunnerStop(props.sendSessionFrame, props.sessionId, run.id);
|
|
759
|
-
},
|
|
760
|
-
children: stopping ? "Stopping…" : "Stop"
|
|
761
|
-
}) : null
|
|
762
|
-
]
|
|
763
|
-
})
|
|
764
|
-
]
|
|
942
|
+
/* @__PURE__ */ jsx(Text, {
|
|
943
|
+
numberOfLines: 1,
|
|
944
|
+
style: styles.name,
|
|
945
|
+
children: run.name
|
|
946
|
+
}),
|
|
947
|
+
/* @__PURE__ */ jsx(Text, {
|
|
948
|
+
style: styles.meta,
|
|
949
|
+
children: formatRunnerUptime(run.startedAt, now)
|
|
950
|
+
}),
|
|
951
|
+
props.sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
952
|
+
testID: `activity-runner-stop-${run.id}`,
|
|
953
|
+
size: "xs",
|
|
954
|
+
variant: stopping ? "outline" : "danger-outline",
|
|
955
|
+
disabled: stopping,
|
|
956
|
+
onPress: () => {
|
|
957
|
+
if (props.sessionId !== null) requestRunnerStop(props.sendSessionFrame, props.sessionId, run.id);
|
|
958
|
+
},
|
|
959
|
+
children: stopping ? "Stopping…" : "Stop"
|
|
960
|
+
}) : null
|
|
961
|
+
]
|
|
962
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
963
|
+
numberOfLines: 1,
|
|
964
|
+
style: styles.tail,
|
|
965
|
+
children: tail ?? `$ ${run.command}`
|
|
966
|
+
})]
|
|
765
967
|
});
|
|
766
968
|
}
|
|
767
969
|
function RunnersActivitySection(props) {
|
|
@@ -773,15 +975,23 @@ function RunnersActivitySection(props) {
|
|
|
773
975
|
return () => clearInterval(timer);
|
|
774
976
|
}, []);
|
|
775
977
|
return running.length === 0 ? /* @__PURE__ */ jsxs(View, {
|
|
776
|
-
style: styles.
|
|
978
|
+
style: styles.idle,
|
|
777
979
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
778
980
|
style: styles.meta,
|
|
779
981
|
children: "Idle"
|
|
780
|
-
}), props.sessionId !== null ? /* @__PURE__ */
|
|
781
|
-
|
|
982
|
+
}), props.sessionId !== null ? /* @__PURE__ */ jsxs(Pressable, {
|
|
983
|
+
accessibilityRole: "button",
|
|
782
984
|
testID: "activity-runner-launch",
|
|
985
|
+
hitSlop: 10,
|
|
783
986
|
onPress: () => props.openTransientTab(runnersTab()),
|
|
784
|
-
|
|
987
|
+
style: styles.launch,
|
|
988
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
989
|
+
style: styles.launchText,
|
|
990
|
+
children: "Launch a runner"
|
|
991
|
+
}), /* @__PURE__ */ jsx(ArrowRightIcon, {
|
|
992
|
+
size: 14,
|
|
993
|
+
color: "green"
|
|
994
|
+
})]
|
|
785
995
|
}) : null]
|
|
786
996
|
}) : /* @__PURE__ */ jsx(View, { children: running.map((run) => /* @__PURE__ */ jsx(RunnerRow, {
|
|
787
997
|
run,
|
|
@@ -790,13 +1000,22 @@ function RunnersActivitySection(props) {
|
|
|
790
1000
|
stopping: session.stopRequested.includes(run.id)
|
|
791
1001
|
}, run.id)) });
|
|
792
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Every running runner and the one place to start another: type a command
|
|
1005
|
+
* and Run hosts it on a terminal (plain output when the host has no rmux or
|
|
1006
|
+
* tmux), or leave it empty for a login shell. The started runner opens on
|
|
1007
|
+
* its own screen.
|
|
1008
|
+
*/
|
|
793
1009
|
function RunnersPanel(props) {
|
|
794
1010
|
const { openTransientTab } = props;
|
|
795
1011
|
const session = useRunners(props.sessionId);
|
|
796
1012
|
const running = session.runs.filter((run) => run.state === "running");
|
|
797
|
-
const [
|
|
1013
|
+
const [command, setCommand] = useState("");
|
|
1014
|
+
const [cwd, setCwd] = useState("");
|
|
798
1015
|
const [now, setNow] = useState(() => Date.now());
|
|
799
1016
|
const baseline = useRef(null);
|
|
1017
|
+
/** What the pending launch ran, put back if the start fails. */
|
|
1018
|
+
const launched = useRef("");
|
|
800
1019
|
const timeout = useRef(void 0);
|
|
801
1020
|
useEffect(() => {
|
|
802
1021
|
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
@@ -804,81 +1023,117 @@ function RunnersPanel(props) {
|
|
|
804
1023
|
}, []);
|
|
805
1024
|
useEffect(() => {
|
|
806
1025
|
if (baseline.current === null) return;
|
|
807
|
-
const
|
|
808
|
-
if (
|
|
1026
|
+
const started = session.runs.find((run) => run.state === "running" && !baseline.current?.has(run.id));
|
|
1027
|
+
if (started === void 0) return;
|
|
809
1028
|
baseline.current = null;
|
|
810
1029
|
clearTimeout(timeout.current);
|
|
811
|
-
openTransientTab(
|
|
1030
|
+
openTransientTab(runnerTab(started));
|
|
812
1031
|
}, [session.runs, openTransientTab]);
|
|
1032
|
+
const failureId = session.startFailure?.id;
|
|
1033
|
+
useEffect(() => {
|
|
1034
|
+
if (failureId === void 0 || baseline.current === null) return;
|
|
1035
|
+
baseline.current = null;
|
|
1036
|
+
clearTimeout(timeout.current);
|
|
1037
|
+
setCommand(launched.current);
|
|
1038
|
+
}, [failureId]);
|
|
813
1039
|
useEffect(() => () => clearTimeout(timeout.current), []);
|
|
1040
|
+
const launch = () => {
|
|
1041
|
+
if (props.sessionId === null) return;
|
|
1042
|
+
baseline.current = new Set(session.runs.map((run) => run.id));
|
|
1043
|
+
clearTimeout(timeout.current);
|
|
1044
|
+
timeout.current = setTimeout(() => {
|
|
1045
|
+
baseline.current = null;
|
|
1046
|
+
}, 3e4);
|
|
1047
|
+
requestRunnerStart(props.sendSessionFrame, props.sessionId, runnerRunRequest(command, cwd));
|
|
1048
|
+
launched.current = command;
|
|
1049
|
+
setCommand("");
|
|
1050
|
+
};
|
|
814
1051
|
return /* @__PURE__ */ jsxs(View, {
|
|
815
1052
|
style: styles.panel,
|
|
816
1053
|
children: [
|
|
817
|
-
/* @__PURE__ */
|
|
818
|
-
style: styles.
|
|
819
|
-
children:
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
onPress: () => {
|
|
826
|
-
if (props.sessionId === null) return;
|
|
827
|
-
baseline.current = new Set(session.runs.map((run) => run.id));
|
|
828
|
-
clearTimeout(timeout.current);
|
|
829
|
-
timeout.current = setTimeout(() => {
|
|
830
|
-
baseline.current = null;
|
|
831
|
-
}, 3e4);
|
|
832
|
-
requestRunnerStart(props.sendSessionFrame, props.sessionId, RUNNER_SHELL_REQUEST);
|
|
833
|
-
},
|
|
834
|
-
children: "Start shell"
|
|
835
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
836
|
-
onPress: () => setLaunching(true),
|
|
837
|
-
children: "Run command"
|
|
1054
|
+
/* @__PURE__ */ jsxs(View, {
|
|
1055
|
+
style: styles.header,
|
|
1056
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1057
|
+
style: styles.title,
|
|
1058
|
+
children: "Runners"
|
|
1059
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
1060
|
+
style: styles.meta,
|
|
1061
|
+
children: running.length === 0 ? "Nothing running" : `${running.length} running`
|
|
838
1062
|
})]
|
|
1063
|
+
}),
|
|
1064
|
+
props.sessionId !== null ? /* @__PURE__ */ jsx(RunnerCommandBar, {
|
|
1065
|
+
command,
|
|
1066
|
+
onCommandChange: setCommand,
|
|
1067
|
+
cwd,
|
|
1068
|
+
onCwdChange: setCwd,
|
|
1069
|
+
folders: running.map((run) => run.cwd),
|
|
1070
|
+
...session.sessionDirectory === void 0 ? {} : { sessionDirectory: session.sessionDirectory },
|
|
1071
|
+
...session.startFailure === void 0 ? {} : { problem: session.startFailure.message },
|
|
1072
|
+
onLaunch: launch
|
|
839
1073
|
}) : null,
|
|
840
1074
|
/* @__PURE__ */ jsx(ScrollView, { children: running.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
841
1075
|
style: styles.meta,
|
|
842
|
-
children:
|
|
843
|
-
}) : running.map((run) => /* @__PURE__ */ jsx(
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
defaultCwd: running[0]?.cwd,
|
|
854
|
-
onClose: () => setLaunching(false)
|
|
855
|
-
}) : null
|
|
1076
|
+
children: props.sessionId === null ? "Open a session to start a runner." : "Nothing running yet. Type a command above, like pnpm dev, and it opens as a live terminal."
|
|
1077
|
+
}) : running.map((run) => /* @__PURE__ */ jsx(ListRow, {
|
|
1078
|
+
leading: /* @__PURE__ */ jsx(Dot, {
|
|
1079
|
+
tone: "yellow",
|
|
1080
|
+
pulse: true
|
|
1081
|
+
}),
|
|
1082
|
+
title: run.name,
|
|
1083
|
+
description: `${run.command} · ${formatRunnerUptime(run.startedAt, now)}`,
|
|
1084
|
+
disclosure: true,
|
|
1085
|
+
onPress: () => openTransientTab(runnerTab(run))
|
|
1086
|
+
}, run.id)) })
|
|
856
1087
|
]
|
|
857
1088
|
});
|
|
858
1089
|
}
|
|
859
1090
|
const styles = StyleSheet.create((theme) => ({
|
|
860
1091
|
panel: {
|
|
861
1092
|
flex: 1,
|
|
862
|
-
gap: theme.spacing(
|
|
863
|
-
padding: theme.spacing(
|
|
1093
|
+
gap: theme.spacing(3),
|
|
1094
|
+
padding: theme.spacing(4),
|
|
864
1095
|
backgroundColor: theme.colors.bg,
|
|
865
1096
|
uni__dependencies: [0]
|
|
866
1097
|
},
|
|
1098
|
+
header: {
|
|
1099
|
+
flexDirection: "row",
|
|
1100
|
+
alignItems: "center",
|
|
1101
|
+
gap: theme.spacing(2),
|
|
1102
|
+
uni__dependencies: [0]
|
|
1103
|
+
},
|
|
867
1104
|
row: {
|
|
868
1105
|
gap: theme.spacing(1),
|
|
869
1106
|
paddingVertical: theme.spacing(2),
|
|
870
1107
|
uni__dependencies: [0]
|
|
871
1108
|
},
|
|
872
|
-
|
|
1109
|
+
pressed: { opacity: .6 },
|
|
1110
|
+
line: {
|
|
873
1111
|
flexDirection: "row",
|
|
874
|
-
|
|
1112
|
+
alignItems: "center",
|
|
875
1113
|
gap: theme.spacing(2),
|
|
876
1114
|
uni__dependencies: [0]
|
|
877
1115
|
},
|
|
878
|
-
|
|
1116
|
+
name: {
|
|
1117
|
+
flex: 1,
|
|
879
1118
|
color: theme.colors.hi,
|
|
880
1119
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
881
1120
|
fontSize: theme.fontSize.base,
|
|
1121
|
+
lineHeight: theme.lineHeight.base,
|
|
1122
|
+
fontWeight: theme.fontWeight.medium,
|
|
1123
|
+
uni__dependencies: [0]
|
|
1124
|
+
},
|
|
1125
|
+
tail: {
|
|
1126
|
+
paddingLeft: theme.spacing(3.5),
|
|
1127
|
+
color: theme.colors.dim,
|
|
1128
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
1129
|
+
fontSize: theme.fontSize.sm,
|
|
1130
|
+
lineHeight: theme.lineHeight.sm,
|
|
1131
|
+
uni__dependencies: [0]
|
|
1132
|
+
},
|
|
1133
|
+
title: {
|
|
1134
|
+
color: theme.colors.hi,
|
|
1135
|
+
fontFamily: fontOf(theme.fontFamily.sans),
|
|
1136
|
+
fontSize: theme.fontSize.lg,
|
|
882
1137
|
fontWeight: "600",
|
|
883
1138
|
uni__dependencies: [0]
|
|
884
1139
|
},
|
|
@@ -888,6 +1143,27 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
888
1143
|
fontSize: theme.fontSize.sm,
|
|
889
1144
|
lineHeight: theme.lineHeight.sm,
|
|
890
1145
|
uni__dependencies: [0]
|
|
1146
|
+
},
|
|
1147
|
+
idle: {
|
|
1148
|
+
flexDirection: "row",
|
|
1149
|
+
alignItems: "center",
|
|
1150
|
+
flexWrap: "wrap",
|
|
1151
|
+
gap: theme.spacing(4),
|
|
1152
|
+
uni__dependencies: [0]
|
|
1153
|
+
},
|
|
1154
|
+
launch: {
|
|
1155
|
+
flexDirection: "row",
|
|
1156
|
+
alignItems: "center",
|
|
1157
|
+
gap: theme.spacing(1),
|
|
1158
|
+
uni__dependencies: [0]
|
|
1159
|
+
},
|
|
1160
|
+
launchText: {
|
|
1161
|
+
color: theme.colors.green,
|
|
1162
|
+
fontFamily: fontOf(theme.fontFamily.sans),
|
|
1163
|
+
fontSize: theme.fontSize.base,
|
|
1164
|
+
lineHeight: theme.lineHeight.base,
|
|
1165
|
+
fontWeight: theme.fontWeight.medium,
|
|
1166
|
+
uni__dependencies: [0]
|
|
891
1167
|
}
|
|
892
1168
|
}));
|
|
893
1169
|
//#endregion
|
|
@@ -895,6 +1171,7 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
895
1171
|
var runners_native_default = defineActivityGroup({
|
|
896
1172
|
name: "runners",
|
|
897
1173
|
keys: "r l",
|
|
1174
|
+
icon: () => createElement(PlayCircleIcon),
|
|
898
1175
|
statusKey: "po-runner-runners",
|
|
899
1176
|
activeSource: runnerActivitySource,
|
|
900
1177
|
transientTab: runnersTab,
|