@researai/deepscientist 1.5.15 → 1.5.17
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 +385 -104
- package/bin/ds.js +1241 -110
- package/docs/en/00_QUICK_START.md +100 -19
- package/docs/en/01_SETTINGS_REFERENCE.md +34 -1
- package/docs/en/02_START_RESEARCH_GUIDE.md +7 -0
- package/docs/en/05_TUI_GUIDE.md +6 -0
- package/docs/en/06_RUNTIME_AND_CANVAS.md +4 -3
- package/docs/en/09_DOCTOR.md +25 -8
- package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
- package/docs/en/15_CODEX_PROVIDER_SETUP.md +37 -11
- package/docs/en/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
- package/docs/en/19_LOCAL_BROWSER_AUTH.md +70 -0
- package/docs/en/20_WORKSPACE_MODES_GUIDE.md +250 -0
- package/docs/en/21_LOCAL_MODEL_BACKENDS_GUIDE.md +283 -0
- package/docs/en/91_DEVELOPMENT.md +237 -0
- package/docs/en/README.md +24 -2
- package/docs/zh/00_QUICK_START.md +89 -19
- package/docs/zh/01_SETTINGS_REFERENCE.md +34 -1
- package/docs/zh/02_START_RESEARCH_GUIDE.md +7 -0
- package/docs/zh/05_TUI_GUIDE.md +6 -0
- package/docs/zh/09_DOCTOR.md +26 -9
- package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
- package/docs/zh/15_CODEX_PROVIDER_SETUP.md +37 -11
- package/docs/zh/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
- package/docs/zh/19_LOCAL_BROWSER_AUTH.md +68 -0
- package/docs/zh/20_WORKSPACE_MODES_GUIDE.md +251 -0
- package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +281 -0
- package/docs/zh/README.md +24 -2
- package/install.sh +46 -4
- package/package.json +2 -1
- package/pyproject.toml +1 -1
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/acp/envelope.py +6 -0
- package/src/deepscientist/artifact/service.py +647 -22
- package/src/deepscientist/bash_exec/service.py +234 -9
- package/src/deepscientist/bridges/connectors.py +8 -2
- package/src/deepscientist/cli.py +115 -19
- package/src/deepscientist/codex_cli_compat.py +367 -22
- package/src/deepscientist/config/models.py +2 -1
- package/src/deepscientist/config/service.py +183 -13
- package/src/deepscientist/daemon/api/handlers.py +255 -31
- package/src/deepscientist/daemon/api/router.py +9 -0
- package/src/deepscientist/daemon/app.py +1146 -105
- package/src/deepscientist/diagnostics/__init__.py +6 -0
- package/src/deepscientist/diagnostics/runner_failures.py +130 -0
- package/src/deepscientist/doctor.py +207 -3
- package/src/deepscientist/gitops/__init__.py +10 -1
- package/src/deepscientist/gitops/diff.py +129 -0
- package/src/deepscientist/gitops/service.py +4 -1
- package/src/deepscientist/mcp/server.py +39 -0
- package/src/deepscientist/prompts/builder.py +275 -34
- package/src/deepscientist/quest/layout.py +15 -2
- package/src/deepscientist/quest/service.py +707 -55
- package/src/deepscientist/quest/stage_views.py +6 -1
- package/src/deepscientist/runners/codex.py +143 -43
- package/src/deepscientist/shared.py +19 -0
- package/src/deepscientist/skills/__init__.py +2 -2
- package/src/deepscientist/skills/installer.py +196 -5
- package/src/deepscientist/skills/registry.py +66 -0
- package/src/prompts/connectors/qq.md +18 -8
- package/src/prompts/connectors/weixin.md +16 -6
- package/src/prompts/contracts/shared_interaction.md +14 -2
- package/src/prompts/system.md +23 -5
- package/src/prompts/system_copilot.md +56 -0
- package/src/skills/analysis-campaign/SKILL.md +1 -0
- package/src/skills/baseline/SKILL.md +8 -0
- package/src/skills/decision/SKILL.md +8 -0
- package/src/skills/experiment/SKILL.md +8 -0
- package/src/skills/figure-polish/SKILL.md +1 -0
- package/src/skills/finalize/SKILL.md +1 -0
- package/src/skills/idea/SKILL.md +1 -0
- package/src/skills/intake-audit/SKILL.md +8 -0
- package/src/skills/mentor/SKILL.md +217 -0
- package/src/skills/mentor/references/correction-rules.md +210 -0
- package/src/skills/mentor/references/knowledge-profile.md +91 -0
- package/src/skills/mentor/references/persona-profile.md +138 -0
- package/src/skills/mentor/references/taste-profile.md +128 -0
- package/src/skills/mentor/references/thought-style-profile.md +138 -0
- package/src/skills/mentor/references/work-profile.md +289 -0
- package/src/skills/mentor/references/workflow-profile.md +240 -0
- package/src/skills/optimize/SKILL.md +1 -0
- package/src/skills/rebuttal/SKILL.md +1 -0
- package/src/skills/review/SKILL.md +1 -0
- package/src/skills/scout/SKILL.md +8 -0
- package/src/skills/write/SKILL.md +1 -0
- package/src/tui/dist/app/AppContainer.js +19 -11
- package/src/tui/dist/index.js +4 -1
- package/src/tui/dist/lib/api.js +33 -3
- package/src/tui/package.json +1 -1
- package/src/ui/dist/assets/AiManusChatView-Bv-Z8YpU.js +204 -0
- package/src/ui/dist/assets/AnalysisPlugin-BCKAfjba.js +1 -0
- package/src/ui/dist/assets/CliPlugin-BCKcpc35.js +109 -0
- package/src/ui/dist/assets/CodeEditorPlugin-DbOfSJ8K.js +2 -0
- package/src/ui/dist/assets/CodeViewerPlugin-CbaFRrUU.js +270 -0
- package/src/ui/dist/assets/DocViewerPlugin-DAjLVeQD.js +7 -0
- package/src/ui/dist/assets/GitCommitViewerPlugin-CIUqbUDO.js +1 -0
- package/src/ui/dist/assets/GitDiffViewerPlugin-CQACjoAA.js +6 -0
- package/src/ui/dist/assets/GitSnapshotViewer-0r4nLPke.js +30 -0
- package/src/ui/dist/assets/ImageViewerPlugin-nBOmI2v_.js +26 -0
- package/src/ui/dist/assets/LabCopilotPanel-BHxOxF4z.js +14 -0
- package/src/ui/dist/assets/LabPlugin-BKoZGs95.js +22 -0
- package/src/ui/dist/assets/LatexPlugin-ZwtV8pIp.js +25 -0
- package/src/ui/dist/assets/MarkdownViewerPlugin-DKqVfKyW.js +128 -0
- package/src/ui/dist/assets/MarketplacePlugin-BwxStZ9D.js +13 -0
- package/src/ui/dist/assets/NotebookEditor-BEQhaQbt.js +81 -0
- package/src/ui/dist/assets/{NotebookEditor-CccQYZjX.css → NotebookEditor-BHH8rdGj.css} +1 -1
- package/src/ui/dist/assets/NotebookEditor-BOr3x3Ej.css +1 -0
- package/src/ui/dist/assets/NotebookEditor-DB9N_T9q.js +361 -0
- package/src/ui/dist/assets/PdfLoader-Cy5jtWrr.css +1 -0
- package/src/ui/dist/assets/PdfLoader-eWBONbQP.js +16 -0
- package/src/ui/dist/assets/PdfMarkdownPlugin-D22YOZL3.js +1 -0
- package/src/ui/dist/assets/PdfViewerPlugin-c-RK9DLM.js +17 -0
- package/src/ui/dist/assets/PdfViewerPlugin-nwwE-fjJ.css +1 -0
- package/src/ui/dist/assets/SearchPlugin-CxF9ytAx.js +16 -0
- package/src/ui/dist/assets/SearchPlugin-DA4en4hK.css +1 -0
- package/src/ui/dist/assets/TextViewerPlugin-C5xqeeUH.js +54 -0
- package/src/ui/dist/assets/VNCViewer-BoLGLnHz.js +11 -0
- package/src/ui/dist/assets/bot-DREQOxzP.js +6 -0
- package/src/ui/dist/assets/browser-CTB2jwNe.js +8 -0
- package/src/ui/dist/assets/chevron-up-C9Qpx4DE.js +6 -0
- package/src/ui/dist/assets/code-WlFHE7z_.js +6 -0
- package/src/ui/dist/assets/file-content-BZMz3RYp.js +1 -0
- package/src/ui/dist/assets/file-diff-panel-CQhw0jS2.js +1 -0
- package/src/ui/dist/assets/file-jump-queue-DA-SdG__.js +1 -0
- package/src/ui/dist/assets/file-socket-CfQPKQKj.js +1 -0
- package/src/ui/dist/assets/git-commit-horizontal-DxZ8DCZh.js +6 -0
- package/src/ui/dist/assets/image-Bgl4VIyx.js +6 -0
- package/src/ui/dist/assets/index-BpV6lusQ.css +33 -0
- package/src/ui/dist/assets/index-CBNVuWcP.js +2496 -0
- package/src/ui/dist/assets/index-CwNu1aH4.js +11 -0
- package/src/ui/dist/assets/index-DrUnlf6K.js +1 -0
- package/src/ui/dist/assets/index-NW-h8VzN.js +1 -0
- package/src/ui/dist/assets/monaco-CiHMMNH_.js +1 -0
- package/src/ui/dist/assets/pdf-effect-queue-J8OnM0jE.js +6 -0
- package/src/ui/dist/assets/plugin-monaco-C8UgLomw.js +19 -0
- package/src/ui/dist/assets/plugin-notebook-HbW2K-1c.js +169 -0
- package/src/ui/dist/assets/plugin-pdf-CR8hgQBV.js +357 -0
- package/src/ui/dist/assets/plugin-terminal-MXFIPun8.js +227 -0
- package/src/ui/dist/assets/popover-CLc0pPP8.js +1 -0
- package/src/ui/dist/assets/project-sync-C9IdzdZW.js +1 -0
- package/src/ui/dist/assets/select-Cs2PmzwL.js +11 -0
- package/src/ui/dist/assets/sigma-ClKcHAXm.js +6 -0
- package/src/ui/dist/assets/trash-DwpbFr3w.js +11 -0
- package/src/ui/dist/assets/useCliAccess-NQ8m0Let.js +1 -0
- package/src/ui/dist/assets/useFileDiffOverlay-FuhcnKiw.js +1 -0
- package/src/ui/dist/assets/wrap-text-BC-Hltpd.js +11 -0
- package/src/ui/dist/assets/zoom-out-E_gaeAxL.js +11 -0
- package/src/ui/dist/index.html +5 -2
- package/src/ui/dist/assets/AiManusChatView-DDjbFnbt.js +0 -26597
- package/src/ui/dist/assets/AnalysisPlugin-Yb5IdmaU.js +0 -123
- package/src/ui/dist/assets/CliPlugin-e64sreyu.js +0 -31037
- package/src/ui/dist/assets/CodeEditorPlugin-C4D2TIkU.js +0 -427
- package/src/ui/dist/assets/CodeViewerPlugin-BVoNZIvC.js +0 -905
- package/src/ui/dist/assets/DocViewerPlugin-CLChbllo.js +0 -278
- package/src/ui/dist/assets/GitDiffViewerPlugin-C4xeFyFQ.js +0 -2661
- package/src/ui/dist/assets/ImageViewerPlugin-OiMUAcLi.js +0 -500
- package/src/ui/dist/assets/LabCopilotPanel-BjD2ThQF.js +0 -4104
- package/src/ui/dist/assets/LabPlugin-DQPg-NrB.js +0 -2677
- package/src/ui/dist/assets/LatexPlugin-CI05XAV9.js +0 -1792
- package/src/ui/dist/assets/MarkdownViewerPlugin-DpeBLYZf.js +0 -308
- package/src/ui/dist/assets/MarketplacePlugin-DolE58Q2.js +0 -413
- package/src/ui/dist/assets/NotebookEditor-7Qm2rSWD.js +0 -4214
- package/src/ui/dist/assets/NotebookEditor-C1kWaxKi.js +0 -84873
- package/src/ui/dist/assets/NotebookEditor-C3VQ7ylN.css +0 -1405
- package/src/ui/dist/assets/PdfLoader-BfOHw8Zw.js +0 -25468
- package/src/ui/dist/assets/PdfLoader-C-Y707R3.css +0 -49
- package/src/ui/dist/assets/PdfMarkdownPlugin-BulDREv1.js +0 -409
- package/src/ui/dist/assets/PdfViewerPlugin-C-daaOaL.js +0 -3095
- package/src/ui/dist/assets/PdfViewerPlugin-DQ11QcSf.css +0 -3627
- package/src/ui/dist/assets/SearchPlugin-CjpaiJ3A.js +0 -741
- package/src/ui/dist/assets/SearchPlugin-DDMrGDkh.css +0 -379
- package/src/ui/dist/assets/TextViewerPlugin-BxIyqPQC.js +0 -472
- package/src/ui/dist/assets/VNCViewer-HAg9mF7M.js +0 -18821
- package/src/ui/dist/assets/awareness-C0NPR2Dj.js +0 -292
- package/src/ui/dist/assets/bot-0DYntytV.js +0 -21
- package/src/ui/dist/assets/browser-BAcuE0Xj.js +0 -2895
- package/src/ui/dist/assets/code-B20Slj_w.js +0 -17
- package/src/ui/dist/assets/file-content-DT24KFma.js +0 -377
- package/src/ui/dist/assets/file-diff-panel-DK13YPql.js +0 -92
- package/src/ui/dist/assets/file-jump-queue-r5XKgJEV.js +0 -16
- package/src/ui/dist/assets/file-socket-B4T2o4nR.js +0 -58
- package/src/ui/dist/assets/function-B5QZkkHC.js +0 -1895
- package/src/ui/dist/assets/image-DSeR_sDS.js +0 -18
- package/src/ui/dist/assets/index-BrFje2Uk.js +0 -120
- package/src/ui/dist/assets/index-BwRJaoTl.js +0 -25
- package/src/ui/dist/assets/index-D_E4281X.js +0 -221322
- package/src/ui/dist/assets/index-DnYB3xb1.js +0 -159
- package/src/ui/dist/assets/index-G7AcWcMu.css +0 -12594
- package/src/ui/dist/assets/monaco-LExaAN3Y.js +0 -623
- package/src/ui/dist/assets/pdf-effect-queue-BJk5okWJ.js +0 -47
- package/src/ui/dist/assets/pdf_viewer-e0g1is2C.js +0 -8206
- package/src/ui/dist/assets/popover-D3Gg_FoV.js +0 -476
- package/src/ui/dist/assets/project-sync-C_ygLlVU.js +0 -297
- package/src/ui/dist/assets/select-CpAK6uWm.js +0 -1690
- package/src/ui/dist/assets/sigma-DEccaSgk.js +0 -22
- package/src/ui/dist/assets/square-check-big-uUfyVsbD.js +0 -17
- package/src/ui/dist/assets/trash-CXvwwSe8.js +0 -32
- package/src/ui/dist/assets/useCliAccess-Bnop4mgR.js +0 -957
- package/src/ui/dist/assets/useFileDiffOverlay-B8eUAX0I.js +0 -53
- package/src/ui/dist/assets/wrap-text-9vbOBpkW.js +0 -35
- package/src/ui/dist/assets/yjs-DncrqiZ8.js +0 -11243
- package/src/ui/dist/assets/zoom-out-BgVMmOW4.js +0 -34
|
@@ -32,6 +32,8 @@ DEFAULT_POLL_INTERVAL_SECONDS = 0.35
|
|
|
32
32
|
TERMINAL_STATUSES = {"completed", "failed", "terminated"}
|
|
33
33
|
DEFAULT_TERMINAL_SESSION_ID = "terminal-main"
|
|
34
34
|
BASH_WATCHDOG_AFTER_SECONDS = 1800
|
|
35
|
+
SUMMARY_RECENT_SESSION_LIMIT = 256
|
|
36
|
+
SUMMARY_RUNNING_SESSION_LIMIT = 64
|
|
35
37
|
INPUT_ESCAPE_SEQUENCE_RE = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b[@-_]")
|
|
36
38
|
|
|
37
39
|
|
|
@@ -373,6 +375,65 @@ class BashExecService:
|
|
|
373
375
|
str(session.get("bash_id") or ""),
|
|
374
376
|
)
|
|
375
377
|
|
|
378
|
+
@classmethod
|
|
379
|
+
def _normalize_summary_session_list(
|
|
380
|
+
cls,
|
|
381
|
+
sessions: Any,
|
|
382
|
+
*,
|
|
383
|
+
limit: int,
|
|
384
|
+
) -> list[dict[str, Any]]:
|
|
385
|
+
max_items = max(0, int(limit or 0))
|
|
386
|
+
if max_items <= 0:
|
|
387
|
+
return []
|
|
388
|
+
normalized: dict[str, dict[str, Any]] = {}
|
|
389
|
+
for raw in sessions or []:
|
|
390
|
+
if not isinstance(raw, dict):
|
|
391
|
+
continue
|
|
392
|
+
compact = cls._summary_session_payload(raw)
|
|
393
|
+
bash_id = _normalize_string(compact.get("bash_id"))
|
|
394
|
+
if not bash_id:
|
|
395
|
+
continue
|
|
396
|
+
normalized[bash_id] = compact
|
|
397
|
+
ordered = sorted(normalized.values(), key=cls._summary_sort_key, reverse=True)
|
|
398
|
+
return ordered[:max_items]
|
|
399
|
+
|
|
400
|
+
@classmethod
|
|
401
|
+
def _merge_summary_session_list(
|
|
402
|
+
cls,
|
|
403
|
+
sessions: Any,
|
|
404
|
+
compact: dict[str, Any],
|
|
405
|
+
*,
|
|
406
|
+
limit: int,
|
|
407
|
+
) -> list[dict[str, Any]]:
|
|
408
|
+
bash_id = _normalize_string(compact.get("bash_id"))
|
|
409
|
+
merged = cls._normalize_summary_session_list(sessions, limit=max(1, int(limit or 0)) + 1)
|
|
410
|
+
merged = [
|
|
411
|
+
item
|
|
412
|
+
for item in merged
|
|
413
|
+
if _normalize_string(item.get("bash_id")) != bash_id
|
|
414
|
+
]
|
|
415
|
+
if bash_id:
|
|
416
|
+
merged.append(cls._summary_session_payload(compact))
|
|
417
|
+
merged.sort(key=cls._summary_sort_key, reverse=True)
|
|
418
|
+
return merged[: max(1, int(limit or 0))]
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def _remove_summary_session(
|
|
422
|
+
cls,
|
|
423
|
+
sessions: Any,
|
|
424
|
+
bash_id: str,
|
|
425
|
+
*,
|
|
426
|
+
limit: int,
|
|
427
|
+
) -> list[dict[str, Any]]:
|
|
428
|
+
normalized_bash_id = _normalize_string(bash_id)
|
|
429
|
+
if not normalized_bash_id:
|
|
430
|
+
return cls._normalize_summary_session_list(sessions, limit=limit)
|
|
431
|
+
return [
|
|
432
|
+
item
|
|
433
|
+
for item in cls._normalize_summary_session_list(sessions, limit=limit)
|
|
434
|
+
if _normalize_string(item.get("bash_id")) != normalized_bash_id
|
|
435
|
+
][: max(1, int(limit or 0))]
|
|
436
|
+
|
|
376
437
|
@staticmethod
|
|
377
438
|
def _is_active_status(value: object) -> bool:
|
|
378
439
|
return _coerce_session_status(value) in {"running", "terminating"}
|
|
@@ -382,9 +443,31 @@ class BashExecService:
|
|
|
382
443
|
"session_count": 0,
|
|
383
444
|
"running_count": 0,
|
|
384
445
|
"latest_session": None,
|
|
446
|
+
"recent_sessions": [],
|
|
447
|
+
"running_sessions": [],
|
|
385
448
|
"updated_at": utc_now(),
|
|
386
449
|
}
|
|
387
450
|
|
|
451
|
+
def _normalize_summary_payload(self, summary: Any) -> dict[str, Any]:
|
|
452
|
+
merged = {**self._default_summary(), **(summary if isinstance(summary, dict) else {})}
|
|
453
|
+
latest_session = merged.get("latest_session")
|
|
454
|
+
if isinstance(latest_session, dict):
|
|
455
|
+
compact_latest = self._summary_session_payload(latest_session)
|
|
456
|
+
merged["latest_session"] = compact_latest if _normalize_string(compact_latest.get("bash_id")) else None
|
|
457
|
+
else:
|
|
458
|
+
merged["latest_session"] = None
|
|
459
|
+
merged["session_count"] = max(0, int(merged.get("session_count") or 0))
|
|
460
|
+
merged["running_count"] = max(0, int(merged.get("running_count") or 0))
|
|
461
|
+
merged["recent_sessions"] = self._normalize_summary_session_list(
|
|
462
|
+
merged.get("recent_sessions"),
|
|
463
|
+
limit=SUMMARY_RECENT_SESSION_LIMIT,
|
|
464
|
+
)
|
|
465
|
+
merged["running_sessions"] = self._normalize_summary_session_list(
|
|
466
|
+
merged.get("running_sessions"),
|
|
467
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
468
|
+
)
|
|
469
|
+
return merged
|
|
470
|
+
|
|
388
471
|
def _refresh_summary_cache(self, quest_root: Path, summary: dict[str, Any]) -> dict[str, Any]:
|
|
389
472
|
path = self.summary_path(quest_root)
|
|
390
473
|
cache_key = str(path.resolve())
|
|
@@ -397,7 +480,7 @@ class BashExecService:
|
|
|
397
480
|
)
|
|
398
481
|
else:
|
|
399
482
|
state = None
|
|
400
|
-
payload =
|
|
483
|
+
payload = self._normalize_summary_payload(summary)
|
|
401
484
|
with self._summary_cache_lock:
|
|
402
485
|
self._summary_cache[cache_key] = {
|
|
403
486
|
"state": state,
|
|
@@ -423,38 +506,109 @@ class BashExecService:
|
|
|
423
506
|
summary = read_json(path, None)
|
|
424
507
|
if not isinstance(summary, dict):
|
|
425
508
|
return None
|
|
426
|
-
merged =
|
|
509
|
+
merged = self._normalize_summary_payload(summary)
|
|
427
510
|
return self._refresh_summary_cache(quest_root, merged)
|
|
428
511
|
|
|
429
512
|
def _write_summary(self, quest_root: Path, summary: dict[str, Any]) -> dict[str, Any]:
|
|
430
|
-
normalized =
|
|
513
|
+
normalized = self._normalize_summary_payload(summary)
|
|
514
|
+
normalized["updated_at"] = utc_now()
|
|
431
515
|
_atomic_write_json(self.summary_path(quest_root), normalized)
|
|
432
516
|
return self._refresh_summary_cache(quest_root, normalized)
|
|
433
517
|
|
|
518
|
+
def _hydrate_summary_from_index(
|
|
519
|
+
self,
|
|
520
|
+
quest_root: Path,
|
|
521
|
+
summary: dict[str, Any],
|
|
522
|
+
) -> dict[str, Any]:
|
|
523
|
+
needs_recent_sessions = not bool(summary.get("recent_sessions"))
|
|
524
|
+
needs_running_sessions = int(summary.get("running_count") or 0) > 0 and not bool(summary.get("running_sessions"))
|
|
525
|
+
if not needs_recent_sessions and not needs_running_sessions:
|
|
526
|
+
return summary
|
|
527
|
+
|
|
528
|
+
index_path = self.index_path(quest_root)
|
|
529
|
+
if not index_path.exists():
|
|
530
|
+
return summary
|
|
531
|
+
|
|
532
|
+
candidate_ids: list[str] = []
|
|
533
|
+
seen_ids: set[str] = set()
|
|
534
|
+
max_candidates = max(SUMMARY_RECENT_SESSION_LIMIT, SUMMARY_RUNNING_SESSION_LIMIT * 4)
|
|
535
|
+
for entry in reversed(read_jsonl(index_path)):
|
|
536
|
+
bash_id = _normalize_string((entry or {}).get("bash_id") if isinstance(entry, dict) else "")
|
|
537
|
+
if not bash_id or bash_id in seen_ids:
|
|
538
|
+
continue
|
|
539
|
+
seen_ids.add(bash_id)
|
|
540
|
+
candidate_ids.append(bash_id)
|
|
541
|
+
if len(candidate_ids) >= max_candidates:
|
|
542
|
+
break
|
|
543
|
+
|
|
544
|
+
if not candidate_ids:
|
|
545
|
+
return summary
|
|
546
|
+
|
|
547
|
+
recent_sessions: list[dict[str, Any]] = []
|
|
548
|
+
running_sessions: list[dict[str, Any]] = []
|
|
549
|
+
for bash_id in candidate_ids:
|
|
550
|
+
meta = read_json(self.meta_path(quest_root, bash_id), {})
|
|
551
|
+
if not isinstance(meta, dict) or not meta:
|
|
552
|
+
continue
|
|
553
|
+
compact = self._summary_session_payload(meta)
|
|
554
|
+
recent_sessions.append(compact)
|
|
555
|
+
if self._is_active_status(meta.get("status")):
|
|
556
|
+
running_sessions.append(compact)
|
|
557
|
+
|
|
558
|
+
if not recent_sessions and not running_sessions:
|
|
559
|
+
return summary
|
|
560
|
+
|
|
561
|
+
updated_summary = dict(summary)
|
|
562
|
+
if needs_recent_sessions and recent_sessions:
|
|
563
|
+
updated_summary["recent_sessions"] = self._normalize_summary_session_list(
|
|
564
|
+
recent_sessions,
|
|
565
|
+
limit=SUMMARY_RECENT_SESSION_LIMIT,
|
|
566
|
+
)
|
|
567
|
+
if updated_summary["recent_sessions"] and not isinstance(updated_summary.get("latest_session"), dict):
|
|
568
|
+
updated_summary["latest_session"] = updated_summary["recent_sessions"][0]
|
|
569
|
+
if needs_running_sessions:
|
|
570
|
+
updated_summary["running_sessions"] = self._normalize_summary_session_list(
|
|
571
|
+
running_sessions,
|
|
572
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
573
|
+
)
|
|
574
|
+
return self._write_summary(quest_root, updated_summary)
|
|
575
|
+
|
|
434
576
|
def _rebuild_summary(self, quest_root: Path) -> dict[str, Any]:
|
|
435
577
|
summary = self._default_summary()
|
|
436
578
|
latest_session: dict[str, Any] | None = None
|
|
437
579
|
session_count = 0
|
|
438
580
|
running_count = 0
|
|
581
|
+
recent_sessions: list[dict[str, Any]] = []
|
|
582
|
+
running_sessions: list[dict[str, Any]] = []
|
|
439
583
|
for meta_path in self.sessions_root(quest_root).glob("*/meta.json"):
|
|
440
584
|
meta = read_json(meta_path, {})
|
|
441
585
|
if not isinstance(meta, dict) or not meta:
|
|
442
586
|
continue
|
|
443
587
|
session_count += 1
|
|
588
|
+
compact = self._summary_session_payload(meta)
|
|
589
|
+
recent_sessions.append(compact)
|
|
444
590
|
if self._is_active_status(meta.get("status")):
|
|
445
591
|
running_count += 1
|
|
446
|
-
|
|
592
|
+
running_sessions.append(compact)
|
|
447
593
|
if latest_session is None or self._summary_sort_key(compact) >= self._summary_sort_key(latest_session):
|
|
448
594
|
latest_session = compact
|
|
449
595
|
summary["session_count"] = session_count
|
|
450
596
|
summary["running_count"] = running_count
|
|
451
597
|
summary["latest_session"] = latest_session
|
|
598
|
+
summary["recent_sessions"] = self._normalize_summary_session_list(
|
|
599
|
+
recent_sessions,
|
|
600
|
+
limit=SUMMARY_RECENT_SESSION_LIMIT,
|
|
601
|
+
)
|
|
602
|
+
summary["running_sessions"] = self._normalize_summary_session_list(
|
|
603
|
+
running_sessions,
|
|
604
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
605
|
+
)
|
|
452
606
|
return self._write_summary(quest_root, summary)
|
|
453
607
|
|
|
454
608
|
def summary(self, quest_root: Path) -> dict[str, Any]:
|
|
455
609
|
loaded = self._load_summary_from_disk(quest_root)
|
|
456
610
|
if loaded is not None:
|
|
457
|
-
return loaded
|
|
611
|
+
return self._hydrate_summary_from_index(quest_root, loaded)
|
|
458
612
|
return self._rebuild_summary(quest_root)
|
|
459
613
|
|
|
460
614
|
def _write_meta(self, quest_root: Path, bash_id: str, meta: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -481,6 +635,23 @@ class BashExecService:
|
|
|
481
635
|
or self._summary_sort_key(compact) >= self._summary_sort_key(latest_session)
|
|
482
636
|
):
|
|
483
637
|
summary["latest_session"] = compact
|
|
638
|
+
summary["recent_sessions"] = self._merge_summary_session_list(
|
|
639
|
+
summary.get("recent_sessions"),
|
|
640
|
+
compact,
|
|
641
|
+
limit=SUMMARY_RECENT_SESSION_LIMIT,
|
|
642
|
+
)
|
|
643
|
+
if new_running:
|
|
644
|
+
summary["running_sessions"] = self._merge_summary_session_list(
|
|
645
|
+
summary.get("running_sessions"),
|
|
646
|
+
compact,
|
|
647
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
648
|
+
)
|
|
649
|
+
else:
|
|
650
|
+
summary["running_sessions"] = self._remove_summary_session(
|
|
651
|
+
summary.get("running_sessions"),
|
|
652
|
+
str(compact.get("bash_id") or ""),
|
|
653
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
654
|
+
)
|
|
484
655
|
return self._write_summary(quest_root, summary)
|
|
485
656
|
|
|
486
657
|
def reconcile_session(self, quest_root: Path, bash_id: str) -> dict[str, Any]:
|
|
@@ -540,10 +711,59 @@ class BashExecService:
|
|
|
540
711
|
normalized_agent_instance_ids = {item for item in (agent_instance_ids or []) if item}
|
|
541
712
|
normalized_agent_ids = {item for item in (agent_ids or []) if item}
|
|
542
713
|
normalized_chat_session = _normalize_string(chat_session_id)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
714
|
+
summary = self.summary(quest_root)
|
|
715
|
+
if normalized_status in {"running", "terminating"} and int(summary.get("running_count") or 0) <= 0:
|
|
716
|
+
return []
|
|
717
|
+
can_use_summary_fast_path = (
|
|
718
|
+
not normalized_agent_instance_ids
|
|
719
|
+
and not normalized_agent_ids
|
|
720
|
+
and not normalized_chat_session
|
|
721
|
+
)
|
|
722
|
+
if can_use_summary_fast_path:
|
|
723
|
+
candidate_compacts: list[dict[str, Any]] | None = None
|
|
724
|
+
if normalized_status in {"running", "terminating"}:
|
|
725
|
+
running_sessions = self._normalize_summary_session_list(
|
|
726
|
+
summary.get("running_sessions"),
|
|
727
|
+
limit=SUMMARY_RUNNING_SESSION_LIMIT,
|
|
728
|
+
)
|
|
729
|
+
filtered_running = [
|
|
730
|
+
item
|
|
731
|
+
for item in running_sessions
|
|
732
|
+
if (not normalized_kind or _normalize_string(item.get("kind")).lower() == normalized_kind)
|
|
733
|
+
and (not normalized_status or _normalize_string(item.get("status")).lower() == normalized_status)
|
|
734
|
+
]
|
|
735
|
+
running_count = int(summary.get("running_count") or 0)
|
|
736
|
+
if len(filtered_running) >= max(1, limit) or running_count <= len(running_sessions):
|
|
737
|
+
candidate_compacts = filtered_running
|
|
738
|
+
elif not normalized_status:
|
|
739
|
+
recent_sessions = self._normalize_summary_session_list(
|
|
740
|
+
summary.get("recent_sessions"),
|
|
741
|
+
limit=SUMMARY_RECENT_SESSION_LIMIT,
|
|
742
|
+
)
|
|
743
|
+
if not normalized_kind:
|
|
744
|
+
if len(recent_sessions) >= max(1, limit) or int(summary.get("session_count") or 0) <= len(recent_sessions):
|
|
745
|
+
candidate_compacts = recent_sessions
|
|
746
|
+
else:
|
|
747
|
+
filtered_recent = [
|
|
748
|
+
item
|
|
749
|
+
for item in recent_sessions
|
|
750
|
+
if _normalize_string(item.get("kind")).lower() == normalized_kind
|
|
751
|
+
]
|
|
752
|
+
if len(filtered_recent) >= max(1, limit) or int(summary.get("session_count") or 0) <= len(recent_sessions):
|
|
753
|
+
candidate_compacts = filtered_recent
|
|
754
|
+
if candidate_compacts is not None:
|
|
755
|
+
resolved_sessions: list[dict[str, Any]] = []
|
|
756
|
+
for compact in candidate_compacts:
|
|
757
|
+
bash_id = _normalize_string(compact.get("bash_id"))
|
|
758
|
+
if not bash_id:
|
|
759
|
+
continue
|
|
760
|
+
try:
|
|
761
|
+
resolved_sessions.append(self.reconcile_session(quest_root, bash_id))
|
|
762
|
+
except FileNotFoundError:
|
|
763
|
+
continue
|
|
764
|
+
if len(resolved_sessions) >= max(1, limit):
|
|
765
|
+
break
|
|
766
|
+
return resolved_sessions[: max(1, limit)]
|
|
547
767
|
sessions: list[dict[str, Any]] = []
|
|
548
768
|
for bash_id in self._list_session_ids(quest_root):
|
|
549
769
|
try:
|
|
@@ -571,6 +791,11 @@ class BashExecService:
|
|
|
571
791
|
if self.meta_path(quest_root, normalized).exists():
|
|
572
792
|
return normalized
|
|
573
793
|
raise FileNotFoundError(f"Unknown bash session `{normalized}`.")
|
|
794
|
+
summary = self.summary(quest_root)
|
|
795
|
+
latest_session = summary.get("latest_session")
|
|
796
|
+
latest_bash_id = _normalize_string((latest_session or {}).get("bash_id") if isinstance(latest_session, dict) else "")
|
|
797
|
+
if latest_bash_id and self.meta_path(quest_root, latest_bash_id).exists():
|
|
798
|
+
return latest_bash_id
|
|
574
799
|
sessions = self.list_sessions(quest_root, limit=1)
|
|
575
800
|
if not sessions:
|
|
576
801
|
raise FileNotFoundError("No bash session found.")
|
|
@@ -1021,8 +1021,13 @@ class WeixinConnectorBridge(BaseConnectorBridge):
|
|
|
1021
1021
|
@classmethod
|
|
1022
1022
|
def _retry_delays_for_item(cls, item: dict[str, Any], exc: Exception) -> tuple[float, ...]:
|
|
1023
1023
|
message = str(exc or "").strip().lower()
|
|
1024
|
-
if "ret=-2" in message
|
|
1024
|
+
if "ret=-2" not in message:
|
|
1025
|
+
return ()
|
|
1026
|
+
item_type = cls._item_type(item)
|
|
1027
|
+
if item_type in {4, 5}:
|
|
1025
1028
|
return cls._MEDIA_SEND_RETRY_DELAYS_SECONDS
|
|
1029
|
+
if item_type == 1:
|
|
1030
|
+
return cls._TEXT_SEND_RETRY_DELAYS_SECONDS
|
|
1026
1031
|
return ()
|
|
1027
1032
|
|
|
1028
1033
|
def _send_items(
|
|
@@ -1044,7 +1049,8 @@ class WeixinConnectorBridge(BaseConnectorBridge):
|
|
|
1044
1049
|
if media_item:
|
|
1045
1050
|
time.sleep(self._MEDIA_SEND_INITIAL_DELAY_SECONDS)
|
|
1046
1051
|
retry_delays: tuple[float, ...] = ()
|
|
1047
|
-
|
|
1052
|
+
max_retries = max(len(self._TEXT_SEND_RETRY_DELAYS_SECONDS), len(self._MEDIA_SEND_RETRY_DELAYS_SECONDS))
|
|
1053
|
+
for attempt in range(1 + max_retries):
|
|
1048
1054
|
client_id = self._next_client_id()
|
|
1049
1055
|
try:
|
|
1050
1056
|
send_weixin_message(
|
package/src/deepscientist/cli.py
CHANGED
|
@@ -25,23 +25,78 @@ from .registries import BaselineRegistry
|
|
|
25
25
|
from .runners import CodexRunner, RunRequest, get_runner_factory, register_builtin_runners
|
|
26
26
|
from .runtime_tools import RuntimeToolService
|
|
27
27
|
from .runtime_logs import JsonlLogger
|
|
28
|
-
from .shared import ensure_dir, read_yaml
|
|
28
|
+
from .shared import ensure_dir, read_json, read_yaml
|
|
29
29
|
from .skills import SkillInstaller
|
|
30
30
|
from .tui import watch_tui
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
class DeepScientistArgumentParser(argparse.ArgumentParser):
|
|
34
|
+
def error(self, message: str) -> None:
|
|
35
|
+
self.print_usage(sys.stderr)
|
|
36
|
+
self.exit(2, f"DeepScientist argument error: {message}\nRun `{self.prog} --help` for usage.\n")
|
|
37
|
+
|
|
38
|
+
|
|
33
39
|
def _local_ui_url(host: str, port: int) -> str:
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
normalized = str(host or "").strip()
|
|
41
|
+
connect_host = "127.0.0.1" if normalized in {"0.0.0.0", "::", "[::]", ""} else normalized
|
|
42
|
+
if connect_host.startswith("[") and connect_host.endswith("]"):
|
|
43
|
+
rendered_host = connect_host
|
|
44
|
+
elif ":" in connect_host:
|
|
45
|
+
rendered_host = f"[{connect_host}]"
|
|
46
|
+
else:
|
|
47
|
+
rendered_host = connect_host
|
|
48
|
+
return f"http://{rendered_host}:{port}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _parse_optional_bool(value: object) -> bool | None:
|
|
52
|
+
if isinstance(value, bool):
|
|
53
|
+
return value
|
|
54
|
+
normalized = str(value or "").strip().lower()
|
|
55
|
+
if not normalized:
|
|
56
|
+
return None
|
|
57
|
+
if normalized in {"1", "true", "yes", "on"}:
|
|
58
|
+
return True
|
|
59
|
+
if normalized in {"0", "false", "no", "off"}:
|
|
60
|
+
return False
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _daemon_request_headers(home: Path) -> dict[str, str]:
|
|
65
|
+
headers = {"Content-Type": "application/json"}
|
|
66
|
+
state = read_json(home / "runtime" / "daemon.json", {})
|
|
67
|
+
if not isinstance(state, dict):
|
|
68
|
+
return headers
|
|
69
|
+
if bool(state.get("auth_enabled")):
|
|
70
|
+
token = str(state.get("auth_token") or "").strip()
|
|
71
|
+
if token:
|
|
72
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
73
|
+
return headers
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _daemon_launch_url(home: Path, *, host: str, port: int) -> str:
|
|
77
|
+
state = read_json(home / "runtime" / "daemon.json", {})
|
|
78
|
+
if isinstance(state, dict):
|
|
79
|
+
launch_url = str(state.get("launch_url") or "").strip()
|
|
80
|
+
if launch_url:
|
|
81
|
+
return launch_url
|
|
82
|
+
return _local_ui_url(host, port)
|
|
36
83
|
|
|
37
84
|
|
|
38
85
|
def build_parser() -> argparse.ArgumentParser:
|
|
39
|
-
parser =
|
|
86
|
+
parser = DeepScientistArgumentParser(
|
|
87
|
+
prog="ds",
|
|
88
|
+
description="DeepScientist Core skeleton",
|
|
89
|
+
allow_abbrev=False,
|
|
90
|
+
)
|
|
40
91
|
parser.add_argument("--home", default=None, help="Override DeepScientist home")
|
|
41
92
|
parser.add_argument("--proxy", default=None, help="Explicit outbound HTTP/WS proxy, for example `http://127.0.0.1:7890`.")
|
|
42
93
|
parser.add_argument("--codex", default=None, help="Override the Codex executable path for this invocation.")
|
|
43
94
|
|
|
44
|
-
subparsers = parser.add_subparsers(
|
|
95
|
+
subparsers = parser.add_subparsers(
|
|
96
|
+
dest="command",
|
|
97
|
+
required=True,
|
|
98
|
+
parser_class=DeepScientistArgumentParser,
|
|
99
|
+
)
|
|
45
100
|
|
|
46
101
|
subparsers.add_parser("init")
|
|
47
102
|
|
|
@@ -60,12 +115,24 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
60
115
|
daemon_parser = subparsers.add_parser("daemon")
|
|
61
116
|
daemon_parser.add_argument("--host", default=None)
|
|
62
117
|
daemon_parser.add_argument("--port", type=int, default=None)
|
|
118
|
+
daemon_parser.add_argument("--auth", default=None)
|
|
119
|
+
daemon_parser.add_argument("--auth-token", default=None)
|
|
120
|
+
daemon_parser.add_argument(
|
|
121
|
+
"--prompt-version",
|
|
122
|
+
default=None,
|
|
123
|
+
help="Use `latest` managed prompts, an official historical prompt version such as `1.5.13`, or an exact backup id from `.codex/prompt_versions/` for this daemon session.",
|
|
124
|
+
)
|
|
63
125
|
|
|
64
126
|
run_parser = subparsers.add_parser("run")
|
|
65
127
|
run_parser.add_argument("skill_id")
|
|
66
128
|
run_parser.add_argument("--quest-id", required=True)
|
|
67
129
|
run_parser.add_argument("--message", required=True)
|
|
68
130
|
run_parser.add_argument("--model", default=None)
|
|
131
|
+
run_parser.add_argument(
|
|
132
|
+
"--prompt-version",
|
|
133
|
+
default=None,
|
|
134
|
+
help="Use `latest` managed prompts, an official historical prompt version such as `1.5.13`, or an exact backup id from `.codex/prompt_versions/` for this one-off run.",
|
|
135
|
+
)
|
|
69
136
|
|
|
70
137
|
ui_parser = subparsers.add_parser("ui")
|
|
71
138
|
ui_parser.add_argument("--mode", choices=("web", "tui", "both"), default="web")
|
|
@@ -186,7 +253,6 @@ def resume_command(home: Path, quest_id: str) -> int:
|
|
|
186
253
|
print(json.dumps(snapshot, ensure_ascii=False, indent=2))
|
|
187
254
|
return 0
|
|
188
255
|
|
|
189
|
-
|
|
190
256
|
def _daemon_control_quest(home: Path, quest_id: str, *, action: str) -> dict | None:
|
|
191
257
|
config = ConfigManager(home).load_named("config", create_optional=False)
|
|
192
258
|
ui_config = config.get("ui", {})
|
|
@@ -194,7 +260,7 @@ def _daemon_control_quest(home: Path, quest_id: str, *, action: str) -> dict | N
|
|
|
194
260
|
request = Request(
|
|
195
261
|
url,
|
|
196
262
|
data=json.dumps({"action": action, "source": "cli"}).encode("utf-8"),
|
|
197
|
-
headers=
|
|
263
|
+
headers=_daemon_request_headers(home),
|
|
198
264
|
method="POST",
|
|
199
265
|
)
|
|
200
266
|
try:
|
|
@@ -211,7 +277,7 @@ def _daemon_create_quest(home: Path, *, goal: str, quest_id: str | None) -> dict
|
|
|
211
277
|
request = Request(
|
|
212
278
|
url,
|
|
213
279
|
data=json.dumps({"goal": goal, "quest_id": quest_id, "source": "cli"}).encode("utf-8"),
|
|
214
|
-
headers=
|
|
280
|
+
headers=_daemon_request_headers(home),
|
|
215
281
|
method="POST",
|
|
216
282
|
)
|
|
217
283
|
try:
|
|
@@ -221,18 +287,37 @@ def _daemon_create_quest(home: Path, *, goal: str, quest_id: str | None) -> dict
|
|
|
221
287
|
return None
|
|
222
288
|
|
|
223
289
|
|
|
224
|
-
def daemon_command(
|
|
290
|
+
def daemon_command(
|
|
291
|
+
home: Path,
|
|
292
|
+
host: str | None,
|
|
293
|
+
port: int | None,
|
|
294
|
+
auth: str | None,
|
|
295
|
+
auth_token: str | None,
|
|
296
|
+
prompt_version: str | None,
|
|
297
|
+
) -> int:
|
|
225
298
|
ensure_home_layout(home)
|
|
226
299
|
config_manager = ConfigManager(home)
|
|
227
300
|
config_manager.ensure_files()
|
|
228
301
|
config = config_manager.load_named("config")
|
|
229
302
|
ui_config = config.get("ui", {})
|
|
230
|
-
daemon = DaemonApp(
|
|
303
|
+
daemon = DaemonApp(
|
|
304
|
+
home,
|
|
305
|
+
browser_auth_enabled=_parse_optional_bool(auth),
|
|
306
|
+
browser_auth_token=str(auth_token or "").strip() or None,
|
|
307
|
+
prompt_version_selection=str(prompt_version or "").strip() or None,
|
|
308
|
+
)
|
|
231
309
|
daemon.serve(host or ui_config.get("host", "0.0.0.0"), port or ui_config.get("port", 20999))
|
|
232
310
|
return 0
|
|
233
311
|
|
|
234
312
|
|
|
235
|
-
def run_command(
|
|
313
|
+
def run_command(
|
|
314
|
+
home: Path,
|
|
315
|
+
quest_id: str,
|
|
316
|
+
skill_id: str,
|
|
317
|
+
message: str,
|
|
318
|
+
model: str | None,
|
|
319
|
+
prompt_version: str | None,
|
|
320
|
+
) -> int:
|
|
236
321
|
ensure_home_layout(home)
|
|
237
322
|
config_manager = ConfigManager(home)
|
|
238
323
|
config_manager.ensure_files()
|
|
@@ -246,7 +331,11 @@ def run_command(home: Path, quest_id: str, skill_id: str, message: str, model: s
|
|
|
246
331
|
repo_root=repo_root(),
|
|
247
332
|
binary=codex_cfg.get("binary", "codex"),
|
|
248
333
|
logger=logger,
|
|
249
|
-
prompt_builder=PromptBuilder(
|
|
334
|
+
prompt_builder=PromptBuilder(
|
|
335
|
+
repo_root(),
|
|
336
|
+
home,
|
|
337
|
+
prompt_version_selection=str(prompt_version or "").strip() or None,
|
|
338
|
+
),
|
|
250
339
|
artifact_service=ArtifactService(home),
|
|
251
340
|
)
|
|
252
341
|
register_builtin_runners(codex_runner=runner)
|
|
@@ -322,19 +411,26 @@ def launch_ink_tui(home: Path, url: str) -> int:
|
|
|
322
411
|
)
|
|
323
412
|
)
|
|
324
413
|
return 1
|
|
325
|
-
|
|
414
|
+
state = read_json(home / "runtime" / "daemon.json", {})
|
|
415
|
+
args = [node_binary, str(entry), "--base-url", url]
|
|
416
|
+
if isinstance(state, dict) and bool(state.get("auth_enabled")):
|
|
417
|
+
token = str(state.get("auth_token") or "").strip()
|
|
418
|
+
if token:
|
|
419
|
+
args.extend(["--auth-token", token])
|
|
420
|
+
return subprocess.call(args)
|
|
326
421
|
|
|
327
422
|
|
|
328
423
|
def ui_command(home: Path, mode: str) -> int:
|
|
329
424
|
config = ConfigManager(home).load_named("config", create_optional=False)
|
|
330
425
|
host = config.get("ui", {}).get("host", "0.0.0.0")
|
|
331
426
|
port = config.get("ui", {}).get("port", 20999)
|
|
332
|
-
|
|
427
|
+
base_url = _local_ui_url(str(host), int(port))
|
|
428
|
+
launch_url = _daemon_launch_url(home, host=str(host), port=int(port))
|
|
333
429
|
if mode in {"web", "both"}:
|
|
334
|
-
webbrowser.open(
|
|
335
|
-
print(f"Opened {
|
|
430
|
+
webbrowser.open(launch_url)
|
|
431
|
+
print(f"Opened {launch_url}")
|
|
336
432
|
if mode in {"tui", "both"}:
|
|
337
|
-
return launch_ink_tui(home,
|
|
433
|
+
return launch_ink_tui(home, base_url)
|
|
338
434
|
return 0
|
|
339
435
|
|
|
340
436
|
|
|
@@ -492,9 +588,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
492
588
|
if args.command == "resume":
|
|
493
589
|
return resume_command(home, args.quest_id)
|
|
494
590
|
if args.command == "daemon":
|
|
495
|
-
return daemon_command(home, args.host, args.port)
|
|
591
|
+
return daemon_command(home, args.host, args.port, args.auth, args.auth_token, args.prompt_version)
|
|
496
592
|
if args.command == "run":
|
|
497
|
-
return run_command(home, args.quest_id, args.skill_id, args.message, args.model)
|
|
593
|
+
return run_command(home, args.quest_id, args.skill_id, args.message, args.model, args.prompt_version)
|
|
498
594
|
if args.command == "ui":
|
|
499
595
|
return ui_command(home, args.mode)
|
|
500
596
|
if args.command == "note":
|