quadwork 1.6.0 → 1.6.2
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/bridges/discord/discord_bridge.py +480 -0
- package/bridges/discord/requirements.txt +2 -0
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +1 -1
- package/out/__next._full.txt +2 -2
- package/out/__next._head.txt +1 -1
- package/out/__next._index.txt +2 -2
- package/out/__next._tree.txt +2 -2
- package/out/_next/static/chunks/0j-zyy6.adwtl.css +2 -0
- package/out/_next/static/chunks/{07wbvkahjk4k3.js → 0ksh60pbzmyjf.js} +2 -2
- package/out/_next/static/chunks/{0c280.d83m4fs.js → 0zy-ktdv5zuby.js} +1 -1
- package/out/_not-found/__next._full.txt +2 -2
- package/out/_not-found/__next._head.txt +1 -1
- package/out/_not-found/__next._index.txt +2 -2
- 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 +2 -2
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +2 -2
- package/out/app-shell/__next._full.txt +2 -2
- package/out/app-shell/__next._head.txt +1 -1
- package/out/app-shell/__next._index.txt +2 -2
- package/out/app-shell/__next._tree.txt +2 -2
- 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 +2 -2
- package/out/icon.svg +9 -0
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/project/_/__next._full.txt +3 -3
- package/out/project/_/__next._head.txt +1 -1
- package/out/project/_/__next._index.txt +2 -2
- package/out/project/_/__next._tree.txt +2 -2
- 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 +2 -2
- package/out/project/_/memory/__next._head.txt +1 -1
- package/out/project/_/memory/__next._index.txt +2 -2
- package/out/project/_/memory/__next._tree.txt +2 -2
- 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 +2 -2
- package/out/project/_/queue/__next._full.txt +2 -2
- package/out/project/_/queue/__next._head.txt +1 -1
- package/out/project/_/queue/__next._index.txt +2 -2
- package/out/project/_/queue/__next._tree.txt +2 -2
- 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 +2 -2
- package/out/project/_.html +1 -1
- package/out/project/_.txt +3 -3
- package/out/settings/__next._full.txt +2 -2
- package/out/settings/__next._head.txt +1 -1
- package/out/settings/__next._index.txt +2 -2
- package/out/settings/__next._tree.txt +2 -2
- 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 +2 -2
- package/out/setup/__next._full.txt +2 -2
- package/out/setup/__next._head.txt +1 -1
- package/out/setup/__next._index.txt +2 -2
- package/out/setup/__next._tree.txt +2 -2
- 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 +2 -2
- package/package.json +2 -1
- package/server/index.js +194 -21
- package/out/_next/static/chunks/0gbucesq78fzb.css +0 -2
- /package/out/_next/static/{vgerah8Gaf36Lt50oHob8 → AQ0US7_Pm9gOOelb-ks5q}/_buildManifest.js +0 -0
- /package/out/_next/static/{vgerah8Gaf36Lt50oHob8 → AQ0US7_Pm9gOOelb-ks5q}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{vgerah8Gaf36Lt50oHob8 → AQ0US7_Pm9gOOelb-ks5q}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Discord ↔ AgentChattr bridge.
|
|
4
|
+
|
|
5
|
+
Bidirectional relay: messages from a Discord channel appear in
|
|
6
|
+
AgentChattr, and agent messages from AC appear in Discord.
|
|
7
|
+
|
|
8
|
+
Mirrors the Telegram bridge (agentchattr-telegram/telegram_bridge.py)
|
|
9
|
+
as closely as possible. Bundled inside the quadwork npm package at
|
|
10
|
+
bridges/discord/ instead of a separate repo.
|
|
11
|
+
|
|
12
|
+
Config: read from TOML [discord] section, env var overrides win.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import asyncio
|
|
17
|
+
import atexit
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import signal
|
|
22
|
+
import sys
|
|
23
|
+
import threading
|
|
24
|
+
import time
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import tomllib # Python 3.11+
|
|
29
|
+
except ModuleNotFoundError:
|
|
30
|
+
try:
|
|
31
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
32
|
+
except ModuleNotFoundError:
|
|
33
|
+
tomllib = None # type: ignore[assignment]
|
|
34
|
+
|
|
35
|
+
import discord
|
|
36
|
+
import requests
|
|
37
|
+
|
|
38
|
+
log = logging.getLogger("discord-bridge")
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Config
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
DEFAULT_CONFIG = {
|
|
45
|
+
"bot_token": "",
|
|
46
|
+
"channel_id": "",
|
|
47
|
+
"agentchattr_url": "http://127.0.0.1:8300",
|
|
48
|
+
"poll_interval": 2,
|
|
49
|
+
"bridge_sender": "discord-bridge",
|
|
50
|
+
"cursor_file": "",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ENV_MAP = {
|
|
54
|
+
"DISCORD_BOT_TOKEN": "bot_token",
|
|
55
|
+
"DISCORD_CHANNEL_ID": "channel_id",
|
|
56
|
+
"AGENTCHATTR_URL": "agentchattr_url",
|
|
57
|
+
"CURSOR_FILE": "cursor_file",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def load_config(toml_path=None):
|
|
62
|
+
"""Load config: defaults → TOML [discord] → env vars."""
|
|
63
|
+
cfg = dict(DEFAULT_CONFIG)
|
|
64
|
+
|
|
65
|
+
if toml_path and os.path.isfile(toml_path):
|
|
66
|
+
if tomllib is None:
|
|
67
|
+
log.warning("tomli not installed and Python < 3.11; skipping TOML config")
|
|
68
|
+
else:
|
|
69
|
+
with open(toml_path, "rb") as f:
|
|
70
|
+
data = tomllib.load(f)
|
|
71
|
+
section = data.get("discord", {})
|
|
72
|
+
for key in cfg:
|
|
73
|
+
if key in section:
|
|
74
|
+
cfg[key] = section[key]
|
|
75
|
+
# Resolve cursor_file relative to TOML directory
|
|
76
|
+
if cfg["cursor_file"] and not os.path.isabs(cfg["cursor_file"]):
|
|
77
|
+
cfg["cursor_file"] = os.path.join(
|
|
78
|
+
os.path.dirname(os.path.abspath(toml_path)),
|
|
79
|
+
cfg["cursor_file"],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Env vars always override
|
|
83
|
+
for env_key, cfg_key in ENV_MAP.items():
|
|
84
|
+
val = os.environ.get(env_key)
|
|
85
|
+
if val:
|
|
86
|
+
cfg[cfg_key] = val
|
|
87
|
+
|
|
88
|
+
# bot_token may use "env:VAR" indirection (same as TG bridge)
|
|
89
|
+
if cfg["bot_token"].startswith("env:"):
|
|
90
|
+
env_name = cfg["bot_token"][4:]
|
|
91
|
+
cfg["bot_token"] = os.environ.get(env_name, "")
|
|
92
|
+
|
|
93
|
+
# channel_id must be an integer for discord.py comparisons
|
|
94
|
+
if cfg["channel_id"]:
|
|
95
|
+
cfg["channel_id"] = int(cfg["channel_id"])
|
|
96
|
+
|
|
97
|
+
return cfg
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def validate_config(cfg):
|
|
101
|
+
"""Raise on missing required fields."""
|
|
102
|
+
if not cfg["bot_token"]:
|
|
103
|
+
raise SystemExit("bot_token is required (TOML [discord] or DISCORD_BOT_TOKEN env)")
|
|
104
|
+
if not cfg["channel_id"]:
|
|
105
|
+
raise SystemExit("channel_id is required (TOML [discord] or DISCORD_CHANNEL_ID env)")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Cursor persistence
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
_cursor = {"last_seen_id": 0}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def load_cursor(path):
|
|
116
|
+
"""Load cursor from JSON file. Non-fatal on error."""
|
|
117
|
+
global _cursor
|
|
118
|
+
if not path or not os.path.isfile(path):
|
|
119
|
+
return
|
|
120
|
+
try:
|
|
121
|
+
with open(path) as f:
|
|
122
|
+
data = json.load(f)
|
|
123
|
+
if isinstance(data, dict) and "last_seen_id" in data:
|
|
124
|
+
_cursor["last_seen_id"] = int(data["last_seen_id"])
|
|
125
|
+
log.info("Loaded cursor: last_seen_id=%d", _cursor["last_seen_id"])
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
log.warning("Failed to load cursor from %s: %s", path, exc)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def save_cursor(path):
|
|
131
|
+
"""Save cursor to JSON file. Non-fatal on error."""
|
|
132
|
+
if not path:
|
|
133
|
+
return
|
|
134
|
+
try:
|
|
135
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
136
|
+
with open(path, "w") as f:
|
|
137
|
+
json.dump(_cursor, f)
|
|
138
|
+
except Exception as exc:
|
|
139
|
+
log.warning("Failed to save cursor to %s: %s", path, exc)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
# AgentChattr registration + heartbeat
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
# Mutable dict so heartbeat thread sees re-registration updates.
|
|
147
|
+
# bridge_sender is set from cfg during main() so all callers can read it.
|
|
148
|
+
ac = {"token": "", "name": "", "bridge_sender": "discord-bridge", "known_names": set()}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def ac_register(url, base=None, label="Discord Bridge"):
|
|
152
|
+
"""Register with AgentChattr. Returns {name, token} or raises."""
|
|
153
|
+
if base is None:
|
|
154
|
+
base = ac["bridge_sender"]
|
|
155
|
+
resp = requests.post(
|
|
156
|
+
f"{url}/api/register",
|
|
157
|
+
json={"base": base, "label": label},
|
|
158
|
+
timeout=10,
|
|
159
|
+
)
|
|
160
|
+
resp.raise_for_status()
|
|
161
|
+
data = resp.json()
|
|
162
|
+
ac["name"] = data["name"]
|
|
163
|
+
ac["token"] = data["token"]
|
|
164
|
+
ac["known_names"].add(data["name"])
|
|
165
|
+
log.info("Registered with AC as %s (known: %s)", ac["name"], ac["known_names"])
|
|
166
|
+
return data
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def ac_deregister(url):
|
|
170
|
+
"""Best-effort deregister from AC."""
|
|
171
|
+
if not ac["name"]:
|
|
172
|
+
return
|
|
173
|
+
try:
|
|
174
|
+
requests.post(
|
|
175
|
+
f"{url}/api/deregister/{ac['name']}",
|
|
176
|
+
headers={"Authorization": f"Bearer {ac['token']}"},
|
|
177
|
+
timeout=5,
|
|
178
|
+
)
|
|
179
|
+
log.info("Deregistered %s from AC", ac["name"])
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _heartbeat_loop(url):
|
|
185
|
+
"""Daemon thread: POST /api/heartbeat/{name} every 5s."""
|
|
186
|
+
while True:
|
|
187
|
+
name = ac["name"]
|
|
188
|
+
token = ac["token"]
|
|
189
|
+
if name:
|
|
190
|
+
try:
|
|
191
|
+
resp = requests.post(
|
|
192
|
+
f"{url}/api/heartbeat/{name}",
|
|
193
|
+
headers={"Authorization": f"Bearer {token}"} if token else {},
|
|
194
|
+
timeout=5,
|
|
195
|
+
)
|
|
196
|
+
if resp.status_code == 409:
|
|
197
|
+
# AC restarted — re-register
|
|
198
|
+
log.warning("Heartbeat 409 — AC restarted, re-registering")
|
|
199
|
+
try:
|
|
200
|
+
ac_register(url)
|
|
201
|
+
except Exception as exc:
|
|
202
|
+
log.error("Re-register failed: %s", exc)
|
|
203
|
+
except Exception:
|
|
204
|
+
pass
|
|
205
|
+
time.sleep(5)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def start_heartbeat(url):
|
|
209
|
+
"""Start the heartbeat daemon thread."""
|
|
210
|
+
t = threading.Thread(target=_heartbeat_loop, args=(url,), daemon=True)
|
|
211
|
+
t.start()
|
|
212
|
+
return t
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
# AC → Discord polling
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
async def poll_ac_to_discord(cfg, channel):
|
|
220
|
+
"""Poll AC for new messages and forward to Discord channel."""
|
|
221
|
+
url = cfg["agentchattr_url"]
|
|
222
|
+
bridge_sender = cfg["bridge_sender"]
|
|
223
|
+
interval = cfg["poll_interval"]
|
|
224
|
+
|
|
225
|
+
while True:
|
|
226
|
+
try:
|
|
227
|
+
# Drain all available messages before sleeping. When AC
|
|
228
|
+
# returns a full batch (limit messages), immediately
|
|
229
|
+
# re-fetch with the updated cursor to avoid dropping
|
|
230
|
+
# overflow under high volume.
|
|
231
|
+
while True:
|
|
232
|
+
params = {"limit": 50}
|
|
233
|
+
if _cursor["last_seen_id"]:
|
|
234
|
+
params["since_id"] = _cursor["last_seen_id"]
|
|
235
|
+
headers = {}
|
|
236
|
+
if ac["token"]:
|
|
237
|
+
headers["Authorization"] = f"Bearer {ac['token']}"
|
|
238
|
+
|
|
239
|
+
resp = requests.get(
|
|
240
|
+
f"{url}/api/messages",
|
|
241
|
+
params=params,
|
|
242
|
+
headers=headers,
|
|
243
|
+
timeout=10,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if resp.status_code in (401, 403):
|
|
247
|
+
log.warning("AC poll %d — re-registering", resp.status_code)
|
|
248
|
+
try:
|
|
249
|
+
ac_register(url)
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
log.error("Re-register failed: %s", exc)
|
|
252
|
+
break
|
|
253
|
+
|
|
254
|
+
resp.raise_for_status()
|
|
255
|
+
messages = resp.json()
|
|
256
|
+
|
|
257
|
+
if not isinstance(messages, list):
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
for msg in messages:
|
|
261
|
+
msg_id = msg.get("id", 0)
|
|
262
|
+
sender = msg.get("sender", "")
|
|
263
|
+
text = msg.get("text", "")
|
|
264
|
+
|
|
265
|
+
# Echo prevention: skip our own messages (any name
|
|
266
|
+
# the bridge has ever registered as this session)
|
|
267
|
+
if sender in ac["known_names"] or sender == bridge_sender:
|
|
268
|
+
if msg_id > _cursor["last_seen_id"]:
|
|
269
|
+
_cursor["last_seen_id"] = msg_id
|
|
270
|
+
continue
|
|
271
|
+
|
|
272
|
+
# Skip system auto-recovery messages
|
|
273
|
+
if sender == "system":
|
|
274
|
+
if msg_id > _cursor["last_seen_id"]:
|
|
275
|
+
_cursor["last_seen_id"] = msg_id
|
|
276
|
+
continue
|
|
277
|
+
|
|
278
|
+
if not text:
|
|
279
|
+
if msg_id > _cursor["last_seen_id"]:
|
|
280
|
+
_cursor["last_seen_id"] = msg_id
|
|
281
|
+
continue
|
|
282
|
+
|
|
283
|
+
# Forward to Discord
|
|
284
|
+
try:
|
|
285
|
+
discord_text = f"**{sender}**: {text}"
|
|
286
|
+
# Discord message limit is 2000 chars
|
|
287
|
+
if len(discord_text) > 2000:
|
|
288
|
+
discord_text = discord_text[:1997] + "..."
|
|
289
|
+
await channel.send(discord_text)
|
|
290
|
+
except Exception as exc:
|
|
291
|
+
log.error("Failed to send to Discord: %s", exc)
|
|
292
|
+
|
|
293
|
+
if msg_id > _cursor["last_seen_id"]:
|
|
294
|
+
_cursor["last_seen_id"] = msg_id
|
|
295
|
+
|
|
296
|
+
# Persist cursor after each page
|
|
297
|
+
save_cursor(cfg["cursor_file"])
|
|
298
|
+
|
|
299
|
+
# If we got a full batch, there may be more — drain immediately
|
|
300
|
+
if len(messages) >= 50:
|
|
301
|
+
continue
|
|
302
|
+
break
|
|
303
|
+
|
|
304
|
+
except requests.RequestException as exc:
|
|
305
|
+
log.warning("AC poll error: %s", exc)
|
|
306
|
+
except Exception as exc:
|
|
307
|
+
log.error("Unexpected AC poll error: %s", exc)
|
|
308
|
+
|
|
309
|
+
await asyncio.sleep(interval)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ---------------------------------------------------------------------------
|
|
313
|
+
# Discord → AC path
|
|
314
|
+
# ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
def send_to_ac(cfg, text, channel_name="general"):
|
|
317
|
+
"""Forward a message from Discord to AgentChattr."""
|
|
318
|
+
url = cfg["agentchattr_url"]
|
|
319
|
+
headers = {}
|
|
320
|
+
if ac["token"]:
|
|
321
|
+
headers["Authorization"] = f"Bearer {ac['token']}"
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
resp = requests.post(
|
|
325
|
+
f"{url}/api/send",
|
|
326
|
+
json={
|
|
327
|
+
"text": text,
|
|
328
|
+
"channel": channel_name,
|
|
329
|
+
"sender": cfg["bridge_sender"],
|
|
330
|
+
},
|
|
331
|
+
headers=headers,
|
|
332
|
+
timeout=10,
|
|
333
|
+
)
|
|
334
|
+
if resp.status_code in (401, 403):
|
|
335
|
+
log.warning("AC send %d — re-registering", resp.status_code)
|
|
336
|
+
ac_register(url)
|
|
337
|
+
# Retry once after re-register
|
|
338
|
+
headers["Authorization"] = f"Bearer {ac['token']}"
|
|
339
|
+
resp = requests.post(
|
|
340
|
+
f"{url}/api/send",
|
|
341
|
+
json={
|
|
342
|
+
"text": text,
|
|
343
|
+
"channel": channel_name,
|
|
344
|
+
"sender": cfg["bridge_sender"],
|
|
345
|
+
},
|
|
346
|
+
headers=headers,
|
|
347
|
+
timeout=10,
|
|
348
|
+
)
|
|
349
|
+
resp.raise_for_status()
|
|
350
|
+
except requests.RequestException as exc:
|
|
351
|
+
log.error("Failed to send to AC: %s", exc)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# ---------------------------------------------------------------------------
|
|
355
|
+
# Discord client
|
|
356
|
+
# ---------------------------------------------------------------------------
|
|
357
|
+
|
|
358
|
+
def create_client(cfg):
|
|
359
|
+
"""Create and configure the Discord client."""
|
|
360
|
+
intents = discord.Intents.default()
|
|
361
|
+
intents.message_content = True # Privileged — must be enabled in Developer Portal
|
|
362
|
+
client = discord.Client(intents=intents)
|
|
363
|
+
target_channel_id = cfg["channel_id"]
|
|
364
|
+
|
|
365
|
+
@client.event
|
|
366
|
+
async def on_ready():
|
|
367
|
+
log.info("Discord bot logged in as %s (id=%s)", client.user, client.user.id)
|
|
368
|
+
channel = client.get_channel(target_channel_id)
|
|
369
|
+
if not channel:
|
|
370
|
+
log.error(
|
|
371
|
+
"Cannot find channel %s — check channel_id and bot permissions",
|
|
372
|
+
target_channel_id,
|
|
373
|
+
)
|
|
374
|
+
return
|
|
375
|
+
log.info("Monitoring Discord channel: #%s (%s)", channel.name, channel.id)
|
|
376
|
+
# Start the AC → Discord poll loop
|
|
377
|
+
client.loop.create_task(poll_ac_to_discord(cfg, channel))
|
|
378
|
+
|
|
379
|
+
@client.event
|
|
380
|
+
async def on_message(message):
|
|
381
|
+
# Ignore own messages
|
|
382
|
+
if message.author == client.user:
|
|
383
|
+
return
|
|
384
|
+
# Ignore other bots
|
|
385
|
+
if message.author.bot:
|
|
386
|
+
return
|
|
387
|
+
# Only relay from the configured channel
|
|
388
|
+
if message.channel.id != target_channel_id:
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
text = message.content
|
|
392
|
+
if not text:
|
|
393
|
+
# Warn about missing MESSAGE_CONTENT intent
|
|
394
|
+
if not message.flags.value and not message.embeds and not message.attachments:
|
|
395
|
+
log.warning(
|
|
396
|
+
"Received message with empty content from %s — "
|
|
397
|
+
"MESSAGE_CONTENT intent may not be enabled in the Developer Portal",
|
|
398
|
+
message.author,
|
|
399
|
+
)
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
# Prefix with Discord username for attribution
|
|
403
|
+
ac_text = f"[discord:{message.author.display_name}] {text}"
|
|
404
|
+
log.debug("Discord → AC: %s", ac_text[:100])
|
|
405
|
+
send_to_ac(cfg, ac_text)
|
|
406
|
+
|
|
407
|
+
return client
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# ---------------------------------------------------------------------------
|
|
411
|
+
# Shutdown
|
|
412
|
+
# ---------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
_shutdown_done = []
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def shutdown(cfg):
|
|
418
|
+
"""Graceful shutdown: deregister from AC, save cursor."""
|
|
419
|
+
if _shutdown_done:
|
|
420
|
+
return
|
|
421
|
+
_shutdown_done.append(True)
|
|
422
|
+
log.info("Shutting down...")
|
|
423
|
+
ac_deregister(cfg["agentchattr_url"])
|
|
424
|
+
save_cursor(cfg["cursor_file"])
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
# ---------------------------------------------------------------------------
|
|
428
|
+
# Main
|
|
429
|
+
# ---------------------------------------------------------------------------
|
|
430
|
+
|
|
431
|
+
def main():
|
|
432
|
+
parser = argparse.ArgumentParser(description="Discord ↔ AgentChattr bridge")
|
|
433
|
+
parser.add_argument(
|
|
434
|
+
"-c", "--config",
|
|
435
|
+
help="Path to TOML config file (reads [discord] section)",
|
|
436
|
+
)
|
|
437
|
+
parser.add_argument(
|
|
438
|
+
"-v", "--verbose",
|
|
439
|
+
action="store_true",
|
|
440
|
+
help="Enable debug logging",
|
|
441
|
+
)
|
|
442
|
+
args = parser.parse_args()
|
|
443
|
+
|
|
444
|
+
logging.basicConfig(
|
|
445
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
446
|
+
format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
cfg = load_config(args.config)
|
|
450
|
+
validate_config(cfg)
|
|
451
|
+
|
|
452
|
+
# Set bridge_sender so ac_register uses the configured base name
|
|
453
|
+
ac["bridge_sender"] = cfg["bridge_sender"]
|
|
454
|
+
|
|
455
|
+
# Load cursor
|
|
456
|
+
load_cursor(cfg["cursor_file"])
|
|
457
|
+
|
|
458
|
+
# Register with AgentChattr
|
|
459
|
+
try:
|
|
460
|
+
ac_register(cfg["agentchattr_url"])
|
|
461
|
+
except Exception as exc:
|
|
462
|
+
log.error("Initial AC registration failed: %s", exc)
|
|
463
|
+
log.info("Will retry on first message send")
|
|
464
|
+
|
|
465
|
+
# Start heartbeat
|
|
466
|
+
start_heartbeat(cfg["agentchattr_url"])
|
|
467
|
+
|
|
468
|
+
# Register shutdown handlers
|
|
469
|
+
atexit.register(shutdown, cfg)
|
|
470
|
+
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
471
|
+
signal.signal(sig, lambda *_: (shutdown(cfg), sys.exit(0)))
|
|
472
|
+
|
|
473
|
+
# Start Discord client
|
|
474
|
+
client = create_client(cfg)
|
|
475
|
+
log.info("Starting Discord bridge (channel_id=%s)", cfg["channel_id"])
|
|
476
|
+
client.run(cfg["bot_token"], log_handler=None)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
if __name__ == "__main__":
|
|
480
|
+
main()
|
package/out/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/chunks/0gbucesq78fzb.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0ze4gu236oq96.js"/><script src="/_next/static/chunks/0.bbxho1vnxin.js" async=""></script><script src="/_next/static/chunks/16g.ca89g7fib.js" async=""></script><script src="/_next/static/chunks/0zfotsowwll1x.js" async=""></script><script src="/_next/static/chunks/0pqt~8bl3ukh4.js" async=""></script><script src="/_next/static/chunks/turbopack-0lcwh84lrj9gi.js" async=""></script><script src="/_next/static/chunks/0o3_.p5ivp5sp.js" async=""></script><script src="/_next/static/chunks/0d3shmwh5_nmn.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex flex-col"><div hidden=""><!--$--><!--/$--></div><header class="sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur" aria-hidden="true"></header><div class="flex flex-1 min-h-0"><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>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)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0ze4gu236oq96.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[43688,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n3:I[26704,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n4:I[22140,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n5:I[39756,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n6:I[37457,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n7:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\nb:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"ViewportBoundary\"]\nd:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"MetadataBoundary\"]\nf:I[68027,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0gbucesq78fzb.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0gbucesq78fzb.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0d3shmwh5_nmn.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,{}],[\"$\",\"$L3\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L4\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",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,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:style\",\"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\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,\"$@a\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$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/0gbucesq78fzb.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"vgerah8Gaf36Lt50oHob8\"}\n"])</script><script>self.__next_f.push([1,"10:[]\na:\"$W10\"\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"11:I[27201,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"IconMark\"]\n9:null\ne:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L11\",\"3\",{}]]\n"])</script></body></html>
|
|
1
|
+
<!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/chunks/0j-zyy6.adwtl.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0ze4gu236oq96.js"/><script src="/_next/static/chunks/0.bbxho1vnxin.js" async=""></script><script src="/_next/static/chunks/16g.ca89g7fib.js" async=""></script><script src="/_next/static/chunks/0zfotsowwll1x.js" async=""></script><script src="/_next/static/chunks/0pqt~8bl3ukh4.js" async=""></script><script src="/_next/static/chunks/turbopack-0lcwh84lrj9gi.js" async=""></script><script src="/_next/static/chunks/0o3_.p5ivp5sp.js" async=""></script><script src="/_next/static/chunks/0d3shmwh5_nmn.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex flex-col"><div hidden=""><!--$--><!--/$--></div><header class="sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur" aria-hidden="true"></header><div class="flex flex-1 min-h-0"><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>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)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0ze4gu236oq96.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[43688,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n3:I[26704,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n4:I[22140,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n5:I[39756,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n6:I[37457,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n7:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\nb:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"ViewportBoundary\"]\nd:I[97367,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"MetadataBoundary\"]\nf:I[68027,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0j-zyy6.adwtl.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0j-zyy6.adwtl.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0d3shmwh5_nmn.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,{}],[\"$\",\"$L3\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L4\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",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,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:style\",\"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\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:2:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,\"$@a\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$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/0j-zyy6.adwtl.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"AQ0US7_Pm9gOOelb-ks5q\"}\n"])</script><script>self.__next_f.push([1,"10:[]\na:\"$W10\"\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"11:I[27201,[\"/_next/static/chunks/0o3_.p5ivp5sp.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"IconMark\"]\n9:null\ne:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L11\",\"3\",{}]]\n"])</script></body></html>
|
package/out/__next.__PAGE__.txt
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
2:I[54338,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js","/_next/static/chunks/0n7b.b.q4nmo..js"],"default"]
|
|
3
3
|
3:I[97367,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"OutletBoundary"]
|
|
4
4
|
4:"$Sreact.suspense"
|
|
5
|
-
0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0n7b.b.q4nmo..js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"
|
|
5
|
+
0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0n7b.b.q4nmo..js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"AQ0US7_Pm9gOOelb-ks5q"}
|
|
6
6
|
5:null
|
package/out/__next._full.txt
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
b:I[97367,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"ViewportBoundary"]
|
|
11
11
|
d:I[97367,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"MetadataBoundary"]
|
|
12
12
|
f:I[68027,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default",1]
|
|
13
|
-
:HL["/_next/static/chunks/
|
|
13
|
+
:HL["/_next/static/chunks/0j-zyy6.adwtl.css","style"]
|
|
14
14
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
15
|
-
0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/
|
|
15
|
+
0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0j-zyy6.adwtl.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/0o3_.p5ivp5sp.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0d3shmwh5_nmn.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,{}],["$","$L3",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L4",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L5",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",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":[["$","$L7",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0n7b.b.q4nmo..js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",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/0j-zyy6.adwtl.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"AQ0US7_Pm9gOOelb-ks5q"}
|
|
16
16
|
c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
|
17
17
|
10:I[27201,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"IconMark"]
|
|
18
18
|
a:null
|
package/out/__next._head.txt
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
3:I[97367,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"MetadataBoundary"]
|
|
4
4
|
4:"$Sreact.suspense"
|
|
5
5
|
5:I[27201,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"IconMark"]
|
|
6
|
-
0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"QuadWork"}],["$","meta","1",{"name":"description","content":"Unified dashboard for multi-agent coding teams"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0x3dzn~oxb6tn.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"
|
|
6
|
+
0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"QuadWork"}],["$","meta","1",{"name":"description","content":"Unified dashboard for multi-agent coding teams"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0x3dzn~oxb6tn.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"AQ0US7_Pm9gOOelb-ks5q"}
|
package/out/__next._index.txt
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
4:I[22140,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
5
5
|
5:I[39756,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
6
6
|
6:I[37457,["/_next/static/chunks/0o3_.p5ivp5sp.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
7
|
-
:HL["/_next/static/chunks/
|
|
8
|
-
0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/
|
|
7
|
+
:HL["/_next/static/chunks/0j-zyy6.adwtl.css","style"]
|
|
8
|
+
0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0j-zyy6.adwtl.css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/0o3_.p5ivp5sp.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0d3shmwh5_nmn.js","async":true}]],["$","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,{}],["$","$L3",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L4",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L5",null,{"parallelRouterKey":"children","template":["$","$L6",null,{}],"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."}]}]]}]}]],[]]}]}]]}]]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"AQ0US7_Pm9gOOelb-ks5q"}
|
package/out/__next._tree.txt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
:HL["/_next/static/chunks/
|
|
1
|
+
:HL["/_next/static/chunks/0j-zyy6.adwtl.css","style"]
|
|
2
2
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
3
|
-
0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}},"staleTime":300,"buildId":"
|
|
3
|
+
0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}},"staleTime":300,"buildId":"AQ0US7_Pm9gOOelb-ks5q"}
|