march-cli 0.1.34 → 0.1.35
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/package.json +12 -1
- package/src/agent/lifecycle/runner-lifecycle.mjs +16 -0
- package/src/agent/lifecycle/runtime-restart-tool.mjs +22 -0
- package/src/agent/runner.mjs +9 -14
- package/src/agent/runtime/remote-runner-client.mjs +7 -15
- package/src/agent/runtime/runner-ipc-target.mjs +3 -22
- package/src/agent/runtime/runner-process-client.mjs +101 -24
- package/src/agent/runtime/runner-runtime-host.mjs +2 -0
- package/src/agent/runtime/state/runner-state.mjs +80 -0
- package/src/agent/session/session-options.mjs +2 -1
- package/src/agent/tools.mjs +3 -1
- package/src/cli/args.mjs +14 -3
- package/src/cli/commands/catalog/visible-commands.mjs +5 -0
- package/src/cli/commands/help-command.mjs +1 -7
- package/src/cli/commands/registry/slash-command-registry.mjs +293 -0
- package/src/cli/input/autocomplete.mjs +2 -25
- package/src/cli/repl-loop.mjs +24 -41
- package/src/cli/slash-commands.mjs +19 -185
- package/src/cli/startup/app-runtime.mjs +201 -0
- package/src/cli/startup/configured-command.mjs +9 -0
- package/src/cli/startup/early-command.mjs +29 -0
- package/src/cli/turn/turn-input-preparer.mjs +41 -0
- package/src/main.mjs +47 -242
- package/src/web-ui/command.mjs +112 -0
- package/src/web-ui/dist/assets/index-BUmhnID4.css +1 -0
- package/src/web-ui/dist/assets/index-CtuqTjcB.js +1845 -0
- package/src/web-ui/dist/index.html +13 -0
- package/src/web-ui/index.html +12 -0
- package/src/web-ui/runtime-host.mjs +185 -0
- package/src/web-ui/server.mjs +139 -0
- package/src/web-ui/session-manager.mjs +109 -0
- package/src/web-ui/src/App.tsx +7 -0
- package/src/web-ui/src/components/AppShell.tsx +47 -0
- package/src/web-ui/src/components/Composer.tsx +47 -0
- package/src/web-ui/src/components/FileExplorer.tsx +46 -0
- package/src/web-ui/src/components/RightSidebar.tsx +70 -0
- package/src/web-ui/src/components/SessionTimeline.tsx +31 -0
- package/src/web-ui/src/components/timeline/TimelineBlocks.tsx +109 -0
- package/src/web-ui/src/components/timeline/TimelineList.tsx +14 -0
- package/src/web-ui/src/fileTreeAdapter.ts +51 -0
- package/src/web-ui/src/main.tsx +11 -0
- package/src/web-ui/src/mockData.ts +87 -0
- package/src/web-ui/src/model.ts +62 -0
- package/src/web-ui/src/runtime/client.ts +74 -0
- package/src/web-ui/src/runtime/runtimeTimeline.ts +88 -0
- package/src/web-ui/src/runtime/useWebRuntime.ts +132 -0
- package/src/web-ui/src/styles/shell.css +156 -0
- package/src/web-ui/src/styles/tokens.css +116 -0
- package/src/web-ui/src/timelineAdapter.ts +43 -0
- package/src/web-ui/src/vite-env.d.ts +1 -0
- package/src/web-ui/tsconfig.json +20 -0
- package/src/web-ui/vite.config.mjs +11 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { createWebUiServer } from "./server.mjs";
|
|
4
|
+
import { createWebSessionManager, resolveWorkspace } from "./session-manager.mjs";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
7
|
+
const DEFAULT_PORT = 4174;
|
|
8
|
+
|
|
9
|
+
export async function runWebUiCommand(args, { config, cwd, stateRoot, useRuntimeProcess = true } = {}) {
|
|
10
|
+
const host = args.host ?? DEFAULT_HOST;
|
|
11
|
+
assertLoopbackHost(host);
|
|
12
|
+
const port = Number.parseInt(args.port ?? "", 10) || DEFAULT_PORT;
|
|
13
|
+
const runtime = createWebSessionManager({ args, config, launchCwd: cwd, stateRoot, useRuntimeProcess });
|
|
14
|
+
const initialWorkspace = resolveInitialWorkspace(args, cwd);
|
|
15
|
+
if (initialWorkspace) await runtime.createSession(initialWorkspace);
|
|
16
|
+
|
|
17
|
+
if (args.dev) return runWebUiDevCommand({ args, host, port, runtime, initialWorkspace });
|
|
18
|
+
assertWebBuildReady();
|
|
19
|
+
const server = createWebUiServer({ runtime });
|
|
20
|
+
await listen(server, port, host);
|
|
21
|
+
process.stdout.write(`March Web running at http://${host}:${port}\n`);
|
|
22
|
+
if (initialWorkspace) process.stdout.write(`Workspace: ${initialWorkspace}\n`);
|
|
23
|
+
await waitForShutdown({ servers: [server], runtime });
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveInitialWorkspace(args, launchCwd) {
|
|
28
|
+
const positional = args.command?.args ?? [];
|
|
29
|
+
if (positional.length > 1) throw new Error("Usage: march web [workspace] [--host <host>] [--port <port>]");
|
|
30
|
+
if (args.workspace && positional.length > 0) throw new Error("Use either march web <workspace> or --workspace <path>, not both");
|
|
31
|
+
const requested = args.workspace ?? positional[0];
|
|
32
|
+
return requested ? resolveWorkspace(requested, launchCwd) : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertLoopbackHost(host) {
|
|
36
|
+
if (["127.0.0.1", "localhost", "::1"].includes(host)) return;
|
|
37
|
+
throw new Error("march web only exposes local filesystem APIs on 127.0.0.1/localhost");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function assertWebBuildReady() {
|
|
41
|
+
if (existsSync(new URL("./dist/index.html", import.meta.url))) return;
|
|
42
|
+
throw new Error("Web UI build not found. Run: npm run web:build or use march web --dev");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function runWebUiDevCommand({ args, host, port, runtime, initialWorkspace }) {
|
|
46
|
+
const apiPort = Number.parseInt(args.apiPort ?? "", 10) || port + 1;
|
|
47
|
+
const apiServer = createWebUiServer({ runtime });
|
|
48
|
+
let apiStarted = false;
|
|
49
|
+
try {
|
|
50
|
+
await listen(apiServer, apiPort, host);
|
|
51
|
+
apiStarted = true;
|
|
52
|
+
const vite = await createViteDevServer({ host, port, apiPort });
|
|
53
|
+
process.stdout.write(`March Web dev running at http://${host}:${port}\n`);
|
|
54
|
+
process.stdout.write(`March Web API running at http://${host}:${apiPort}\n`);
|
|
55
|
+
if (initialWorkspace) process.stdout.write(`Workspace: ${initialWorkspace}\n`);
|
|
56
|
+
await waitForShutdown({ servers: [apiServer], runtime, vite });
|
|
57
|
+
return 0;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
if (apiStarted) await closeServer(apiServer);
|
|
60
|
+
await runtime.dispose?.();
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function createViteDevServer({ host, port, apiPort }) {
|
|
66
|
+
let createServer;
|
|
67
|
+
try {
|
|
68
|
+
({ createServer } = await import("vite"));
|
|
69
|
+
} catch {
|
|
70
|
+
throw new Error("Vite is required for march web --dev. Run npm install in the March repo.");
|
|
71
|
+
}
|
|
72
|
+
const vite = await createServer({
|
|
73
|
+
configFile: fileURLToPath(new URL("./vite.config.mjs", import.meta.url)),
|
|
74
|
+
server: {
|
|
75
|
+
host,
|
|
76
|
+
port,
|
|
77
|
+
strictPort: true,
|
|
78
|
+
proxy: { "/api": `http://${host}:${apiPort}` },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
await vite.listen();
|
|
82
|
+
return vite;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function listen(server, port, host) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
server.once("error", reject);
|
|
88
|
+
server.listen(port, host, () => {
|
|
89
|
+
server.off("error", reject);
|
|
90
|
+
resolve();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function closeServer(server) {
|
|
96
|
+
return new Promise((resolve) => server.close(resolve));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function waitForShutdown({ servers, runtime, vite = null }) {
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
const shutdown = async () => {
|
|
102
|
+
process.off("SIGINT", shutdown);
|
|
103
|
+
process.off("SIGTERM", shutdown);
|
|
104
|
+
if (vite) await vite.close();
|
|
105
|
+
await Promise.all(servers.map(closeServer));
|
|
106
|
+
await runtime.dispose?.();
|
|
107
|
+
resolve();
|
|
108
|
+
};
|
|
109
|
+
process.once("SIGINT", shutdown);
|
|
110
|
+
process.once("SIGTERM", shutdown);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer theme.palette{:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--font-sans:"Segoe UI Variable Text", "Segoe UI Variable", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, DengXian, "等线", "Microsoft YaHei UI", sans-serif;--font-mono:ui-monospace, "SFMono-Regular", "Cascadia Code", "JetBrains Mono", "Fira Code", Consolas, "Liberation Mono", Menlo, monospace;--font-weight-ui-normal:500;--font-weight-content-normal:600;--font-weight-ui-medium:600;--font-weight-ui-strong:700;--palette-white:#fff;--palette-slate-50:#f8fafc;--palette-slate-100:#f1f5f9;--palette-slate-200:#e2e8f0;--palette-slate-400:#94a3b8;--palette-slate-500:#64748b;--palette-slate-700:#334155;--palette-slate-900:#0f172a;--palette-slate-950:#020617;--palette-blue-400:#60a5fa;--palette-blue-600:#2563eb;--palette-green-700:#15803d;--palette-amber-600:#d97706;--palette-red-600:#dc2626}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}}@layer theme.semantic{:root{--color-bg-page:#f3f4f6;--color-bg-surface:var(--palette-white);--color-bg-sidebar:#ffffffbd;--color-bg-subtle:var(--palette-slate-50);--color-text-primary:var(--palette-slate-900);--color-text-muted:var(--palette-slate-500);--color-text-inverse:var(--palette-white);--color-border-subtle:#0f172a14;--color-border-strong:#0f172a24;--color-accent:var(--palette-blue-600);--color-accent-soft:#2563eb14;--color-success:var(--palette-green-700);--color-warning:var(--palette-amber-600);--color-danger:var(--palette-red-600);--color-hover:#0000000d;--color-shadow-drawer:#00000026;--color-scrim:#0000004d}@media (prefers-color-scheme:dark){:root{--color-bg-page:var(--palette-slate-900);--color-bg-surface:var(--palette-slate-950);--color-bg-sidebar:var(--palette-slate-900);--color-bg-subtle:var(--palette-slate-900);--color-text-primary:var(--palette-slate-50);--color-text-muted:var(--palette-slate-400);--color-border-subtle:#94a3b81f;--color-border-strong:#94a3b833;--color-accent:var(--palette-blue-400);--color-accent-soft:#60a5fa24;--color-hover:#94a3b814}}}@layer theme.component{:root{--shell-left-width:260px;--shell-right-width:280px;--shell-mobile-panel-width:75vw;--header-height:36px;--composer-min-height:46px;--radius-row:6px;--radius-control:8px;--radius-composer:12px;--space-panel:8px;--tree-indent:16px}}@layer base{*{box-sizing:border-box}html,body,#root{height:100%;margin:0;overflow:hidden}body{background:var(--color-bg-page);color:var(--color-text-primary);font-family:var(--font-sans);font-weight:var(--font-weight-ui-normal);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility}button,textarea{font:inherit}button{cursor:pointer}}@layer components{.app-shell{grid-template-columns:var(--shell-left-width) minmax(0, 1fr) var(--shell-right-width);background:var(--color-bg-page);height:100dvh;color:var(--color-text-primary);isolation:isolate;grid-template-rows:minmax(0,1fr) auto;grid-template-areas:"sidebar main right""sidebar footer right";display:grid;overflow:hidden}.panel{background:var(--color-bg-sidebar);min-height:0;overflow:hidden}.left-panel{border-right:1px solid var(--color-border-subtle);grid-area:sidebar}.right-panel{border-left:1px solid var(--color-border-subtle);flex-direction:column;grid-area:right;display:flex}.projects-header,.right-header,.main-header{height:var(--header-height);border-bottom:1px solid var(--color-border-subtle);background:var(--color-bg-sidebar);color:var(--color-text-muted);font-size:11px;font-weight:var(--font-weight-ui-medium);text-transform:uppercase;justify-content:space-between;align-items:center;padding:0 16px;display:flex}.projects-header h3{font:inherit;letter-spacing:.5px;margin:0}.menu-button{border-radius:var(--radius-control);width:28px;height:28px;color:var(--color-text-muted);background:0 0;border:0;justify-content:center;align-items:center;gap:3px;display:inline-flex}.menu-button span{background:currentColor;border-radius:999px;width:3.6px;height:3.6px}.menu-button:hover,.header-button:hover,.session-row:hover,.activity-row:hover{background:var(--color-hover)}.projects-body{height:calc(100% - var(--header-height));padding:var(--space-panel) 0;overflow:hidden}.project-tree-host{--trees-accent-override:var(--color-accent);--trees-bg-override:var(--color-bg-sidebar);--trees-bg-muted-override:var(--color-hover);--trees-border-color-override:var(--color-border-subtle);--trees-fg-override:var(--color-text-primary);--trees-fg-muted-override:var(--color-text-muted);--trees-selected-bg-override:var(--color-accent-soft);--trees-selected-fg-override:var(--color-accent);--trees-font-family-override:var(--font-sans);--trees-font-size-override:13px;--trees-border-radius-override:var(--radius-row);--trees-item-height:30px;--trees-item-padding-x-override:8px;--trees-item-margin-x-override:8px;--trees-item-row-gap-override:4px;--trees-level-gap-override:var(--tree-indent);--trees-padding-inline-override:0px;--trees-scrollbar-gutter-override:6px;height:100%}.timeline{background:var(--color-bg-surface);flex-direction:column;grid-area:main;min-width:0;min-height:0;display:flex;overflow:hidden}.main-header{background:var(--color-bg-surface)}.runtime-pill{color:var(--color-text-muted);background:var(--color-bg-subtle);border-radius:999px;padding:2px 7px;font-size:12px}.runtime-pill.connected{color:var(--color-accent);background:var(--color-accent-soft)}.timeline-scroll{flex:1;min-height:0;padding:16px 18px 24px;overflow:auto}.session-title{align-items:baseline;gap:8px;margin:0 0 14px 40px;display:flex}.session-title h1{font-size:17px;font-weight:var(--font-weight-ui-strong);letter-spacing:-.01em;margin:0}.session-title span{color:var(--color-text-muted);font-size:12px}.timeline-list{max-width:920px;margin:0 auto}.message-row{grid-template-columns:30px minmax(0,1fr);gap:10px;padding:10px 0;display:grid}.message-row+.message-row{border-top:1px solid var(--color-border-subtle)}.agent-dot{border-radius:var(--radius-control);background:var(--palette-slate-700);width:28px;height:28px;color:var(--color-text-inverse);place-items:center;font-size:12px;font-weight:800;display:grid}.agent-dot.march{background:var(--color-accent)}.message-body{min-width:0;color:var(--color-text-primary);font-size:14px;font-weight:var(--font-weight-content-normal);padding-top:3px;line-height:1.55}.message-body p{margin:0}.message-body time{color:var(--color-text-muted);margin-top:4px;font-size:11px;display:block}.timeline-aux{border:1px solid var(--color-border-subtle);border-radius:var(--radius-control);background:var(--color-bg-subtle);padding:7px 9px}.timeline-aux summary,.aux-title{align-items:center;gap:8px;min-height:20px;display:flex}.timeline-aux summary{cursor:pointer}.timeline-aux span,.timeline-aux em{color:var(--color-text-muted);font-size:12px;font-style:normal}.timeline-aux strong{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;font-weight:var(--font-weight-ui-medium);overflow:hidden}.timeline-aux p{color:var(--color-text-muted);margin:7px 0 0;font-size:13px}.tool-block span,.diff-inline,.terminal-block pre{font-family:var(--font-mono)}.diff-inline{border-left:2px solid var(--color-border-strong);margin-top:7px;padding-left:10px;font-size:13px}.diff-line{padding:2px 0}.diff-line.add{color:var(--color-success)}.diff-line.remove,.error-block strong{color:var(--color-danger)}.diff-line.keep{color:var(--color-text-muted)}.terminal-block pre{color:var(--color-text-muted);white-space:pre-wrap;margin:7px 0 0;font-size:12px}.permission-block.pending strong{color:var(--color-warning)}.permission-block.approved strong{color:var(--color-success)}.right-body{flex:1;min-height:0;padding:12px;overflow:auto}.workspace-picker{gap:7px;padding-bottom:10px;display:grid}.workspace-picker label{color:var(--color-text-muted);font-size:11px;font-weight:var(--font-weight-ui-medium);text-transform:uppercase}.workspace-input-row{grid-template-columns:minmax(0,1fr) auto;gap:4px;display:grid}.workspace-input-row input{border:1px solid var(--color-border-subtle);border-radius:var(--radius-control);background:var(--color-bg-surface);min-width:0;color:var(--color-text-primary);padding:7px 8px}.workspace-input-row button,.fs-entry-row button,.fs-row{border-radius:var(--radius-control);color:var(--color-text-muted);background:0 0;border:0}.workspace-input-row button{background:var(--color-accent-soft);color:var(--color-accent);padding:0 8px}.workspace-input-row button:disabled{opacity:.45}.workspace-path{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--color-text-muted);font-size:12px;overflow:hidden}.fs-entry-row{grid-template-columns:minmax(0,1fr) auto;gap:4px;display:grid}.fs-entry-row button,.fs-row{text-align:left;min-height:28px;padding:0 7px}.fs-entry-row button:first-child{text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary);overflow:hidden}.session-row,.activity-row{border-radius:var(--radius-control);width:100%;min-height:34px;color:var(--color-text-primary);text-align:left;background:0 0;border:0;justify-content:space-between;align-items:center;gap:8px;padding:7px 8px;font-size:13px;display:flex}.session-row.active{background:var(--color-accent-soft);color:var(--color-accent);font-weight:var(--font-weight-ui-medium)}.session-row time,.activity-row time{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--color-text-muted);font-size:12px;overflow:hidden}.right-divider{border-top:1px solid var(--color-border-subtle);color:var(--color-text-muted);font-size:11px;font-weight:var(--font-weight-ui-medium);text-transform:uppercase;margin:12px 0 4px;padding-top:12px}.activity-row{color:var(--color-text-muted)}.composer{background:var(--color-bg-surface);grid-area:footer;grid-template-columns:1fr;align-items:end;min-width:0;padding:0 16px 12px;display:grid}.composer-box{width:100%;min-height:var(--composer-min-height);border:1px solid var(--color-border-strong);border-radius:var(--radius-composer);background:var(--color-bg-sidebar);align-items:center;display:flex;position:relative;overflow:visible}.composer-box:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 1px var(--color-accent-soft)}.composer textarea{resize:none;width:100%;max-height:160px;color:var(--color-text-primary);font-weight:var(--font-weight-content-normal);background:0 0;border:0;outline:0;padding:12px 124px 12px 12px}.composer-actions{align-items:center;gap:2px;display:flex;position:absolute;bottom:8px;right:8px}.session-ring,.chip-button,.icon-action,.send-icon,.mobile-toggle{border-radius:var(--radius-control);width:28px;height:28px;color:var(--color-text-muted);background:0 0;border:0;justify-content:center;align-items:center;display:inline-flex}.session-ring:before{content:"";border:2px solid var(--color-accent);border-radius:999px;width:14px;height:14px}.chip-button{width:auto;padding:0 8px;font-size:12px}.icon-action:hover,.chip-button:hover,.mobile-toggle:hover{background:var(--color-hover)}.send-icon{background:var(--color-accent);color:var(--color-text-inverse);font-size:16px;font-weight:700}.send-icon:disabled{opacity:.45}.mobile-toggle,.overlay{display:none}@media (width<=920px){html,body{width:100%;min-height:0;position:fixed;inset:0}.app-shell{height:100dvh;padding-top:env(safe-area-inset-top,0);flex-direction:column;display:flex}.timeline{flex:1;min-height:0}.composer{padding:0 1px max(2px, env(safe-area-inset-bottom));flex:none;grid-template-columns:30px minmax(0,1fr) 30px;gap:1px}.mobile-toggle{align-self:center;height:44px;display:inline-flex}.composer-box{border-radius:10px;min-height:44px}.composer textarea{padding-right:114px}.left-panel,.right-panel{top:env(safe-area-inset-top,0);z-index:30;width:var(--shell-mobile-panel-width);background:var(--color-bg-sidebar);box-shadow:4px 0 24px var(--color-shadow-drawer);transition:transform .22s cubic-bezier(.2,.8,.2,1);position:fixed;bottom:0;transform:translate(-100%)}.left-panel{border-top-right-radius:14px;border-bottom-right-radius:14px;left:0}.right-panel{box-shadow:-4px 0 24px var(--color-shadow-drawer);border-top-left-radius:14px;border-bottom-left-radius:14px;right:0;transform:translate(100%)}.app-shell[data-left-open=true] .left-panel,.app-shell[data-right-open=true] .right-panel{transform:translate(0)}.app-shell[data-left-open=true] .overlay,.app-shell[data-right-open=true] .overlay{z-index:20;background:var(--color-scrim);display:block;position:fixed;inset:0}}}
|