@agimon-ai/po-runner 0.0.0-alpha.24 → 0.0.0-alpha.26
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.
|
@@ -515,6 +515,8 @@ function useRunnerTail(sessionId, runId, running) {
|
|
|
515
515
|
/** The top of both runner screens: name, outcome, an action, and the command in its folder. */
|
|
516
516
|
function RunnerRunHeader({ run, name, sessionDirectory, action }) {
|
|
517
517
|
const badge = runnerBadge(run);
|
|
518
|
+
/** A long command would eat a phone screen, so it starts as one truncated line. */
|
|
519
|
+
const [commandOpen, setCommandOpen] = useState(false);
|
|
518
520
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(View, {
|
|
519
521
|
style: styles$2.header,
|
|
520
522
|
children: [
|
|
@@ -530,20 +532,26 @@ function RunnerRunHeader({ run, name, sessionDirectory, action }) {
|
|
|
530
532
|
/* @__PURE__ */ jsx(View, { style: styles$2.grow }),
|
|
531
533
|
action
|
|
532
534
|
]
|
|
533
|
-
}), run ? /* @__PURE__ */ jsxs(
|
|
535
|
+
}), run ? /* @__PURE__ */ jsxs(Pressable, {
|
|
536
|
+
testID: "runner-log-command-toggle",
|
|
537
|
+
accessibilityRole: "button",
|
|
538
|
+
accessibilityState: { expanded: commandOpen },
|
|
539
|
+
accessibilityHint: commandOpen ? "Collapses the command" : "Shows the full command",
|
|
534
540
|
style: styles$2.commandBox,
|
|
541
|
+
onPress: () => setCommandOpen((open) => !open),
|
|
535
542
|
children: [/* @__PURE__ */ jsxs(Text, {
|
|
536
|
-
selectable:
|
|
543
|
+
selectable: commandOpen,
|
|
544
|
+
numberOfLines: commandOpen ? void 0 : 1,
|
|
537
545
|
style: styles$2.command,
|
|
538
546
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
539
547
|
style: styles$2.prompt,
|
|
540
548
|
children: "$ "
|
|
541
549
|
}), run.command]
|
|
542
|
-
}), /* @__PURE__ */ jsxs(Text, {
|
|
550
|
+
}), commandOpen ? /* @__PURE__ */ jsxs(Text, {
|
|
543
551
|
numberOfLines: 1,
|
|
544
552
|
style: styles$2.folder,
|
|
545
553
|
children: ["in ", runnerFolderLabel(run.cwd, sessionDirectory)]
|
|
546
|
-
})]
|
|
554
|
+
}) : null]
|
|
547
555
|
}) : /* @__PURE__ */ jsx(Text, {
|
|
548
556
|
style: styles$2.meta,
|
|
549
557
|
children: "No longer listed; its log stays readable."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.mjs","names":["isRecord","Button","FolderIcon","fontOf","Input","ListRow","Sheet","SheetBody","SheetContent","SheetFooter","SheetHeader","SheetTitle","PoNativeTheme","useState","StyleSheet","RunnerCommandBarProps","plainPunctuation","runnerFolderLabel","runnerFolders","RunnerCommandBar","command","onCommandChange","cwd","onCwdChange","sessionDirectory","folders","problem","onLaunch","defaultFolderOpen","folderOpen","setFolderOpen","path","setPath","pick","next","choices","styles","bar","line","grow","text","trim","undefined","meta","map","folder","create","theme","gap","spacing","uni__dependencies","flexDirection","alignItems","flex","color","colors","dim","fontFamily","sans","fontSize","sm","lineHeight","red","styles","WebPluginSlotProps","fontOf","Button","Input","StatusBadge","PoNativeTheme","ReactNode","useEffect","useState","StyleSheet","RunnerLogResponse","RunnerRunView","logViewLines","runnerBadge","runnerFolderLabel","tailLineNumbers","fetchRunnerLog","requestRunnerStop","useRunners","RunnerRunHeader","run","name","sessionDirectory","action","badge","styles","header","title","tone","label","grow","commandBox","command","prompt","folder","cwd","meta","runnerLogTab","id","panel","props","RunnerLogPanel","sessionId","runId","sendSessionFrame","session","runs","find","candidate","typed","setTyped","query","setQuery","ignoreCase","setIgnoreCase","context","setContext","following","setFollowing","requestKey","loaded","setLoaded","key","slice","error","undefined","timer","setTimeout","clearTimeout","controller","AbortController","ReturnType","read","result","lines","grep","contextLines","signal","aborted","running","abort","text","numbers","totalLines","length","lineNumbers","gutter","String","at","stopping","stopRequested","includes","live","state","actions","selected","value","lineCount","log","logBody","exists","map","line","index","number","padStart","path","logPath","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","alignItems","minHeight","flexShrink","color","hi","fontWeight","fontFamily","sans","fontSize","base","paddingHorizontal","paddingVertical","borderRadius","md","deep","mono","sm","faint","dim","xs","lineHeight","red","flexWrap","styles","WebPluginSlotProps","fontOf","Button","SearchIcon","PoNativeTheme","useMemo","StyleSheet","RunnerRunView","requestRunnerStop","readRunnerScreen","sendRunnerInput","useRunners","RunnerRunHeader","runnerLogTab","runnerShellTab","run","id","label","name","panel","props","RunnerShellPanel","sessionId","runId","renderRunnerTerminal","sendSessionFrame","openTransientTab","session","runs","find","candidate","live","state","stopping","stopRequested","includes","request","undefined","key","read","from","generation","signal","AbortSignal","write","text","styles","sessionDirectory","terminal","meta","actions","action","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","color","dim","fontFamily","sans","fontSize","sm","styles","WebPluginSlotProps","ArrowRightIcon","fontOf","Button","Dot","ListRow","PoNativeTheme","useEffect","useRef","useState","StyleSheet","RunnerRunView","formatRunnerUptime","runnerRunRequest","requestRunnerStart","requestRunnerStop","RunnerCommandBar","useRunners","useRunnerTail","runnerLogTab","runnerShellTab","runnerTab","run","interactive","runnersTab","id","label","panel","RunnersPanel","RunnerRow","props","stopping","now","tail","sessionId","state","name","openTransientTab","pressed","styles","row","line","meta","startedAt","sendSessionFrame","command","RunnersActivitySection","session","running","runs","filter","setNow","Date","timer","setInterval","clearInterval","length","idle","launch","launchText","map","stopRequested","includes","setCommand","cwd","setCwd","baseline","ReadonlySet","launched","timeout","ReturnType","setTimeout","undefined","current","started","find","has","clearTimeout","failureId","startFailure","Set","header","title","sessionDirectory","problem","message","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","alignItems","paddingVertical","opacity","color","hi","fontFamily","sans","fontSize","base","lineHeight","fontWeight","medium","paddingLeft","dim","mono","sm","lg","flexWrap","green","activityGroupRunners","channelRunnerRuns","fillRunners"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/_lib/format.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/launchLine.ts","../../src/constants/webRunners.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/runnersStore.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerCommandBar.native.tsx","../../src/constants/webRunnerLog.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../generated/client.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/logApi.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/runnerHooks.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerLogPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/_lib/screenApi.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerShellPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnersPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/runners.native.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/runner-runs.native.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Runners.activity.runners.native.tsx","../../generated/native.ts"],"sourcesContent":["import type { RunnerRunView } from '../../../../../types/webRunners';\n\n/**\n * Local copy of the runtime's formatUptime (src/services/bashResult/index.ts):\n * the web plugin may reach only src/types, so the one helper it needs lives here.\n */\nexport function formatRunnerUptime(startedAt: string, now: number): string {\n const started = Date.parse(startedAt);\n if (Number.isNaN(started)) return 'unknown';\n const seconds = Math.max(0, Math.floor((now - started) / 1000));\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;\n}\n\n/** A bounded log view: what to render, and what the bound left out. */\nexport interface LogView {\n lines: string[];\n /** Lines dropped off the top to stay within the bound. */\n hidden: number;\n}\n\nexport interface LogViewOptions {\n /**\n * Drop the slice's last line because it is a fragment the follow stream will\n * deliver again, whole. Set only while following: a finished run's last line\n * may legitimately lack a newline, and dropping it would hide real output.\n */\n dropPartialTail?: boolean;\n}\n\n/**\n * The lines a log view shows: the slice's text, then whatever the follow\n * stream appended, bounded to the newest `max`.\n *\n * A log file ends with a newline, and the reader hands its text back with that\n * newline intact, so a naive split reports one blank line more than the file\n * has. Dropping exactly one trailing empty entry keeps the count the reader\n * sees equal to the count the server reported.\n */\nexport function logViewLines(\n text: string,\n appended: readonly string[],\n max: number,\n options: LogViewOptions = {},\n): LogView {\n const split = text.split('\\n');\n if (split.length > 0 && split[split.length - 1] === '') split.pop();\n if (options.dropPartialTail === true && split.length > 0) split.pop();\n const all = [...split, ...appended];\n const lines = all.slice(-max);\n return { lines, hidden: all.length - lines.length };\n}\n\n/**\n * Absolute 1-based numbers for a tail, counted back from the last line.\n *\n * A tail is contiguous, so only its end has to be known. Grep results are not\n * contiguous and carry their own numbers from the reader instead.\n */\nexport function tailLineNumbers(lastLineNumber: number, count: number): number[] {\n const first = Math.max(1, lastLineNumber - count + 1);\n return Array.from({ length: count }, (_, index) => first + index);\n}\n\n/**\n * Whether the view is actually following a growing log.\n *\n * Three things must hold: the reader asked to follow, no query is set (a\n * filtered view is a snapshot of the whole file, which a stream cannot\n * assemble a chunk at a time), and the runner is still writing. Without the\n * last one a finished run would claim to be tailing a file nothing will ever\n * append to.\n */\nexport function isFollowingLive(following: boolean, filtering: boolean, running: boolean): boolean {\n return following && !filtering && running;\n}\n\n/**\n * A folder as the command bar names it: the session directory by its last\n * segment, a folder inside it relative to it, anything else as written.\n */\nexport function runnerFolderLabel(folder: string, sessionDirectory: string | undefined): string {\n if (folder === '' || folder === sessionDirectory) {\n return sessionDirectory === undefined\n ? 'session directory'\n : (sessionDirectory.split('/').pop() ?? sessionDirectory);\n }\n if (sessionDirectory !== undefined && folder.startsWith(`${sessionDirectory}/`)) {\n return folder.slice(sessionDirectory.length + 1);\n }\n return folder;\n}\n\n/** The folders the picker offers past the session directory: those in use, then the one in effect. */\nexport function runnerFolders(\n inUse: readonly string[],\n current: string,\n sessionDirectory: string | undefined,\n): string[] {\n const folders = new Set([...inUse, current]);\n folders.delete('');\n if (sessionDirectory !== undefined) folders.delete(sessionDirectory);\n return [...folders];\n}\n\n/**\n * Undoes iOS smart punctuation, which React Native cannot switch off: a shell\n * reads ‘hi’ as literal quotes and a long dash before port as no flag at all.\n */\nexport function plainPunctuation(text: string): string {\n return text\n .replace(/[‘’]/g, \"'\")\n .replace(/[“”]/g, '\"')\n .replace(/\\u2014/g, '--');\n}\n\n/** The badge a run wears: its state while running, then how it ended. */\nexport function runnerBadge(run: RunnerRunView | undefined): {\n tone: 'neutral' | 'running' | 'ok' | 'error';\n label: string;\n} {\n if (run === undefined) return { tone: 'neutral', label: 'gone' };\n if (run.state === 'running') return { tone: 'running', label: 'running' };\n if (run.exit === undefined || run.exit.reason === 'stopped') return { tone: 'neutral', label: 'stopped' };\n const clean = run.exit.reason === 'completed' && (run.exit.code === 0 || run.exit.code === null);\n return clean ? { tone: 'ok', label: 'done' } : { tone: 'error', label: run.exit.reason.replace('_', ' ') };\n}\n","/**\n * The `/runners start` line the runners panel's command bar sends.\n *\n * WHY THE BUILDER LIVES HERE AND THE PARSER DOES NOT:\n * Cockpit code is compiled into the host bundle and may import only this\n * package's own web and type modules, so it cannot share the session's parser\n * in src/services. The two are pinned together by a test that parses what this\n * writes, which is the only guarantee that matters: the line has to survive\n * the trip.\n */\n\nexport const RUNNERS_START_LINE = '/runners start';\n/** A persistent login shell for the direct shell action in Runner Space. */\nexport const RUNNER_SHELL_COMMAND = 'exec \"${SHELL:-/bin/bash}\" -l';\n/** Ends the options and begins the command; the command may hold `=` and flags of its own. */\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\n\nexport interface RunnerLaunchRequest {\n command: string;\n cwd?: string;\n name?: string;\n interactive?: boolean;\n /** A terminal when rmux or tmux is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\n/** The no-form launch used when a reader asks for a shell rather than a command. */\nexport const RUNNER_SHELL_REQUEST: RunnerLaunchRequest = {\n command: RUNNER_SHELL_COMMAND,\n name: 'shell',\n interactive: true,\n};\n\n/**\n * What the runners panel's Run sends: the typed command on a terminal when\n * one is available, or a login shell when nothing was typed.\n */\nexport function runnerRunRequest(command: string, cwd: string): RunnerLaunchRequest {\n const where = cwd.trim() === '' ? {} : { cwd };\n return command.trim() === '' ? { ...RUNNER_SHELL_REQUEST, ...where } : { command, ...where, preferInteractive: true };\n}\n\n/** Quotes a value that carries whitespace, which is how a value survives tokenizing. */\nfunction quote(value: string): string {\n return /\\s/u.test(value) ? `\"${value}\"` : value;\n}\n\nexport function runnerLaunchLine(request: RunnerLaunchRequest): string {\n const cwd = request.cwd?.trim() ?? '';\n const name = request.name?.trim() ?? '';\n const pairs = [\n ...(name === '' ? [] : [`name=${quote(name)}`]),\n ...(cwd === '' ? [] : [`cwd=${quote(cwd)}`]),\n ...(request.interactive === true\n ? ['interactive=true']\n : request.preferInteractive === true\n ? ['interactive=prefer']\n : []),\n ];\n // The separator is always written, even with no options before it, so the\n // command is never re-read as a `key=value` pair.\n return [RUNNERS_START_LINE, ...pairs, RUNNERS_COMMAND_SEPARATOR, request.command.trim()].join(' ');\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","import { defineSessionStore, type SessionFrameSender } from '@agimon-ai/po-core/native';\n\nimport { RUNNER_RUNS_TYPE } from '../../../../../constants/webRunners';\nimport { type RunnerRunView, type RunnerStartFailureView } from '../../../../../types/webRunners';\nimport { type RunnerLaunchRequest, runnerLaunchLine } from './launchLine';\n\n/** The runtime's slash verb that stops one runner headlessly. */\nconst STOP_COMMAND = '/runners stop';\n\n/** One session's record: the hub's last report plus what this page asked for. */\nexport interface RunnersSession extends RunnerRunsPayload {\n /** Runners a stop was asked for and that have not yet reported an exit. */\n stopRequested: string[];\n}\n\nexport const runners = defineSessionStore<RunnersSession>({ runs: [], stopRequested: [] });\n\n/** The runner channel keeps the dock's busy state aligned with its rendered run list. */\nexport const runnerActivitySource = {\n subscribe(listener: () => void) {\n const subscription = runners.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive(sessionId: string | null) {\n return runners.select(runners.store.state, sessionId).runs.some((run) => run.state === 'running');\n },\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Asks the runtime to stop a runner through the same slash command the TUI's\n * Runner Space uses. The request is remembered until the runner's record\n * reports an exit; the runtime, not this click, decides when that is.\n */\nexport function requestRunnerStop(send: SessionFrameSender, sessionId: string, id: string): void {\n send(sessionId, { type: 'prompt', message: `${STOP_COMMAND} ${id}` });\n runners.update(sessionId, (current) => ({\n ...current,\n stopRequested: [...current.stopRequested.filter((known) => known !== id), id],\n }));\n}\n\n/**\n * Asks the runtime to start a runner, through the same prompt frame the stop\n * request uses and the same way agents and workflows launch their own work.\n *\n * Nothing is recorded as pending here. The runtime names the run, and it\n * arrives in the feed on its own; guessing at an id before it exists would\n * only have to be reconciled later.\n */\nexport function requestRunnerStart(send: SessionFrameSender, sessionId: string, request: RunnerLaunchRequest): void {\n send(sessionId, { type: 'prompt', message: runnerLaunchLine(request) });\n}\n\nexport interface RunnerRunsPayload {\n /** The runners the hub last reported; presented order preserved. */\n runs: RunnerRunView[];\n /** Where a runner starts when no folder is given. */\n sessionDirectory?: string;\n /** The last start that did not happen. */\n startFailure?: RunnerStartFailureView;\n}\n\n/** The plugin's session data channel: 'runner_runs' payloads into the store. */\nexport const runnerRunsChannel = runners.channel<RunnerRunsPayload>({\n channel: RUNNER_RUNS_TYPE,\n parse(input) {\n if (!isRecord(input) || !Array.isArray(input.runs)) return null;\n const failure = input.startFailure;\n return {\n runs: input.runs.filter(isRecord) as unknown as RunnerRunView[],\n ...(typeof input.sessionDirectory === 'string' ? { sessionDirectory: input.sessionDirectory } : {}),\n ...(isRecord(failure) && typeof failure.id === 'string' && typeof failure.message === 'string'\n ? { startFailure: { id: failure.id, message: failure.message } }\n : {}),\n };\n },\n reduce(current, payload) {\n // A stop request is spent once the runner reports an exit or leaves the feed.\n const stillRunning = new Set(payload.runs.filter((run) => run.state === 'running').map((run) => run.id));\n return { ...payload, stopRequested: current.stopRequested.filter((id) => stillRunning.has(id)) };\n },\n});\n","import {\n Button,\n FolderIcon,\n fontOf,\n Input,\n ListRow,\n Sheet,\n SheetBody,\n SheetContent,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n} from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerCommandBarProps } from '../../../../../types/webRunners';\nimport { plainPunctuation, runnerFolderLabel, runnerFolders } from '../_lib/format';\n\n/**\n * The phone's command bar: the command and Run on one line, the folder it runs\n * in on the row under it, chosen from a bottom sheet.\n */\nexport function RunnerCommandBar({\n command,\n onCommandChange,\n cwd,\n onCwdChange,\n sessionDirectory,\n folders,\n problem,\n onLaunch,\n defaultFolderOpen = false,\n}: RunnerCommandBarProps) {\n const [folderOpen, setFolderOpen] = useState(defaultFolderOpen);\n const [path, setPath] = useState('');\n const pick = (next: string): void => {\n onCwdChange(next);\n setPath('');\n setFolderOpen(false);\n };\n const choices = ['', ...runnerFolders(folders, cwd, sessionDirectory)];\n\n return (\n <View style={styles.bar}>\n <View style={styles.line}>\n <View style={styles.grow}>\n <Input\n testID=\"runners-panel-command\"\n accessibilityLabel=\"Command to run\"\n size=\"lg\"\n value={command}\n onChangeText={(text) => onCommandChange(plainPunctuation(text))}\n onSubmitEditing={onLaunch}\n placeholder=\"a command, like pnpm dev\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n returnKeyType=\"go\"\n />\n </View>\n <Button testID=\"runners-panel-run\" variant=\"primary\" size=\"lg\" onPress={onLaunch}>\n {command.trim() === '' ? 'Open shell' : 'Run'}\n </Button>\n </View>\n <ListRow\n leading={<FolderIcon size={14} color={problem === undefined ? 'faint' : 'red'} />}\n title={runnerFolderLabel(cwd, sessionDirectory)}\n description=\"Runs in\"\n disclosure\n accessibilityLabel={`Runs in ${runnerFolderLabel(cwd, sessionDirectory)}`}\n onPress={() => setFolderOpen(true)}\n />\n {problem === undefined ? (\n <Text style={styles.meta}>Leave the command empty for a plain shell.</Text>\n ) : (\n <Text accessibilityRole=\"alert\" testID=\"runners-panel-problem\" style={styles.problem}>\n {problem}\n </Text>\n )}\n <Sheet open={folderOpen} onOpenChange={setFolderOpen}>\n <SheetContent>\n <SheetHeader closeLabel=\"Close folders\">\n <SheetTitle>Runs in</SheetTitle>\n </SheetHeader>\n <SheetBody>\n <View>\n {choices.map((folder) => (\n <ListRow\n key={folder}\n title={folder === '' ? 'Session directory' : runnerFolderLabel(folder, sessionDirectory)}\n description={folder === '' ? (sessionDirectory ?? 'Where this session started') : folder}\n selected={folder === cwd}\n onPress={() => pick(folder)}\n />\n ))}\n </View>\n <Input\n testID=\"runners-panel-cwd-path\"\n accessibilityLabel=\"Another folder\"\n value={path}\n onChangeText={(text) => setPath(plainPunctuation(text))}\n onSubmitEditing={() => {\n if (path.trim() !== '') pick(path.trim());\n }}\n placeholder=\"another folder, like packages/web\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n returnKeyType=\"done\"\n />\n </SheetBody>\n <SheetFooter>A relative folder starts from the session directory.</SheetFooter>\n </SheetContent>\n </Sheet>\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n bar: { gap: theme.spacing(1.5) },\n line: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n grow: { flex: 1 },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n problem: {\n color: theme.colors.red,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n}));\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'runner' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'runner',\n transport: nativeApiTransport,\n sessionAddress: sessionApiAddress,\n});\n","import { api } from '../../../../../../generated/client.native';\nimport { RUN_ID_PARAM } from '../../../../../constants/webRunnerLog';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogQueryParams,\n type RunnerLogResponse,\n} from '../../../../../types/webRunnerLog';\n\n/**\n * The page's half of this package's log API. The only place the cockpit talks\n * HTTP to the hub for a runner, so if the transport ever changes, it changes\n * here alone.\n *\n * The generated client owns every URL: a route is named, a session id selects\n * that session's own copy of the API, and the run id is a path parameter the\n * client substitutes and encodes. Nothing here spells a mount, a prefix or a\n * proxy parameter, which is what the four deleted URL builders each did by\n * hand.\n */\n\nexport type RunnerLogResult = { slice: RunnerLogResponse } | { error: string };\n\nconst UNREACHABLE = 'The cockpit hub is unreachable.';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads one runner's log: the last N lines, or the lines matching a substring with context. */\nexport async function fetchRunnerLog(\n sessionId: string,\n runId: string,\n params: RunnerLogQueryParams = {},\n signal?: AbortSignal,\n): Promise<RunnerLogResult> {\n const result = await api.session(sessionId).log({\n params: { [RUN_ID_PARAM]: runId },\n // An unset parameter is left out rather than sent empty: the route reads a\n // missing one as its own default, and an empty grep as no filter at all.\n query: {\n [RUNNER_LOG_PARAMS.lines]: params.lines,\n [RUNNER_LOG_PARAMS.grep]: params.grep === '' ? undefined : params.grep,\n [RUNNER_LOG_PARAMS.ignoreCase]: params.ignoreCase === true ? true : undefined,\n [RUNNER_LOG_PARAMS.contextLines]: params.contextLines,\n },\n ...(signal === undefined ? {} : { signal }),\n });\n if (!result.ok) {\n // Status 0 is the transport never answering, which covers both a dead hub\n // and the caller replacing this request. An abort is the caller's own doing\n // and is reported as no answer rather than as a failure to show.\n if (result.status === 0) return { error: signal?.aborted === true ? '' : UNREACHABLE };\n return { error: result.error === '' ? `The hub answered ${String(result.status)}.` : result.error };\n }\n if (!isRecord(result.data) || typeof result.data.text !== 'string') {\n return { error: `The hub answered ${String(result.status)}.` };\n }\n return { slice: result.data };\n}\n","import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';\n\nimport { fetchRunnerLog } from '../_lib/logApi.native';\nimport { runners } from '../_lib/runnersStore.native';\n\nexport function useRunners(sessionId: string | null) {\n const subscribe = useCallback((listener: () => void) => {\n const subscription = runners.store.subscribe(listener);\n return () => subscription.unsubscribe();\n }, []);\n return useSyncExternalStore(subscribe, () => runners.select(runners.store.state, sessionId));\n}\n\n/** Native uses finite reads instead of browser SSE. Read a bounded tail through the host's sealed transport. */\nexport function useRunnerTail(sessionId: string | null, runId: string, running: boolean) {\n const [tail, setTail] = useState<{ id: string; text: string }>();\n useEffect(() => {\n if (sessionId === null || !running) return;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const read = async () => {\n try {\n const result = await fetchRunnerLog(sessionId, runId, { lines: 5 }, controller.signal);\n if (controller.signal.aborted) return;\n if ('slice' in result) {\n const text = result.slice.text\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .at(-1);\n if (text !== undefined) setTail({ id: `${sessionId}:${runId}`, text });\n if (!result.slice.running) return;\n }\n } catch {\n // A glance keeps the last known output when a read is unavailable.\n }\n if (!controller.signal.aborted) timer = setTimeout(() => void read(), 2_000);\n };\n void read();\n return () => {\n controller.abort();\n clearTimeout(timer);\n };\n }, [sessionId, runId, running]);\n return running && tail?.id === `${sessionId}:${runId}` ? tail.text : undefined;\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { fontOf, Button, Input, StatusBadge } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { type ReactNode, useEffect, useState } from 'react';\nimport { ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerLogResponse } from '../../../../../types/webRunnerLog';\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { logViewLines, runnerBadge, runnerFolderLabel, tailLineNumbers } from '../_lib/format';\nimport { fetchRunnerLog } from '../_lib/logApi.native';\nimport { requestRunnerStop } from '../_lib/runnersStore.native';\nimport { useRunners } from './runnerHooks.native';\n\n/** The top of both runner screens: name, outcome, an action, and the command in its folder. */\nexport function RunnerRunHeader({\n run,\n name,\n sessionDirectory,\n action,\n}: {\n run: RunnerRunView | undefined;\n name: string;\n sessionDirectory: string | undefined;\n action?: ReactNode;\n}) {\n const badge = runnerBadge(run);\n return (\n <>\n <View style={styles.header}>\n <Text numberOfLines={1} style={styles.title}>\n {name}\n </Text>\n <StatusBadge tone={badge.tone}>{badge.label}</StatusBadge>\n <View style={styles.grow} />\n {action}\n </View>\n {run ? (\n <View style={styles.commandBox}>\n <Text selectable style={styles.command}>\n <Text style={styles.prompt}>$ </Text>\n {run.command}\n </Text>\n <Text numberOfLines={1} style={styles.folder}>\n in {runnerFolderLabel(run.cwd, sessionDirectory)}\n </Text>\n </View>\n ) : (\n <Text style={styles.meta}>No longer listed; its log stays readable.</Text>\n )}\n </>\n );\n}\n\nexport function runnerLogTab(run: RunnerRunView) {\n return {\n id: `runner-log-${run.id}`,\n label: `log · ${run.name}`,\n panel: (props: WebPluginSlotProps) => <RunnerLogPanel {...props} runId={run.id} />,\n };\n}\n\nexport function RunnerLogPanel({ sessionId, runId, sendSessionFrame }: WebPluginSlotProps & { runId: string }) {\n const session = useRunners(sessionId);\n const run = session.runs.find((candidate) => candidate.id === runId);\n const [typed, setTyped] = useState('');\n const [query, setQuery] = useState('');\n const [ignoreCase, setIgnoreCase] = useState(false);\n const [context, setContext] = useState(2);\n const [following, setFollowing] = useState(true);\n const requestKey = `${sessionId}:${runId}:${query}:${ignoreCase}:${context}:${following}`;\n const [loaded, setLoaded] = useState<{ key: string; slice?: RunnerLogResponse; error: string }>();\n const slice = loaded?.key === requestKey ? loaded.slice : undefined;\n const error = loaded?.key === requestKey ? loaded.error : '';\n useEffect(() => {\n const timer = setTimeout(() => setQuery(typed), 200);\n return () => clearTimeout(timer);\n }, [typed]);\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const read = async () => {\n const result = await fetchRunnerLog(\n sessionId,\n runId,\n { lines: 2000, grep: query, ignoreCase, contextLines: context },\n controller.signal,\n );\n if (controller.signal.aborted) return;\n setLoaded(\n 'error' in result\n ? { key: requestKey, error: result.error }\n : { key: requestKey, slice: result.slice, error: '' },\n );\n if (following && query === '' && ('error' in result || result.slice.running))\n timer = setTimeout(() => void read(), 2_000);\n };\n void read();\n return () => {\n controller.abort();\n clearTimeout(timer);\n };\n }, [sessionId, runId, query, ignoreCase, context, following, requestKey]);\n const lines = logViewLines(slice?.text ?? '', [], 2000).lines;\n const numbers = query === '' ? tailLineNumbers(slice?.totalLines ?? 0, lines.length) : (slice?.lineNumbers ?? []);\n const gutter = String(numbers.at(-1) ?? 0).length;\n const stopping = session.stopRequested.includes(runId);\n const live = following && query === '' && slice?.running === true;\n return (\n <View testID=\"runner-log-panel\" style={styles.panel}>\n <RunnerRunHeader\n run={run}\n name={run?.name ?? runId}\n sessionDirectory={session.sessionDirectory}\n action={\n run?.state === 'running' && sessionId !== null ? (\n <Button\n variant={stopping ? 'outline' : 'danger-outline'}\n size=\"sm\"\n disabled={stopping}\n onPress={() => requestRunnerStop(sendSessionFrame, sessionId, runId)}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null\n }\n />\n <Input\n testID=\"runner-log-search\"\n accessibilityLabel=\"Search this log\"\n placeholder=\"Search output\"\n value={typed}\n onChangeText={setTyped}\n autoCapitalize=\"none\"\n returnKeyType=\"search\"\n />\n <View style={styles.actions}>\n <Button\n variant={ignoreCase ? 'subtle' : 'outline'}\n size=\"sm\"\n accessibilityState={{ selected: ignoreCase }}\n onPress={() => setIgnoreCase((value) => !value)}\n >\n {ignoreCase ? 'Any case' : 'Match case'}\n </Button>\n {query === '' ? null : (\n <Button\n variant={context > 0 ? 'subtle' : 'outline'}\n size=\"sm\"\n onPress={() => setContext((value) => (value === 0 ? 2 : 0))}\n >\n {`± ${String(context)} lines`}\n </Button>\n )}\n {query === '' && slice?.running === true ? (\n <Button\n variant={live ? 'subtle' : 'outline'}\n size=\"sm\"\n accessibilityState={{ selected: live }}\n onPress={() => setFollowing((value) => !value)}\n >\n {live ? 'Following' : 'Follow'}\n </Button>\n ) : null}\n </View>\n <Text style={styles.meta}>\n {slice === undefined\n ? error === ''\n ? 'Reading…'\n : ''\n : query !== ''\n ? `${slice.lineCount} matching of ${slice.totalLines} lines`\n : `${live ? 'Tailing' : 'Showing'} last ${lines.length} of ${slice.totalLines} lines`}\n </Text>\n <ScrollView style={styles.log} contentContainerStyle={styles.logBody}>\n {error !== '' ? (\n <Text accessibilityRole=\"alert\" style={styles.error}>\n {error}\n </Text>\n ) : slice?.exists === false ? (\n <Text style={styles.meta}>This runner has not written a log yet.</Text>\n ) : slice && lines.length === 0 ? (\n <Text style={styles.meta}>{query === '' ? 'The log is empty.' : 'Nothing matched.'}</Text>\n ) : (\n lines.map((line, index) => (\n <Text selectable key={`${numbers[index] ?? index}`} style={styles.line}>\n <Text style={styles.number}>{`${String(numbers[index] ?? '').padStart(gutter)} `}</Text>\n {line}\n </Text>\n ))\n )}\n </ScrollView>\n <Text selectable numberOfLines={1} ellipsizeMode=\"middle\" style={styles.path}>\n {run?.logPath ?? slice?.path ?? ''}\n </Text>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(2), padding: theme.spacing(3), backgroundColor: theme.colors.bg },\n header: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minHeight: 32 },\n grow: { flex: 1 },\n title: {\n flexShrink: 1,\n color: theme.colors.hi,\n fontWeight: '600',\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n },\n commandBox: {\n gap: theme.spacing(1),\n paddingHorizontal: theme.spacing(3),\n paddingVertical: theme.spacing(2),\n borderRadius: theme.borderRadius.md,\n backgroundColor: theme.colors.deep,\n },\n command: { color: theme.colors.hi, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.sm },\n prompt: { color: theme.colors.faint },\n folder: { color: theme.colors.dim, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.xs },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n error: { color: theme.colors.red, fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.sm },\n actions: { flexDirection: 'row', gap: theme.spacing(2), flexWrap: 'wrap' },\n log: { flex: 1, borderRadius: theme.borderRadius.md, backgroundColor: theme.colors.deep },\n logBody: { padding: theme.spacing(3) },\n line: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.xs,\n lineHeight: theme.lineHeight.sm,\n },\n number: { color: theme.colors.faint },\n path: { color: theme.colors.faint, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.xs },\n}));\n","import { api } from '../../../../../../generated/client.native';\nimport { RUN_ID_PARAM } from '../../../../../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerOutputResponse } from '../../../../../types/webRunnerLog';\n\n/** Finite native reads preserve raw terminal bytes and generation-based replacement detection. */\nexport async function readRunnerScreen(\n sessionId: string,\n runId: string,\n from: number,\n generation: string | undefined,\n signal: AbortSignal,\n): Promise<RunnerOutputResponse> {\n const result = await api.session(sessionId).screenRead({\n params: { [RUN_ID_PARAM]: runId },\n query: { [RUNNER_LOG_PARAMS.from]: from, [RUNNER_LOG_PARAMS.generation]: generation },\n signal,\n });\n if (!result.ok) throw new Error(result.error || 'The connection to this pane dropped.');\n return result.data;\n}\nexport async function sendRunnerInput(sessionId: string, runId: string, text: string): Promise<void> {\n const result = await api.session(sessionId).input({ params: { [RUN_ID_PARAM]: runId }, body: { text } });\n if (!result.ok) throw new Error(result.error || 'This runner is no longer accepting input.');\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { fontOf, Button, SearchIcon } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useMemo } from 'react';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { requestRunnerStop } from '../_lib/runnersStore.native';\nimport { readRunnerScreen, sendRunnerInput } from '../_lib/screenApi.native';\nimport { useRunners } from './runnerHooks.native';\nimport { RunnerRunHeader, runnerLogTab } from './RunnerLogPanel.native';\n\n/** The host renders genuine terminal bytes; this package owns every scoped API call. */\nexport function runnerShellTab(run: RunnerRunView) {\n return {\n id: `runner-shell-${run.id}`,\n label: `shell · ${run.name}`,\n panel: (props: WebPluginSlotProps) => <RunnerShellPanel {...props} runId={run.id} />,\n };\n}\n\n/**\n * One terminal runner, full screen. A phone keyboard has no Ctrl key, so\n * Ctrl-C is a button; Search opens the runner's log, which the sink keeps\n * scrubbed beside the raw bytes for exactly that.\n */\nexport function RunnerShellPanel({\n sessionId,\n runId,\n renderRunnerTerminal,\n sendSessionFrame,\n openTransientTab,\n}: WebPluginSlotProps & { runId: string }) {\n const session = useRunners(sessionId);\n const run = session.runs.find((candidate) => candidate.id === runId);\n const live = run?.state === 'running';\n const stopping = session.stopRequested.includes(runId);\n const request = useMemo(\n () =>\n sessionId === null\n ? undefined\n : {\n key: `${sessionId}:${runId}`,\n read: (from: number, generation: string | undefined, signal: AbortSignal) =>\n readRunnerScreen(sessionId, runId, from, generation, signal),\n write: (text: string) => sendRunnerInput(sessionId, runId, text),\n },\n [sessionId, runId],\n );\n return (\n <View style={styles.panel}>\n <RunnerRunHeader\n run={run}\n name={run?.name ?? runId}\n sessionDirectory={session.sessionDirectory}\n action={\n live && sessionId !== null ? (\n <Button\n testID=\"runner-shell-stop\"\n variant={stopping ? 'outline' : 'danger-outline'}\n size=\"sm\"\n disabled={stopping}\n onPress={() => requestRunnerStop(sendSessionFrame, sessionId, runId)}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null\n }\n />\n <View style={styles.terminal}>\n {request === undefined ? (\n <Text style={styles.meta}>Select a session to attach to this runner.</Text>\n ) : (\n renderRunnerTerminal?.(request)\n )}\n </View>\n <View style={styles.actions}>\n {run ? (\n <Button\n testID=\"runner-shell-search\"\n variant=\"outline\"\n onPress={() => openTransientTab(runnerLogTab(run))}\n style={styles.action}\n >\n <SearchIcon size={14} />\n Search output\n </Button>\n ) : null}\n {live && sessionId !== null ? (\n <Button\n testID=\"runner-shell-interrupt\"\n variant=\"outline\"\n onPress={() => void sendRunnerInput(sessionId, runId, '\\u0003')}\n style={styles.action}\n >\n Ctrl-C\n </Button>\n ) : null}\n </View>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(2), padding: theme.spacing(3), backgroundColor: theme.colors.bg },\n actions: { flexDirection: 'row', gap: theme.spacing(2) },\n action: { flex: 1 },\n terminal: { flex: 1 },\n meta: { color: theme.colors.dim, fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.sm },\n}));\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { ArrowRightIcon, fontOf, Button, Dot, ListRow } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useEffect, useRef, useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { formatRunnerUptime } from '../_lib/format';\nimport { runnerRunRequest } from '../_lib/launchLine';\nimport { requestRunnerStart, requestRunnerStop } from '../_lib/runnersStore.native';\nimport { RunnerCommandBar } from './RunnerCommandBar.native';\nimport { useRunners, useRunnerTail } from './runnerHooks.native';\nimport { runnerLogTab } from './RunnerLogPanel.native';\nimport { runnerShellTab } from './RunnerShellPanel.native';\n\n/** A runner's own screen: its terminal when it has one, its log when it does not. */\nfunction runnerTab(run: RunnerRunView) {\n return run.interactive ? runnerShellTab(run) : runnerLogTab(run);\n}\n\nexport function runnersTab() {\n return { id: 'runner-runs', label: 'runners', panel: RunnersPanel };\n}\n\nfunction RunnerRow({\n run,\n props,\n stopping,\n now,\n}: {\n run: RunnerRunView;\n props: WebPluginSlotProps;\n stopping: boolean;\n now: number;\n}) {\n const tail = useRunnerTail(props.sessionId, run.id, run.state === 'running');\n // One tap target for the row: the name, its uptime and Stop on one line, then\n // what it is doing, falling back to what it was asked to do.\n return (\n <Pressable\n testID={`activity-runner-${run.id}`}\n accessibilityRole=\"button\"\n accessibilityLabel={`Open ${run.name}`}\n onPress={() => props.openTransientTab(runnerTab(run))}\n style={({ pressed }) => [styles.row, pressed && styles.pressed]}\n >\n <View style={styles.line}>\n <Dot tone=\"yellow\" pulse />\n <Text numberOfLines={1} style={styles.name}>\n {run.name}\n </Text>\n <Text style={styles.meta}>{formatRunnerUptime(run.startedAt, now)}</Text>\n {props.sessionId !== null ? (\n <Button\n testID={`activity-runner-stop-${run.id}`}\n size=\"xs\"\n variant={stopping ? 'outline' : 'danger-outline'}\n disabled={stopping}\n onPress={() => {\n if (props.sessionId !== null) requestRunnerStop(props.sendSessionFrame, props.sessionId, run.id);\n }}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null}\n </View>\n <Text numberOfLines={1} style={styles.tail}>\n {tail ?? `$ ${run.command}`}\n </Text>\n </Pressable>\n );\n}\n\nexport function RunnersActivitySection(props: WebPluginSlotProps) {\n const session = useRunners(props.sessionId);\n const running = session.runs.filter((run) => run.state === 'running');\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), 10_000);\n return () => clearInterval(timer);\n }, []);\n return running.length === 0 ? (\n // The cockpit's idle line: the status, then the one jade action beside it.\n <View style={styles.idle}>\n <Text style={styles.meta}>Idle</Text>\n {props.sessionId !== null ? (\n <Pressable\n accessibilityRole=\"button\"\n testID=\"activity-runner-launch\"\n hitSlop={10}\n onPress={() => props.openTransientTab(runnersTab())}\n style={styles.launch}\n >\n <Text style={styles.launchText}>Launch a runner</Text>\n <ArrowRightIcon size={14} color=\"green\" />\n </Pressable>\n ) : null}\n </View>\n ) : (\n <View>\n {running.map((run) => (\n <RunnerRow key={run.id} run={run} props={props} now={now} stopping={session.stopRequested.includes(run.id)} />\n ))}\n </View>\n );\n}\n\n/**\n * Every running runner and the one place to start another: type a command\n * and Run hosts it on a terminal (plain output when the host has no rmux or\n * tmux), or leave it empty for a login shell. The started runner opens on\n * its own screen.\n */\nexport function RunnersPanel(props: WebPluginSlotProps) {\n const { openTransientTab } = props;\n const session = useRunners(props.sessionId);\n const running = session.runs.filter((run) => run.state === 'running');\n const [command, setCommand] = useState('');\n const [cwd, setCwd] = useState('');\n const [now, setNow] = useState(() => Date.now());\n const baseline = useRef<ReadonlySet<string> | null>(null);\n /** What the pending launch ran, put back if the start fails. */\n const launched = useRef('');\n const timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), 10_000);\n return () => clearInterval(timer);\n }, []);\n // ponytail: the first runner to appear after a launch is taken to be it, the\n // same guess the web panel makes.\n useEffect(() => {\n if (baseline.current === null) return;\n const started = session.runs.find((run) => run.state === 'running' && !baseline.current?.has(run.id));\n if (started === undefined) return;\n baseline.current = null;\n clearTimeout(timeout.current);\n openTransientTab(runnerTab(started));\n }, [session.runs, openTransientTab]);\n // A start that did not happen hands the command back beside its problem.\n const failureId = session.startFailure?.id;\n useEffect(() => {\n if (failureId === undefined || baseline.current === null) return;\n baseline.current = null;\n clearTimeout(timeout.current);\n setCommand(launched.current);\n }, [failureId]);\n useEffect(() => () => clearTimeout(timeout.current), []);\n const launch = () => {\n if (props.sessionId === null) return;\n baseline.current = new Set(session.runs.map((run) => run.id));\n clearTimeout(timeout.current);\n timeout.current = setTimeout(() => {\n baseline.current = null;\n }, 30_000);\n requestRunnerStart(props.sendSessionFrame, props.sessionId, runnerRunRequest(command, cwd));\n launched.current = command;\n setCommand('');\n };\n return (\n <View style={styles.panel}>\n <View style={styles.header}>\n <Text style={styles.title}>Runners</Text>\n <Text style={styles.meta}>{running.length === 0 ? 'Nothing running' : `${running.length} running`}</Text>\n </View>\n {props.sessionId !== null ? (\n <RunnerCommandBar\n command={command}\n onCommandChange={setCommand}\n cwd={cwd}\n onCwdChange={setCwd}\n folders={running.map((run) => run.cwd)}\n {...(session.sessionDirectory === undefined ? {} : { sessionDirectory: session.sessionDirectory })}\n {...(session.startFailure === undefined ? {} : { problem: session.startFailure.message })}\n onLaunch={launch}\n />\n ) : null}\n <ScrollView>\n {running.length === 0 ? (\n <Text style={styles.meta}>\n {props.sessionId === null\n ? 'Open a session to start a runner.'\n : 'Nothing running yet. Type a command above, like pnpm dev, and it opens as a live terminal.'}\n </Text>\n ) : (\n running.map((run) => (\n <ListRow\n key={run.id}\n leading={<Dot tone=\"yellow\" pulse />}\n title={run.name}\n description={`${run.command} · ${formatRunnerUptime(run.startedAt, now)}`}\n disclosure\n onPress={() => openTransientTab(runnerTab(run))}\n />\n ))\n )}\n </ScrollView>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(3), padding: theme.spacing(4), backgroundColor: theme.colors.bg },\n header: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n row: { gap: theme.spacing(1), paddingVertical: theme.spacing(2) },\n pressed: { opacity: 0.6 },\n line: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n name: {\n flex: 1,\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n tail: {\n // Under the name: the small dot plus the line's gap.\n paddingLeft: theme.spacing(3.5),\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n title: {\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.lg,\n fontWeight: '600',\n },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n idle: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: theme.spacing(4) },\n launch: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(1) },\n launchText: {\n color: theme.colors.green,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n}));\n","import { defineActivityGroup } from '@agimon-ai/po-core/native';\nimport { PlayCircleIcon } from '@agimon-ai/po-native-components';\nimport { createElement } from 'react';\n\nimport { runnersTab } from '../_components/RunnersPanel.native';\nimport { runnerActivitySource } from '../_lib/runnersStore.native';\n\nexport default defineActivityGroup({\n name: 'runners',\n keys: 'r l',\n icon: () => createElement(PlayCircleIcon),\n statusKey: 'po-runner-runners',\n activeSource: runnerActivitySource,\n transientTab: runnersTab,\n order: 20,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/native';\n\nimport { runnerRunsChannel } from '../_lib/runnersStore.native';\nexport default defineChannelFile(runnerRunsChannel);\n","import { defineFill } from '@agimon-ai/po-core/native';\n\nimport { RunnersActivitySection } from '../_components/RunnersPanel.native';\n\nexport default defineFill({ slot: 'activity.runners', id: 'runners', component: RunnersActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport activityGroupRunners from '../src/extensions/workspaces/sessions/(frontend)/activity-group/runners.native';\nimport channelRunnerRuns from '../src/extensions/workspaces/sessions/(frontend)/channel/runner-runs.native';\nimport fillRunners from '../src/extensions/workspaces/sessions/(frontend)/fill/Runners.activity.runners.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'runner',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'runners' }, activityGroupRunners)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'runner_runs' }, channelRunnerRuns)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.runners', id: 'runners' }, fillRunners)]; },\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"native.mjs","names":["isRecord","Button","FolderIcon","fontOf","Input","ListRow","Sheet","SheetBody","SheetContent","SheetFooter","SheetHeader","SheetTitle","PoNativeTheme","useState","StyleSheet","RunnerCommandBarProps","plainPunctuation","runnerFolderLabel","runnerFolders","RunnerCommandBar","command","onCommandChange","cwd","onCwdChange","sessionDirectory","folders","problem","onLaunch","defaultFolderOpen","folderOpen","setFolderOpen","path","setPath","pick","next","choices","styles","bar","line","grow","text","trim","undefined","meta","map","folder","create","theme","gap","spacing","uni__dependencies","flexDirection","alignItems","flex","color","colors","dim","fontFamily","sans","fontSize","sm","lineHeight","red","styles","WebPluginSlotProps","fontOf","Button","Input","StatusBadge","PoNativeTheme","ReactNode","useEffect","useState","StyleSheet","RunnerLogResponse","RunnerRunView","logViewLines","runnerBadge","runnerFolderLabel","tailLineNumbers","fetchRunnerLog","requestRunnerStop","useRunners","RunnerRunHeader","run","name","sessionDirectory","action","badge","commandOpen","setCommandOpen","styles","header","title","tone","label","grow","expanded","commandBox","open","undefined","command","prompt","folder","cwd","meta","runnerLogTab","id","panel","props","RunnerLogPanel","sessionId","runId","sendSessionFrame","session","runs","find","candidate","typed","setTyped","query","setQuery","ignoreCase","setIgnoreCase","context","setContext","following","setFollowing","requestKey","loaded","setLoaded","key","slice","error","timer","setTimeout","clearTimeout","controller","AbortController","ReturnType","read","result","lines","grep","contextLines","signal","aborted","running","abort","text","numbers","totalLines","length","lineNumbers","gutter","String","at","stopping","stopRequested","includes","live","state","actions","selected","value","lineCount","log","logBody","exists","map","line","index","number","padStart","path","logPath","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","alignItems","minHeight","flexShrink","color","hi","fontWeight","fontFamily","sans","fontSize","base","paddingHorizontal","paddingVertical","borderRadius","md","deep","mono","sm","faint","dim","xs","lineHeight","red","flexWrap","styles","WebPluginSlotProps","fontOf","Button","SearchIcon","PoNativeTheme","useMemo","StyleSheet","RunnerRunView","requestRunnerStop","readRunnerScreen","sendRunnerInput","useRunners","RunnerRunHeader","runnerLogTab","runnerShellTab","run","id","label","name","panel","props","RunnerShellPanel","sessionId","runId","renderRunnerTerminal","sendSessionFrame","openTransientTab","session","runs","find","candidate","live","state","stopping","stopRequested","includes","request","undefined","key","read","from","generation","signal","AbortSignal","write","text","styles","sessionDirectory","terminal","meta","actions","action","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","color","dim","fontFamily","sans","fontSize","sm","styles","WebPluginSlotProps","ArrowRightIcon","fontOf","Button","Dot","ListRow","PoNativeTheme","useEffect","useRef","useState","StyleSheet","RunnerRunView","formatRunnerUptime","runnerRunRequest","requestRunnerStart","requestRunnerStop","RunnerCommandBar","useRunners","useRunnerTail","runnerLogTab","runnerShellTab","runnerTab","run","interactive","runnersTab","id","label","panel","RunnersPanel","RunnerRow","props","stopping","now","tail","sessionId","state","name","openTransientTab","pressed","styles","row","line","meta","startedAt","sendSessionFrame","command","RunnersActivitySection","session","running","runs","filter","setNow","Date","timer","setInterval","clearInterval","length","idle","launch","launchText","map","stopRequested","includes","setCommand","cwd","setCwd","baseline","ReadonlySet","launched","timeout","ReturnType","setTimeout","undefined","current","started","find","has","clearTimeout","failureId","startFailure","Set","header","title","sessionDirectory","problem","message","create","theme","flex","gap","spacing","padding","backgroundColor","colors","bg","uni__dependencies","flexDirection","alignItems","paddingVertical","opacity","color","hi","fontFamily","sans","fontSize","base","lineHeight","fontWeight","medium","paddingLeft","dim","mono","sm","lg","flexWrap","green","activityGroupRunners","channelRunnerRuns","fillRunners"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/_lib/format.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/launchLine.ts","../../src/constants/webRunners.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/runnersStore.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerCommandBar.native.tsx","../../src/constants/webRunnerLog.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../generated/client.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/logApi.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/runnerHooks.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerLogPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/_lib/screenApi.native.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnerShellPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/RunnersPanel.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/runners.native.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/runner-runs.native.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Runners.activity.runners.native.tsx","../../generated/native.ts"],"sourcesContent":["import type { RunnerRunView } from '../../../../../types/webRunners';\n\n/**\n * Local copy of the runtime's formatUptime (src/services/bashResult/index.ts):\n * the web plugin may reach only src/types, so the one helper it needs lives here.\n */\nexport function formatRunnerUptime(startedAt: string, now: number): string {\n const started = Date.parse(startedAt);\n if (Number.isNaN(started)) return 'unknown';\n const seconds = Math.max(0, Math.floor((now - started) / 1000));\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;\n}\n\n/** A bounded log view: what to render, and what the bound left out. */\nexport interface LogView {\n lines: string[];\n /** Lines dropped off the top to stay within the bound. */\n hidden: number;\n}\n\nexport interface LogViewOptions {\n /**\n * Drop the slice's last line because it is a fragment the follow stream will\n * deliver again, whole. Set only while following: a finished run's last line\n * may legitimately lack a newline, and dropping it would hide real output.\n */\n dropPartialTail?: boolean;\n}\n\n/**\n * The lines a log view shows: the slice's text, then whatever the follow\n * stream appended, bounded to the newest `max`.\n *\n * A log file ends with a newline, and the reader hands its text back with that\n * newline intact, so a naive split reports one blank line more than the file\n * has. Dropping exactly one trailing empty entry keeps the count the reader\n * sees equal to the count the server reported.\n */\nexport function logViewLines(\n text: string,\n appended: readonly string[],\n max: number,\n options: LogViewOptions = {},\n): LogView {\n const split = text.split('\\n');\n if (split.length > 0 && split[split.length - 1] === '') split.pop();\n if (options.dropPartialTail === true && split.length > 0) split.pop();\n const all = [...split, ...appended];\n const lines = all.slice(-max);\n return { lines, hidden: all.length - lines.length };\n}\n\n/**\n * Absolute 1-based numbers for a tail, counted back from the last line.\n *\n * A tail is contiguous, so only its end has to be known. Grep results are not\n * contiguous and carry their own numbers from the reader instead.\n */\nexport function tailLineNumbers(lastLineNumber: number, count: number): number[] {\n const first = Math.max(1, lastLineNumber - count + 1);\n return Array.from({ length: count }, (_, index) => first + index);\n}\n\n/**\n * Whether the view is actually following a growing log.\n *\n * Three things must hold: the reader asked to follow, no query is set (a\n * filtered view is a snapshot of the whole file, which a stream cannot\n * assemble a chunk at a time), and the runner is still writing. Without the\n * last one a finished run would claim to be tailing a file nothing will ever\n * append to.\n */\nexport function isFollowingLive(following: boolean, filtering: boolean, running: boolean): boolean {\n return following && !filtering && running;\n}\n\n/**\n * A folder as the command bar names it: the session directory by its last\n * segment, a folder inside it relative to it, anything else as written.\n */\nexport function runnerFolderLabel(folder: string, sessionDirectory: string | undefined): string {\n if (folder === '' || folder === sessionDirectory) {\n return sessionDirectory === undefined\n ? 'session directory'\n : (sessionDirectory.split('/').pop() ?? sessionDirectory);\n }\n if (sessionDirectory !== undefined && folder.startsWith(`${sessionDirectory}/`)) {\n return folder.slice(sessionDirectory.length + 1);\n }\n return folder;\n}\n\n/** The folders the picker offers past the session directory: those in use, then the one in effect. */\nexport function runnerFolders(\n inUse: readonly string[],\n current: string,\n sessionDirectory: string | undefined,\n): string[] {\n const folders = new Set([...inUse, current]);\n folders.delete('');\n if (sessionDirectory !== undefined) folders.delete(sessionDirectory);\n return [...folders];\n}\n\n/**\n * Undoes iOS smart punctuation, which React Native cannot switch off: a shell\n * reads ‘hi’ as literal quotes and a long dash before port as no flag at all.\n */\nexport function plainPunctuation(text: string): string {\n return text\n .replace(/[‘’]/g, \"'\")\n .replace(/[“”]/g, '\"')\n .replace(/\\u2014/g, '--');\n}\n\n/** The badge a run wears: its state while running, then how it ended. */\nexport function runnerBadge(run: RunnerRunView | undefined): {\n tone: 'neutral' | 'running' | 'ok' | 'error';\n label: string;\n} {\n if (run === undefined) return { tone: 'neutral', label: 'gone' };\n if (run.state === 'running') return { tone: 'running', label: 'running' };\n if (run.exit === undefined || run.exit.reason === 'stopped') return { tone: 'neutral', label: 'stopped' };\n const clean = run.exit.reason === 'completed' && (run.exit.code === 0 || run.exit.code === null);\n return clean ? { tone: 'ok', label: 'done' } : { tone: 'error', label: run.exit.reason.replace('_', ' ') };\n}\n","/**\n * The `/runners start` line the runners panel's command bar sends.\n *\n * WHY THE BUILDER LIVES HERE AND THE PARSER DOES NOT:\n * Cockpit code is compiled into the host bundle and may import only this\n * package's own web and type modules, so it cannot share the session's parser\n * in src/services. The two are pinned together by a test that parses what this\n * writes, which is the only guarantee that matters: the line has to survive\n * the trip.\n */\n\nexport const RUNNERS_START_LINE = '/runners start';\n/** A persistent login shell for the direct shell action in Runner Space. */\nexport const RUNNER_SHELL_COMMAND = 'exec \"${SHELL:-/bin/bash}\" -l';\n/** Ends the options and begins the command; the command may hold `=` and flags of its own. */\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\n\nexport interface RunnerLaunchRequest {\n command: string;\n cwd?: string;\n name?: string;\n interactive?: boolean;\n /** A terminal when rmux or tmux is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\n/** The no-form launch used when a reader asks for a shell rather than a command. */\nexport const RUNNER_SHELL_REQUEST: RunnerLaunchRequest = {\n command: RUNNER_SHELL_COMMAND,\n name: 'shell',\n interactive: true,\n};\n\n/**\n * What the runners panel's Run sends: the typed command on a terminal when\n * one is available, or a login shell when nothing was typed.\n */\nexport function runnerRunRequest(command: string, cwd: string): RunnerLaunchRequest {\n const where = cwd.trim() === '' ? {} : { cwd };\n return command.trim() === '' ? { ...RUNNER_SHELL_REQUEST, ...where } : { command, ...where, preferInteractive: true };\n}\n\n/** Quotes a value that carries whitespace, which is how a value survives tokenizing. */\nfunction quote(value: string): string {\n return /\\s/u.test(value) ? `\"${value}\"` : value;\n}\n\nexport function runnerLaunchLine(request: RunnerLaunchRequest): string {\n const cwd = request.cwd?.trim() ?? '';\n const name = request.name?.trim() ?? '';\n const pairs = [\n ...(name === '' ? [] : [`name=${quote(name)}`]),\n ...(cwd === '' ? [] : [`cwd=${quote(cwd)}`]),\n ...(request.interactive === true\n ? ['interactive=true']\n : request.preferInteractive === true\n ? ['interactive=prefer']\n : []),\n ];\n // The separator is always written, even with no options before it, so the\n // command is never re-read as a `key=value` pair.\n return [RUNNERS_START_LINE, ...pairs, RUNNERS_COMMAND_SEPARATOR, request.command.trim()].join(' ');\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","import { defineSessionStore, type SessionFrameSender } from '@agimon-ai/po-core/native';\n\nimport { RUNNER_RUNS_TYPE } from '../../../../../constants/webRunners';\nimport { type RunnerRunView, type RunnerStartFailureView } from '../../../../../types/webRunners';\nimport { type RunnerLaunchRequest, runnerLaunchLine } from './launchLine';\n\n/** The runtime's slash verb that stops one runner headlessly. */\nconst STOP_COMMAND = '/runners stop';\n\n/** One session's record: the hub's last report plus what this page asked for. */\nexport interface RunnersSession extends RunnerRunsPayload {\n /** Runners a stop was asked for and that have not yet reported an exit. */\n stopRequested: string[];\n}\n\nexport const runners = defineSessionStore<RunnersSession>({ runs: [], stopRequested: [] });\n\n/** The runner channel keeps the dock's busy state aligned with its rendered run list. */\nexport const runnerActivitySource = {\n subscribe(listener: () => void) {\n const subscription = runners.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive(sessionId: string | null) {\n return runners.select(runners.store.state, sessionId).runs.some((run) => run.state === 'running');\n },\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Asks the runtime to stop a runner through the same slash command the TUI's\n * Runner Space uses. The request is remembered until the runner's record\n * reports an exit; the runtime, not this click, decides when that is.\n */\nexport function requestRunnerStop(send: SessionFrameSender, sessionId: string, id: string): void {\n send(sessionId, { type: 'prompt', message: `${STOP_COMMAND} ${id}` });\n runners.update(sessionId, (current) => ({\n ...current,\n stopRequested: [...current.stopRequested.filter((known) => known !== id), id],\n }));\n}\n\n/**\n * Asks the runtime to start a runner, through the same prompt frame the stop\n * request uses and the same way agents and workflows launch their own work.\n *\n * Nothing is recorded as pending here. The runtime names the run, and it\n * arrives in the feed on its own; guessing at an id before it exists would\n * only have to be reconciled later.\n */\nexport function requestRunnerStart(send: SessionFrameSender, sessionId: string, request: RunnerLaunchRequest): void {\n send(sessionId, { type: 'prompt', message: runnerLaunchLine(request) });\n}\n\nexport interface RunnerRunsPayload {\n /** The runners the hub last reported; presented order preserved. */\n runs: RunnerRunView[];\n /** Where a runner starts when no folder is given. */\n sessionDirectory?: string;\n /** The last start that did not happen. */\n startFailure?: RunnerStartFailureView;\n}\n\n/** The plugin's session data channel: 'runner_runs' payloads into the store. */\nexport const runnerRunsChannel = runners.channel<RunnerRunsPayload>({\n channel: RUNNER_RUNS_TYPE,\n parse(input) {\n if (!isRecord(input) || !Array.isArray(input.runs)) return null;\n const failure = input.startFailure;\n return {\n runs: input.runs.filter(isRecord) as unknown as RunnerRunView[],\n ...(typeof input.sessionDirectory === 'string' ? { sessionDirectory: input.sessionDirectory } : {}),\n ...(isRecord(failure) && typeof failure.id === 'string' && typeof failure.message === 'string'\n ? { startFailure: { id: failure.id, message: failure.message } }\n : {}),\n };\n },\n reduce(current, payload) {\n // A stop request is spent once the runner reports an exit or leaves the feed.\n const stillRunning = new Set(payload.runs.filter((run) => run.state === 'running').map((run) => run.id));\n return { ...payload, stopRequested: current.stopRequested.filter((id) => stillRunning.has(id)) };\n },\n});\n","import {\n Button,\n FolderIcon,\n fontOf,\n Input,\n ListRow,\n Sheet,\n SheetBody,\n SheetContent,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n} from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerCommandBarProps } from '../../../../../types/webRunners';\nimport { plainPunctuation, runnerFolderLabel, runnerFolders } from '../_lib/format';\n\n/**\n * The phone's command bar: the command and Run on one line, the folder it runs\n * in on the row under it, chosen from a bottom sheet.\n */\nexport function RunnerCommandBar({\n command,\n onCommandChange,\n cwd,\n onCwdChange,\n sessionDirectory,\n folders,\n problem,\n onLaunch,\n defaultFolderOpen = false,\n}: RunnerCommandBarProps) {\n const [folderOpen, setFolderOpen] = useState(defaultFolderOpen);\n const [path, setPath] = useState('');\n const pick = (next: string): void => {\n onCwdChange(next);\n setPath('');\n setFolderOpen(false);\n };\n const choices = ['', ...runnerFolders(folders, cwd, sessionDirectory)];\n\n return (\n <View style={styles.bar}>\n <View style={styles.line}>\n <View style={styles.grow}>\n <Input\n testID=\"runners-panel-command\"\n accessibilityLabel=\"Command to run\"\n size=\"lg\"\n value={command}\n onChangeText={(text) => onCommandChange(plainPunctuation(text))}\n onSubmitEditing={onLaunch}\n placeholder=\"a command, like pnpm dev\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n returnKeyType=\"go\"\n />\n </View>\n <Button testID=\"runners-panel-run\" variant=\"primary\" size=\"lg\" onPress={onLaunch}>\n {command.trim() === '' ? 'Open shell' : 'Run'}\n </Button>\n </View>\n <ListRow\n leading={<FolderIcon size={14} color={problem === undefined ? 'faint' : 'red'} />}\n title={runnerFolderLabel(cwd, sessionDirectory)}\n description=\"Runs in\"\n disclosure\n accessibilityLabel={`Runs in ${runnerFolderLabel(cwd, sessionDirectory)}`}\n onPress={() => setFolderOpen(true)}\n />\n {problem === undefined ? (\n <Text style={styles.meta}>Leave the command empty for a plain shell.</Text>\n ) : (\n <Text accessibilityRole=\"alert\" testID=\"runners-panel-problem\" style={styles.problem}>\n {problem}\n </Text>\n )}\n <Sheet open={folderOpen} onOpenChange={setFolderOpen}>\n <SheetContent>\n <SheetHeader closeLabel=\"Close folders\">\n <SheetTitle>Runs in</SheetTitle>\n </SheetHeader>\n <SheetBody>\n <View>\n {choices.map((folder) => (\n <ListRow\n key={folder}\n title={folder === '' ? 'Session directory' : runnerFolderLabel(folder, sessionDirectory)}\n description={folder === '' ? (sessionDirectory ?? 'Where this session started') : folder}\n selected={folder === cwd}\n onPress={() => pick(folder)}\n />\n ))}\n </View>\n <Input\n testID=\"runners-panel-cwd-path\"\n accessibilityLabel=\"Another folder\"\n value={path}\n onChangeText={(text) => setPath(plainPunctuation(text))}\n onSubmitEditing={() => {\n if (path.trim() !== '') pick(path.trim());\n }}\n placeholder=\"another folder, like packages/web\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n returnKeyType=\"done\"\n />\n </SheetBody>\n <SheetFooter>A relative folder starts from the session directory.</SheetFooter>\n </SheetContent>\n </Sheet>\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n bar: { gap: theme.spacing(1.5) },\n line: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n grow: { flex: 1 },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n problem: {\n color: theme.colors.red,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n}));\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, nativeApiTransport, sessionApiAddress } from '@agimon-ai/po-core/native';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'runner' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'runner',\n transport: nativeApiTransport,\n sessionAddress: sessionApiAddress,\n});\n","import { api } from '../../../../../../generated/client.native';\nimport { RUN_ID_PARAM } from '../../../../../constants/webRunnerLog';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogQueryParams,\n type RunnerLogResponse,\n} from '../../../../../types/webRunnerLog';\n\n/**\n * The page's half of this package's log API. The only place the cockpit talks\n * HTTP to the hub for a runner, so if the transport ever changes, it changes\n * here alone.\n *\n * The generated client owns every URL: a route is named, a session id selects\n * that session's own copy of the API, and the run id is a path parameter the\n * client substitutes and encodes. Nothing here spells a mount, a prefix or a\n * proxy parameter, which is what the four deleted URL builders each did by\n * hand.\n */\n\nexport type RunnerLogResult = { slice: RunnerLogResponse } | { error: string };\n\nconst UNREACHABLE = 'The cockpit hub is unreachable.';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads one runner's log: the last N lines, or the lines matching a substring with context. */\nexport async function fetchRunnerLog(\n sessionId: string,\n runId: string,\n params: RunnerLogQueryParams = {},\n signal?: AbortSignal,\n): Promise<RunnerLogResult> {\n const result = await api.session(sessionId).log({\n params: { [RUN_ID_PARAM]: runId },\n // An unset parameter is left out rather than sent empty: the route reads a\n // missing one as its own default, and an empty grep as no filter at all.\n query: {\n [RUNNER_LOG_PARAMS.lines]: params.lines,\n [RUNNER_LOG_PARAMS.grep]: params.grep === '' ? undefined : params.grep,\n [RUNNER_LOG_PARAMS.ignoreCase]: params.ignoreCase === true ? true : undefined,\n [RUNNER_LOG_PARAMS.contextLines]: params.contextLines,\n },\n ...(signal === undefined ? {} : { signal }),\n });\n if (!result.ok) {\n // Status 0 is the transport never answering, which covers both a dead hub\n // and the caller replacing this request. An abort is the caller's own doing\n // and is reported as no answer rather than as a failure to show.\n if (result.status === 0) return { error: signal?.aborted === true ? '' : UNREACHABLE };\n return { error: result.error === '' ? `The hub answered ${String(result.status)}.` : result.error };\n }\n if (!isRecord(result.data) || typeof result.data.text !== 'string') {\n return { error: `The hub answered ${String(result.status)}.` };\n }\n return { slice: result.data };\n}\n","import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';\n\nimport { fetchRunnerLog } from '../_lib/logApi.native';\nimport { runners } from '../_lib/runnersStore.native';\n\nexport function useRunners(sessionId: string | null) {\n const subscribe = useCallback((listener: () => void) => {\n const subscription = runners.store.subscribe(listener);\n return () => subscription.unsubscribe();\n }, []);\n return useSyncExternalStore(subscribe, () => runners.select(runners.store.state, sessionId));\n}\n\n/** Native uses finite reads instead of browser SSE. Read a bounded tail through the host's sealed transport. */\nexport function useRunnerTail(sessionId: string | null, runId: string, running: boolean) {\n const [tail, setTail] = useState<{ id: string; text: string }>();\n useEffect(() => {\n if (sessionId === null || !running) return;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const read = async () => {\n try {\n const result = await fetchRunnerLog(sessionId, runId, { lines: 5 }, controller.signal);\n if (controller.signal.aborted) return;\n if ('slice' in result) {\n const text = result.slice.text\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .at(-1);\n if (text !== undefined) setTail({ id: `${sessionId}:${runId}`, text });\n if (!result.slice.running) return;\n }\n } catch {\n // A glance keeps the last known output when a read is unavailable.\n }\n if (!controller.signal.aborted) timer = setTimeout(() => void read(), 2_000);\n };\n void read();\n return () => {\n controller.abort();\n clearTimeout(timer);\n };\n }, [sessionId, runId, running]);\n return running && tail?.id === `${sessionId}:${runId}` ? tail.text : undefined;\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { fontOf, Button, Input, StatusBadge } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { type ReactNode, useEffect, useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerLogResponse } from '../../../../../types/webRunnerLog';\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { logViewLines, runnerBadge, runnerFolderLabel, tailLineNumbers } from '../_lib/format';\nimport { fetchRunnerLog } from '../_lib/logApi.native';\nimport { requestRunnerStop } from '../_lib/runnersStore.native';\nimport { useRunners } from './runnerHooks.native';\n\n/** The top of both runner screens: name, outcome, an action, and the command in its folder. */\nexport function RunnerRunHeader({\n run,\n name,\n sessionDirectory,\n action,\n}: {\n run: RunnerRunView | undefined;\n name: string;\n sessionDirectory: string | undefined;\n action?: ReactNode;\n}) {\n const badge = runnerBadge(run);\n /** A long command would eat a phone screen, so it starts as one truncated line. */\n const [commandOpen, setCommandOpen] = useState(false);\n return (\n <>\n <View style={styles.header}>\n <Text numberOfLines={1} style={styles.title}>\n {name}\n </Text>\n <StatusBadge tone={badge.tone}>{badge.label}</StatusBadge>\n <View style={styles.grow} />\n {action}\n </View>\n {run ? (\n <Pressable\n testID=\"runner-log-command-toggle\"\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: commandOpen }}\n accessibilityHint={commandOpen ? 'Collapses the command' : 'Shows the full command'}\n style={styles.commandBox}\n onPress={() => setCommandOpen((open) => !open)}\n >\n <Text selectable={commandOpen} numberOfLines={commandOpen ? undefined : 1} style={styles.command}>\n <Text style={styles.prompt}>$ </Text>\n {run.command}\n </Text>\n {commandOpen ? (\n <Text numberOfLines={1} style={styles.folder}>\n in {runnerFolderLabel(run.cwd, sessionDirectory)}\n </Text>\n ) : null}\n </Pressable>\n ) : (\n <Text style={styles.meta}>No longer listed; its log stays readable.</Text>\n )}\n </>\n );\n}\n\nexport function runnerLogTab(run: RunnerRunView) {\n return {\n id: `runner-log-${run.id}`,\n label: `log · ${run.name}`,\n panel: (props: WebPluginSlotProps) => <RunnerLogPanel {...props} runId={run.id} />,\n };\n}\n\nexport function RunnerLogPanel({ sessionId, runId, sendSessionFrame }: WebPluginSlotProps & { runId: string }) {\n const session = useRunners(sessionId);\n const run = session.runs.find((candidate) => candidate.id === runId);\n const [typed, setTyped] = useState('');\n const [query, setQuery] = useState('');\n const [ignoreCase, setIgnoreCase] = useState(false);\n const [context, setContext] = useState(2);\n const [following, setFollowing] = useState(true);\n const requestKey = `${sessionId}:${runId}:${query}:${ignoreCase}:${context}:${following}`;\n const [loaded, setLoaded] = useState<{ key: string; slice?: RunnerLogResponse; error: string }>();\n const slice = loaded?.key === requestKey ? loaded.slice : undefined;\n const error = loaded?.key === requestKey ? loaded.error : '';\n useEffect(() => {\n const timer = setTimeout(() => setQuery(typed), 200);\n return () => clearTimeout(timer);\n }, [typed]);\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const read = async () => {\n const result = await fetchRunnerLog(\n sessionId,\n runId,\n { lines: 2000, grep: query, ignoreCase, contextLines: context },\n controller.signal,\n );\n if (controller.signal.aborted) return;\n setLoaded(\n 'error' in result\n ? { key: requestKey, error: result.error }\n : { key: requestKey, slice: result.slice, error: '' },\n );\n if (following && query === '' && ('error' in result || result.slice.running))\n timer = setTimeout(() => void read(), 2_000);\n };\n void read();\n return () => {\n controller.abort();\n clearTimeout(timer);\n };\n }, [sessionId, runId, query, ignoreCase, context, following, requestKey]);\n const lines = logViewLines(slice?.text ?? '', [], 2000).lines;\n const numbers = query === '' ? tailLineNumbers(slice?.totalLines ?? 0, lines.length) : (slice?.lineNumbers ?? []);\n const gutter = String(numbers.at(-1) ?? 0).length;\n const stopping = session.stopRequested.includes(runId);\n const live = following && query === '' && slice?.running === true;\n return (\n <View testID=\"runner-log-panel\" style={styles.panel}>\n <RunnerRunHeader\n run={run}\n name={run?.name ?? runId}\n sessionDirectory={session.sessionDirectory}\n action={\n run?.state === 'running' && sessionId !== null ? (\n <Button\n variant={stopping ? 'outline' : 'danger-outline'}\n size=\"sm\"\n disabled={stopping}\n onPress={() => requestRunnerStop(sendSessionFrame, sessionId, runId)}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null\n }\n />\n <Input\n testID=\"runner-log-search\"\n accessibilityLabel=\"Search this log\"\n placeholder=\"Search output\"\n value={typed}\n onChangeText={setTyped}\n autoCapitalize=\"none\"\n returnKeyType=\"search\"\n />\n <View style={styles.actions}>\n <Button\n variant={ignoreCase ? 'subtle' : 'outline'}\n size=\"sm\"\n accessibilityState={{ selected: ignoreCase }}\n onPress={() => setIgnoreCase((value) => !value)}\n >\n {ignoreCase ? 'Any case' : 'Match case'}\n </Button>\n {query === '' ? null : (\n <Button\n variant={context > 0 ? 'subtle' : 'outline'}\n size=\"sm\"\n onPress={() => setContext((value) => (value === 0 ? 2 : 0))}\n >\n {`± ${String(context)} lines`}\n </Button>\n )}\n {query === '' && slice?.running === true ? (\n <Button\n variant={live ? 'subtle' : 'outline'}\n size=\"sm\"\n accessibilityState={{ selected: live }}\n onPress={() => setFollowing((value) => !value)}\n >\n {live ? 'Following' : 'Follow'}\n </Button>\n ) : null}\n </View>\n <Text style={styles.meta}>\n {slice === undefined\n ? error === ''\n ? 'Reading…'\n : ''\n : query !== ''\n ? `${slice.lineCount} matching of ${slice.totalLines} lines`\n : `${live ? 'Tailing' : 'Showing'} last ${lines.length} of ${slice.totalLines} lines`}\n </Text>\n <ScrollView style={styles.log} contentContainerStyle={styles.logBody}>\n {error !== '' ? (\n <Text accessibilityRole=\"alert\" style={styles.error}>\n {error}\n </Text>\n ) : slice?.exists === false ? (\n <Text style={styles.meta}>This runner has not written a log yet.</Text>\n ) : slice && lines.length === 0 ? (\n <Text style={styles.meta}>{query === '' ? 'The log is empty.' : 'Nothing matched.'}</Text>\n ) : (\n lines.map((line, index) => (\n <Text selectable key={`${numbers[index] ?? index}`} style={styles.line}>\n <Text style={styles.number}>{`${String(numbers[index] ?? '').padStart(gutter)} `}</Text>\n {line}\n </Text>\n ))\n )}\n </ScrollView>\n <Text selectable numberOfLines={1} ellipsizeMode=\"middle\" style={styles.path}>\n {run?.logPath ?? slice?.path ?? ''}\n </Text>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(2), padding: theme.spacing(3), backgroundColor: theme.colors.bg },\n header: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minHeight: 32 },\n grow: { flex: 1 },\n title: {\n flexShrink: 1,\n color: theme.colors.hi,\n fontWeight: '600',\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n },\n commandBox: {\n gap: theme.spacing(1),\n paddingHorizontal: theme.spacing(3),\n paddingVertical: theme.spacing(2),\n borderRadius: theme.borderRadius.md,\n backgroundColor: theme.colors.deep,\n },\n command: { color: theme.colors.hi, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.sm },\n prompt: { color: theme.colors.faint },\n folder: { color: theme.colors.dim, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.xs },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n error: { color: theme.colors.red, fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.sm },\n actions: { flexDirection: 'row', gap: theme.spacing(2), flexWrap: 'wrap' },\n log: { flex: 1, borderRadius: theme.borderRadius.md, backgroundColor: theme.colors.deep },\n logBody: { padding: theme.spacing(3) },\n line: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.xs,\n lineHeight: theme.lineHeight.sm,\n },\n number: { color: theme.colors.faint },\n path: { color: theme.colors.faint, fontFamily: fontOf(theme.fontFamily.mono), fontSize: theme.fontSize.xs },\n}));\n","import { api } from '../../../../../../generated/client.native';\nimport { RUN_ID_PARAM } from '../../../../../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerOutputResponse } from '../../../../../types/webRunnerLog';\n\n/** Finite native reads preserve raw terminal bytes and generation-based replacement detection. */\nexport async function readRunnerScreen(\n sessionId: string,\n runId: string,\n from: number,\n generation: string | undefined,\n signal: AbortSignal,\n): Promise<RunnerOutputResponse> {\n const result = await api.session(sessionId).screenRead({\n params: { [RUN_ID_PARAM]: runId },\n query: { [RUNNER_LOG_PARAMS.from]: from, [RUNNER_LOG_PARAMS.generation]: generation },\n signal,\n });\n if (!result.ok) throw new Error(result.error || 'The connection to this pane dropped.');\n return result.data;\n}\nexport async function sendRunnerInput(sessionId: string, runId: string, text: string): Promise<void> {\n const result = await api.session(sessionId).input({ params: { [RUN_ID_PARAM]: runId }, body: { text } });\n if (!result.ok) throw new Error(result.error || 'This runner is no longer accepting input.');\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { fontOf, Button, SearchIcon } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useMemo } from 'react';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { requestRunnerStop } from '../_lib/runnersStore.native';\nimport { readRunnerScreen, sendRunnerInput } from '../_lib/screenApi.native';\nimport { useRunners } from './runnerHooks.native';\nimport { RunnerRunHeader, runnerLogTab } from './RunnerLogPanel.native';\n\n/** The host renders genuine terminal bytes; this package owns every scoped API call. */\nexport function runnerShellTab(run: RunnerRunView) {\n return {\n id: `runner-shell-${run.id}`,\n label: `shell · ${run.name}`,\n panel: (props: WebPluginSlotProps) => <RunnerShellPanel {...props} runId={run.id} />,\n };\n}\n\n/**\n * One terminal runner, full screen. A phone keyboard has no Ctrl key, so\n * Ctrl-C is a button; Search opens the runner's log, which the sink keeps\n * scrubbed beside the raw bytes for exactly that.\n */\nexport function RunnerShellPanel({\n sessionId,\n runId,\n renderRunnerTerminal,\n sendSessionFrame,\n openTransientTab,\n}: WebPluginSlotProps & { runId: string }) {\n const session = useRunners(sessionId);\n const run = session.runs.find((candidate) => candidate.id === runId);\n const live = run?.state === 'running';\n const stopping = session.stopRequested.includes(runId);\n const request = useMemo(\n () =>\n sessionId === null\n ? undefined\n : {\n key: `${sessionId}:${runId}`,\n read: (from: number, generation: string | undefined, signal: AbortSignal) =>\n readRunnerScreen(sessionId, runId, from, generation, signal),\n write: (text: string) => sendRunnerInput(sessionId, runId, text),\n },\n [sessionId, runId],\n );\n return (\n <View style={styles.panel}>\n <RunnerRunHeader\n run={run}\n name={run?.name ?? runId}\n sessionDirectory={session.sessionDirectory}\n action={\n live && sessionId !== null ? (\n <Button\n testID=\"runner-shell-stop\"\n variant={stopping ? 'outline' : 'danger-outline'}\n size=\"sm\"\n disabled={stopping}\n onPress={() => requestRunnerStop(sendSessionFrame, sessionId, runId)}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null\n }\n />\n <View style={styles.terminal}>\n {request === undefined ? (\n <Text style={styles.meta}>Select a session to attach to this runner.</Text>\n ) : (\n renderRunnerTerminal?.(request)\n )}\n </View>\n <View style={styles.actions}>\n {run ? (\n <Button\n testID=\"runner-shell-search\"\n variant=\"outline\"\n onPress={() => openTransientTab(runnerLogTab(run))}\n style={styles.action}\n >\n <SearchIcon size={14} />\n Search output\n </Button>\n ) : null}\n {live && sessionId !== null ? (\n <Button\n testID=\"runner-shell-interrupt\"\n variant=\"outline\"\n onPress={() => void sendRunnerInput(sessionId, runId, '\\u0003')}\n style={styles.action}\n >\n Ctrl-C\n </Button>\n ) : null}\n </View>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(2), padding: theme.spacing(3), backgroundColor: theme.colors.bg },\n actions: { flexDirection: 'row', gap: theme.spacing(2) },\n action: { flex: 1 },\n terminal: { flex: 1 },\n meta: { color: theme.colors.dim, fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.sm },\n}));\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/native';\nimport { ArrowRightIcon, fontOf, Button, Dot, ListRow } from '@agimon-ai/po-native-components';\nimport type { PoNativeTheme } from '@agimon-ai/po-web-components/nativeTheme';\nimport { useEffect, useRef, useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { RunnerRunView } from '../../../../../types/webRunners';\nimport { formatRunnerUptime } from '../_lib/format';\nimport { runnerRunRequest } from '../_lib/launchLine';\nimport { requestRunnerStart, requestRunnerStop } from '../_lib/runnersStore.native';\nimport { RunnerCommandBar } from './RunnerCommandBar.native';\nimport { useRunners, useRunnerTail } from './runnerHooks.native';\nimport { runnerLogTab } from './RunnerLogPanel.native';\nimport { runnerShellTab } from './RunnerShellPanel.native';\n\n/** A runner's own screen: its terminal when it has one, its log when it does not. */\nfunction runnerTab(run: RunnerRunView) {\n return run.interactive ? runnerShellTab(run) : runnerLogTab(run);\n}\n\nexport function runnersTab() {\n return { id: 'runner-runs', label: 'runners', panel: RunnersPanel };\n}\n\nfunction RunnerRow({\n run,\n props,\n stopping,\n now,\n}: {\n run: RunnerRunView;\n props: WebPluginSlotProps;\n stopping: boolean;\n now: number;\n}) {\n const tail = useRunnerTail(props.sessionId, run.id, run.state === 'running');\n // One tap target for the row: the name, its uptime and Stop on one line, then\n // what it is doing, falling back to what it was asked to do.\n return (\n <Pressable\n testID={`activity-runner-${run.id}`}\n accessibilityRole=\"button\"\n accessibilityLabel={`Open ${run.name}`}\n onPress={() => props.openTransientTab(runnerTab(run))}\n style={({ pressed }) => [styles.row, pressed && styles.pressed]}\n >\n <View style={styles.line}>\n <Dot tone=\"yellow\" pulse />\n <Text numberOfLines={1} style={styles.name}>\n {run.name}\n </Text>\n <Text style={styles.meta}>{formatRunnerUptime(run.startedAt, now)}</Text>\n {props.sessionId !== null ? (\n <Button\n testID={`activity-runner-stop-${run.id}`}\n size=\"xs\"\n variant={stopping ? 'outline' : 'danger-outline'}\n disabled={stopping}\n onPress={() => {\n if (props.sessionId !== null) requestRunnerStop(props.sendSessionFrame, props.sessionId, run.id);\n }}\n >\n {stopping ? 'Stopping…' : 'Stop'}\n </Button>\n ) : null}\n </View>\n <Text numberOfLines={1} style={styles.tail}>\n {tail ?? `$ ${run.command}`}\n </Text>\n </Pressable>\n );\n}\n\nexport function RunnersActivitySection(props: WebPluginSlotProps) {\n const session = useRunners(props.sessionId);\n const running = session.runs.filter((run) => run.state === 'running');\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), 10_000);\n return () => clearInterval(timer);\n }, []);\n return running.length === 0 ? (\n // The cockpit's idle line: the status, then the one jade action beside it.\n <View style={styles.idle}>\n <Text style={styles.meta}>Idle</Text>\n {props.sessionId !== null ? (\n <Pressable\n accessibilityRole=\"button\"\n testID=\"activity-runner-launch\"\n hitSlop={10}\n onPress={() => props.openTransientTab(runnersTab())}\n style={styles.launch}\n >\n <Text style={styles.launchText}>Launch a runner</Text>\n <ArrowRightIcon size={14} color=\"green\" />\n </Pressable>\n ) : null}\n </View>\n ) : (\n <View>\n {running.map((run) => (\n <RunnerRow key={run.id} run={run} props={props} now={now} stopping={session.stopRequested.includes(run.id)} />\n ))}\n </View>\n );\n}\n\n/**\n * Every running runner and the one place to start another: type a command\n * and Run hosts it on a terminal (plain output when the host has no rmux or\n * tmux), or leave it empty for a login shell. The started runner opens on\n * its own screen.\n */\nexport function RunnersPanel(props: WebPluginSlotProps) {\n const { openTransientTab } = props;\n const session = useRunners(props.sessionId);\n const running = session.runs.filter((run) => run.state === 'running');\n const [command, setCommand] = useState('');\n const [cwd, setCwd] = useState('');\n const [now, setNow] = useState(() => Date.now());\n const baseline = useRef<ReadonlySet<string> | null>(null);\n /** What the pending launch ran, put back if the start fails. */\n const launched = useRef('');\n const timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), 10_000);\n return () => clearInterval(timer);\n }, []);\n // ponytail: the first runner to appear after a launch is taken to be it, the\n // same guess the web panel makes.\n useEffect(() => {\n if (baseline.current === null) return;\n const started = session.runs.find((run) => run.state === 'running' && !baseline.current?.has(run.id));\n if (started === undefined) return;\n baseline.current = null;\n clearTimeout(timeout.current);\n openTransientTab(runnerTab(started));\n }, [session.runs, openTransientTab]);\n // A start that did not happen hands the command back beside its problem.\n const failureId = session.startFailure?.id;\n useEffect(() => {\n if (failureId === undefined || baseline.current === null) return;\n baseline.current = null;\n clearTimeout(timeout.current);\n setCommand(launched.current);\n }, [failureId]);\n useEffect(() => () => clearTimeout(timeout.current), []);\n const launch = () => {\n if (props.sessionId === null) return;\n baseline.current = new Set(session.runs.map((run) => run.id));\n clearTimeout(timeout.current);\n timeout.current = setTimeout(() => {\n baseline.current = null;\n }, 30_000);\n requestRunnerStart(props.sendSessionFrame, props.sessionId, runnerRunRequest(command, cwd));\n launched.current = command;\n setCommand('');\n };\n return (\n <View style={styles.panel}>\n <View style={styles.header}>\n <Text style={styles.title}>Runners</Text>\n <Text style={styles.meta}>{running.length === 0 ? 'Nothing running' : `${running.length} running`}</Text>\n </View>\n {props.sessionId !== null ? (\n <RunnerCommandBar\n command={command}\n onCommandChange={setCommand}\n cwd={cwd}\n onCwdChange={setCwd}\n folders={running.map((run) => run.cwd)}\n {...(session.sessionDirectory === undefined ? {} : { sessionDirectory: session.sessionDirectory })}\n {...(session.startFailure === undefined ? {} : { problem: session.startFailure.message })}\n onLaunch={launch}\n />\n ) : null}\n <ScrollView>\n {running.length === 0 ? (\n <Text style={styles.meta}>\n {props.sessionId === null\n ? 'Open a session to start a runner.'\n : 'Nothing running yet. Type a command above, like pnpm dev, and it opens as a live terminal.'}\n </Text>\n ) : (\n running.map((run) => (\n <ListRow\n key={run.id}\n leading={<Dot tone=\"yellow\" pulse />}\n title={run.name}\n description={`${run.command} · ${formatRunnerUptime(run.startedAt, now)}`}\n disclosure\n onPress={() => openTransientTab(runnerTab(run))}\n />\n ))\n )}\n </ScrollView>\n </View>\n );\n}\nconst styles = StyleSheet.create((theme: PoNativeTheme) => ({\n panel: { flex: 1, gap: theme.spacing(3), padding: theme.spacing(4), backgroundColor: theme.colors.bg },\n header: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n row: { gap: theme.spacing(1), paddingVertical: theme.spacing(2) },\n pressed: { opacity: 0.6 },\n line: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2) },\n name: {\n flex: 1,\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n tail: {\n // Under the name: the small dot plus the line's gap.\n paddingLeft: theme.spacing(3.5),\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n title: {\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.lg,\n fontWeight: '600',\n },\n meta: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n idle: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: theme.spacing(4) },\n launch: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(1) },\n launchText: {\n color: theme.colors.green,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n}));\n","import { defineActivityGroup } from '@agimon-ai/po-core/native';\nimport { PlayCircleIcon } from '@agimon-ai/po-native-components';\nimport { createElement } from 'react';\n\nimport { runnersTab } from '../_components/RunnersPanel.native';\nimport { runnerActivitySource } from '../_lib/runnersStore.native';\n\nexport default defineActivityGroup({\n name: 'runners',\n keys: 'r l',\n icon: () => createElement(PlayCircleIcon),\n statusKey: 'po-runner-runners',\n activeSource: runnerActivitySource,\n transientTab: runnersTab,\n order: 20,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/native';\n\nimport { runnerRunsChannel } from '../_lib/runnersStore.native';\nexport default defineChannelFile(runnerRunsChannel);\n","import { defineFill } from '@agimon-ai/po-core/native';\n\nimport { RunnersActivitySection } from '../_components/RunnersPanel.native';\n\nexport default defineFill({ slot: 'activity.runners', id: 'runners', component: RunnersActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport activityGroupRunners from '../src/extensions/workspaces/sessions/(frontend)/activity-group/runners.native';\nimport channelRunnerRuns from '../src/extensions/workspaces/sessions/(frontend)/channel/runner-runs.native';\nimport fillRunners from '../src/extensions/workspaces/sessions/(frontend)/fill/Runners.activity.runners.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'runner',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'runners' }, activityGroupRunners)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'runner_runs' }, channelRunnerRuns)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.runners', id: 'runners' }, fillRunners)]; },\n },\n});\n"],"mappings":"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"}
|
package/dist/extensions/web.mjs
CHANGED
|
@@ -618,6 +618,8 @@ function RunnerLogPanel({ sessionId, runId, sendSessionFrame, onTerminal }) {
|
|
|
618
618
|
const [error, setError] = useState(void 0);
|
|
619
619
|
/** False once the reader scrolls up: following must not yank them back down. */
|
|
620
620
|
const [pinned, setPinned] = useState(true);
|
|
621
|
+
/** A long command would eat a phone screen, so it starts as one truncated line. */
|
|
622
|
+
const [commandOpen, setCommandOpen] = useState(false);
|
|
621
623
|
const bottom = useRef(null);
|
|
622
624
|
const body = useRef(null);
|
|
623
625
|
const filtering = query !== "";
|
|
@@ -756,17 +758,30 @@ function RunnerLogPanel({ sessionId, runId, sendSessionFrame, onTerminal }) {
|
|
|
756
758
|
run ? /* @__PURE__ */ jsx("div", {
|
|
757
759
|
className: "shrink-0 border-b border-po-border-soft px-3 py-3 sm:px-[26px]",
|
|
758
760
|
children: /* @__PURE__ */ jsxs("div", {
|
|
759
|
-
className: "flex
|
|
760
|
-
children: [/* @__PURE__ */ jsxs("
|
|
761
|
-
className: "
|
|
761
|
+
className: "flex items-start gap-2 rounded-md bg-po-deep px-3 py-2",
|
|
762
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
763
|
+
className: "flex min-w-0 flex-1 flex-col gap-1",
|
|
762
764
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
763
|
-
"
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
765
|
+
"data-testid": "runner-log-command",
|
|
766
|
+
title: commandOpen ? void 0 : `${run.command}\nin ${run.cwd}`,
|
|
767
|
+
className: `font-mono text-base text-po-hi ${commandOpen ? "break-all" : "truncate"}`,
|
|
768
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
769
|
+
"aria-hidden": true,
|
|
770
|
+
className: "select-none text-po-faint",
|
|
771
|
+
children: ["$", " "]
|
|
772
|
+
}), run.command]
|
|
773
|
+
}), commandOpen ? /* @__PURE__ */ jsxs("span", {
|
|
774
|
+
className: "break-all font-mono text-sm text-po-dim",
|
|
775
|
+
children: ["in ", run.cwd]
|
|
776
|
+
}) : null]
|
|
777
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
778
|
+
type: "button",
|
|
779
|
+
"data-testid": "runner-log-command-toggle",
|
|
780
|
+
"aria-expanded": commandOpen,
|
|
781
|
+
"aria-label": commandOpen ? "Collapse command" : "Expand command",
|
|
782
|
+
className: "flex size-6 shrink-0 items-center justify-center rounded text-po-faint hover:text-po-hi",
|
|
783
|
+
onClick: () => setCommandOpen((open) => !open),
|
|
784
|
+
children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: `size-4 transition-transform ${commandOpen ? "rotate-180" : ""}` })
|
|
770
785
|
})]
|
|
771
786
|
})
|
|
772
787
|
}) : null,
|