ac-framework 2.0.0 → 2.2.0
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 +1 -1
- package/src/agents/runtime.js +65 -21
- package/src/commands/agents.js +9 -1
package/package.json
CHANGED
package/src/agents/runtime.js
CHANGED
|
@@ -43,6 +43,14 @@ function runCommand(command, args, options = {}) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function sleep(ms) {
|
|
47
|
+
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function stripAnsi(text) {
|
|
51
|
+
return String(text || '').replace(/\x1B\[[0-9;]*m/g, '');
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
function workerCommand(sessionId, role, roleLog) {
|
|
47
55
|
return `bash -lc 'node "${runnerPath}" agents worker --session ${sessionId} --role ${role} 2>&1 | tee -a "${roleLog}"'`;
|
|
48
56
|
}
|
|
@@ -91,26 +99,26 @@ export async function tmuxSessionExists(sessionName) {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
async function writeZellijLayout({ layoutPath, sessionId, sessionDir }) {
|
|
94
|
-
const
|
|
102
|
+
const paneNode = (role) => {
|
|
95
103
|
const roleLog = roleLogPath(sessionDir, role);
|
|
96
104
|
const cmd = workerCommand(sessionId, role, roleLog).replace(/"/g, '\\"');
|
|
97
|
-
return
|
|
98
|
-
|
|
105
|
+
return [
|
|
106
|
+
` pane name="${role}" command="bash" {`,
|
|
107
|
+
` args "-lc" "${cmd}"`,
|
|
108
|
+
' }',
|
|
109
|
+
].join('\n');
|
|
110
|
+
};
|
|
99
111
|
|
|
100
112
|
const content = [
|
|
101
113
|
'layout {',
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
panes[2],
|
|
111
|
-
panes[3],
|
|
112
|
-
' }',
|
|
113
|
-
' }',
|
|
114
|
+
' pane split_direction="vertical" {',
|
|
115
|
+
' pane split_direction="horizontal" {',
|
|
116
|
+
paneNode(COLLAB_ROLES[0]),
|
|
117
|
+
paneNode(COLLAB_ROLES[1]),
|
|
118
|
+
' }',
|
|
119
|
+
' pane split_direction="horizontal" {',
|
|
120
|
+
paneNode(COLLAB_ROLES[2]),
|
|
121
|
+
paneNode(COLLAB_ROLES[3]),
|
|
114
122
|
' }',
|
|
115
123
|
' }',
|
|
116
124
|
'}',
|
|
@@ -120,19 +128,55 @@ async function writeZellijLayout({ layoutPath, sessionId, sessionDir }) {
|
|
|
120
128
|
await writeFile(layoutPath, content, 'utf8');
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
export async function spawnZellijSession({
|
|
131
|
+
export async function spawnZellijSession({
|
|
132
|
+
sessionName,
|
|
133
|
+
sessionDir,
|
|
134
|
+
sessionId,
|
|
135
|
+
binaryPath,
|
|
136
|
+
waitForSessionMs = 10000,
|
|
137
|
+
pollIntervalMs = 250,
|
|
138
|
+
runCommandImpl,
|
|
139
|
+
}) {
|
|
124
140
|
const layoutPath = resolve(sessionDir, 'synapsegrid-layout.kdl');
|
|
125
141
|
await writeZellijLayout({ layoutPath, sessionId, sessionDir });
|
|
126
142
|
const command = binaryPath || process.env.ACFM_ZELLIJ_BIN || 'zellij';
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
const runner = runCommandImpl || runCommand;
|
|
144
|
+
|
|
145
|
+
const existing = await zellijSessionExists(sessionName, binaryPath, { runCommandImpl: runner });
|
|
146
|
+
if (existing) {
|
|
147
|
+
return { layoutPath };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await runner(command, ['--layout', layoutPath, 'attach', '--create-background', sessionName], {
|
|
151
|
+
cwd: sessionDir,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const startedAt = Date.now();
|
|
155
|
+
while ((Date.now() - startedAt) < waitForSessionMs) {
|
|
156
|
+
// eslint-disable-next-line no-await-in-loop
|
|
157
|
+
const exists = await zellijSessionExists(sessionName, binaryPath, { runCommandImpl: runner });
|
|
158
|
+
if (exists) {
|
|
159
|
+
return { layoutPath };
|
|
160
|
+
}
|
|
161
|
+
// eslint-disable-next-line no-await-in-loop
|
|
162
|
+
await sleep(pollIntervalMs);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Timed out waiting for zellij session '${sessionName}' to start (binary: ${command}). ` +
|
|
167
|
+
'Try `acfm agents doctor` or fallback with `acfm agents start --mux tmux ...`'
|
|
168
|
+
);
|
|
129
169
|
}
|
|
130
170
|
|
|
131
|
-
export async function zellijSessionExists(sessionName, binaryPath) {
|
|
171
|
+
export async function zellijSessionExists(sessionName, binaryPath, options = {}) {
|
|
132
172
|
try {
|
|
173
|
+
const runner = options.runCommandImpl || runCommand;
|
|
133
174
|
const command = binaryPath || process.env.ACFM_ZELLIJ_BIN || 'zellij';
|
|
134
|
-
const result = await
|
|
135
|
-
const lines = result.stdout
|
|
175
|
+
const result = await runner(command, ['list-sessions']);
|
|
176
|
+
const lines = stripAnsi(result.stdout)
|
|
177
|
+
.split('\n')
|
|
178
|
+
.map((line) => line.trim())
|
|
179
|
+
.filter(Boolean);
|
|
136
180
|
return lines.some((line) => line === sessionName || line.startsWith(`${sessionName} `));
|
|
137
181
|
} catch {
|
|
138
182
|
return false;
|
package/src/commands/agents.js
CHANGED
|
@@ -1503,7 +1503,15 @@ Examples:
|
|
|
1503
1503
|
try {
|
|
1504
1504
|
await runZellij(['delete-session', muxSessionName], { binaryPath: zellijPath });
|
|
1505
1505
|
} catch {
|
|
1506
|
-
|
|
1506
|
+
try {
|
|
1507
|
+
await runZellij(['kill-session', muxSessionName], { binaryPath: zellijPath });
|
|
1508
|
+
} catch {
|
|
1509
|
+
try {
|
|
1510
|
+
await runZellij(['delete-session', '--force', muxSessionName], { binaryPath: zellijPath });
|
|
1511
|
+
} catch {
|
|
1512
|
+
// ignore if already closed
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1507
1515
|
}
|
|
1508
1516
|
}
|
|
1509
1517
|
if (multiplexer === 'tmux' && muxSessionName && hasCommand('tmux')) {
|