@townco/cli 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -13,7 +13,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
13
13
|
import { LogsPane } from "../components/LogsPane.js";
|
|
14
14
|
import { TabbedOutput } from "../components/TabbedOutput.js";
|
|
15
15
|
import { findAvailablePort } from "../lib/port-utils.js";
|
|
16
|
-
function TuiRunner({ agentPath, workingDir, onExit }) {
|
|
16
|
+
function TuiRunner({ agentPath, workingDir, noSession, onExit, }) {
|
|
17
17
|
const [client, setClient] = useState(null);
|
|
18
18
|
const [error, setError] = useState(null);
|
|
19
19
|
// Configure logs directory for UI package loggers BEFORE any loggers are created
|
|
@@ -38,6 +38,7 @@ function TuiRunner({ agentPath, workingDir, onExit }) {
|
|
|
38
38
|
options: {
|
|
39
39
|
agentPath,
|
|
40
40
|
workingDirectory: workingDir,
|
|
41
|
+
environment: noSession ? { TOWN_NO_SESSION: "true" } : {},
|
|
41
42
|
},
|
|
42
43
|
});
|
|
43
44
|
setClient(newClient);
|
|
@@ -54,7 +55,7 @@ function TuiRunner({ agentPath, workingDir, onExit }) {
|
|
|
54
55
|
setError(errorMsg);
|
|
55
56
|
return undefined;
|
|
56
57
|
}
|
|
57
|
-
}, [agentPath, workingDir, logger]);
|
|
58
|
+
}, [agentPath, workingDir, logger, noSession]);
|
|
58
59
|
const customTabs = useMemo(() => [
|
|
59
60
|
{
|
|
60
61
|
name: "Chat",
|
|
@@ -143,7 +144,7 @@ async function loadEnvVars(projectRoot, logger) {
|
|
|
143
144
|
return envVars;
|
|
144
145
|
}
|
|
145
146
|
export async function runCommand(options) {
|
|
146
|
-
const { name, http = false, gui = false, port = 3100 } = options;
|
|
147
|
+
const { name, http = false, gui = false, port = 3100, noSession = false, } = options;
|
|
147
148
|
// Check if we're inside a Town project
|
|
148
149
|
const projectRoot = await isInsideTownProject();
|
|
149
150
|
if (projectRoot === null) {
|
|
@@ -219,6 +220,7 @@ export async function runCommand(options) {
|
|
|
219
220
|
...configEnvVars,
|
|
220
221
|
NODE_ENV: process.env.NODE_ENV || "production",
|
|
221
222
|
PORT: availablePort.toString(),
|
|
223
|
+
...(noSession ? { TOWN_NO_SESSION: "true" } : {}),
|
|
222
224
|
},
|
|
223
225
|
});
|
|
224
226
|
// Start the GUI dev server (no package.json, run vite directly)
|
|
@@ -264,6 +266,7 @@ export async function runCommand(options) {
|
|
|
264
266
|
...configEnvVars,
|
|
265
267
|
NODE_ENV: process.env.NODE_ENV || "production",
|
|
266
268
|
PORT: availablePort.toString(),
|
|
269
|
+
...(noSession ? { TOWN_NO_SESSION: "true" } : {}),
|
|
267
270
|
},
|
|
268
271
|
});
|
|
269
272
|
agentProcess.on("error", (error) => {
|
|
@@ -291,7 +294,7 @@ export async function runCommand(options) {
|
|
|
291
294
|
process.stdin.setRawMode(true);
|
|
292
295
|
}
|
|
293
296
|
// Render the tabbed UI with Chat and Logs
|
|
294
|
-
const { waitUntilExit } = render(_jsx(TuiRunner, { agentPath: binPath, workingDir: agentPath, onExit: () => {
|
|
297
|
+
const { waitUntilExit } = render(_jsx(TuiRunner, { agentPath: binPath, workingDir: agentPath, noSession: noSession, onExit: () => {
|
|
295
298
|
// Cleanup is handled by the ACP client disconnect
|
|
296
299
|
} }));
|
|
297
300
|
await waitUntilExit();
|
|
@@ -39,7 +39,6 @@ export function MergedLogsPane({ services, onClear }) {
|
|
|
39
39
|
const [searchQuery, setSearchQuery] = useState("");
|
|
40
40
|
const [serviceFilter, setServiceFilter] = useState(null);
|
|
41
41
|
const [levelFilter, setLevelFilter] = useState(null);
|
|
42
|
-
const maxLines = 25;
|
|
43
42
|
// Only include services that actually have output
|
|
44
43
|
const availableServices = useMemo(() => services.filter((s) => s.output.length > 0).map((s) => s.service), [services]);
|
|
45
44
|
// Clear service filter if the selected service is no longer available
|
|
@@ -153,16 +152,13 @@ export function MergedLogsPane({ services, onClear }) {
|
|
|
153
152
|
return levelFilteredLines.filter((log) => fuzzyMatch(searchQuery, log.line));
|
|
154
153
|
}, [levelFilteredLines, searchQuery]);
|
|
155
154
|
const isAtBottom = scrollOffset === 0;
|
|
156
|
-
//
|
|
155
|
+
// Show all lines - no truncation
|
|
157
156
|
const displayLines = useMemo(() => {
|
|
158
157
|
if (isAtBottom) {
|
|
159
|
-
return searchedLines
|
|
158
|
+
return searchedLines; // Show all logs when at bottom
|
|
160
159
|
}
|
|
161
|
-
return searchedLines.slice(
|
|
160
|
+
return searchedLines.slice(0, searchedLines.length - scrollOffset);
|
|
162
161
|
}, [searchedLines, scrollOffset, isAtBottom]);
|
|
163
|
-
const _canScrollUp = searchedLines.length > maxLines &&
|
|
164
|
-
scrollOffset < searchedLines.length - maxLines;
|
|
165
|
-
const _canScrollDown = scrollOffset > 0;
|
|
166
162
|
// Get overall status (error if any error, stopped if all stopped, etc.)
|
|
167
163
|
const overallStatus = useMemo(() => {
|
|
168
164
|
if (services.some((s) => s.status === "error"))
|
|
@@ -20,7 +20,6 @@ export function ProcessPane({ title, output, port, status, onClear, }) {
|
|
|
20
20
|
const [scrollOffset, setScrollOffset] = useState(0); // 0 = at bottom, positive = scrolled up
|
|
21
21
|
const [searchMode, setSearchMode] = useState(false);
|
|
22
22
|
const [searchQuery, setSearchQuery] = useState("");
|
|
23
|
-
const maxLines = 25;
|
|
24
23
|
// Handle keyboard input for scrolling, clearing, and search
|
|
25
24
|
useInput((input, key) => {
|
|
26
25
|
// '/' to enter search mode
|
|
@@ -45,7 +44,7 @@ export function ProcessPane({ title, output, port, status, onClear, }) {
|
|
|
45
44
|
}
|
|
46
45
|
// Up arrow to scroll up
|
|
47
46
|
if (key.upArrow) {
|
|
48
|
-
setScrollOffset((prev) => Math.min(prev + 1,
|
|
47
|
+
setScrollOffset((prev) => Math.min(prev + 1, output.length - 1));
|
|
49
48
|
return;
|
|
50
49
|
}
|
|
51
50
|
// Down arrow to scroll down
|
|
@@ -69,16 +68,14 @@ export function ProcessPane({ title, output, port, status, onClear, }) {
|
|
|
69
68
|
return output;
|
|
70
69
|
return output.filter((line) => fuzzyMatch(searchQuery, line));
|
|
71
70
|
}, [output, searchQuery]);
|
|
72
|
-
//
|
|
71
|
+
// Show all lines - no truncation
|
|
73
72
|
const displayOutput = useMemo(() => {
|
|
74
|
-
const source = searchedOutput;
|
|
75
73
|
if (isAtBottom) {
|
|
76
|
-
return
|
|
74
|
+
return searchedOutput; // Show all logs when at bottom
|
|
77
75
|
}
|
|
78
|
-
return
|
|
76
|
+
return searchedOutput.slice(0, searchedOutput.length - scrollOffset);
|
|
79
77
|
}, [searchedOutput, scrollOffset, isAtBottom]);
|
|
80
|
-
const canScrollUp = searchedOutput.length
|
|
81
|
-
scrollOffset < searchedOutput.length - maxLines;
|
|
78
|
+
const canScrollUp = scrollOffset < searchedOutput.length - 1;
|
|
82
79
|
const canScrollDown = scrollOffset > 0;
|
|
83
80
|
const statusColor = status === "running"
|
|
84
81
|
? "green"
|
|
@@ -87,5 +84,5 @@ export function ProcessPane({ title, output, port, status, onClear, }) {
|
|
|
87
84
|
: status === "starting"
|
|
88
85
|
? "yellow"
|
|
89
86
|
: "gray";
|
|
90
|
-
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { borderStyle: "single", paddingX: 1, marginBottom: 1, flexShrink: 0, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: title }), port && _jsxs(Text, { color: "gray", children: [" - http://localhost:", port] }), _jsx(Text, { children: " " }), _jsx(Text, { color: statusColor, children: "\u25CF" }), _jsxs(Text, { children: [" ", status] }), searchQuery && _jsxs(Text, { color: "cyan", children: [" [SEARCH: ", searchQuery, "]"] }), !isAtBottom && _jsx(Text, { color: "yellow", children: " [SCROLLED]" })] }), _jsx(Text, { color: "gray", children: "(/)search | \u2191\u2193 | (c)lear | ESC" })] }), searchMode && (_jsxs(Box, { borderStyle: "single", borderBottom: true, borderColor: "cyan", paddingX: 1, marginBottom: 1, flexShrink: 0, children: [_jsx(Text, { color: "cyan", children: "Search: " }), _jsx(TextInput, { value: searchQuery, onChange: setSearchQuery, placeholder: "Type to search..." }), _jsx(Text, { dimColor: true, children: " (ESC to exit)" })] })), _jsx(Box, { flexDirection: "column", flexGrow: 1, minHeight: 0, paddingX: 1, children: displayOutput.length === 0 ? (_jsx(Text, { color: "gray", children: "Waiting for output..." })) : (displayOutput.map((line, idx) => (_jsx(Text, { children: line }, `${idx}-${line.slice(0, 20)}`)))) }), _jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexShrink: 0, justifyContent: "space-between", children: [_jsxs(Text, { color: "gray", children: ["Showing ", displayOutput.length, " of ", searchedOutput.length, " lines", searchQuery && ` (${searchedOutput.length} matches)`, scrollOffset > 0 && ` (${scrollOffset} from bottom)`] }),
|
|
87
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { borderStyle: "single", paddingX: 1, marginBottom: 1, flexShrink: 0, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: title }), port && _jsxs(Text, { color: "gray", children: [" - http://localhost:", port] }), _jsx(Text, { children: " " }), _jsx(Text, { color: statusColor, children: "\u25CF" }), _jsxs(Text, { children: [" ", status] }), searchQuery && _jsxs(Text, { color: "cyan", children: [" [SEARCH: ", searchQuery, "]"] }), !isAtBottom && _jsx(Text, { color: "yellow", children: " [SCROLLED]" })] }), _jsx(Text, { color: "gray", children: "(/)search | \u2191\u2193 | (c)lear | ESC" })] }), searchMode && (_jsxs(Box, { borderStyle: "single", borderBottom: true, borderColor: "cyan", paddingX: 1, marginBottom: 1, flexShrink: 0, children: [_jsx(Text, { color: "cyan", children: "Search: " }), _jsx(TextInput, { value: searchQuery, onChange: setSearchQuery, placeholder: "Type to search..." }), _jsx(Text, { dimColor: true, children: " (ESC to exit)" })] })), _jsx(Box, { flexDirection: "column", flexGrow: 1, minHeight: 0, paddingX: 1, children: displayOutput.length === 0 ? (_jsx(Text, { color: "gray", children: "Waiting for output..." })) : (displayOutput.map((line, idx) => (_jsx(Text, { children: line }, `${idx}-${line.slice(0, 20)}`)))) }), _jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexShrink: 0, justifyContent: "space-between", children: [_jsxs(Text, { color: "gray", children: ["Showing ", displayOutput.length, " of ", searchedOutput.length, " lines", searchQuery && ` (${searchedOutput.length} matches)`, scrollOffset > 0 && ` (${scrollOffset} from bottom)`] }), (canScrollUp || canScrollDown) && (_jsxs(Text, { color: "gray", children: [canScrollUp && "↑ ", canScrollDown && "↓"] }))] })] }));
|
|
91
88
|
}
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ const parser = or(command("deploy", constant("deploy"), { brief: message `Deploy
|
|
|
45
45
|
http: optional(flag("--http")),
|
|
46
46
|
gui: optional(flag("--gui")),
|
|
47
47
|
port: optional(option("-p", "--port", integer())),
|
|
48
|
+
noSession: optional(flag("--no-session")),
|
|
48
49
|
}), { brief: message `Run an agent.` }), command("secret", object({
|
|
49
50
|
command: constant("secret"),
|
|
50
51
|
subcommand: or(command("list", constant("list"), { brief: message `List secrets.` }), command("add", object({
|
|
@@ -145,11 +146,12 @@ async function main(parser, meta) {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
})
|
|
148
|
-
.with({ command: "run" }, async ({ name, http, gui, port }) => {
|
|
149
|
+
.with({ command: "run" }, async ({ name, http, gui, port, noSession }) => {
|
|
149
150
|
const options = {
|
|
150
151
|
name,
|
|
151
152
|
http: http === true,
|
|
152
153
|
gui: gui === true,
|
|
154
|
+
noSession: noSession === true,
|
|
153
155
|
};
|
|
154
156
|
if (port !== null && port !== undefined) {
|
|
155
157
|
options.port = port;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@townco/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"town": "./dist/index.js"
|
|
@@ -15,17 +15,17 @@
|
|
|
15
15
|
"build": "tsc"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@townco/tsconfig": "0.1.
|
|
18
|
+
"@townco/tsconfig": "0.1.26",
|
|
19
19
|
"@types/bun": "^1.3.1",
|
|
20
20
|
"@types/react": "^19.2.2"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@optique/core": "^0.6.2",
|
|
24
24
|
"@optique/run": "^0.6.2",
|
|
25
|
-
"@townco/agent": "0.1.
|
|
26
|
-
"@townco/core": "0.0.
|
|
27
|
-
"@townco/secret": "0.1.
|
|
28
|
-
"@townco/ui": "0.1.
|
|
25
|
+
"@townco/agent": "0.1.34",
|
|
26
|
+
"@townco/core": "0.0.7",
|
|
27
|
+
"@townco/secret": "0.1.29",
|
|
28
|
+
"@townco/ui": "0.1.29",
|
|
29
29
|
"@types/inquirer": "^9.0.9",
|
|
30
30
|
"ink": "^6.4.0",
|
|
31
31
|
"ink-text-input": "^6.0.0",
|