aimux-cli 0.1.9 → 0.1.11
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/builtin-metadata-watchers.js +2 -103
- package/dist/builtin-metadata-watchers.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.js +45 -10
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/targets.js +3 -1
- package/dist/dashboard/targets.js.map +1 -1
- package/dist/default-plugins/transcript-length.d.ts +6 -0
- package/dist/default-plugins/transcript-length.js +84 -0
- package/dist/default-plugins/transcript-length.js.map +1 -0
- package/dist/http-client.js +1 -0
- package/dist/http-client.js.map +1 -1
- package/dist/main.js +80 -83
- package/dist/main.js.map +1 -1
- package/dist/metadata-server.d.ts +1 -0
- package/dist/metadata-server.js +17 -0
- package/dist/metadata-server.js.map +1 -1
- package/dist/metadata-store.d.ts +10 -0
- package/dist/metadata-store.js +1 -1
- package/dist/metadata-store.js.map +1 -1
- package/dist/multiplexer/dashboard-interaction.js +0 -1
- package/dist/multiplexer/dashboard-interaction.js.map +1 -1
- package/dist/multiplexer/dashboard-model.js +43 -8
- package/dist/multiplexer/dashboard-model.js.map +1 -1
- package/dist/multiplexer/index.js +2 -24
- package/dist/multiplexer/index.js.map +1 -1
- package/dist/multiplexer/persistence-methods.js +5 -2
- package/dist/multiplexer/persistence-methods.js.map +1 -1
- package/dist/multiplexer/runtime-state.js +0 -1
- package/dist/multiplexer/runtime-state.js.map +1 -1
- package/dist/multiplexer/runtime-sync.js +0 -1
- package/dist/multiplexer/runtime-sync.js.map +1 -1
- package/dist/multiplexer/session-launch.js +13 -26
- package/dist/multiplexer/session-launch.js.map +1 -1
- package/dist/plugin-runtime.d.ts +11 -2
- package/dist/plugin-runtime.js +111 -42
- package/dist/plugin-runtime.js.map +1 -1
- package/dist/session-bootstrap.d.ts +7 -4
- package/dist/session-bootstrap.js +183 -112
- package/dist/session-bootstrap.js.map +1 -1
- package/dist/shell-hooks.js +37 -15
- package/dist/shell-hooks.js.map +1 -1
- package/dist/shell-state.d.ts +28 -0
- package/dist/shell-state.js +51 -0
- package/dist/shell-state.js.map +1 -0
- package/dist/statusline-model.d.ts +12 -0
- package/dist/statusline-model.js.map +1 -1
- package/dist/tmux/runtime-manager.js +9 -2
- package/dist/tmux/runtime-manager.js.map +1 -1
- package/dist/tmux/statusline.js +35 -4
- package/dist/tmux/statusline.js.map +1 -1
- package/dist/worktree.d.ts +1 -0
- package/dist/worktree.js +59 -35
- package/dist/worktree.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, copyFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, copyFileSync, mkdirSync, chmodSync, renameSync, } from "node:fs";
|
|
3
3
|
import { join as pathJoin, resolve as pathResolve, dirname as pathDirname } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { Multiplexer } from "./multiplexer/index.js";
|
|
7
7
|
import { llmCompact } from "./context/compactor.js";
|
|
8
|
-
import { initProject
|
|
9
|
-
import { initPaths, getHistoryDir, getGraveyardPath, getStatePath, getContextDir, getProjectId } from "./paths.js";
|
|
8
|
+
import { initProject } from "./config.js";
|
|
9
|
+
import { initPaths, getHistoryDir, getGraveyardPath, getStatePath, getContextDir, getProjectId, getProjectStateDirFor, } from "./paths.js";
|
|
10
10
|
import { loadTeamConfig, saveTeamConfig, getDefaultTeamConfig } from "./team.js";
|
|
11
11
|
import { createWorktree, findMainRepo, listWorktrees } from "./worktree.js";
|
|
12
12
|
import { TmuxRuntimeManager } from "./tmux/runtime-manager.js";
|
|
13
13
|
import { buildTmuxDoctorReport, renderTmuxDoctorReport, renderTmuxRepairResult, repairTmuxRuntime, } from "./tmux/doctor.js";
|
|
14
|
-
import { loadMetadataEndpoint,
|
|
14
|
+
import { loadMetadataEndpoint, resolveProjectServiceEndpoint as resolveStoredProjectServiceEndpoint, updateSessionMetadata, clearSessionLogs, removeMetadataEndpoint, } from "./metadata-store.js";
|
|
15
15
|
import { AgentTracker } from "./agent-tracker.js";
|
|
16
16
|
import { AimuxDaemon, ensureDaemonRunning, ensureProjectService, loadDaemonInfo, loadDaemonState, projectServiceStatus, requestDaemonJson, stopDaemon, stopProjectService, } from "./daemon.js";
|
|
17
17
|
import { getProjectServiceManifest, manifestsMatch } from "./project-service-manifest.js";
|
|
@@ -26,6 +26,7 @@ import { runTmuxSwitcher } from "./tmux/switcher.js";
|
|
|
26
26
|
import { getDashboardCommandSpec } from "./dashboard/command-spec.js";
|
|
27
27
|
import { findLiveDashboardTarget, openDashboardTarget, pruneDashboardArtifacts, resolveDashboardTarget, } from "./dashboard/targets.js";
|
|
28
28
|
import { invalidateTmuxStatuslineArtifacts } from "./tmux/statusline-cache.js";
|
|
29
|
+
import { loadStatusline, renderTmuxStatuslineFromData } from "./tmux/statusline.js";
|
|
29
30
|
const program = new Command();
|
|
30
31
|
class ProjectServiceVersionError extends Error {
|
|
31
32
|
projectRoot;
|
|
@@ -65,7 +66,9 @@ async function restartStaleControlPlane(projectRoot) {
|
|
|
65
66
|
pruneDashboardArtifacts(projectRoot, dashboardBuildStamp, new TmuxRuntimeManager());
|
|
66
67
|
}
|
|
67
68
|
async function fetchProjectServiceHealth(endpoint) {
|
|
68
|
-
const { status, json } = await requestJson(`http://${endpoint.host}:${endpoint.port}/health
|
|
69
|
+
const { status, json } = await requestJson(`http://${endpoint.host}:${endpoint.port}/health`, {
|
|
70
|
+
timeoutMs: 1000,
|
|
71
|
+
});
|
|
69
72
|
if (status < 200 || status >= 300 || json?.ok === false) {
|
|
70
73
|
throw new Error(json?.error || `health request failed: ${status}`);
|
|
71
74
|
}
|
|
@@ -73,7 +76,9 @@ async function fetchProjectServiceHealth(endpoint) {
|
|
|
73
76
|
}
|
|
74
77
|
async function waitForVerifiedProjectService(projectRoot, opts) {
|
|
75
78
|
const expected = getProjectServiceManifest();
|
|
76
|
-
const
|
|
79
|
+
const timeoutMs = opts?.timeoutMs ?? 8000;
|
|
80
|
+
const startedAt = Date.now();
|
|
81
|
+
const deadline = startedAt + timeoutMs;
|
|
77
82
|
let lastError = "project service did not become reachable";
|
|
78
83
|
let lastServiceInfo = null;
|
|
79
84
|
let respawnAttempted = false;
|
|
@@ -122,12 +127,53 @@ async function waitForVerifiedProjectService(projectRoot, opts) {
|
|
|
122
127
|
typeof lastServiceInfo === "object") {
|
|
123
128
|
throw new ProjectServiceVersionError(lastError, projectRoot, expected, lastServiceInfo);
|
|
124
129
|
}
|
|
125
|
-
|
|
130
|
+
const elapsedMs = Date.now() - startedAt;
|
|
131
|
+
const elapsedSeconds = (elapsedMs / 1000).toFixed(1);
|
|
132
|
+
throw new Error(`project service did not become ready after ${elapsedSeconds}s (budget ${timeoutMs}ms); last error: ${lastError}${lastServiceInfo ? `; last serviceInfo=${JSON.stringify(lastServiceInfo)}` : ""}`);
|
|
133
|
+
}
|
|
134
|
+
function rewriteLocalStatuslineArtifacts(projectRoot, tmux, dashboardSessionName) {
|
|
135
|
+
const data = loadStatusline(projectRoot);
|
|
136
|
+
if (!data)
|
|
137
|
+
return;
|
|
138
|
+
const statusDir = pathJoin(getProjectStateDirFor(projectRoot), "tmux-statusline");
|
|
139
|
+
mkdirSync(statusDir, { recursive: true });
|
|
140
|
+
const writeStatusFile = (name, content) => {
|
|
141
|
+
const filePath = pathJoin(statusDir, name);
|
|
142
|
+
const tmpPath = `${filePath}.tmp`;
|
|
143
|
+
writeFileSync(tmpPath, `${content}\n`);
|
|
144
|
+
renameSync(tmpPath, filePath);
|
|
145
|
+
};
|
|
146
|
+
const dashboardTop = renderTmuxStatuslineFromData(data, projectRoot, "top", {
|
|
147
|
+
currentWindow: "dashboard",
|
|
148
|
+
currentPath: projectRoot,
|
|
149
|
+
});
|
|
150
|
+
const dashboardBottom = renderTmuxStatuslineFromData(data, projectRoot, "bottom", {
|
|
151
|
+
currentWindow: "dashboard",
|
|
152
|
+
currentPath: projectRoot,
|
|
153
|
+
currentSession: dashboardSessionName,
|
|
154
|
+
});
|
|
155
|
+
writeStatusFile("top-dashboard.txt", dashboardTop);
|
|
156
|
+
writeStatusFile("bottom-dashboard.txt", dashboardBottom);
|
|
157
|
+
if (dashboardSessionName) {
|
|
158
|
+
writeStatusFile(`bottom-dashboard-${dashboardSessionName}.txt`, dashboardBottom);
|
|
159
|
+
}
|
|
160
|
+
for (const entry of data.sessions ?? []) {
|
|
161
|
+
if (!entry.tmuxWindowId)
|
|
162
|
+
continue;
|
|
163
|
+
const renderOptions = {
|
|
164
|
+
currentWindow: entry.windowName,
|
|
165
|
+
currentWindowId: entry.tmuxWindowId,
|
|
166
|
+
currentPath: entry.worktreePath ?? projectRoot,
|
|
167
|
+
};
|
|
168
|
+
writeStatusFile(`top-${entry.tmuxWindowId}.txt`, renderTmuxStatuslineFromData(data, projectRoot, "top", renderOptions));
|
|
169
|
+
writeStatusFile(`bottom-${entry.tmuxWindowId}.txt`, renderTmuxStatuslineFromData(data, projectRoot, "bottom", renderOptions));
|
|
170
|
+
}
|
|
171
|
+
tmux.refreshStatus();
|
|
126
172
|
}
|
|
127
|
-
async function postProjectServiceJson(path, body) {
|
|
173
|
+
async function postProjectServiceJson(path, body, options) {
|
|
128
174
|
let endpoint = await resolveProjectServiceEndpoint();
|
|
129
175
|
if (!endpoint) {
|
|
130
|
-
await
|
|
176
|
+
await ensureDaemonProjectReady(resolveProjectRoot(process.cwd()));
|
|
131
177
|
endpoint = await resolveProjectServiceEndpoint();
|
|
132
178
|
}
|
|
133
179
|
if (!endpoint) {
|
|
@@ -137,6 +183,7 @@ async function postProjectServiceJson(path, body) {
|
|
|
137
183
|
method: "POST",
|
|
138
184
|
headers: { "content-type": "application/json" },
|
|
139
185
|
body,
|
|
186
|
+
timeoutMs: options?.timeoutMs,
|
|
140
187
|
});
|
|
141
188
|
if (status < 200 || status >= 300 || json?.ok === false) {
|
|
142
189
|
throw new Error(json?.error || `request failed: ${status}`);
|
|
@@ -146,7 +193,7 @@ async function postProjectServiceJson(path, body) {
|
|
|
146
193
|
async function getProjectServiceJson(path) {
|
|
147
194
|
let endpoint = await resolveProjectServiceEndpoint();
|
|
148
195
|
if (!endpoint) {
|
|
149
|
-
await
|
|
196
|
+
await ensureDaemonProjectReady(resolveProjectRoot(process.cwd()));
|
|
150
197
|
endpoint = await resolveProjectServiceEndpoint();
|
|
151
198
|
}
|
|
152
199
|
if (!endpoint) {
|
|
@@ -166,6 +213,9 @@ async function postProjectServiceJsonOrLocal(path, body, fallback) {
|
|
|
166
213
|
return fallback();
|
|
167
214
|
}
|
|
168
215
|
}
|
|
216
|
+
function exitAfterOpen() {
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
169
219
|
async function postLiveProjectServiceJsonOrLocal(projectRoot, path, body, fallback) {
|
|
170
220
|
try {
|
|
171
221
|
const endpoint = await resolveProjectServiceEndpoint(projectRoot);
|
|
@@ -228,7 +278,13 @@ async function ensureDaemonProjectReady(projectRoot, opts) {
|
|
|
228
278
|
throw error;
|
|
229
279
|
}
|
|
230
280
|
await restartStaleControlPlane(projectRoot);
|
|
231
|
-
|
|
281
|
+
try {
|
|
282
|
+
await waitForVerifiedProjectService(projectRoot, { timeoutMs: 15_000 });
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
await ensureProjectService(projectRoot);
|
|
286
|
+
await waitForVerifiedProjectService(projectRoot, { timeoutMs: 15_000 });
|
|
287
|
+
}
|
|
232
288
|
}
|
|
233
289
|
}
|
|
234
290
|
async function ensureDaemonProjectSpawned(projectRoot) {
|
|
@@ -340,13 +396,13 @@ program
|
|
|
340
396
|
insideTmux: tmux.isInsideTmux(),
|
|
341
397
|
alreadyResolved: true,
|
|
342
398
|
});
|
|
343
|
-
|
|
399
|
+
exitAfterOpen();
|
|
344
400
|
}
|
|
345
401
|
}
|
|
346
|
-
await
|
|
402
|
+
await ensureDaemonProjectReady(projectRoot);
|
|
347
403
|
if (!tool && !opts.resume && !opts.restore) {
|
|
348
404
|
openDashboardTarget(projectRoot, tmux);
|
|
349
|
-
|
|
405
|
+
exitAfterOpen();
|
|
350
406
|
}
|
|
351
407
|
}
|
|
352
408
|
const mux = new Multiplexer();
|
|
@@ -419,15 +475,19 @@ program
|
|
|
419
475
|
try {
|
|
420
476
|
const originalCwd = process.cwd();
|
|
421
477
|
const projectRoot = resolveProjectRoot(originalCwd);
|
|
422
|
-
await
|
|
478
|
+
await ensureDaemonProjectReady(projectRoot);
|
|
423
479
|
invalidateTmuxStatuslineArtifacts(projectRoot);
|
|
424
480
|
const tmux = new TmuxRuntimeManager();
|
|
425
481
|
ensureTmuxAvailable(tmux);
|
|
426
482
|
const { dashboardSession, dashboardTarget } = resolveDashboardTarget(projectRoot, tmux, { forceReload: true });
|
|
427
|
-
|
|
483
|
+
try {
|
|
484
|
+
await postProjectServiceJson("/statusline/refresh", { force: true }, { timeoutMs: 1500 });
|
|
485
|
+
}
|
|
486
|
+
catch { }
|
|
487
|
+
rewriteLocalStatuslineArtifacts(projectRoot, tmux, dashboardSession.sessionName);
|
|
428
488
|
if (opts.open) {
|
|
429
489
|
tmux.openTarget(dashboardTarget, { insideTmux: tmux.isInsideTmux(), alreadyResolved: true });
|
|
430
|
-
|
|
490
|
+
exitAfterOpen();
|
|
431
491
|
}
|
|
432
492
|
console.log(`Reloaded dashboard for ${dashboardSession.sessionName}`);
|
|
433
493
|
}
|
|
@@ -499,7 +559,7 @@ program
|
|
|
499
559
|
await initPaths(projectRoot);
|
|
500
560
|
const result = await restartProjectRuntime(projectRoot, { open: opts.open });
|
|
501
561
|
if (opts.open)
|
|
502
|
-
|
|
562
|
+
exitAfterOpen();
|
|
503
563
|
if (opts.json) {
|
|
504
564
|
console.log(JSON.stringify({
|
|
505
565
|
ok: true,
|
|
@@ -1946,14 +2006,14 @@ repairCmd
|
|
|
1946
2006
|
.action(async (opts) => {
|
|
1947
2007
|
const projectRoot = resolveProjectRoot(opts.projectRoot);
|
|
1948
2008
|
await initPaths(projectRoot);
|
|
1949
|
-
await
|
|
2009
|
+
await ensureDaemonProjectReady(projectRoot);
|
|
1950
2010
|
const tmux = new TmuxRuntimeManager();
|
|
1951
2011
|
ensureTmuxAvailable(tmux);
|
|
1952
2012
|
const result = repairTmuxRuntime(tmux, { projectRoot });
|
|
1953
2013
|
if (opts.open) {
|
|
1954
2014
|
const { dashboardTarget } = resolveDashboardTarget(projectRoot, tmux);
|
|
1955
2015
|
tmux.openTarget(dashboardTarget, { insideTmux: tmux.isInsideTmux(), alreadyResolved: true });
|
|
1956
|
-
|
|
2016
|
+
exitAfterOpen();
|
|
1957
2017
|
}
|
|
1958
2018
|
if (opts.json) {
|
|
1959
2019
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -2149,69 +2209,6 @@ program
|
|
|
2149
2209
|
}
|
|
2150
2210
|
console.log("OK");
|
|
2151
2211
|
});
|
|
2152
|
-
program
|
|
2153
|
-
.command("shell-hook <state>")
|
|
2154
|
-
.description("Internal generic shell-state adapter modeled after cmux")
|
|
2155
|
-
.requiredOption("--session <sessionId>", "Aimux session id")
|
|
2156
|
-
.requiredOption("--project <path>", "Project path")
|
|
2157
|
-
.option("--tool <tool>", "Tool label", "shell")
|
|
2158
|
-
.option("--json", "Emit JSON output")
|
|
2159
|
-
.action(async (state, opts) => {
|
|
2160
|
-
const projectRoot = resolveProjectRoot(pathResolve(opts.project));
|
|
2161
|
-
await initPaths(projectRoot);
|
|
2162
|
-
const sessionId = opts.session.trim();
|
|
2163
|
-
const tool = opts.tool?.trim() || "shell";
|
|
2164
|
-
const previous = loadMetadataState(projectRoot).sessions[sessionId]?.derived;
|
|
2165
|
-
const previousActivity = previous?.activity;
|
|
2166
|
-
const result = { ok: true, state, sessionId, tool };
|
|
2167
|
-
const setActivity = async (activity) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-activity", { session: sessionId, activity }, () => metadataTracker.setActivity(sessionId, activity, projectRoot));
|
|
2168
|
-
const setAttention = async (attention) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-attention", { session: sessionId, attention }, () => metadataTracker.setAttention(sessionId, attention, projectRoot));
|
|
2169
|
-
const clearSessionNotifications = async () => postLiveProjectServiceJsonOrLocal(projectRoot, "/notifications/clear", { sessionId }, () => ({
|
|
2170
|
-
ok: true,
|
|
2171
|
-
cleared: clearNotifications({ sessionId }),
|
|
2172
|
-
}));
|
|
2173
|
-
if (state === "running" || state === "command" || state === "busy") {
|
|
2174
|
-
if (previousActivity !== "running") {
|
|
2175
|
-
await clearSessionNotifications();
|
|
2176
|
-
await setActivity("running");
|
|
2177
|
-
await setAttention("normal");
|
|
2178
|
-
await postLiveProjectServiceJsonOrLocal(projectRoot, "/mark-seen", { session: sessionId }, () => metadataTracker.markSeen(sessionId, projectRoot));
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
else if (state === "prompt" || state === "idle") {
|
|
2182
|
-
if (previousActivity !== "idle") {
|
|
2183
|
-
await setActivity("idle");
|
|
2184
|
-
await setAttention("normal");
|
|
2185
|
-
}
|
|
2186
|
-
const config = loadConfig().notifications;
|
|
2187
|
-
if (config.enabled && config.onComplete && previousActivity === "running") {
|
|
2188
|
-
await postLiveProjectServiceJsonOrLocal(projectRoot, "/notify", {
|
|
2189
|
-
title: tool,
|
|
2190
|
-
subtitle: "Command complete",
|
|
2191
|
-
message: "Shell returned to a prompt.",
|
|
2192
|
-
sessionId,
|
|
2193
|
-
kind: "task_done",
|
|
2194
|
-
}, () => ({
|
|
2195
|
-
ok: true,
|
|
2196
|
-
notification: addNotification({
|
|
2197
|
-
title: tool,
|
|
2198
|
-
subtitle: "Command complete",
|
|
2199
|
-
body: "Shell returned to a prompt.",
|
|
2200
|
-
sessionId,
|
|
2201
|
-
kind: "task_done",
|
|
2202
|
-
}),
|
|
2203
|
-
}));
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
else {
|
|
2207
|
-
throw new Error(`Unsupported shell hook state: ${state}`);
|
|
2208
|
-
}
|
|
2209
|
-
if (opts.json) {
|
|
2210
|
-
console.log(JSON.stringify(result));
|
|
2211
|
-
return;
|
|
2212
|
-
}
|
|
2213
|
-
console.log("OK");
|
|
2214
|
-
});
|
|
2215
2212
|
program
|
|
2216
2213
|
.command("list-notifications")
|
|
2217
2214
|
.description("List project notifications")
|