@agimon-ai/po-runner 0.0.0-alpha.21 → 0.0.0-alpha.22
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/{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/native.mjs +212 -203
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +2 -2
- package/dist/extensions/server.mjs +2 -2
- package/dist/extensions/web.mjs +334 -500
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/{headless-Dko_yBiy.cjs → headless-C4Bvl2B-.cjs} +8 -4
- package/dist/headless-C4Bvl2B-.cjs.map +1 -0
- package/dist/{headless-DOlCixyf.mjs → headless-DidUBSSf.mjs} +8 -4
- package/dist/headless-DidUBSSf.mjs.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-BzeXJk6V.d.mts → index-DJkjYgir.d.mts} +3 -3
- package/dist/{index-BzeXJk6V.d.mts.map → index-DJkjYgir.d.mts.map} +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/responseEnvelope.d.mts +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-vkpJfxZZ.cjs} +2 -2
- package/dist/{runnerDependencies-DSun3xO-.cjs.map → runnerDependencies-vkpJfxZZ.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
|
@@ -2,7 +2,7 @@ import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, de
|
|
|
2
2
|
import { View } from "react-native-unistyles/components/native/View";
|
|
3
3
|
import { Text } from "react-native-unistyles/components/native/Text";
|
|
4
4
|
import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
|
|
5
|
-
import { Button, Dot, Input, Label, ListRow,
|
|
5
|
+
import { Button, Dot, Input, Label, ListRow, StatusBadge, fontOf } from "@agimon-ai/po-native-components";
|
|
6
6
|
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
7
7
|
import { StyleSheet } from "react-native-unistyles";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -54,7 +54,7 @@ function tailLineNumbers(lastLineNumber, count) {
|
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/extensions/workspaces/sessions/(frontend)/_lib/launchLine.ts
|
|
56
56
|
/**
|
|
57
|
-
* The `/runners start` line the
|
|
57
|
+
* The `/runners start` line the runners panel's command bar sends.
|
|
58
58
|
*
|
|
59
59
|
* WHY THE BUILDER LIVES HERE AND THE PARSER DOES NOT:
|
|
60
60
|
* Cockpit code is compiled into the host bundle and may import only this
|
|
@@ -70,6 +70,21 @@ const RUNNER_SHELL_REQUEST = {
|
|
|
70
70
|
name: "shell",
|
|
71
71
|
interactive: true
|
|
72
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* What the runners panel's Run sends: the typed command on a terminal when
|
|
75
|
+
* one is available, or a login shell when nothing was typed.
|
|
76
|
+
*/
|
|
77
|
+
function runnerRunRequest(command, cwd) {
|
|
78
|
+
const where = cwd.trim() === "" ? {} : { cwd };
|
|
79
|
+
return command.trim() === "" ? {
|
|
80
|
+
...RUNNER_SHELL_REQUEST,
|
|
81
|
+
...where
|
|
82
|
+
} : {
|
|
83
|
+
command,
|
|
84
|
+
...where,
|
|
85
|
+
preferInteractive: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
73
88
|
/** Quotes a value that carries whitespace, which is how a value survives tokenizing. */
|
|
74
89
|
function quote(value) {
|
|
75
90
|
return /\s/u.test(value) ? `"${value}"` : value;
|
|
@@ -80,7 +95,7 @@ function runnerLaunchLine(request) {
|
|
|
80
95
|
const pairs = [
|
|
81
96
|
...name === "" ? [] : [`name=${quote(name)}`],
|
|
82
97
|
...cwd === "" ? [] : [`cwd=${quote(cwd)}`],
|
|
83
|
-
...request.interactive === true ? ["interactive=true"] : []
|
|
98
|
+
...request.interactive === true ? ["interactive=true"] : request.preferInteractive === true ? ["interactive=prefer"] : []
|
|
84
99
|
];
|
|
85
100
|
return [
|
|
86
101
|
RUNNERS_START_LINE,
|
|
@@ -89,12 +104,6 @@ function runnerLaunchLine(request) {
|
|
|
89
104
|
request.command.trim()
|
|
90
105
|
].join(" ");
|
|
91
106
|
}
|
|
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
107
|
//#endregion
|
|
99
108
|
//#region src/constants/webRunners.ts
|
|
100
109
|
const RUNNER_RUNS_TYPE = "runner_runs";
|
|
@@ -164,115 +173,6 @@ const runnerRunsChannel = runners.channel({
|
|
|
164
173
|
}
|
|
165
174
|
});
|
|
166
175
|
//#endregion
|
|
167
|
-
//#region src/extensions/workspaces/sessions/(frontend)/_components/LaunchRunnerSheet.native.tsx
|
|
168
|
-
function LaunchRunnerSheet({ sessionId, send, defaultCwd, onClose }) {
|
|
169
|
-
const [command, setCommand] = useState("");
|
|
170
|
-
const [cwd, setCwd] = useState(defaultCwd ?? "");
|
|
171
|
-
const [name, setName] = useState("");
|
|
172
|
-
const [interactive, setInteractive] = useState(false);
|
|
173
|
-
const request = {
|
|
174
|
-
command,
|
|
175
|
-
cwd,
|
|
176
|
-
name,
|
|
177
|
-
interactive
|
|
178
|
-
};
|
|
179
|
-
const problems = launchProblems(request);
|
|
180
|
-
return /* @__PURE__ */ jsx(Sheet, {
|
|
181
|
-
open: true,
|
|
182
|
-
onOpenChange: (open) => {
|
|
183
|
-
if (!open) onClose();
|
|
184
|
-
},
|
|
185
|
-
children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "Launch a runner" }) }), /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsxs(View, {
|
|
186
|
-
style: styles$3.form,
|
|
187
|
-
children: [
|
|
188
|
-
/* @__PURE__ */ jsx(Label, { children: "Command" }),
|
|
189
|
-
/* @__PURE__ */ jsx(Textarea, {
|
|
190
|
-
testID: "runner-launch-command",
|
|
191
|
-
value: command,
|
|
192
|
-
onChangeText: setCommand,
|
|
193
|
-
placeholder: "pnpm test"
|
|
194
|
-
}),
|
|
195
|
-
/* @__PURE__ */ jsx(Label, { children: "Cwd" }),
|
|
196
|
-
/* @__PURE__ */ jsx(Input, {
|
|
197
|
-
testID: "runner-launch-cwd",
|
|
198
|
-
value: cwd,
|
|
199
|
-
onChangeText: setCwd,
|
|
200
|
-
placeholder: "the session's own directory"
|
|
201
|
-
}),
|
|
202
|
-
/* @__PURE__ */ jsx(Label, { children: "Name" }),
|
|
203
|
-
/* @__PURE__ */ jsx(Input, {
|
|
204
|
-
testID: "runner-launch-name",
|
|
205
|
-
value: name,
|
|
206
|
-
onChangeText: setName,
|
|
207
|
-
placeholder: "chosen for you when empty"
|
|
208
|
-
}),
|
|
209
|
-
/* @__PURE__ */ jsx(Label, { children: "Needs a terminal, for a command that prompts (requires rmux or tmux)" }),
|
|
210
|
-
/* @__PURE__ */ jsx(Button, {
|
|
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"
|
|
242
|
-
}),
|
|
243
|
-
/* @__PURE__ */ jsx(Text, {
|
|
244
|
-
style: styles$3.meta,
|
|
245
|
-
children: "It appears above once the runtime starts it."
|
|
246
|
-
})
|
|
247
|
-
]
|
|
248
|
-
}) })] })
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
const styles$3 = StyleSheet.create((theme) => ({
|
|
252
|
-
form: {
|
|
253
|
-
gap: theme.spacing(2),
|
|
254
|
-
uni__dependencies: [0]
|
|
255
|
-
},
|
|
256
|
-
line: {
|
|
257
|
-
color: theme.colors.green,
|
|
258
|
-
fontFamily: fontOf(theme.fontFamily.mono),
|
|
259
|
-
fontSize: theme.fontSize.sm,
|
|
260
|
-
uni__dependencies: [0]
|
|
261
|
-
},
|
|
262
|
-
error: {
|
|
263
|
-
color: theme.colors.red,
|
|
264
|
-
fontFamily: fontOf(theme.fontFamily.sans),
|
|
265
|
-
fontSize: theme.fontSize.base,
|
|
266
|
-
uni__dependencies: [0]
|
|
267
|
-
},
|
|
268
|
-
meta: {
|
|
269
|
-
color: theme.colors.dim,
|
|
270
|
-
fontFamily: fontOf(theme.fontFamily.sans),
|
|
271
|
-
fontSize: theme.fontSize.sm,
|
|
272
|
-
uni__dependencies: [0]
|
|
273
|
-
}
|
|
274
|
-
}));
|
|
275
|
-
//#endregion
|
|
276
176
|
//#region src/constants/webRunnerLog.ts
|
|
277
177
|
const RUN_ID_PARAM = "runId";
|
|
278
178
|
//#endregion
|
|
@@ -672,26 +572,110 @@ function runnerShellTab(run) {
|
|
|
672
572
|
})
|
|
673
573
|
};
|
|
674
574
|
}
|
|
675
|
-
|
|
575
|
+
/**
|
|
576
|
+
* One terminal runner, full screen. A phone keyboard has no Ctrl key, so
|
|
577
|
+
* Ctrl-C is a button; Search opens the runner's log, which the sink keeps
|
|
578
|
+
* scrubbed beside the raw bytes for exactly that.
|
|
579
|
+
*/
|
|
580
|
+
function RunnerShellPanel({ sessionId, runId, renderRunnerTerminal, sendSessionFrame, openTransientTab }) {
|
|
581
|
+
const session = useRunners(sessionId);
|
|
582
|
+
const run = session.runs.find((candidate) => candidate.id === runId);
|
|
583
|
+
const live = run?.state === "running";
|
|
584
|
+
const stopping = session.stopRequested.includes(runId);
|
|
676
585
|
const request = useMemo(() => sessionId === null ? void 0 : {
|
|
677
586
|
key: `${sessionId}:${runId}`,
|
|
678
587
|
read: (from, generation, signal) => readRunnerScreen(sessionId, runId, from, generation, signal),
|
|
679
588
|
write: (text) => sendRunnerInput(sessionId, runId, text)
|
|
680
589
|
}, [sessionId, runId]);
|
|
681
|
-
return /* @__PURE__ */
|
|
590
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
682
591
|
style: styles$1.panel,
|
|
683
|
-
children:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
592
|
+
children: [
|
|
593
|
+
/* @__PURE__ */ jsxs(View, {
|
|
594
|
+
style: styles$1.header,
|
|
595
|
+
children: [
|
|
596
|
+
/* @__PURE__ */ jsx(Text, {
|
|
597
|
+
numberOfLines: 1,
|
|
598
|
+
style: styles$1.title,
|
|
599
|
+
children: run?.name ?? runId
|
|
600
|
+
}),
|
|
601
|
+
/* @__PURE__ */ jsx(StatusBadge, {
|
|
602
|
+
tone: live ? "running" : "neutral",
|
|
603
|
+
children: live ? "Running" : "Ended"
|
|
604
|
+
}),
|
|
605
|
+
/* @__PURE__ */ jsx(View, { style: styles$1.grow }),
|
|
606
|
+
live && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
607
|
+
testID: "runner-shell-stop",
|
|
608
|
+
variant: stopping ? "outline" : "danger-outline",
|
|
609
|
+
disabled: stopping,
|
|
610
|
+
onPress: () => requestRunnerStop(sendSessionFrame, sessionId, runId),
|
|
611
|
+
children: stopping ? "Stopping…" : "Stop"
|
|
612
|
+
}) : null
|
|
613
|
+
]
|
|
614
|
+
}),
|
|
615
|
+
run ? /* @__PURE__ */ jsx(Text, {
|
|
616
|
+
numberOfLines: 1,
|
|
617
|
+
style: styles$1.command,
|
|
618
|
+
children: run.command
|
|
619
|
+
}) : null,
|
|
620
|
+
/* @__PURE__ */ jsx(View, {
|
|
621
|
+
style: styles$1.terminal,
|
|
622
|
+
children: request === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
623
|
+
style: styles$1.meta,
|
|
624
|
+
children: "Select a session to attach to this runner."
|
|
625
|
+
}) : renderRunnerTerminal?.(request)
|
|
626
|
+
}),
|
|
627
|
+
/* @__PURE__ */ jsxs(View, {
|
|
628
|
+
style: styles$1.actions,
|
|
629
|
+
children: [live && sessionId !== null ? /* @__PURE__ */ jsx(Button, {
|
|
630
|
+
testID: "runner-shell-interrupt",
|
|
631
|
+
variant: "outline",
|
|
632
|
+
onPress: () => void sendRunnerInput(sessionId, runId, ""),
|
|
633
|
+
children: "Ctrl-C"
|
|
634
|
+
}) : null, run ? /* @__PURE__ */ jsx(Button, {
|
|
635
|
+
testID: "runner-shell-search",
|
|
636
|
+
variant: "outline",
|
|
637
|
+
onPress: () => openTransientTab(runnerLogTab(run)),
|
|
638
|
+
children: "Search output"
|
|
639
|
+
}) : null]
|
|
640
|
+
})
|
|
641
|
+
]
|
|
687
642
|
});
|
|
688
643
|
}
|
|
689
644
|
const styles$1 = StyleSheet.create((theme) => ({
|
|
690
645
|
panel: {
|
|
691
646
|
flex: 1,
|
|
647
|
+
gap: theme.spacing(2),
|
|
648
|
+
padding: theme.spacing(3),
|
|
692
649
|
backgroundColor: theme.colors.bg,
|
|
693
650
|
uni__dependencies: [0]
|
|
694
651
|
},
|
|
652
|
+
header: {
|
|
653
|
+
flexDirection: "row",
|
|
654
|
+
alignItems: "center",
|
|
655
|
+
gap: theme.spacing(2),
|
|
656
|
+
uni__dependencies: [0]
|
|
657
|
+
},
|
|
658
|
+
actions: {
|
|
659
|
+
flexDirection: "row",
|
|
660
|
+
gap: theme.spacing(2),
|
|
661
|
+
uni__dependencies: [0]
|
|
662
|
+
},
|
|
663
|
+
grow: { flex: 1 },
|
|
664
|
+
terminal: { flex: 1 },
|
|
665
|
+
title: {
|
|
666
|
+
flexShrink: 1,
|
|
667
|
+
color: theme.colors.hi,
|
|
668
|
+
fontFamily: fontOf(theme.fontFamily.sans),
|
|
669
|
+
fontSize: theme.fontSize.lg,
|
|
670
|
+
fontWeight: "600",
|
|
671
|
+
uni__dependencies: [0]
|
|
672
|
+
},
|
|
673
|
+
command: {
|
|
674
|
+
color: theme.colors.dim,
|
|
675
|
+
fontFamily: fontOf(theme.fontFamily.mono),
|
|
676
|
+
fontSize: theme.fontSize.sm,
|
|
677
|
+
uni__dependencies: [0]
|
|
678
|
+
},
|
|
695
679
|
meta: {
|
|
696
680
|
color: theme.colors.dim,
|
|
697
681
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
@@ -701,6 +685,10 @@ const styles$1 = StyleSheet.create((theme) => ({
|
|
|
701
685
|
}));
|
|
702
686
|
//#endregion
|
|
703
687
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/RunnersPanel.native.tsx
|
|
688
|
+
/** A runner's own screen: its terminal when it has one, its log when it does not. */
|
|
689
|
+
function runnerTab(run) {
|
|
690
|
+
return run.interactive ? runnerShellTab(run) : runnerLogTab(run);
|
|
691
|
+
}
|
|
704
692
|
function runnersTab() {
|
|
705
693
|
return {
|
|
706
694
|
id: "runner-runs",
|
|
@@ -708,7 +696,7 @@ function runnersTab() {
|
|
|
708
696
|
panel: RunnersPanel
|
|
709
697
|
};
|
|
710
698
|
}
|
|
711
|
-
function RunnerRow({ run, props, stopping, now
|
|
699
|
+
function RunnerRow({ run, props, stopping, now }) {
|
|
712
700
|
const tail = useRunnerTail(props.sessionId, run.id, run.state === "running");
|
|
713
701
|
return /* @__PURE__ */ jsxs(View, {
|
|
714
702
|
testID: `activity-runner-${run.id}`,
|
|
@@ -720,47 +708,27 @@ function RunnerRow({ run, props, stopping, now, detail = false }) {
|
|
|
720
708
|
pulse: true
|
|
721
709
|
}),
|
|
722
710
|
title: run.name,
|
|
723
|
-
description: `${formatRunnerUptime(run.startedAt, now)}
|
|
724
|
-
|
|
711
|
+
description: `${run.command} · ${formatRunnerUptime(run.startedAt, now)}`,
|
|
712
|
+
disclosure: true,
|
|
713
|
+
onPress: () => props.openTransientTab(runnerTab(run))
|
|
725
714
|
}),
|
|
726
|
-
/* @__PURE__ */ jsx(Text, {
|
|
727
|
-
numberOfLines:
|
|
715
|
+
tail === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
716
|
+
numberOfLines: 1,
|
|
728
717
|
style: styles.meta,
|
|
729
|
-
children: tail
|
|
718
|
+
children: tail
|
|
730
719
|
}),
|
|
731
|
-
|
|
732
|
-
selectable: true,
|
|
733
|
-
style: styles.meta,
|
|
734
|
-
children: run.command
|
|
735
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
736
|
-
selectable: true,
|
|
737
|
-
style: styles.meta,
|
|
738
|
-
children: run.cwd
|
|
739
|
-
})] }) : null,
|
|
740
|
-
/* @__PURE__ */ jsxs(View, {
|
|
720
|
+
props.sessionId !== null ? /* @__PURE__ */ jsx(View, {
|
|
741
721
|
style: styles.actions,
|
|
742
|
-
children:
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
-
})
|
|
722
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
723
|
+
testID: `activity-runner-stop-${run.id}`,
|
|
724
|
+
variant: stopping ? "outline" : "danger-outline",
|
|
725
|
+
disabled: stopping,
|
|
726
|
+
onPress: () => {
|
|
727
|
+
if (props.sessionId !== null) requestRunnerStop(props.sendSessionFrame, props.sessionId, run.id);
|
|
728
|
+
},
|
|
729
|
+
children: stopping ? "Stopping…" : "Stop"
|
|
730
|
+
})
|
|
731
|
+
}) : null
|
|
764
732
|
]
|
|
765
733
|
});
|
|
766
734
|
}
|
|
@@ -790,11 +758,17 @@ function RunnersActivitySection(props) {
|
|
|
790
758
|
stopping: session.stopRequested.includes(run.id)
|
|
791
759
|
}, run.id)) });
|
|
792
760
|
}
|
|
761
|
+
/**
|
|
762
|
+
* Every running runner and the one place to start another: type a command
|
|
763
|
+
* and Run hosts it on a terminal (plain output when the host has no rmux or
|
|
764
|
+
* tmux), or leave it empty for a login shell. The started runner opens on
|
|
765
|
+
* its own screen.
|
|
766
|
+
*/
|
|
793
767
|
function RunnersPanel(props) {
|
|
794
768
|
const { openTransientTab } = props;
|
|
795
769
|
const session = useRunners(props.sessionId);
|
|
796
770
|
const running = session.runs.filter((run) => run.state === "running");
|
|
797
|
-
const [
|
|
771
|
+
const [command, setCommand] = useState("");
|
|
798
772
|
const [now, setNow] = useState(() => Date.now());
|
|
799
773
|
const baseline = useRef(null);
|
|
800
774
|
const timeout = useRef(void 0);
|
|
@@ -804,66 +778,101 @@ function RunnersPanel(props) {
|
|
|
804
778
|
}, []);
|
|
805
779
|
useEffect(() => {
|
|
806
780
|
if (baseline.current === null) return;
|
|
807
|
-
const
|
|
808
|
-
if (
|
|
781
|
+
const started = session.runs.find((run) => run.state === "running" && !baseline.current?.has(run.id));
|
|
782
|
+
if (started === void 0) return;
|
|
809
783
|
baseline.current = null;
|
|
810
784
|
clearTimeout(timeout.current);
|
|
811
|
-
openTransientTab(
|
|
785
|
+
openTransientTab(runnerTab(started));
|
|
812
786
|
}, [session.runs, openTransientTab]);
|
|
813
787
|
useEffect(() => () => clearTimeout(timeout.current), []);
|
|
788
|
+
const launch = () => {
|
|
789
|
+
if (props.sessionId === null) return;
|
|
790
|
+
baseline.current = new Set(session.runs.map((run) => run.id));
|
|
791
|
+
clearTimeout(timeout.current);
|
|
792
|
+
timeout.current = setTimeout(() => {
|
|
793
|
+
baseline.current = null;
|
|
794
|
+
}, 3e4);
|
|
795
|
+
requestRunnerStart(props.sendSessionFrame, props.sessionId, runnerRunRequest(command, ""));
|
|
796
|
+
setCommand("");
|
|
797
|
+
};
|
|
814
798
|
return /* @__PURE__ */ jsxs(View, {
|
|
815
799
|
style: styles.panel,
|
|
816
800
|
children: [
|
|
817
|
-
/* @__PURE__ */
|
|
818
|
-
style: styles.
|
|
819
|
-
children:
|
|
801
|
+
/* @__PURE__ */ jsxs(View, {
|
|
802
|
+
style: styles.header,
|
|
803
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
804
|
+
style: styles.title,
|
|
805
|
+
children: "Runners"
|
|
806
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
807
|
+
style: styles.meta,
|
|
808
|
+
children: running.length === 0 ? "Nothing running" : `${running.length} running`
|
|
809
|
+
})]
|
|
820
810
|
}),
|
|
821
811
|
props.sessionId !== null ? /* @__PURE__ */ jsxs(View, {
|
|
822
|
-
style: styles.
|
|
823
|
-
children: [/* @__PURE__ */
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
812
|
+
style: styles.bar,
|
|
813
|
+
children: [/* @__PURE__ */ jsxs(View, {
|
|
814
|
+
style: styles.actions,
|
|
815
|
+
children: [/* @__PURE__ */ jsx(View, {
|
|
816
|
+
style: styles.grow,
|
|
817
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
818
|
+
testID: "runners-panel-command",
|
|
819
|
+
accessibilityLabel: "Command to run",
|
|
820
|
+
size: "lg",
|
|
821
|
+
value: command,
|
|
822
|
+
onChangeText: setCommand,
|
|
823
|
+
onSubmitEditing: launch,
|
|
824
|
+
placeholder: "a command, like pnpm dev",
|
|
825
|
+
autoCapitalize: "none",
|
|
826
|
+
autoCorrect: false,
|
|
827
|
+
returnKeyType: "go"
|
|
828
|
+
})
|
|
829
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
830
|
+
testID: "runners-panel-run",
|
|
831
|
+
variant: "primary",
|
|
832
|
+
size: "lg",
|
|
833
|
+
onPress: launch,
|
|
834
|
+
children: command.trim() === "" ? "Open shell" : "Run"
|
|
835
|
+
})]
|
|
836
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
837
|
+
style: styles.meta,
|
|
838
|
+
children: "Runs in the session's directory. Leave it empty for a plain shell."
|
|
838
839
|
})]
|
|
839
840
|
}) : null,
|
|
840
841
|
/* @__PURE__ */ jsx(ScrollView, { children: running.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
841
842
|
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
|
|
843
|
+
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."
|
|
844
|
+
}) : running.map((run) => /* @__PURE__ */ jsx(ListRow, {
|
|
845
|
+
leading: /* @__PURE__ */ jsx(Dot, {
|
|
846
|
+
tone: "yellow",
|
|
847
|
+
pulse: true
|
|
848
|
+
}),
|
|
849
|
+
title: run.name,
|
|
850
|
+
description: `${run.command} · ${formatRunnerUptime(run.startedAt, now)}`,
|
|
851
|
+
disclosure: true,
|
|
852
|
+
onPress: () => openTransientTab(runnerTab(run))
|
|
853
|
+
}, run.id)) })
|
|
856
854
|
]
|
|
857
855
|
});
|
|
858
856
|
}
|
|
859
857
|
const styles = StyleSheet.create((theme) => ({
|
|
860
858
|
panel: {
|
|
861
859
|
flex: 1,
|
|
862
|
-
gap: theme.spacing(
|
|
863
|
-
padding: theme.spacing(
|
|
860
|
+
gap: theme.spacing(3),
|
|
861
|
+
padding: theme.spacing(4),
|
|
864
862
|
backgroundColor: theme.colors.bg,
|
|
865
863
|
uni__dependencies: [0]
|
|
866
864
|
},
|
|
865
|
+
header: {
|
|
866
|
+
flexDirection: "row",
|
|
867
|
+
alignItems: "center",
|
|
868
|
+
gap: theme.spacing(2),
|
|
869
|
+
uni__dependencies: [0]
|
|
870
|
+
},
|
|
871
|
+
bar: {
|
|
872
|
+
gap: theme.spacing(1.5),
|
|
873
|
+
uni__dependencies: [0]
|
|
874
|
+
},
|
|
875
|
+
grow: { flex: 1 },
|
|
867
876
|
row: {
|
|
868
877
|
gap: theme.spacing(1),
|
|
869
878
|
paddingVertical: theme.spacing(2),
|
|
@@ -871,14 +880,14 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
871
880
|
},
|
|
872
881
|
actions: {
|
|
873
882
|
flexDirection: "row",
|
|
874
|
-
|
|
883
|
+
alignItems: "center",
|
|
875
884
|
gap: theme.spacing(2),
|
|
876
885
|
uni__dependencies: [0]
|
|
877
886
|
},
|
|
878
887
|
title: {
|
|
879
888
|
color: theme.colors.hi,
|
|
880
889
|
fontFamily: fontOf(theme.fontFamily.sans),
|
|
881
|
-
fontSize: theme.fontSize.
|
|
890
|
+
fontSize: theme.fontSize.lg,
|
|
882
891
|
fontWeight: "600",
|
|
883
892
|
uni__dependencies: [0]
|
|
884
893
|
},
|