@researai/deepscientist 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/bin/ds.js +399 -175
- package/docs/en/00_QUICK_START.md +22 -0
- package/docs/en/01_SETTINGS_REFERENCE.md +13 -4
- package/docs/en/99_ACKNOWLEDGEMENTS.md +1 -0
- package/docs/images/connectors/discord-setup-overview.svg +52 -0
- package/docs/images/connectors/feishu-setup-overview.svg +53 -0
- package/docs/images/connectors/slack-setup-overview.svg +51 -0
- package/docs/images/connectors/telegram-setup-overview.svg +55 -0
- package/docs/images/connectors/whatsapp-setup-overview.svg +51 -0
- package/docs/images/lingzhu/lingzhu-openclaw-config.svg +17 -0
- package/docs/images/lingzhu/lingzhu-platform-values.svg +16 -0
- package/docs/images/lingzhu/lingzhu-settings-overview.svg +30 -0
- package/docs/images/qq/tencent-cloud-qq-chat.png +0 -0
- package/docs/images/qq/tencent-cloud-qq-register.png +0 -0
- package/docs/images/quickstart/00-home.png +0 -0
- package/docs/images/quickstart/01-start-research.png +0 -0
- package/docs/images/quickstart/02-list-quest.png +0 -0
- package/docs/zh/00_QUICK_START.md +22 -0
- package/docs/zh/01_SETTINGS_REFERENCE.md +14 -5
- package/docs/zh/99_ACKNOWLEDGEMENTS.md +1 -0
- package/install.sh +120 -4
- package/package.json +8 -4
- package/pyproject.toml +1 -1
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/artifact/service.py +1 -1
- package/src/deepscientist/bash_exec/monitor.py +23 -4
- package/src/deepscientist/bash_exec/runtime.py +3 -0
- package/src/deepscientist/bash_exec/service.py +132 -4
- package/src/deepscientist/bridges/base.py +12 -20
- package/src/deepscientist/bridges/connectors.py +2 -1
- package/src/deepscientist/channels/discord_gateway.py +27 -4
- package/src/deepscientist/channels/feishu_long_connection.py +41 -3
- package/src/deepscientist/channels/qq.py +524 -64
- package/src/deepscientist/channels/qq_gateway.py +24 -5
- package/src/deepscientist/channels/relay.py +429 -90
- package/src/deepscientist/channels/slack_socket.py +31 -7
- package/src/deepscientist/channels/telegram_polling.py +27 -3
- package/src/deepscientist/channels/whatsapp_local_session.py +32 -4
- package/src/deepscientist/cli.py +31 -1
- package/src/deepscientist/config/models.py +13 -43
- package/src/deepscientist/config/service.py +216 -157
- package/src/deepscientist/connector_profiles.py +346 -0
- package/src/deepscientist/connector_runtime.py +88 -43
- package/src/deepscientist/daemon/api/handlers.py +53 -16
- package/src/deepscientist/daemon/api/router.py +2 -2
- package/src/deepscientist/daemon/app.py +747 -228
- package/src/deepscientist/mcp/server.py +60 -7
- package/src/deepscientist/migration.py +114 -0
- package/src/deepscientist/network.py +78 -0
- package/src/deepscientist/prompts/builder.py +50 -4
- package/src/deepscientist/qq_profiles.py +186 -0
- package/src/deepscientist/quest/service.py +1 -1
- package/src/deepscientist/skills/installer.py +77 -1
- package/src/prompts/connectors/qq.md +42 -2
- package/src/prompts/system.md +162 -6
- package/src/skills/analysis-campaign/SKILL.md +19 -5
- package/src/skills/baseline/SKILL.md +66 -31
- package/src/skills/decision/SKILL.md +1 -1
- package/src/skills/experiment/SKILL.md +11 -5
- package/src/skills/finalize/SKILL.md +1 -1
- package/src/skills/idea/SKILL.md +246 -4
- package/src/skills/intake-audit/SKILL.md +1 -1
- package/src/skills/rebuttal/SKILL.md +1 -1
- package/src/skills/review/SKILL.md +1 -1
- package/src/skills/scout/SKILL.md +1 -1
- package/src/skills/write/SKILL.md +152 -2
- package/src/tui/package.json +1 -1
- package/src/ui/dist/assets/{AiManusChatView-CZpg376x.js → AiManusChatView-BGLArZRn.js} +14 -37
- package/src/ui/dist/assets/{AnalysisPlugin-CtHA22g3.js → AnalysisPlugin-BgDGSigG.js} +1 -1
- package/src/ui/dist/assets/{AutoFigurePlugin-BSWmLMmF.js → AutoFigurePlugin-B65HD7L4.js} +5 -5
- package/src/ui/dist/assets/{CliPlugin-CJ7jdm_s.js → CliPlugin-CUqgsFHC.js} +17 -110
- package/src/ui/dist/assets/{CodeEditorPlugin-DhInVGFf.js → CodeEditorPlugin-CF5EdvaS.js} +8 -8
- package/src/ui/dist/assets/{CodeViewerPlugin-D1n8S9r5.js → CodeViewerPlugin-DEeU063D.js} +5 -5
- package/src/ui/dist/assets/{DocViewerPlugin-C4XM_kqk.js → DocViewerPlugin-Df-FuDlZ.js} +3 -3
- package/src/ui/dist/assets/{GitDiffViewerPlugin-W6kS9r6v.js → GitDiffViewerPlugin-RAnNaRxM.js} +1 -1
- package/src/ui/dist/assets/{ImageViewerPlugin-DPeUx_Oz.js → ImageViewerPlugin-DXJ0ZJGg.js} +5 -5
- package/src/ui/dist/assets/{LabCopilotPanel-eAelUaub.js → LabCopilotPanel-BlO-sKsj.js} +10 -10
- package/src/ui/dist/assets/{LabPlugin-BbOrBxKY.js → LabPlugin-BajPZW5v.js} +1 -1
- package/src/ui/dist/assets/{LatexPlugin-C-HhkVXY.js → LatexPlugin-F1OEol8D.js} +7 -7
- package/src/ui/dist/assets/{MarkdownViewerPlugin-BDIzIBfh.js → MarkdownViewerPlugin-MhUupqwT.js} +4 -4
- package/src/ui/dist/assets/{MarketplacePlugin-DAOJphwr.js → MarketplacePlugin-DxhIEsv0.js} +3 -3
- package/src/ui/dist/assets/{NotebookEditor-BsoMvDoU.js → NotebookEditor-q7TkhewC.js} +1 -1
- package/src/ui/dist/assets/{PdfLoader-fiC7RtHf.js → PdfLoader-B8ZOTKFc.js} +1 -1
- package/src/ui/dist/assets/{PdfMarkdownPlugin-C5OxZBFK.js → PdfMarkdownPlugin-xFPvzvWh.js} +3 -3
- package/src/ui/dist/assets/{PdfViewerPlugin-CAbxQebk.js → PdfViewerPlugin-EjEcsIB8.js} +10 -10
- package/src/ui/dist/assets/{SearchPlugin-SE33Lb9B.js → SearchPlugin-ixY-1lgW.js} +1 -1
- package/src/ui/dist/assets/{Stepper-0Av7GfV7.js → Stepper-gYFK2Pgz.js} +1 -1
- package/src/ui/dist/assets/{TextViewerPlugin-Daf2gJDI.js → TextViewerPlugin-Cym6pv_n.js} +4 -4
- package/src/ui/dist/assets/{VNCViewer-BKrMUIOX.js → VNCViewer-BPmIHcmK.js} +9 -9
- package/src/ui/dist/assets/{bibtex-JBdOEe45.js → bibtex-Btv6Wi7f.js} +1 -1
- package/src/ui/dist/assets/{code-B0TDFCZz.js → code-BlG7g85c.js} +1 -1
- package/src/ui/dist/assets/{file-content-3YtrSacz.js → file-content-DBT5OfTZ.js} +1 -1
- package/src/ui/dist/assets/{file-diff-panel-CJEg5OG1.js → file-diff-panel-BWXYzqHk.js} +1 -1
- package/src/ui/dist/assets/{file-socket-CYQYdmB1.js → file-socket-wDlx6byM.js} +1 -1
- package/src/ui/dist/assets/{file-utils-Cd1C9Ppl.js → file-utils-Ba3nJmH0.js} +1 -1
- package/src/ui/dist/assets/{image-B33ctrvC.js → image-BwtCyguk.js} +1 -1
- package/src/ui/dist/assets/{index-BNQWqmJ2.js → index-B-2scqCJ.js} +11 -11
- package/src/ui/dist/assets/{index-BVXsmS7V.js → index-Bz5AaWL7.js} +52383 -51440
- package/src/ui/dist/assets/{index-Buw_N1VQ.js → index-CfRpE209.js} +2 -2
- package/src/ui/dist/assets/{index-9CLPVeZh.js → index-DcqvKzeJ.js} +1 -1
- package/src/ui/dist/assets/{index-SwmFAld3.css → index-DpMZw8aM.css} +49 -2
- package/src/ui/dist/assets/{message-square-D0cUJ9yU.js → message-square-BnlyWVH0.js} +1 -1
- package/src/ui/dist/assets/{monaco-UZLYkp2n.js → monaco-CXe0pAVe.js} +1 -1
- package/src/ui/dist/assets/{popover-CTeiY-dK.js → popover-BCHmVhHj.js} +1 -1
- package/src/ui/dist/assets/{project-sync-Dbs01Xky.js → project-sync-Brk6kaOD.js} +1 -1
- package/src/ui/dist/assets/{sigma-CM08S-xT.js → sigma-D72eSUep.js} +1 -1
- package/src/ui/dist/assets/{tooltip-pDtzvU9p.js → tooltip-BMWd0dqX.js} +1 -1
- package/src/ui/dist/assets/{trash-YvPCP-da.js → trash-BIt_eWIS.js} +1 -1
- package/src/ui/dist/assets/{useCliAccess-Bavi74Ac.js → useCliAccess-N1hkTRrR.js} +1 -1
- package/src/ui/dist/assets/{useFileDiffOverlay-CVXY6oeg.js → useFileDiffOverlay-DPRPv6rv.js} +1 -1
- package/src/ui/dist/assets/{wrap-text-Cf4flRW7.js → wrap-text-E5-UheyP.js} +1 -1
- package/src/ui/dist/assets/{zoom-out-Hb0Z1YpT.js → zoom-out-D4TR-ZZ_.js} +1 -1
- package/src/ui/dist/index.html +2 -2
|
@@ -205,19 +205,12 @@ npm --prefix src/ui run build</pre>
|
|
|
205
205
|
def connectors(self) -> list[dict]:
|
|
206
206
|
return self.app.list_connector_statuses()
|
|
207
207
|
|
|
208
|
+
def connectors_availability(self) -> dict:
|
|
209
|
+
return self.app.connector_availability_summary()
|
|
210
|
+
|
|
208
211
|
def baselines(self) -> list[dict]:
|
|
209
212
|
return self.app.artifact_service.baselines.list_entries()
|
|
210
213
|
|
|
211
|
-
def bridge_webhook(self, connector: str, *, method: str, path: str, raw_body: bytes, headers: dict[str, str], body: dict) -> tuple[int, dict, bytes | str] | dict:
|
|
212
|
-
return self.app.handle_bridge_webhook(
|
|
213
|
-
connector,
|
|
214
|
-
method=method,
|
|
215
|
-
path=path,
|
|
216
|
-
raw_body=raw_body,
|
|
217
|
-
headers=headers,
|
|
218
|
-
body=body,
|
|
219
|
-
)
|
|
220
|
-
|
|
221
214
|
def qq_bindings(self) -> list[dict]:
|
|
222
215
|
return self.app.list_qq_bindings()
|
|
223
216
|
|
|
@@ -246,12 +239,33 @@ npm --prefix src/ui run build</pre>
|
|
|
246
239
|
preferred_connector_conversation_id = (
|
|
247
240
|
str(body.get("preferred_connector_conversation_id") or "").strip() or None
|
|
248
241
|
)
|
|
242
|
+
requested_connector_bindings = (
|
|
243
|
+
[dict(item) for item in body.get("requested_connector_bindings") if isinstance(item, dict)]
|
|
244
|
+
if isinstance(body.get("requested_connector_bindings"), list)
|
|
245
|
+
else []
|
|
246
|
+
)
|
|
247
|
+
force_connector_rebind_raw = body.get("force_connector_rebind")
|
|
248
|
+
force_connector_rebind = bool(force_connector_rebind_raw) and str(force_connector_rebind_raw).strip().lower() not in {
|
|
249
|
+
"0",
|
|
250
|
+
"false",
|
|
251
|
+
"no",
|
|
252
|
+
"off",
|
|
253
|
+
}
|
|
249
254
|
requested_baseline_ref = body.get("requested_baseline_ref")
|
|
250
255
|
startup_contract = body.get("startup_contract")
|
|
251
256
|
auto_start = body.get("auto_start") is True
|
|
252
257
|
initial_message = str(body.get("initial_message") or "").strip()
|
|
253
258
|
if not goal:
|
|
254
259
|
return {"ok": False, "message": "Quest goal is required."}
|
|
260
|
+
if requested_connector_bindings and not force_connector_rebind:
|
|
261
|
+
conflicts = self.app.preview_connector_binding_conflicts(requested_connector_bindings)
|
|
262
|
+
if conflicts:
|
|
263
|
+
return 409, {
|
|
264
|
+
"ok": False,
|
|
265
|
+
"conflict": True,
|
|
266
|
+
"message": "One or more selected connector targets are already bound to another quest.",
|
|
267
|
+
"conflicts": conflicts,
|
|
268
|
+
}
|
|
255
269
|
try:
|
|
256
270
|
snapshot = self.app.create_quest(
|
|
257
271
|
goal=goal,
|
|
@@ -259,6 +273,8 @@ npm --prefix src/ui run build</pre>
|
|
|
259
273
|
quest_id=quest_id,
|
|
260
274
|
source=source,
|
|
261
275
|
preferred_connector_conversation_id=preferred_connector_conversation_id,
|
|
276
|
+
requested_connector_bindings=requested_connector_bindings,
|
|
277
|
+
force_connector_rebind=force_connector_rebind,
|
|
262
278
|
requested_baseline_ref=requested_baseline_ref if isinstance(requested_baseline_ref, dict) else None,
|
|
263
279
|
startup_contract=startup_contract if isinstance(startup_contract, dict) else None,
|
|
264
280
|
)
|
|
@@ -386,9 +402,19 @@ npm --prefix src/ui run build</pre>
|
|
|
386
402
|
}
|
|
387
403
|
|
|
388
404
|
def quest_bindings(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
|
|
405
|
+
requested_bindings = (
|
|
406
|
+
[dict(item) for item in body.get("bindings") if isinstance(item, dict)]
|
|
407
|
+
if isinstance(body.get("bindings"), list)
|
|
408
|
+
else []
|
|
409
|
+
)
|
|
389
410
|
conversation_id = str(body.get("conversation_id") or body.get("source") or "").strip() or None
|
|
411
|
+
connector_name = str(body.get("connector") or "").strip() or None
|
|
390
412
|
force_raw = body.get("force")
|
|
391
413
|
force = bool(force_raw) and str(force_raw).strip().lower() not in {"0", "false", "no", "off"}
|
|
414
|
+
if requested_bindings:
|
|
415
|
+
return self.app.update_quest_bindings(quest_id, requested_bindings, force=force)
|
|
416
|
+
if connector_name:
|
|
417
|
+
return self.app.update_quest_connector_binding(quest_id, connector_name, conversation_id, force=force)
|
|
392
418
|
return self.app.update_quest_binding(quest_id, conversation_id, force=force)
|
|
393
419
|
|
|
394
420
|
def quest_session(self, quest_id: str) -> dict:
|
|
@@ -455,6 +481,7 @@ npm --prefix src/ui run build</pre>
|
|
|
455
481
|
def bash_sessions(self, quest_id: str, path: str) -> list[dict]:
|
|
456
482
|
query = self.parse_query(path)
|
|
457
483
|
status = ((query.get("status") or [""])[0] or "").strip() or None
|
|
484
|
+
kind = ((query.get("kind") or [""])[0] or "").strip() or None
|
|
458
485
|
chat_session_id = ((query.get("chat_session_id") or [""])[0] or "").strip() or None
|
|
459
486
|
limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
|
|
460
487
|
try:
|
|
@@ -475,6 +502,7 @@ npm --prefix src/ui run build</pre>
|
|
|
475
502
|
return self.app.bash_exec_service.list_sessions(
|
|
476
503
|
quest_root,
|
|
477
504
|
status=status,
|
|
505
|
+
kind=kind,
|
|
478
506
|
agent_ids=agent_ids or None,
|
|
479
507
|
agent_instance_ids=agent_instance_ids or None,
|
|
480
508
|
chat_session_id=chat_session_id,
|
|
@@ -492,12 +520,14 @@ npm --prefix src/ui run build</pre>
|
|
|
492
520
|
query = self.parse_query(path)
|
|
493
521
|
limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
|
|
494
522
|
before_seq_raw = ((query.get("before_seq") or [""])[0] or "").strip()
|
|
523
|
+
after_seq_raw = ((query.get("after_seq") or [""])[0] or "").strip()
|
|
495
524
|
order = ((query.get("order") or ["asc"])[0] or "asc").strip().lower()
|
|
496
525
|
try:
|
|
497
526
|
limit = max(1, min(int(limit_raw), 1000))
|
|
498
527
|
except ValueError:
|
|
499
528
|
limit = 200
|
|
500
529
|
before_seq = int(before_seq_raw) if before_seq_raw.isdigit() else None
|
|
530
|
+
after_seq = int(after_seq_raw) if after_seq_raw.isdigit() else None
|
|
501
531
|
quest_root = self.app.quest_service._quest_root(quest_id)
|
|
502
532
|
try:
|
|
503
533
|
entries, meta = self.app.bash_exec_service.read_log_entries(
|
|
@@ -505,6 +535,7 @@ npm --prefix src/ui run build</pre>
|
|
|
505
535
|
bash_id,
|
|
506
536
|
limit=limit,
|
|
507
537
|
before_seq=before_seq,
|
|
538
|
+
after_seq=after_seq,
|
|
508
539
|
order=order,
|
|
509
540
|
)
|
|
510
541
|
except FileNotFoundError:
|
|
@@ -521,15 +552,21 @@ npm --prefix src/ui run build</pre>
|
|
|
521
552
|
|
|
522
553
|
def bash_stop(self, quest_id: str, bash_id: str, body: dict) -> dict | tuple[int, dict]:
|
|
523
554
|
quest_root = self.app.quest_service._quest_root(quest_id)
|
|
555
|
+
wait = bool(body.get("wait"))
|
|
556
|
+
timeout_seconds_raw = body.get("timeout_seconds")
|
|
557
|
+
timeout_seconds = timeout_seconds_raw if isinstance(timeout_seconds_raw, int) and timeout_seconds_raw > 0 else None
|
|
524
558
|
try:
|
|
525
559
|
session = self.app.bash_exec_service.request_stop(
|
|
526
560
|
quest_root,
|
|
527
561
|
bash_id,
|
|
528
562
|
reason=str(body.get("reason") or "").strip() or None,
|
|
529
563
|
user_id="web-react",
|
|
564
|
+
force=bool(body.get("force")),
|
|
530
565
|
)
|
|
531
566
|
except FileNotFoundError:
|
|
532
567
|
return 404, {"success": False, "status": "not_found"}
|
|
568
|
+
if wait:
|
|
569
|
+
session = self.app.bash_exec_service.wait_for_session(quest_root, bash_id, timeout_seconds=timeout_seconds)
|
|
533
570
|
return {
|
|
534
571
|
"success": True,
|
|
535
572
|
"status": session.get("status"),
|
|
@@ -917,7 +954,7 @@ npm --prefix src/ui run build</pre>
|
|
|
917
954
|
def latex_compile(self, project_id: str, folder_id: str, body: dict) -> dict:
|
|
918
955
|
return self.app.latex_service.compile(
|
|
919
956
|
project_id,
|
|
920
|
-
|
|
957
|
+
folder_id,
|
|
921
958
|
compiler=body.get("compiler"),
|
|
922
959
|
main_file_id=body.get("main_file_id"),
|
|
923
960
|
stop_on_first_error=body.get("stop_on_first_error"),
|
|
@@ -931,13 +968,13 @@ npm --prefix src/ui run build</pre>
|
|
|
931
968
|
limit = int(limit_raw)
|
|
932
969
|
except ValueError:
|
|
933
970
|
limit = 10
|
|
934
|
-
return self.app.latex_service.list_builds(project_id,
|
|
971
|
+
return self.app.latex_service.list_builds(project_id, folder_id, limit=limit)
|
|
935
972
|
|
|
936
973
|
def latex_build(self, project_id: str, folder_id: str, build_id: str) -> dict:
|
|
937
|
-
return self.app.latex_service.get_build(project_id,
|
|
974
|
+
return self.app.latex_service.get_build(project_id, folder_id, build_id)
|
|
938
975
|
|
|
939
976
|
def latex_build_pdf(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
|
|
940
|
-
payload, file_name = self.app.latex_service.get_build_pdf(project_id,
|
|
977
|
+
payload, file_name = self.app.latex_service.get_build_pdf(project_id, folder_id, build_id)
|
|
941
978
|
headers = {
|
|
942
979
|
**self._asset_headers("application/pdf"),
|
|
943
980
|
"Content-Disposition": f'inline; filename="{file_name}"',
|
|
@@ -945,11 +982,11 @@ npm --prefix src/ui run build</pre>
|
|
|
945
982
|
return 200, headers, payload
|
|
946
983
|
|
|
947
984
|
def latex_build_log(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
|
|
948
|
-
text = self.app.latex_service.get_build_log_text(project_id,
|
|
985
|
+
text = self.app.latex_service.get_build_log_text(project_id, folder_id, build_id)
|
|
949
986
|
return 200, self._asset_headers("text/plain; charset=utf-8"), text.encode("utf-8")
|
|
950
987
|
|
|
951
988
|
def latex_archive(self, project_id: str, folder_id: str) -> tuple[int, dict, bytes]:
|
|
952
|
-
payload, file_name = self.app.latex_service.create_sources_archive(project_id,
|
|
989
|
+
payload, file_name = self.app.latex_service.create_sources_archive(project_id, folder_id)
|
|
953
990
|
headers = {
|
|
954
991
|
**self._asset_headers("application/zip"),
|
|
955
992
|
"Content-Disposition": f'attachment; filename="{file_name}"',
|
|
@@ -14,8 +14,7 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
|
|
|
14
14
|
("POST", re.compile(r"^/api/admin/shutdown$"), "admin_shutdown"),
|
|
15
15
|
("GET", re.compile(r"^/api/acp/status$"), "acp_status"),
|
|
16
16
|
("GET", re.compile(r"^/api/connectors$"), "connectors"),
|
|
17
|
-
("GET", re.compile(r"^/api/
|
|
18
|
-
("POST", re.compile(r"^/api/bridges/(?P<connector>[^/]+)/webhook$"), "bridge_webhook"),
|
|
17
|
+
("GET", re.compile(r"^/api/connectors/availability$"), "connectors_availability"),
|
|
19
18
|
("GET", re.compile(r"^/api/connectors/qq/bindings$"), "qq_bindings"),
|
|
20
19
|
("POST", re.compile(r"^/api/connectors/qq/inbound$"), "qq_inbound"),
|
|
21
20
|
("GET", re.compile(r"^/api/connectors/(?P<connector>[^/]+)/bindings$"), "connector_bindings"),
|
|
@@ -30,6 +29,7 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
|
|
|
30
29
|
("DELETE", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/baseline-binding$"), "quest_baseline_unbind"),
|
|
31
30
|
("PATCH", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/settings$"), "quest_settings"),
|
|
32
31
|
("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/bindings$"), "quest_bindings"),
|
|
32
|
+
("PUT", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/bindings$"), "quest_bindings"),
|
|
33
33
|
("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/session$"), "quest_session"),
|
|
34
34
|
("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/events$"), "quest_events"),
|
|
35
35
|
("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/artifacts$"), "quest_artifacts"),
|