quadwork 1.19.3 → 2.0.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/README.md +19 -35
- package/bin/quadwork.js +48 -1118
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +3 -3
- package/out/__next._full.txt +14 -14
- package/out/__next._head.txt +4 -4
- package/out/__next._index.txt +8 -8
- package/out/__next._tree.txt +2 -2
- package/out/_next/static/chunks/{030cjkhts487t.js → 079wdniva~de1.js} +1 -1
- package/out/_next/static/chunks/{0n~dq4kpx9xxx.js → 07lhk_q6pmm3r.js} +1 -1
- package/out/_next/static/chunks/0_79hkefw1mo2.js +1 -0
- package/out/_next/static/chunks/{08tog0xc~.es_.js → 0_lyyn..t63bc.js} +1 -1
- package/out/_next/static/chunks/0oxv9vrvc17to.js +2 -0
- package/out/_next/static/chunks/0py7102i226n5.js +1 -0
- package/out/_next/static/chunks/{13fv-yi7.v52g.js → 0q4bm04c1jl_3.js} +1 -1
- package/out/_next/static/chunks/{0_idxioyl0p7h.js → 0sjhy6oe3mbon.js} +1 -1
- package/out/_next/static/chunks/13xk0vgfbrcld.css +2 -0
- package/out/_next/static/chunks/14k3bfe537f9_.js +25 -0
- package/out/_next/static/chunks/{turbopack-0qm-e3ifrz~2u.js → turbopack-0y2u-q0l2m67w.js} +1 -1
- package/out/_not-found/__next._full.txt +13 -13
- package/out/_not-found/__next._head.txt +4 -4
- package/out/_not-found/__next._index.txt +8 -8
- package/out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/out/_not-found/__next._not-found.txt +3 -3
- package/out/_not-found/__next._tree.txt +2 -2
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +13 -13
- package/out/app-shell/__next._full.txt +13 -13
- package/out/app-shell/__next._head.txt +4 -4
- package/out/app-shell/__next._index.txt +8 -8
- package/out/app-shell/__next._tree.txt +2 -2
- package/out/app-shell/__next.app-shell.__PAGE__.txt +2 -2
- package/out/app-shell/__next.app-shell.txt +3 -3
- package/out/app-shell.html +1 -1
- package/out/app-shell.txt +13 -13
- package/out/index.html +1 -1
- package/out/index.txt +14 -14
- package/out/project/_/__next._full.txt +14 -14
- package/out/project/_/__next._head.txt +4 -4
- package/out/project/_/__next._index.txt +8 -8
- package/out/project/_/__next._tree.txt +2 -2
- package/out/project/_/__next.project.$d$id.__PAGE__.txt +3 -3
- package/out/project/_/__next.project.$d$id.txt +3 -3
- package/out/project/_/__next.project.txt +3 -3
- package/out/project/_/queue/__next._full.txt +14 -14
- package/out/project/_/queue/__next._head.txt +4 -4
- package/out/project/_/queue/__next._index.txt +8 -8
- package/out/project/_/queue/__next._tree.txt +2 -2
- package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +3 -3
- package/out/project/_/queue/__next.project.$d$id.queue.txt +3 -3
- package/out/project/_/queue/__next.project.$d$id.txt +3 -3
- package/out/project/_/queue/__next.project.txt +3 -3
- package/out/project/_/queue.html +1 -1
- package/out/project/_/queue.txt +14 -14
- package/out/project/_.html +1 -1
- package/out/project/_.txt +14 -14
- package/out/settings/__next._full.txt +14 -14
- package/out/settings/__next._head.txt +4 -4
- package/out/settings/__next._index.txt +8 -8
- package/out/settings/__next._tree.txt +2 -2
- package/out/settings/__next.settings.__PAGE__.txt +3 -3
- package/out/settings/__next.settings.txt +3 -3
- package/out/settings.html +1 -1
- package/out/settings.txt +14 -14
- package/out/setup/__next._full.txt +14 -14
- package/out/setup/__next._head.txt +4 -4
- package/out/setup/__next._index.txt +8 -8
- package/out/setup/__next._tree.txt +2 -2
- package/out/setup/__next.setup.__PAGE__.txt +3 -3
- package/out/setup/__next.setup.txt +3 -3
- package/out/setup.html +1 -1
- package/out/setup.txt +14 -14
- package/package.json +4 -2
- package/server/ac-restore.js +128 -0
- package/server/bridges/discord.js +183 -0
- package/server/bridges/telegram.js +210 -0
- package/server/config.js +4 -60
- package/server/file-chat.js +318 -0
- package/server/index.js +129 -1294
- package/server/install-agentchattr.js +3 -284
- package/server/mcp-chat-shim.js +171 -0
- package/server/migrate-ac.js +158 -0
- package/server/pty-dispatcher.js +188 -0
- package/server/routes.js +149 -1397
- package/templates/CLAUDE.md +2 -2
- package/templates/OVERNIGHT-QUEUE.md +1 -1
- package/templates/seeds/butler.CLAUDE.md +30 -62
- package/templates/seeds/dev.AGENTS.md +10 -1
- package/templates/seeds/head.AGENTS.md +3 -3
- package/templates/seeds/re1.AGENTS.md +3 -3
- package/templates/seeds/re2.AGENTS.md +3 -3
- package/bridges/discord/__pycache__/discord_bridge.cpython-314.pyc +0 -0
- package/bridges/discord/discord_bridge.py +0 -666
- package/bridges/discord/requirements.txt +0 -2
- package/out/_next/static/chunks/08kw.2kplxa.6.css +0 -2
- package/out/_next/static/chunks/0_nm7se0m3twm.js +0 -25
- package/out/_next/static/chunks/0uz5svjlo9dwl.js +0 -1
- package/out/_next/static/chunks/0zahstmgdrpy5.js +0 -1
- package/out/_next/static/chunks/0zfotsowwll1x.js +0 -2
- package/server/__tests__/bridge-auto-stop-guard.test.js +0 -134
- package/server/__tests__/rate-limit-handling.test.js +0 -168
- package/server/__tests__/scrub-secrets.test.js +0 -235
- package/server/__tests__/v1110-security-qa.test.js +0 -312
- package/server/agentchattr-registry.js +0 -188
- package/server/install-agentchattr.patchCrashTimeout.test.js +0 -71
- package/server/queue-watcher.js +0 -171
- package/server/queue-watcher.test.js +0 -64
- package/server/routes.batchProgress.test.js +0 -94
- package/server/routes.chatWsSend.test.js +0 -161
- package/server/routes.discordBridge.test.js +0 -80
- package/server/routes.parseActiveBatch.test.js +0 -88
- package/server/routes.telegramBridge.test.js +0 -241
- package/templates/config.toml +0 -72
- package/templates/wrapper.py +0 -70
- /package/out/_next/static/{D66Um4H226QD5y4w5xTKq → 479UD5Kit4YvCmtgO25VT}/_buildManifest.js +0 -0
- /package/out/_next/static/{D66Um4H226QD5y4w5xTKq → 479UD5Kit4YvCmtgO25VT}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{D66Um4H226QD5y4w5xTKq → 479UD5Kit4YvCmtgO25VT}/_ssgManifest.js +0 -0
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
// #353 / quadwork#353: readLastLines helper test. Plain
|
|
2
|
-
// node:assert script — run with
|
|
3
|
-
// `node server/routes.telegramBridge.test.js`.
|
|
4
|
-
//
|
|
5
|
-
// The bridge start/install handlers are integration-shaped (they
|
|
6
|
-
// spawn python3), so this file covers only the pure log-tailing
|
|
7
|
-
// helper that the handlers rely on for error reporting.
|
|
8
|
-
|
|
9
|
-
const assert = require("node:assert/strict");
|
|
10
|
-
const fs = require("node:fs");
|
|
11
|
-
const os = require("node:os");
|
|
12
|
-
const path = require("node:path");
|
|
13
|
-
const { execFileSync } = require("node:child_process");
|
|
14
|
-
const {
|
|
15
|
-
readLastLines,
|
|
16
|
-
checkTelegramBridgePythonDeps,
|
|
17
|
-
resolveProjectAgentchattrUrl,
|
|
18
|
-
buildTelegramBridgeToml,
|
|
19
|
-
patchAgentchattrConfigForTelegramBridge,
|
|
20
|
-
buildTelegramBridgeSpawnEnv,
|
|
21
|
-
} = require("./routes");
|
|
22
|
-
|
|
23
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qw-bridge-log-"));
|
|
24
|
-
function write(name, content) {
|
|
25
|
-
const p = path.join(tmp, name);
|
|
26
|
-
fs.writeFileSync(p, content);
|
|
27
|
-
return p;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
// 1) Missing file returns empty string.
|
|
32
|
-
assert.equal(readLastLines(path.join(tmp, "missing.log"), 5), "");
|
|
33
|
-
|
|
34
|
-
// 2) Empty file returns empty string.
|
|
35
|
-
assert.equal(readLastLines(write("empty.log", ""), 5), "");
|
|
36
|
-
|
|
37
|
-
// 3) Fewer lines than N → return them all, joined with \n.
|
|
38
|
-
assert.equal(
|
|
39
|
-
readLastLines(write("two.log", "a\nb\n"), 5),
|
|
40
|
-
"a\nb",
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
// 4) More lines than N → return only the last N in order.
|
|
44
|
-
assert.equal(
|
|
45
|
-
readLastLines(write("many.log", "a\nb\nc\nd\ne\nf\n"), 3),
|
|
46
|
-
"d\ne\nf",
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
// 5) \r\n line endings handled.
|
|
50
|
-
assert.equal(
|
|
51
|
-
readLastLines(write("crlf.log", "x\r\ny\r\nz\r\n"), 2),
|
|
52
|
-
"y\nz",
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
// 6) Blank lines inside the tail are skipped (filter non-empty).
|
|
56
|
-
// This matches readLastLines' `.filter((l) => l.length > 0)`.
|
|
57
|
-
assert.equal(
|
|
58
|
-
readLastLines(write("blanks.log", "a\n\nb\n\n\nc\n"), 2),
|
|
59
|
-
"b\nc",
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// 7) Simulated crash trace — the caller should get the final
|
|
63
|
-
// frame so the operator can see the ModuleNotFoundError.
|
|
64
|
-
const crash = [
|
|
65
|
-
"Traceback (most recent call last):",
|
|
66
|
-
' File "telegram_bridge.py", line 3, in <module>',
|
|
67
|
-
" import requests",
|
|
68
|
-
"ModuleNotFoundError: No module named 'requests'",
|
|
69
|
-
"",
|
|
70
|
-
].join("\n");
|
|
71
|
-
const got = readLastLines(write("crash.log", crash), 20);
|
|
72
|
-
assert.match(got, /ModuleNotFoundError/);
|
|
73
|
-
assert.match(got, /No module named 'requests'/);
|
|
74
|
-
|
|
75
|
-
// 8) #372: pre-flight dep-check failures must be persisted to
|
|
76
|
-
// the bridge log file so a subsequent status poll can surface
|
|
77
|
-
// them via last_error. Without this the widget's local
|
|
78
|
-
// actionError got clobbered by the next 5s polling cycle and
|
|
79
|
-
// the failure appeared as a silent Start → Stopped flicker.
|
|
80
|
-
// Here we simulate the exact fs.writeFileSync the start
|
|
81
|
-
// handler now runs on pre-flight failure, then round-trip it
|
|
82
|
-
// through readLastLines to confirm the error text survives.
|
|
83
|
-
const preflightLog = path.join(tmp, "preflight.log");
|
|
84
|
-
const msg =
|
|
85
|
-
"Bridge Python dependencies not installed. Click \"Install Bridge\" to install them, " +
|
|
86
|
-
"or run: pip3 install -r /tmp/fake/requirements.txt\n\n" +
|
|
87
|
-
"Import error: Traceback (most recent call last):\n" +
|
|
88
|
-
" File \"<string>\", line 1, in <module>\n" +
|
|
89
|
-
" import requests\n" +
|
|
90
|
-
"ModuleNotFoundError: No module named 'requests'";
|
|
91
|
-
fs.writeFileSync(
|
|
92
|
-
preflightLog,
|
|
93
|
-
`[${new Date().toISOString()}] pre-flight dep check failed\n${msg}\n`,
|
|
94
|
-
);
|
|
95
|
-
const tail = readLastLines(preflightLog, 20);
|
|
96
|
-
assert.match(tail, /pre-flight dep check failed/);
|
|
97
|
-
assert.match(tail, /ModuleNotFoundError/);
|
|
98
|
-
assert.match(tail, /Install Bridge/);
|
|
99
|
-
|
|
100
|
-
// 9) #380: checkTelegramBridgePythonDeps accepts an explicit
|
|
101
|
-
// interpreter path. Passing a guaranteed-broken path must
|
|
102
|
-
// return { ok: false, error } without throwing.
|
|
103
|
-
const broken = checkTelegramBridgePythonDeps(path.join(tmp, "nope", "python3"));
|
|
104
|
-
assert.equal(broken.ok, false);
|
|
105
|
-
assert.ok(broken.error && broken.error.length > 0);
|
|
106
|
-
|
|
107
|
-
// 10) #380: start handler's missing-venv branch — we don't boot
|
|
108
|
-
// the server here, but the branch reduces to a plain
|
|
109
|
-
// fs.existsSync check on `<BRIDGE_DIR>/.venv/bin/python3`,
|
|
110
|
-
// so we verify the check returns false for a fixture dir
|
|
111
|
-
// that has no `.venv` subdir at all.
|
|
112
|
-
const fixtureBridgeDir = fs.mkdtempSync(path.join(tmp, "bridge-no-venv-"));
|
|
113
|
-
const missingVenvPython = path.join(fixtureBridgeDir, ".venv", "bin", "python3");
|
|
114
|
-
assert.equal(fs.existsSync(missingVenvPython), false);
|
|
115
|
-
|
|
116
|
-
// 11) #380: round-trip — build a real venv in a tmp dir, install
|
|
117
|
-
// a stdlib-only sentinel is trivially importable, and confirm
|
|
118
|
-
// checkTelegramBridgePythonDeps reports ok when `requests`
|
|
119
|
-
// is installed into that venv. Skipped gracefully on CI if
|
|
120
|
-
// `python3 -m venv` or network-backed pip install fails.
|
|
121
|
-
const venvDir = path.join(tmp, "case11-venv");
|
|
122
|
-
let venvSkipped = false;
|
|
123
|
-
try {
|
|
124
|
-
execFileSync("python3", ["-m", "venv", venvDir], { timeout: 30000, stdio: "pipe" });
|
|
125
|
-
const venvPython = path.join(venvDir, "bin", "python3");
|
|
126
|
-
const venvPip = path.join(venvDir, "bin", "pip");
|
|
127
|
-
// Without `requests` installed yet, the check must fail.
|
|
128
|
-
const before = checkTelegramBridgePythonDeps(venvPython);
|
|
129
|
-
assert.equal(before.ok, false);
|
|
130
|
-
try {
|
|
131
|
-
execFileSync(venvPip, ["install", "--quiet", "requests"], { timeout: 120000, stdio: "pipe" });
|
|
132
|
-
} catch {
|
|
133
|
-
venvSkipped = true;
|
|
134
|
-
}
|
|
135
|
-
if (!venvSkipped) {
|
|
136
|
-
const after = checkTelegramBridgePythonDeps(venvPython);
|
|
137
|
-
assert.equal(after.ok, true);
|
|
138
|
-
}
|
|
139
|
-
} catch {
|
|
140
|
-
venvSkipped = true;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// 12) #383 Bug 1: resolveProjectAgentchattrUrl prefers the
|
|
144
|
-
// per-project URL over the global default. Every project
|
|
145
|
-
// after the first uses a distinct port, so silently reading
|
|
146
|
-
// the global default routed bridge traffic to the wrong AC
|
|
147
|
-
// instance.
|
|
148
|
-
assert.equal(
|
|
149
|
-
resolveProjectAgentchattrUrl(
|
|
150
|
-
{ agentchattr_url: "http://127.0.0.1:8300" },
|
|
151
|
-
{ id: "quadwork", agentchattr_url: "http://127.0.0.1:8301" },
|
|
152
|
-
),
|
|
153
|
-
"http://127.0.0.1:8301",
|
|
154
|
-
);
|
|
155
|
-
// Falls back to global default when the project has no URL of
|
|
156
|
-
// its own (legacy single-project installs).
|
|
157
|
-
assert.equal(
|
|
158
|
-
resolveProjectAgentchattrUrl(
|
|
159
|
-
{ agentchattr_url: "http://127.0.0.1:8300" },
|
|
160
|
-
{ id: "legacy" },
|
|
161
|
-
),
|
|
162
|
-
"http://127.0.0.1:8300",
|
|
163
|
-
);
|
|
164
|
-
// Hard-coded fallback when neither is set.
|
|
165
|
-
assert.equal(
|
|
166
|
-
resolveProjectAgentchattrUrl({}, {}),
|
|
167
|
-
"http://127.0.0.1:8300",
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
// 13) #383 Bug 2: buildTelegramBridgeToml writes agentchattr_url
|
|
171
|
-
// inside [telegram]. The upstream bridge's load_config only
|
|
172
|
-
// reads from that section — a separate [agentchattr] section
|
|
173
|
-
// is silently ignored and the bridge falls back to its
|
|
174
|
-
// hardcoded :8300 default.
|
|
175
|
-
const toml13 = buildTelegramBridgeToml({
|
|
176
|
-
bot_token: "123:abc",
|
|
177
|
-
chat_id: "-42",
|
|
178
|
-
agentchattr_url: "http://127.0.0.1:8301",
|
|
179
|
-
}, "testproject");
|
|
180
|
-
assert.match(toml13, /^\[telegram\]/);
|
|
181
|
-
assert.match(toml13, /bot_token = "123:abc"/);
|
|
182
|
-
assert.match(toml13, /chat_id = "-42"/);
|
|
183
|
-
assert.match(toml13, /agentchattr_url = "http:\/\/127\.0\.0\.1:8301"/);
|
|
184
|
-
// #404: cursor_file must be per-project so multiple bridges
|
|
185
|
-
// don't clobber each other's position.
|
|
186
|
-
assert.match(toml13, /cursor_file = ".*tg-bridge-cursor-testproject\.json"/);
|
|
187
|
-
// Must NOT emit a separate [agentchattr] section — the bridge
|
|
188
|
-
// would silently ignore it.
|
|
189
|
-
assert.equal(toml13.includes("\n[agentchattr]\n"), false);
|
|
190
|
-
|
|
191
|
-
// 14) #383 Bug 3: patchAgentchattrConfigForTelegramBridge is
|
|
192
|
-
// idempotent. The Install Bridge migration may run multiple
|
|
193
|
-
// times; it must not duplicate the section or corrupt the
|
|
194
|
-
// file.
|
|
195
|
-
const baseConfig =
|
|
196
|
-
"[agents.head]\nlabel = \"Head\"\n\n[agents.dev]\nlabel = \"Dev\"\n";
|
|
197
|
-
const first = patchAgentchattrConfigForTelegramBridge(baseConfig);
|
|
198
|
-
assert.equal(first.changed, true);
|
|
199
|
-
assert.match(first.text, /^\[agents\.tg\]$/m);
|
|
200
|
-
assert.match(first.text, /label = "Telegram Bridge"/);
|
|
201
|
-
// Running a second time is a no-op.
|
|
202
|
-
const second = patchAgentchattrConfigForTelegramBridge(first.text);
|
|
203
|
-
assert.equal(second.changed, false);
|
|
204
|
-
assert.equal(second.text, first.text);
|
|
205
|
-
// #439: a config with old slug [agents.telegram-bridge] is migrated
|
|
206
|
-
// to [agents.tg] and flagged as changed.
|
|
207
|
-
const handPatched =
|
|
208
|
-
baseConfig + "\n[agents.telegram-bridge]\nlabel = \"Telegram Bridge\"\n";
|
|
209
|
-
const third = patchAgentchattrConfigForTelegramBridge(handPatched);
|
|
210
|
-
assert.equal(third.changed, true);
|
|
211
|
-
assert.match(third.text, /^\[agents\.tg\]$/m);
|
|
212
|
-
|
|
213
|
-
// 15) #383 Bug 4: buildTelegramBridgeSpawnEnv strips the three
|
|
214
|
-
// env vars the upstream bridge treats as higher-precedence
|
|
215
|
-
// than TOML. Without this, an operator shell that exported a
|
|
216
|
-
// different bot's token (common on machines running AC2)
|
|
217
|
-
// silently overrode the QuadWork-written TOML and the bridge
|
|
218
|
-
// ran as the wrong identity.
|
|
219
|
-
const scrubbed = buildTelegramBridgeSpawnEnv({
|
|
220
|
-
PATH: "/usr/bin",
|
|
221
|
-
HOME: "/home/op",
|
|
222
|
-
TELEGRAM_BOT_TOKEN: "wrong-token",
|
|
223
|
-
TELEGRAM_CHAT_ID: "-999",
|
|
224
|
-
AGENTCHATTR_URL: "http://127.0.0.1:9999",
|
|
225
|
-
});
|
|
226
|
-
assert.equal(scrubbed.TELEGRAM_BOT_TOKEN, undefined);
|
|
227
|
-
assert.equal(scrubbed.TELEGRAM_CHAT_ID, undefined);
|
|
228
|
-
assert.equal(scrubbed.AGENTCHATTR_URL, undefined);
|
|
229
|
-
// Non-telegram keys must pass through untouched — the bridge
|
|
230
|
-
// still needs PATH/HOME/etc. to find python and open files.
|
|
231
|
-
assert.equal(scrubbed.PATH, "/usr/bin");
|
|
232
|
-
assert.equal(scrubbed.HOME, "/home/op");
|
|
233
|
-
|
|
234
|
-
console.log(
|
|
235
|
-
"routes.telegramBridge.test.js: all assertions passed (15 cases" +
|
|
236
|
-
(venvSkipped ? ", case 11 pip step skipped" : "") +
|
|
237
|
-
")",
|
|
238
|
-
);
|
|
239
|
-
} finally {
|
|
240
|
-
try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {}
|
|
241
|
-
}
|
package/templates/config.toml
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# QuadWork AgentChattr Configuration
|
|
2
|
-
# Generated by: npx quadwork init
|
|
3
|
-
#
|
|
4
|
-
# Agent "command" is the CLI tool: "claude", "codex", "gemini", or a script path.
|
|
5
|
-
# "cwd" is the working directory (git worktree) for each agent.
|
|
6
|
-
# MCP injection is auto-detected from the command name (claude/codex/gemini).
|
|
7
|
-
|
|
8
|
-
[meta]
|
|
9
|
-
version = "1.0.0"
|
|
10
|
-
|
|
11
|
-
[server]
|
|
12
|
-
port = 8300
|
|
13
|
-
host = "127.0.0.1"
|
|
14
|
-
data_dir = "./data"
|
|
15
|
-
|
|
16
|
-
[agents.head]
|
|
17
|
-
command = "codex"
|
|
18
|
-
cwd = "{{head_cwd}}"
|
|
19
|
-
color = "#10a37f"
|
|
20
|
-
label = "Lead"
|
|
21
|
-
|
|
22
|
-
[agents.re1]
|
|
23
|
-
command = "codex"
|
|
24
|
-
cwd = "{{re1_cwd}}"
|
|
25
|
-
color = "#22c55e"
|
|
26
|
-
label = "Reviewer 1"
|
|
27
|
-
|
|
28
|
-
[agents.re2]
|
|
29
|
-
command = "claude"
|
|
30
|
-
cwd = "{{re2_cwd}}"
|
|
31
|
-
color = "#f59e0b"
|
|
32
|
-
label = "Reviewer 2"
|
|
33
|
-
|
|
34
|
-
[agents.dev]
|
|
35
|
-
command = "claude"
|
|
36
|
-
cwd = "{{dev_cwd}}"
|
|
37
|
-
color = "#da7756"
|
|
38
|
-
label = "Builder"
|
|
39
|
-
|
|
40
|
-
# #592: CLI-based agent sections for AC HEAD compatibility.
|
|
41
|
-
# HEAD AC validates `base` against [agents.*] keys in config.toml.
|
|
42
|
-
# These sections let registration succeed with CLI names as base,
|
|
43
|
-
# regardless of whether AC is running pinned or at HEAD.
|
|
44
|
-
[agents.claude]
|
|
45
|
-
command = "claude"
|
|
46
|
-
label = "claude"
|
|
47
|
-
mcp_inject = "flag"
|
|
48
|
-
|
|
49
|
-
[agents.codex]
|
|
50
|
-
command = "codex"
|
|
51
|
-
label = "codex"
|
|
52
|
-
mcp_inject = "proxy_flag"
|
|
53
|
-
|
|
54
|
-
# #383: AC's registry rejects bases not declared in config.toml.
|
|
55
|
-
# The Telegram bridge registers as `tg` (#439: renamed from
|
|
56
|
-
# `telegram-bridge`), so every per-project AC config must declare
|
|
57
|
-
# it. The bridge has no command/cwd of its own — it is a long-running
|
|
58
|
-
# external client that posts to AC's HTTP API.
|
|
59
|
-
[agents.tg]
|
|
60
|
-
label = "Telegram Bridge"
|
|
61
|
-
|
|
62
|
-
# #399/#439: Discord bridge registers as `dc` (renamed from `discord-bridge`).
|
|
63
|
-
[agents.dc]
|
|
64
|
-
label = "Discord Bridge"
|
|
65
|
-
|
|
66
|
-
[routing]
|
|
67
|
-
default = "none"
|
|
68
|
-
max_agent_hops = 30
|
|
69
|
-
|
|
70
|
-
[mcp]
|
|
71
|
-
http_port = 8200
|
|
72
|
-
sse_port = 8201
|
package/templates/wrapper.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Agent process wrapper — manages lifecycle, auto-trigger, and REMINDER injection.
|
|
3
|
-
|
|
4
|
-
Usage:
|
|
5
|
-
python wrapper.py --agent <agent_id> --config <config_path> [--project <project_id>]
|
|
6
|
-
|
|
7
|
-
This is an optional advanced template for automating agent process management.
|
|
8
|
-
Copy to your project and customize as needed.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import argparse
|
|
12
|
-
import json
|
|
13
|
-
import os
|
|
14
|
-
import signal
|
|
15
|
-
import subprocess
|
|
16
|
-
import sys
|
|
17
|
-
import time
|
|
18
|
-
|
|
19
|
-
def load_config(config_path: str) -> dict:
|
|
20
|
-
with open(config_path) as f:
|
|
21
|
-
return json.load(f)
|
|
22
|
-
|
|
23
|
-
def resolve_agent(config: dict, project_id: str, agent_id: str) -> dict | None:
|
|
24
|
-
for project in config.get("projects", []):
|
|
25
|
-
if project.get("id") == project_id:
|
|
26
|
-
return project.get("agents", {}).get(agent_id)
|
|
27
|
-
return None
|
|
28
|
-
|
|
29
|
-
def run_agent(agent: dict, agent_id: str) -> subprocess.Popen:
|
|
30
|
-
command = agent.get("command", os.environ.get("SHELL", "/bin/zsh"))
|
|
31
|
-
cwd = agent.get("cwd", os.getcwd())
|
|
32
|
-
env = {**os.environ, "QUADWORK_AGENT": agent_id}
|
|
33
|
-
|
|
34
|
-
proc = subprocess.Popen(
|
|
35
|
-
[command],
|
|
36
|
-
cwd=cwd,
|
|
37
|
-
env=env,
|
|
38
|
-
stdin=sys.stdin,
|
|
39
|
-
stdout=sys.stdout,
|
|
40
|
-
stderr=sys.stderr,
|
|
41
|
-
)
|
|
42
|
-
return proc
|
|
43
|
-
|
|
44
|
-
def main():
|
|
45
|
-
parser = argparse.ArgumentParser(description="Agent process wrapper")
|
|
46
|
-
parser.add_argument("--agent", required=True, help="Agent ID (e.g. t3)")
|
|
47
|
-
parser.add_argument("--config", required=True, help="Path to config.json")
|
|
48
|
-
parser.add_argument("--project", default=None, help="Project ID (uses first project if omitted)")
|
|
49
|
-
args = parser.parse_args()
|
|
50
|
-
|
|
51
|
-
config = load_config(args.config)
|
|
52
|
-
project_id = args.project or config.get("projects", [{}])[0].get("id", "")
|
|
53
|
-
agent = resolve_agent(config, project_id, args.agent)
|
|
54
|
-
|
|
55
|
-
if not agent:
|
|
56
|
-
print(f"Agent '{args.agent}' not found in project '{project_id}'", file=sys.stderr)
|
|
57
|
-
sys.exit(1)
|
|
58
|
-
|
|
59
|
-
proc = run_agent(agent, args.agent)
|
|
60
|
-
|
|
61
|
-
def handle_signal(signum, _frame):
|
|
62
|
-
proc.send_signal(signum)
|
|
63
|
-
|
|
64
|
-
signal.signal(signal.SIGTERM, handle_signal)
|
|
65
|
-
signal.signal(signal.SIGINT, handle_signal)
|
|
66
|
-
|
|
67
|
-
sys.exit(proc.wait())
|
|
68
|
-
|
|
69
|
-
if __name__ == "__main__":
|
|
70
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|