quadwork 1.5.2 → 1.5.3
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/bin/quadwork.js +13 -1
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +1 -1
- package/out/__next._full.txt +1 -1
- package/out/__next._head.txt +1 -1
- package/out/__next._index.txt +1 -1
- package/out/__next._tree.txt +1 -1
- package/out/_next/static/chunks/{0-7v31f-nsgw-.js → 09sq17vme9g6p.js} +1 -1
- package/out/_next/static/chunks/{0m83k84.midd1.js → 0wreuebrwlg.2.js} +1 -1
- package/out/_not-found/__next._full.txt +1 -1
- package/out/_not-found/__next._head.txt +1 -1
- package/out/_not-found/__next._index.txt +1 -1
- package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/out/_not-found/__next._not-found.txt +1 -1
- package/out/_not-found/__next._tree.txt +1 -1
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +1 -1
- package/out/app-shell/__next._full.txt +1 -1
- package/out/app-shell/__next._head.txt +1 -1
- package/out/app-shell/__next._index.txt +1 -1
- package/out/app-shell/__next._tree.txt +1 -1
- package/out/app-shell/__next.app-shell.__PAGE__.txt +1 -1
- package/out/app-shell/__next.app-shell.txt +1 -1
- package/out/app-shell.html +1 -1
- package/out/app-shell.txt +1 -1
- package/out/index.html +1 -1
- package/out/index.txt +1 -1
- package/out/project/_/__next._full.txt +2 -2
- package/out/project/_/__next._head.txt +1 -1
- package/out/project/_/__next._index.txt +1 -1
- package/out/project/_/__next._tree.txt +1 -1
- package/out/project/_/__next.project.$d$id.__PAGE__.txt +2 -2
- package/out/project/_/__next.project.$d$id.txt +1 -1
- package/out/project/_/__next.project.txt +1 -1
- package/out/project/_/memory/__next._full.txt +1 -1
- package/out/project/_/memory/__next._head.txt +1 -1
- package/out/project/_/memory/__next._index.txt +1 -1
- package/out/project/_/memory/__next._tree.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.txt +1 -1
- package/out/project/_/memory/__next.project.txt +1 -1
- package/out/project/_/memory.html +1 -1
- package/out/project/_/memory.txt +1 -1
- package/out/project/_/queue/__next._full.txt +1 -1
- package/out/project/_/queue/__next._head.txt +1 -1
- package/out/project/_/queue/__next._index.txt +1 -1
- package/out/project/_/queue/__next._tree.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.txt +1 -1
- package/out/project/_/queue/__next.project.txt +1 -1
- package/out/project/_/queue.html +1 -1
- package/out/project/_/queue.txt +1 -1
- package/out/project/_.html +1 -1
- package/out/project/_.txt +2 -2
- package/out/settings/__next._full.txt +1 -1
- package/out/settings/__next._head.txt +1 -1
- package/out/settings/__next._index.txt +1 -1
- package/out/settings/__next._tree.txt +1 -1
- package/out/settings/__next.settings.__PAGE__.txt +1 -1
- package/out/settings/__next.settings.txt +1 -1
- package/out/settings.html +1 -1
- package/out/settings.txt +1 -1
- package/out/setup/__next._full.txt +1 -1
- package/out/setup/__next._head.txt +1 -1
- package/out/setup/__next._index.txt +1 -1
- package/out/setup/__next._tree.txt +1 -1
- package/out/setup/__next.setup.__PAGE__.txt +1 -1
- package/out/setup/__next.setup.txt +1 -1
- package/out/setup.html +1 -1
- package/out/setup.txt +1 -1
- package/package.json +1 -1
- package/server/routes.js +100 -3
- package/server/routes.telegramBridge.test.js +97 -2
- package/templates/config.toml +8 -0
- /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → X4zdS6Y6HkLOaElNeHwnq}/_buildManifest.js +0 -0
- /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → X4zdS6Y6HkLOaElNeHwnq}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → X4zdS6Y6HkLOaElNeHwnq}/_ssgManifest.js +0 -0
package/out/setup.txt
CHANGED
|
@@ -11,7 +11,7 @@ d:I[11717,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_s
|
|
|
11
11
|
f:I[92243,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
|
|
12
12
|
:HL["/_next/static/chunks/0ccoe1hsu70ql.css","style"]
|
|
13
13
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
14
|
-
0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/04_t39bv8y9pe.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex flex-col","children":[["$","$L2",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L3",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L6",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/084lff9v4p_vh.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"
|
|
14
|
+
0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/04_t39bv8y9pe.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex flex-col","children":[["$","$L2",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L3",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L6",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/084lff9v4p_vh.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"X4zdS6Y6HkLOaElNeHwnq"}
|
|
15
15
|
10:[]
|
|
16
16
|
a:"$W10"
|
|
17
17
|
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
package/package.json
CHANGED
package/server/routes.js
CHANGED
|
@@ -2385,6 +2385,63 @@ function telegramConfigToml(projectId) {
|
|
|
2385
2385
|
return path.join(CONFIG_DIR, `telegram-${projectId}.toml`);
|
|
2386
2386
|
}
|
|
2387
2387
|
|
|
2388
|
+
// #383: path to a project's AgentChattr config.toml. The install
|
|
2389
|
+
// handler patches this file to declare the `telegram-bridge` agent
|
|
2390
|
+
// so AC's registry accepts the bridge's register call.
|
|
2391
|
+
function projectAgentchattrConfigPath(projectId) {
|
|
2392
|
+
return path.join(CONFIG_DIR, projectId, "agentchattr", "config.toml");
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
// #383 Bug 1: prefer the per-project agentchattr_url. Every project
|
|
2396
|
+
// after the first uses a distinct port (8301, 8302, ...), so reading
|
|
2397
|
+
// the global default silently routed bridge traffic to the wrong AC
|
|
2398
|
+
// instance.
|
|
2399
|
+
function resolveProjectAgentchattrUrl(cfg, project) {
|
|
2400
|
+
return (
|
|
2401
|
+
(project && project.agentchattr_url) ||
|
|
2402
|
+
(cfg && cfg.agentchattr_url) ||
|
|
2403
|
+
"http://127.0.0.1:8300"
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// #383 Bug 2: the upstream bridge only reads `agentchattr_url` from
|
|
2408
|
+
// inside `[telegram]`. A separate `[agentchattr]` section is silently
|
|
2409
|
+
// ignored and the bridge falls back to its hardcoded :8300 default.
|
|
2410
|
+
function buildTelegramBridgeToml(tg) {
|
|
2411
|
+
return (
|
|
2412
|
+
`[telegram]\n` +
|
|
2413
|
+
`bot_token = "${tg.bot_token}"\n` +
|
|
2414
|
+
`chat_id = "${tg.chat_id}"\n` +
|
|
2415
|
+
`agentchattr_url = "${tg.agentchattr_url}"\n`
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// #383 Bug 3: AC's registry rejects any base name not pre-declared
|
|
2420
|
+
// in config.toml with `400 unknown base`. The bridge registers as
|
|
2421
|
+
// `telegram-bridge`, so every per-project AC config must declare it.
|
|
2422
|
+
// Idempotent: only appends if the section is not already present.
|
|
2423
|
+
function patchAgentchattrConfigForTelegramBridge(tomlText) {
|
|
2424
|
+
if (/^\[agents\.telegram-bridge\]\s*$/m.test(tomlText)) {
|
|
2425
|
+
return { text: tomlText, changed: false };
|
|
2426
|
+
}
|
|
2427
|
+
const sep = tomlText.length === 0 || tomlText.endsWith("\n") ? "" : "\n";
|
|
2428
|
+
const block = `\n[agents.telegram-bridge]\nlabel = "Telegram Bridge"\n`;
|
|
2429
|
+
return { text: tomlText + sep + block, changed: true };
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// #383 Bug 4: the upstream bridge treats env vars as higher
|
|
2433
|
+
// precedence than TOML values. If the parent shell exported
|
|
2434
|
+
// TELEGRAM_BOT_TOKEN for a different bot, the bridge silently ran
|
|
2435
|
+
// as the wrong identity. Scrub those keys from the child's env so
|
|
2436
|
+
// the TOML is the single source of truth.
|
|
2437
|
+
function buildTelegramBridgeSpawnEnv(parentEnv) {
|
|
2438
|
+
const env = { ...parentEnv };
|
|
2439
|
+
delete env.TELEGRAM_BOT_TOKEN;
|
|
2440
|
+
delete env.TELEGRAM_CHAT_ID;
|
|
2441
|
+
delete env.AGENTCHATTR_URL;
|
|
2442
|
+
return env;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2388
2445
|
// #353: per-project log file for the bridge subprocess. The start
|
|
2389
2446
|
// handler redirects stdout + stderr here so crashes (ImportError,
|
|
2390
2447
|
// config parse, auth failure) are recoverable instead of
|
|
@@ -2502,7 +2559,8 @@ function getProjectTelegram(projectId) {
|
|
|
2502
2559
|
return {
|
|
2503
2560
|
bot_token: resolveToken(project.telegram.bot_token || ""),
|
|
2504
2561
|
chat_id: project.telegram.chat_id || "",
|
|
2505
|
-
|
|
2562
|
+
// #383 Bug 1: prefer per-project URL over the global default.
|
|
2563
|
+
agentchattr_url: resolveProjectAgentchattrUrl(cfg, project),
|
|
2506
2564
|
};
|
|
2507
2565
|
} catch {
|
|
2508
2566
|
return null;
|
|
@@ -2638,7 +2696,31 @@ router.post("/api/telegram", async (req, res) => {
|
|
|
2638
2696
|
`pip output tail:\n${pipOutput.split("\n").slice(-10).join("\n")}`,
|
|
2639
2697
|
});
|
|
2640
2698
|
}
|
|
2641
|
-
|
|
2699
|
+
// #383 Bug 3: ensure every known project's AC config declares
|
|
2700
|
+
// the `telegram-bridge` agent. Without this, AC's registry
|
|
2701
|
+
// rejects the bridge's register call with `400 unknown base`
|
|
2702
|
+
// and the bridge enters an infinite re-register loop.
|
|
2703
|
+
// Idempotent — append-only, skips configs that already have
|
|
2704
|
+
// the section. Does NOT restart AC servers; the operator
|
|
2705
|
+
// must click SERVER → Restart to load the new agent slug.
|
|
2706
|
+
const patched = [];
|
|
2707
|
+
try {
|
|
2708
|
+
const cfgAll = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
2709
|
+
for (const proj of cfgAll.projects || []) {
|
|
2710
|
+
if (!proj || !proj.id) continue;
|
|
2711
|
+
const acPath = projectAgentchattrConfigPath(proj.id);
|
|
2712
|
+
if (!fs.existsSync(acPath)) continue;
|
|
2713
|
+
try {
|
|
2714
|
+
const before = fs.readFileSync(acPath, "utf-8");
|
|
2715
|
+
const { text, changed } = patchAgentchattrConfigForTelegramBridge(before);
|
|
2716
|
+
if (changed) {
|
|
2717
|
+
fs.writeFileSync(acPath, text);
|
|
2718
|
+
patched.push(proj.id);
|
|
2719
|
+
}
|
|
2720
|
+
} catch {}
|
|
2721
|
+
}
|
|
2722
|
+
} catch {}
|
|
2723
|
+
return res.json({ ok: true, patched_projects: patched });
|
|
2642
2724
|
}
|
|
2643
2725
|
case "start": {
|
|
2644
2726
|
const projectId = body.project_id;
|
|
@@ -2661,7 +2743,9 @@ router.post("/api/telegram", async (req, res) => {
|
|
|
2661
2743
|
const tg = getProjectTelegram(projectId);
|
|
2662
2744
|
if (!tg || !tg.bot_token || !tg.chat_id) return res.json({ ok: false, error: "Save bot_token and chat_id in project settings first." });
|
|
2663
2745
|
const tomlPath = telegramConfigToml(projectId);
|
|
2664
|
-
|
|
2746
|
+
// #383 Bug 2: write agentchattr_url inside [telegram]; the
|
|
2747
|
+
// bridge's load_config only reads from that section.
|
|
2748
|
+
const tomlContent = buildTelegramBridgeToml(tg);
|
|
2665
2749
|
fs.writeFileSync(tomlPath, tomlContent, { mode: 0o600 });
|
|
2666
2750
|
fs.chmodSync(tomlPath, 0o600);
|
|
2667
2751
|
// #353: pre-flight import check so a fresh install with no
|
|
@@ -2710,9 +2794,15 @@ router.post("/api/telegram", async (req, res) => {
|
|
|
2710
2794
|
}
|
|
2711
2795
|
let child;
|
|
2712
2796
|
try {
|
|
2797
|
+
// #383 Bug 4: scrub TELEGRAM_*/AGENTCHATTR_URL from the child
|
|
2798
|
+
// env so an operator shell that exports a different bot's
|
|
2799
|
+
// token (common on machines running AC2) can't silently
|
|
2800
|
+
// override the TOML. Makes the TOML the single source of
|
|
2801
|
+
// truth for the bridge's identity.
|
|
2713
2802
|
child = spawn(venvPython, [bridgeScript, "--config", tomlPath], {
|
|
2714
2803
|
detached: true,
|
|
2715
2804
|
stdio: ["ignore", outFd, errFd],
|
|
2805
|
+
env: buildTelegramBridgeSpawnEnv(process.env),
|
|
2716
2806
|
});
|
|
2717
2807
|
child.unref();
|
|
2718
2808
|
if (child.pid) fs.writeFileSync(telegramPidFile(projectId), String(child.pid));
|
|
@@ -2898,6 +2988,13 @@ module.exports.readLastLines = readLastLines;
|
|
|
2898
2988
|
// #380: expose checkTelegramBridgePythonDeps so the bridge test can
|
|
2899
2989
|
// exercise the venv-path interpreter argument round trip.
|
|
2900
2990
|
module.exports.checkTelegramBridgePythonDeps = checkTelegramBridgePythonDeps;
|
|
2991
|
+
// #383: pure helpers exposed for unit tests in
|
|
2992
|
+
// routes.telegramBridge.test.js. No production callers outside
|
|
2993
|
+
// this file.
|
|
2994
|
+
module.exports.resolveProjectAgentchattrUrl = resolveProjectAgentchattrUrl;
|
|
2995
|
+
module.exports.buildTelegramBridgeToml = buildTelegramBridgeToml;
|
|
2996
|
+
module.exports.patchAgentchattrConfigForTelegramBridge = patchAgentchattrConfigForTelegramBridge;
|
|
2997
|
+
module.exports.buildTelegramBridgeSpawnEnv = buildTelegramBridgeSpawnEnv;
|
|
2901
2998
|
// #236: expose sendViaWebSocket so the chat-ws-send regression test
|
|
2902
2999
|
// can verify the ack/body/error paths against a fake AC ws server.
|
|
2903
3000
|
module.exports.sendViaWebSocket = sendViaWebSocket;
|
|
@@ -11,7 +11,14 @@ const fs = require("node:fs");
|
|
|
11
11
|
const os = require("node:os");
|
|
12
12
|
const path = require("node:path");
|
|
13
13
|
const { execFileSync } = require("node:child_process");
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
readLastLines,
|
|
16
|
+
checkTelegramBridgePythonDeps,
|
|
17
|
+
resolveProjectAgentchattrUrl,
|
|
18
|
+
buildTelegramBridgeToml,
|
|
19
|
+
patchAgentchattrConfigForTelegramBridge,
|
|
20
|
+
buildTelegramBridgeSpawnEnv,
|
|
21
|
+
} = require("./routes");
|
|
15
22
|
|
|
16
23
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qw-bridge-log-"));
|
|
17
24
|
function write(name, content) {
|
|
@@ -133,8 +140,96 @@ try {
|
|
|
133
140
|
venvSkipped = true;
|
|
134
141
|
}
|
|
135
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
|
+
});
|
|
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
|
+
// Must NOT emit a separate [agentchattr] section — the bridge
|
|
185
|
+
// would silently ignore it.
|
|
186
|
+
assert.equal(toml13.includes("\n[agentchattr]\n"), false);
|
|
187
|
+
|
|
188
|
+
// 14) #383 Bug 3: patchAgentchattrConfigForTelegramBridge is
|
|
189
|
+
// idempotent. The Install Bridge migration may run multiple
|
|
190
|
+
// times; it must not duplicate the section or corrupt the
|
|
191
|
+
// file.
|
|
192
|
+
const baseConfig =
|
|
193
|
+
"[agents.head]\nlabel = \"Head\"\n\n[agents.dev]\nlabel = \"Dev\"\n";
|
|
194
|
+
const first = patchAgentchattrConfigForTelegramBridge(baseConfig);
|
|
195
|
+
assert.equal(first.changed, true);
|
|
196
|
+
assert.match(first.text, /^\[agents\.telegram-bridge\]$/m);
|
|
197
|
+
assert.match(first.text, /label = "Telegram Bridge"/);
|
|
198
|
+
// Running a second time is a no-op.
|
|
199
|
+
const second = patchAgentchattrConfigForTelegramBridge(first.text);
|
|
200
|
+
assert.equal(second.changed, false);
|
|
201
|
+
assert.equal(second.text, first.text);
|
|
202
|
+
// A config that was hand-patched during diagnosis is recognized
|
|
203
|
+
// as already-correct — do not clobber the operator's edit.
|
|
204
|
+
const handPatched =
|
|
205
|
+
baseConfig + "\n[agents.telegram-bridge]\nlabel = \"Telegram Bridge\"\n";
|
|
206
|
+
const third = patchAgentchattrConfigForTelegramBridge(handPatched);
|
|
207
|
+
assert.equal(third.changed, false);
|
|
208
|
+
assert.equal(third.text, handPatched);
|
|
209
|
+
|
|
210
|
+
// 15) #383 Bug 4: buildTelegramBridgeSpawnEnv strips the three
|
|
211
|
+
// env vars the upstream bridge treats as higher-precedence
|
|
212
|
+
// than TOML. Without this, an operator shell that exported a
|
|
213
|
+
// different bot's token (common on machines running AC2)
|
|
214
|
+
// silently overrode the QuadWork-written TOML and the bridge
|
|
215
|
+
// ran as the wrong identity.
|
|
216
|
+
const scrubbed = buildTelegramBridgeSpawnEnv({
|
|
217
|
+
PATH: "/usr/bin",
|
|
218
|
+
HOME: "/home/op",
|
|
219
|
+
TELEGRAM_BOT_TOKEN: "wrong-token",
|
|
220
|
+
TELEGRAM_CHAT_ID: "-999",
|
|
221
|
+
AGENTCHATTR_URL: "http://127.0.0.1:9999",
|
|
222
|
+
});
|
|
223
|
+
assert.equal(scrubbed.TELEGRAM_BOT_TOKEN, undefined);
|
|
224
|
+
assert.equal(scrubbed.TELEGRAM_CHAT_ID, undefined);
|
|
225
|
+
assert.equal(scrubbed.AGENTCHATTR_URL, undefined);
|
|
226
|
+
// Non-telegram keys must pass through untouched — the bridge
|
|
227
|
+
// still needs PATH/HOME/etc. to find python and open files.
|
|
228
|
+
assert.equal(scrubbed.PATH, "/usr/bin");
|
|
229
|
+
assert.equal(scrubbed.HOME, "/home/op");
|
|
230
|
+
|
|
136
231
|
console.log(
|
|
137
|
-
"routes.telegramBridge.test.js: all assertions passed (
|
|
232
|
+
"routes.telegramBridge.test.js: all assertions passed (15 cases" +
|
|
138
233
|
(venvSkipped ? ", case 11 pip step skipped" : "") +
|
|
139
234
|
")",
|
|
140
235
|
);
|
package/templates/config.toml
CHANGED
|
@@ -37,6 +37,14 @@ cwd = "{{dev_cwd}}"
|
|
|
37
37
|
color = "#da7756"
|
|
38
38
|
label = "Dev Builder"
|
|
39
39
|
|
|
40
|
+
# #383: AC's registry rejects bases not declared in config.toml.
|
|
41
|
+
# The Telegram bridge registers as `telegram-bridge`, so every
|
|
42
|
+
# per-project AC config must declare it. The bridge has no
|
|
43
|
+
# command/cwd of its own — it is a long-running external client
|
|
44
|
+
# that posts to AC's HTTP API.
|
|
45
|
+
[agents.telegram-bridge]
|
|
46
|
+
label = "Telegram Bridge"
|
|
47
|
+
|
|
40
48
|
[routing]
|
|
41
49
|
default = "none"
|
|
42
50
|
max_agent_hops = 30
|
|
File without changes
|
|
File without changes
|
|
File without changes
|