quadwork 1.5.1 → 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.
Files changed (80) hide show
  1. package/bin/quadwork.js +13 -1
  2. package/out/404.html +1 -1
  3. package/out/__next.__PAGE__.txt +1 -1
  4. package/out/__next._full.txt +1 -1
  5. package/out/__next._head.txt +1 -1
  6. package/out/__next._index.txt +1 -1
  7. package/out/__next._tree.txt +1 -1
  8. package/out/_next/static/chunks/{0-7v31f-nsgw-.js → 09sq17vme9g6p.js} +1 -1
  9. package/out/_next/static/chunks/{0m83k84.midd1.js → 0wreuebrwlg.2.js} +1 -1
  10. package/out/_not-found/__next._full.txt +1 -1
  11. package/out/_not-found/__next._head.txt +1 -1
  12. package/out/_not-found/__next._index.txt +1 -1
  13. package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
  14. package/out/_not-found/__next._not-found.txt +1 -1
  15. package/out/_not-found/__next._tree.txt +1 -1
  16. package/out/_not-found.html +1 -1
  17. package/out/_not-found.txt +1 -1
  18. package/out/app-shell/__next._full.txt +1 -1
  19. package/out/app-shell/__next._head.txt +1 -1
  20. package/out/app-shell/__next._index.txt +1 -1
  21. package/out/app-shell/__next._tree.txt +1 -1
  22. package/out/app-shell/__next.app-shell.__PAGE__.txt +1 -1
  23. package/out/app-shell/__next.app-shell.txt +1 -1
  24. package/out/app-shell.html +1 -1
  25. package/out/app-shell.txt +1 -1
  26. package/out/index.html +1 -1
  27. package/out/index.txt +1 -1
  28. package/out/project/_/__next._full.txt +2 -2
  29. package/out/project/_/__next._head.txt +1 -1
  30. package/out/project/_/__next._index.txt +1 -1
  31. package/out/project/_/__next._tree.txt +1 -1
  32. package/out/project/_/__next.project.$d$id.__PAGE__.txt +2 -2
  33. package/out/project/_/__next.project.$d$id.txt +1 -1
  34. package/out/project/_/__next.project.txt +1 -1
  35. package/out/project/_/memory/__next._full.txt +1 -1
  36. package/out/project/_/memory/__next._head.txt +1 -1
  37. package/out/project/_/memory/__next._index.txt +1 -1
  38. package/out/project/_/memory/__next._tree.txt +1 -1
  39. package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +1 -1
  40. package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
  41. package/out/project/_/memory/__next.project.$d$id.txt +1 -1
  42. package/out/project/_/memory/__next.project.txt +1 -1
  43. package/out/project/_/memory.html +1 -1
  44. package/out/project/_/memory.txt +1 -1
  45. package/out/project/_/queue/__next._full.txt +1 -1
  46. package/out/project/_/queue/__next._head.txt +1 -1
  47. package/out/project/_/queue/__next._index.txt +1 -1
  48. package/out/project/_/queue/__next._tree.txt +1 -1
  49. package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
  50. package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
  51. package/out/project/_/queue/__next.project.$d$id.txt +1 -1
  52. package/out/project/_/queue/__next.project.txt +1 -1
  53. package/out/project/_/queue.html +1 -1
  54. package/out/project/_/queue.txt +1 -1
  55. package/out/project/_.html +1 -1
  56. package/out/project/_.txt +2 -2
  57. package/out/settings/__next._full.txt +1 -1
  58. package/out/settings/__next._head.txt +1 -1
  59. package/out/settings/__next._index.txt +1 -1
  60. package/out/settings/__next._tree.txt +1 -1
  61. package/out/settings/__next.settings.__PAGE__.txt +1 -1
  62. package/out/settings/__next.settings.txt +1 -1
  63. package/out/settings.html +1 -1
  64. package/out/settings.txt +1 -1
  65. package/out/setup/__next._full.txt +1 -1
  66. package/out/setup/__next._head.txt +1 -1
  67. package/out/setup/__next._index.txt +1 -1
  68. package/out/setup/__next._tree.txt +1 -1
  69. package/out/setup/__next.setup.__PAGE__.txt +1 -1
  70. package/out/setup/__next.setup.txt +1 -1
  71. package/out/setup.html +1 -1
  72. package/out/setup.txt +1 -1
  73. package/package.json +1 -1
  74. package/server/routes.chatWsSend.test.js +161 -0
  75. package/server/routes.js +250 -32
  76. package/server/routes.telegramBridge.test.js +145 -2
  77. package/templates/config.toml +8 -0
  78. /package/out/_next/static/{wxXtT0v8ALxniu3OdJwt5 → X4zdS6Y6HkLOaElNeHwnq}/_buildManifest.js +0 -0
  79. /package/out/_next/static/{wxXtT0v8ALxniu3OdJwt5 → X4zdS6Y6HkLOaElNeHwnq}/_clientMiddlewareManifest.js +0 -0
  80. /package/out/_next/static/{wxXtT0v8ALxniu3OdJwt5 → X4zdS6Y6HkLOaElNeHwnq}/_ssgManifest.js +0 -0
@@ -10,7 +10,15 @@ const assert = require("node:assert/strict");
10
10
  const fs = require("node:fs");
11
11
  const os = require("node:os");
12
12
  const path = require("node:path");
13
- const { readLastLines } = require("./routes");
13
+ const { execFileSync } = require("node:child_process");
14
+ const {
15
+ readLastLines,
16
+ checkTelegramBridgePythonDeps,
17
+ resolveProjectAgentchattrUrl,
18
+ buildTelegramBridgeToml,
19
+ patchAgentchattrConfigForTelegramBridge,
20
+ buildTelegramBridgeSpawnEnv,
21
+ } = require("./routes");
14
22
 
15
23
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qw-bridge-log-"));
16
24
  function write(name, content) {
@@ -89,7 +97,142 @@ try {
89
97
  assert.match(tail, /ModuleNotFoundError/);
90
98
  assert.match(tail, /Install Bridge/);
91
99
 
92
- console.log("routes.telegramBridge.test.js: all assertions passed (8 cases)");
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
+ });
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
+
231
+ console.log(
232
+ "routes.telegramBridge.test.js: all assertions passed (15 cases" +
233
+ (venvSkipped ? ", case 11 pip step skipped" : "") +
234
+ ")",
235
+ );
93
236
  } finally {
94
237
  try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {}
95
238
  }
@@ -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