@researai/deepscientist 1.5.5 → 1.5.7
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 +28 -0
- package/bin/ds.js +54 -13
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/cli.py +1 -1
- package/src/deepscientist/config/models.py +6 -6
- package/src/deepscientist/config/service.py +5 -0
- package/src/deepscientist/connector_profiles.py +34 -6
- package/src/deepscientist/daemon/api/handlers.py +6 -1
- package/src/deepscientist/daemon/api/router.py +1 -0
- package/src/deepscientist/daemon/app.py +230 -3
- package/src/deepscientist/prompts/builder.py +21 -5
- package/src/deepscientist/qq_profiles.py +19 -9
- package/src/deepscientist/quest/layout.py +1 -0
- package/src/deepscientist/quest/service.py +2 -2
- package/src/deepscientist/runners/codex.py +31 -13
- package/src/deepscientist/runners/runtime_overrides.py +46 -0
- package/src/deepscientist/skills/installer.py +7 -0
- package/src/prompts/connectors/qq.md +3 -0
- package/src/prompts/system.md +32 -1
- package/src/skills/analysis-campaign/SKILL.md +30 -0
- package/src/skills/analysis-campaign/references/campaign-checklist-template.md +41 -0
- package/src/skills/analysis-campaign/references/campaign-plan-template.md +68 -0
- package/src/skills/baseline/SKILL.md +105 -5
- package/src/skills/baseline/references/baseline-checklist-template.md +57 -0
- package/src/skills/baseline/references/baseline-plan-template.md +103 -0
- package/src/skills/experiment/SKILL.md +30 -0
- package/src/skills/experiment/references/main-experiment-checklist-template.md +52 -0
- package/src/skills/experiment/references/main-experiment-plan-template.md +77 -0
- package/src/tui/package.json +1 -1
- package/src/ui/dist/assets/{AiManusChatView-BGLArZRn.js → AiManusChatView-BS3V4ZOk.js} +11 -11
- package/src/ui/dist/assets/{AnalysisPlugin-BgDGSigG.js → AnalysisPlugin-DLPXQsmr.js} +1 -1
- package/src/ui/dist/assets/{AutoFigurePlugin-B65HD7L4.js → AutoFigurePlugin-C-Fr9knQ.js} +5 -5
- package/src/ui/dist/assets/{CliPlugin-CUqgsFHC.js → CliPlugin-Dd8AHzFg.js} +9 -9
- package/src/ui/dist/assets/{CodeEditorPlugin-CF5EdvaS.js → CodeEditorPlugin-Dg-RepTl.js} +8 -8
- package/src/ui/dist/assets/{CodeViewerPlugin-DEeU063D.js → CodeViewerPlugin-D2J_3nyt.js} +5 -5
- package/src/ui/dist/assets/{DocViewerPlugin-Df-FuDlZ.js → DocViewerPlugin-ChRLLKNb.js} +3 -3
- package/src/ui/dist/assets/{GitDiffViewerPlugin-RAnNaRxM.js → GitDiffViewerPlugin-DgHfcved.js} +1 -1
- package/src/ui/dist/assets/{ImageViewerPlugin-DXJ0ZJGg.js → ImageViewerPlugin-C89GZMBy.js} +5 -5
- package/src/ui/dist/assets/{LabCopilotPanel-BlO-sKsj.js → LabCopilotPanel-BUfIwUcb.js} +10 -10
- package/src/ui/dist/assets/{LabPlugin-BajPZW5v.js → LabPlugin-zvUmQUMq.js} +1 -1
- package/src/ui/dist/assets/{LatexPlugin-F1OEol8D.js → LatexPlugin-C1SSNuWp.js} +7 -7
- package/src/ui/dist/assets/{MarkdownViewerPlugin-MhUupqwT.js → MarkdownViewerPlugin-D2Mf5tU5.js} +4 -4
- package/src/ui/dist/assets/{MarketplacePlugin-DxhIEsv0.js → MarketplacePlugin-CF4LgiS2.js} +3 -3
- package/src/ui/dist/assets/{NotebookEditor-q7TkhewC.js → NotebookEditor-BM7Bgwlv.js} +1 -1
- package/src/ui/dist/assets/{PdfLoader-B8ZOTKFc.js → PdfLoader-Bc5qfD-Z.js} +1 -1
- package/src/ui/dist/assets/{PdfMarkdownPlugin-xFPvzvWh.js → PdfMarkdownPlugin-sh1-IRcp.js} +3 -3
- package/src/ui/dist/assets/{PdfViewerPlugin-EjEcsIB8.js → PdfViewerPlugin-C_a7CpWG.js} +10 -10
- package/src/ui/dist/assets/{SearchPlugin-ixY-1lgW.js → SearchPlugin-L4z3HcLf.js} +1 -1
- package/src/ui/dist/assets/{Stepper-gYFK2Pgz.js → Stepper-Dk4aQ3fN.js} +1 -1
- package/src/ui/dist/assets/{TextViewerPlugin-Cym6pv_n.js → TextViewerPlugin-BsNtlKVo.js} +4 -4
- package/src/ui/dist/assets/{VNCViewer-BPmIHcmK.js → VNCViewer-BpeDcZ5_.js} +9 -9
- package/src/ui/dist/assets/{bibtex-Btv6Wi7f.js → bibtex-C4QI-bbj.js} +1 -1
- package/src/ui/dist/assets/{code-BlG7g85c.js → code-DuMINRsg.js} +1 -1
- package/src/ui/dist/assets/{file-content-DBT5OfTZ.js → file-content-C3N-432K.js} +1 -1
- package/src/ui/dist/assets/{file-diff-panel-BWXYzqHk.js → file-diff-panel-CffQ4ZMg.js} +1 -1
- package/src/ui/dist/assets/{file-socket-wDlx6byM.js → file-socket-CRH59PCO.js} +1 -1
- package/src/ui/dist/assets/{file-utils-Ba3nJmH0.js → file-utils-vYGtW2mI.js} +1 -1
- package/src/ui/dist/assets/{image-BwtCyguk.js → image-DBVGaooo.js} +1 -1
- package/src/ui/dist/assets/{index-Bz5AaWL7.js → index-B1P6hQRJ.js} +166 -32
- package/src/ui/dist/assets/{index-B-2scqCJ.js → index-Be0NAmh8.js} +11 -11
- package/src/ui/dist/assets/{index-DcqvKzeJ.js → index-BpjYH9Vg.js} +1 -1
- package/src/ui/dist/assets/{index-CfRpE209.js → index-DjSFDmgB.js} +2 -2
- package/src/ui/dist/assets/{index-DpMZw8aM.css → index-Do9N28uB.css} +1 -1
- package/src/ui/dist/assets/{message-square-BnlyWVH0.js → message-square-BsPDBhiY.js} +1 -1
- package/src/ui/dist/assets/{monaco-CXe0pAVe.js → monaco-BTkdPojV.js} +1 -1
- package/src/ui/dist/assets/{popover-BCHmVhHj.js → popover-cWjCk-vc.js} +1 -1
- package/src/ui/dist/assets/{project-sync-Brk6kaOD.js → project-sync-CXn530xb.js} +1 -1
- package/src/ui/dist/assets/{sigma-D72eSUep.js → sigma-04Jr12jg.js} +1 -1
- package/src/ui/dist/assets/{tooltip-BMWd0dqX.js → tooltip-BdVDl0G5.js} +1 -1
- package/src/ui/dist/assets/{trash-BIt_eWIS.js → trash-CB_GlQyC.js} +1 -1
- package/src/ui/dist/assets/{useCliAccess-N1hkTRrR.js → useCliAccess-BL932NwS.js} +1 -1
- package/src/ui/dist/assets/{useFileDiffOverlay-DPRPv6rv.js → useFileDiffOverlay-B2WK7Tvq.js} +1 -1
- package/src/ui/dist/assets/{wrap-text-E5-UheyP.js → wrap-text-YC68g12z.js} +1 -1
- package/src/ui/dist/assets/{zoom-out-D4TR-ZZ_.js → zoom-out-C0RJvFiJ.js} +1 -1
- package/src/ui/dist/index.html +2 -2
package/README.md
CHANGED
|
@@ -25,6 +25,20 @@ ds
|
|
|
25
25
|
|
|
26
26
|
DeepScientist starts the local web workspace at `http://127.0.0.1:20999` by default.
|
|
27
27
|
|
|
28
|
+
By default, DeepScientist keeps Codex on the standard profile: `approval_policy=on-request` and `sandbox_mode=workspace-write`. Use `--yolo` only when you want explicit full-access execution.
|
|
29
|
+
|
|
30
|
+
Recommended command when you want the current directory as the home and Codex full-access execution:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ds --yolo --port 20999 --here
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Parameter meanings:
|
|
37
|
+
|
|
38
|
+
- `--yolo`: run Codex in YOLO mode, which sets `approval_policy=never` and `sandbox_mode=danger-full-access`
|
|
39
|
+
- `--port 20999`: bind the local web workspace to port `20999`
|
|
40
|
+
- `--here`: use the current working directory as the DeepScientist home
|
|
41
|
+
|
|
28
42
|
On first start, `ds` will:
|
|
29
43
|
|
|
30
44
|
- bootstrap a local `uv` runtime manager automatically if your machine does not already have one
|
|
@@ -37,6 +51,12 @@ If you want another port:
|
|
|
37
51
|
ds --port 21000
|
|
38
52
|
```
|
|
39
53
|
|
|
54
|
+
If you want YOLO mode on another port:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ds --yolo --port 21000
|
|
58
|
+
```
|
|
59
|
+
|
|
40
60
|
If you want to bind on all interfaces:
|
|
41
61
|
|
|
42
62
|
```bash
|
|
@@ -60,6 +80,14 @@ ds --here
|
|
|
60
80
|
|
|
61
81
|
This is equivalent to launching with `ds --home "$PWD"`.
|
|
62
82
|
|
|
83
|
+
Useful launch examples:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ds --yolo --port 20999 --here
|
|
87
|
+
ds --host 0.0.0.0 --port 21000
|
|
88
|
+
ds --yolo --host 0.0.0.0 --port 21000 --here
|
|
89
|
+
```
|
|
90
|
+
|
|
63
91
|
If you want to install the bundled CLI tree into another base path from a source checkout:
|
|
64
92
|
|
|
65
93
|
```bash
|
package/bin/ds.js
CHANGED
|
@@ -38,6 +38,15 @@ const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
|
|
|
38
38
|
|
|
39
39
|
const optionsWithValues = new Set(['--home', '--host', '--port', '--quest-id', '--mode', '--proxy']);
|
|
40
40
|
|
|
41
|
+
function buildCodexOverrideEnv({ yolo = false } = {}) {
|
|
42
|
+
if (!yolo) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
DEEPSCIENTIST_CODEX_YOLO: '1',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
function printLauncherHelp() {
|
|
42
51
|
console.log(`DeepScientist launcher
|
|
43
52
|
|
|
@@ -48,6 +57,7 @@ Usage:
|
|
|
48
57
|
ds update --yes
|
|
49
58
|
ds migrate /data/DeepScientist
|
|
50
59
|
ds --here
|
|
60
|
+
ds --yolo --port 20999 --here
|
|
51
61
|
ds --here doctor
|
|
52
62
|
ds --tui
|
|
53
63
|
ds --both
|
|
@@ -73,6 +83,7 @@ Launcher flags:
|
|
|
73
83
|
--home <path> Use a custom DeepScientist home
|
|
74
84
|
--here Use the current working directory as DeepScientist home
|
|
75
85
|
--proxy <url> Use an outbound HTTP/WS proxy for npm and Python runtime traffic
|
|
86
|
+
--yolo Run Codex in YOLO mode: approval_policy=never and sandbox_mode=danger-full-access
|
|
76
87
|
--quest-id <id> Open the TUI on one quest directly
|
|
77
88
|
|
|
78
89
|
Update:
|
|
@@ -581,13 +592,14 @@ function pythonVersionText(probe) {
|
|
|
581
592
|
return version;
|
|
582
593
|
}
|
|
583
594
|
|
|
584
|
-
function renderLaunchHints({ home, url, bindUrl, pythonSelection }) {
|
|
595
|
+
function renderLaunchHints({ home, url, bindUrl, pythonSelection, yolo }) {
|
|
585
596
|
const runtimeRows = [
|
|
586
597
|
['Version', packageJson.version],
|
|
587
598
|
['Home', truncateMiddle(home)],
|
|
588
599
|
['Browser URL', url],
|
|
589
600
|
['Bind URL', bindUrl],
|
|
590
601
|
['Python', truncateMiddle(pythonVersionText(pythonSelection))],
|
|
602
|
+
['Codex mode', yolo ? 'YOLO (never + danger-full-access)' : 'Default (on-request + workspace-write)'],
|
|
591
603
|
];
|
|
592
604
|
if (pythonSelection && pythonSelection.sourceLabel) {
|
|
593
605
|
runtimeRows.push(['Python source', pythonSelection.sourceLabel]);
|
|
@@ -598,6 +610,7 @@ function renderLaunchHints({ home, url, bindUrl, pythonSelection }) {
|
|
|
598
610
|
|
|
599
611
|
console.log(colorize('\u001B[1;38;5;39m', 'Quick Flags'));
|
|
600
612
|
renderKeyValueRows([
|
|
613
|
+
['ds --yolo --port 20999 --here', 'Start in the current directory with YOLO Codex access'],
|
|
601
614
|
['ds --port 21000', 'Change the web port'],
|
|
602
615
|
['ds --host 0.0.0.0 --port 21000', 'Bind on all interfaces'],
|
|
603
616
|
['ds --here', 'Use the current directory as home'],
|
|
@@ -622,6 +635,7 @@ function printLaunchCard({
|
|
|
622
635
|
daemonOnly,
|
|
623
636
|
home,
|
|
624
637
|
pythonSelection,
|
|
638
|
+
yolo,
|
|
625
639
|
}) {
|
|
626
640
|
const width = Math.max(72, Math.min(process.stdout.columns || 100, 108));
|
|
627
641
|
const divider = colorize('\u001B[38;5;245m', '─'.repeat(Math.max(36, width - 6)));
|
|
@@ -680,7 +694,7 @@ function printLaunchCard({
|
|
|
680
694
|
console.log(centerText('Run ds --stop to stop the managed daemon.', width));
|
|
681
695
|
console.log(centerText('Need to move this installation later? Use ds migrate /new/path.', width));
|
|
682
696
|
console.log('');
|
|
683
|
-
renderLaunchHints({ home, url, bindUrl, pythonSelection });
|
|
697
|
+
renderLaunchHints({ home, url, bindUrl, pythonSelection, yolo });
|
|
684
698
|
}
|
|
685
699
|
|
|
686
700
|
function escapeHtml(value) {
|
|
@@ -803,7 +817,7 @@ function writeCodexPreflightReport(home, probe) {
|
|
|
803
817
|
};
|
|
804
818
|
}
|
|
805
819
|
|
|
806
|
-
function readCodexBootstrapState(home, runtimePython) {
|
|
820
|
+
function readCodexBootstrapState(home, runtimePython, envOverrides = {}) {
|
|
807
821
|
const snippet = [
|
|
808
822
|
'import json, pathlib, sys',
|
|
809
823
|
'from deepscientist.config import ConfigManager',
|
|
@@ -811,7 +825,14 @@ function readCodexBootstrapState(home, runtimePython) {
|
|
|
811
825
|
'manager = ConfigManager(home)',
|
|
812
826
|
'print(json.dumps(manager.codex_bootstrap_state(), ensure_ascii=False))',
|
|
813
827
|
].join('\n');
|
|
814
|
-
const result = runSync(runtimePython, ['-c', snippet, home], {
|
|
828
|
+
const result = runSync(runtimePython, ['-c', snippet, home], {
|
|
829
|
+
capture: true,
|
|
830
|
+
allowFailure: true,
|
|
831
|
+
env: {
|
|
832
|
+
...process.env,
|
|
833
|
+
...envOverrides,
|
|
834
|
+
},
|
|
835
|
+
});
|
|
815
836
|
if (result.status !== 0) {
|
|
816
837
|
return { codex_ready: false, codex_last_checked_at: null, codex_last_result: {} };
|
|
817
838
|
}
|
|
@@ -822,7 +843,7 @@ function readCodexBootstrapState(home, runtimePython) {
|
|
|
822
843
|
}
|
|
823
844
|
}
|
|
824
845
|
|
|
825
|
-
function probeCodexBootstrap(home, runtimePython) {
|
|
846
|
+
function probeCodexBootstrap(home, runtimePython, envOverrides = {}) {
|
|
826
847
|
const snippet = [
|
|
827
848
|
'import json, pathlib, sys',
|
|
828
849
|
'from deepscientist.config import ConfigManager',
|
|
@@ -830,7 +851,14 @@ function probeCodexBootstrap(home, runtimePython) {
|
|
|
830
851
|
'manager = ConfigManager(home)',
|
|
831
852
|
'print(json.dumps(manager.probe_codex_bootstrap(persist=True), ensure_ascii=False))',
|
|
832
853
|
].join('\n');
|
|
833
|
-
const result = runSync(runtimePython, ['-c', snippet, home], {
|
|
854
|
+
const result = runSync(runtimePython, ['-c', snippet, home], {
|
|
855
|
+
capture: true,
|
|
856
|
+
allowFailure: true,
|
|
857
|
+
env: {
|
|
858
|
+
...process.env,
|
|
859
|
+
...envOverrides,
|
|
860
|
+
},
|
|
861
|
+
});
|
|
834
862
|
let payload = null;
|
|
835
863
|
try {
|
|
836
864
|
payload = JSON.parse(result.stdout || '{}');
|
|
@@ -881,6 +909,7 @@ function parseLauncherArgs(argv) {
|
|
|
881
909
|
let status = false;
|
|
882
910
|
let daemonOnly = false;
|
|
883
911
|
let skipUpdateCheck = false;
|
|
912
|
+
let yolo = false;
|
|
884
913
|
|
|
885
914
|
if (args[0] === 'ui') {
|
|
886
915
|
args.shift();
|
|
@@ -899,6 +928,7 @@ function parseLauncherArgs(argv) {
|
|
|
899
928
|
else if (arg === '--open-browser') openBrowser = true;
|
|
900
929
|
else if (arg === '--daemon-only') daemonOnly = true;
|
|
901
930
|
else if (arg === '--skip-update-check') skipUpdateCheck = true;
|
|
931
|
+
else if (arg === '--yolo') yolo = true;
|
|
902
932
|
else if (arg === '--host' && args[index + 1]) host = args[++index];
|
|
903
933
|
else if (arg === '--port' && args[index + 1]) port = Number(args[++index]);
|
|
904
934
|
else if (arg === '--home' && args[index + 1]) home = path.resolve(args[++index]);
|
|
@@ -923,6 +953,7 @@ function parseLauncherArgs(argv) {
|
|
|
923
953
|
questId,
|
|
924
954
|
daemonOnly,
|
|
925
955
|
skipUpdateCheck,
|
|
956
|
+
yolo,
|
|
926
957
|
};
|
|
927
958
|
}
|
|
928
959
|
|
|
@@ -2074,6 +2105,9 @@ function normalizePythonCliArgs(args, home) {
|
|
|
2074
2105
|
if (arg === '--here') {
|
|
2075
2106
|
continue;
|
|
2076
2107
|
}
|
|
2108
|
+
if (arg === '--yolo') {
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2077
2111
|
normalized.push(arg);
|
|
2078
2112
|
}
|
|
2079
2113
|
return ['--home', home, ...normalized];
|
|
@@ -2856,7 +2890,7 @@ function readConfiguredUiAddressFromFile(home, fallbackHost, fallbackPort) {
|
|
|
2856
2890
|
}
|
|
2857
2891
|
}
|
|
2858
2892
|
|
|
2859
|
-
async function startDaemon(home, runtimePython, host, port, proxy = null) {
|
|
2893
|
+
async function startDaemon(home, runtimePython, host, port, proxy = null, envOverrides = {}) {
|
|
2860
2894
|
const browserUrl = browserUiUrl(host, port);
|
|
2861
2895
|
const daemonBindUrl = bindUiUrl(host, port);
|
|
2862
2896
|
const state = readDaemonState(home);
|
|
@@ -2880,10 +2914,10 @@ async function startDaemon(home, runtimePython, host, port, proxy = null) {
|
|
|
2880
2914
|
removeDaemonState(home);
|
|
2881
2915
|
}
|
|
2882
2916
|
|
|
2883
|
-
const bootstrapState = readCodexBootstrapState(home, runtimePython);
|
|
2917
|
+
const bootstrapState = readCodexBootstrapState(home, runtimePython, envOverrides);
|
|
2884
2918
|
if (!bootstrapState.codex_ready) {
|
|
2885
2919
|
console.log('Codex is not marked ready yet. Running startup probe...');
|
|
2886
|
-
const probe = probeCodexBootstrap(home, runtimePython);
|
|
2920
|
+
const probe = probeCodexBootstrap(home, runtimePython, envOverrides);
|
|
2887
2921
|
if (!probe || probe.ok !== true) {
|
|
2888
2922
|
throw createCodexPreflightError(home, probe);
|
|
2889
2923
|
}
|
|
@@ -2915,6 +2949,7 @@ async function startDaemon(home, runtimePython, host, port, proxy = null) {
|
|
|
2915
2949
|
stdio: ['ignore', out, out],
|
|
2916
2950
|
env: {
|
|
2917
2951
|
...process.env,
|
|
2952
|
+
...envOverrides,
|
|
2918
2953
|
DEEPSCIENTIST_REPO_ROOT: repoRoot,
|
|
2919
2954
|
DEEPSCIENTIST_NODE_BINARY: process.execPath,
|
|
2920
2955
|
DEEPSCIENTIST_LAUNCHER_PATH: path.join(repoRoot, 'bin', 'ds.js'),
|
|
@@ -3309,6 +3344,7 @@ async function launcherMain(rawArgs) {
|
|
|
3309
3344
|
|
|
3310
3345
|
const pythonRuntime = ensurePythonRuntime(home);
|
|
3311
3346
|
const runtimePython = pythonRuntime.runtimePython;
|
|
3347
|
+
const codexOverrideEnv = buildCodexOverrideEnv({ yolo: options.yolo });
|
|
3312
3348
|
ensureInitialized(home, runtimePython);
|
|
3313
3349
|
if (await maybeHandleStartupUpdate(home, rawArgs, options)) {
|
|
3314
3350
|
return true;
|
|
@@ -3331,7 +3367,7 @@ async function launcherMain(rawArgs) {
|
|
|
3331
3367
|
step(4, 4, 'Starting local daemon and UI surfaces');
|
|
3332
3368
|
let started;
|
|
3333
3369
|
try {
|
|
3334
|
-
started = await startDaemon(home, runtimePython, host, port, options.proxy);
|
|
3370
|
+
started = await startDaemon(home, runtimePython, host, port, options.proxy, codexOverrideEnv);
|
|
3335
3371
|
} catch (error) {
|
|
3336
3372
|
if (handleCodexPreflightFailure(error)) return true;
|
|
3337
3373
|
throw error;
|
|
@@ -3346,6 +3382,7 @@ async function launcherMain(rawArgs) {
|
|
|
3346
3382
|
daemonOnly: options.daemonOnly,
|
|
3347
3383
|
home,
|
|
3348
3384
|
pythonSelection: pythonRuntime.runtimeProbe,
|
|
3385
|
+
yolo: options.yolo,
|
|
3349
3386
|
});
|
|
3350
3387
|
|
|
3351
3388
|
if (options.daemonOnly) {
|
|
@@ -3381,14 +3418,15 @@ async function main() {
|
|
|
3381
3418
|
const home = resolveHome(args);
|
|
3382
3419
|
const pythonRuntime = ensurePythonRuntime(home);
|
|
3383
3420
|
const runtimePython = pythonRuntime.runtimePython;
|
|
3421
|
+
const codexOverrideEnv = buildCodexOverrideEnv({ yolo: args.includes('--yolo') });
|
|
3384
3422
|
if (positional.value === 'run' || positional.value === 'daemon') {
|
|
3385
3423
|
maybePrintOptionalLatexNotice(home);
|
|
3386
3424
|
}
|
|
3387
3425
|
if (positional.value === 'run' || positional.value === 'daemon') {
|
|
3388
|
-
const bootstrapState = readCodexBootstrapState(home, runtimePython);
|
|
3426
|
+
const bootstrapState = readCodexBootstrapState(home, runtimePython, codexOverrideEnv);
|
|
3389
3427
|
if (!bootstrapState.codex_ready) {
|
|
3390
3428
|
try {
|
|
3391
|
-
const probe = probeCodexBootstrap(home, runtimePython);
|
|
3429
|
+
const probe = probeCodexBootstrap(home, runtimePython, codexOverrideEnv);
|
|
3392
3430
|
if (!probe || probe.ok !== true) {
|
|
3393
3431
|
throw createCodexPreflightError(home, probe);
|
|
3394
3432
|
}
|
|
@@ -3398,7 +3436,10 @@ async function main() {
|
|
|
3398
3436
|
}
|
|
3399
3437
|
}
|
|
3400
3438
|
}
|
|
3401
|
-
const result = runPythonCli(runtimePython, normalizePythonCliArgs(args, home), {
|
|
3439
|
+
const result = runPythonCli(runtimePython, normalizePythonCliArgs(args, home), {
|
|
3440
|
+
allowFailure: true,
|
|
3441
|
+
env: codexOverrideEnv,
|
|
3442
|
+
});
|
|
3402
3443
|
process.exit(result.status ?? 0);
|
|
3403
3444
|
return;
|
|
3404
3445
|
}
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/deepscientist/cli.py
CHANGED
|
@@ -236,7 +236,7 @@ def run_command(home: Path, quest_id: str, skill_id: str, message: str, model: s
|
|
|
236
236
|
config_manager = ConfigManager(home)
|
|
237
237
|
config_manager.ensure_files()
|
|
238
238
|
config = config_manager.load_named("config")
|
|
239
|
-
runners = config_manager.
|
|
239
|
+
runners = config_manager.load_runners_config()
|
|
240
240
|
quest_root = home / "quests" / quest_id
|
|
241
241
|
codex_cfg = runners.get("codex", {})
|
|
242
242
|
logger = JsonlLogger(home / "logs", level=config.get("logging", {}).get("level", "info"))
|
|
@@ -128,7 +128,7 @@ def default_connectors() -> dict:
|
|
|
128
128
|
"profiles": [],
|
|
129
129
|
"app_id": None,
|
|
130
130
|
"app_secret": None,
|
|
131
|
-
"app_secret_env":
|
|
131
|
+
"app_secret_env": None,
|
|
132
132
|
"bot_name": "DeepScientist",
|
|
133
133
|
"command_prefix": "/",
|
|
134
134
|
"main_chat_id": None,
|
|
@@ -149,7 +149,7 @@ def default_connectors() -> dict:
|
|
|
149
149
|
"bot_name": "DeepScientist",
|
|
150
150
|
"command_prefix": "/",
|
|
151
151
|
"bot_token": None,
|
|
152
|
-
"bot_token_env":
|
|
152
|
+
"bot_token_env": None,
|
|
153
153
|
"dm_policy": "pairing",
|
|
154
154
|
"allow_from": [],
|
|
155
155
|
"group_policy": "open",
|
|
@@ -165,7 +165,7 @@ def default_connectors() -> dict:
|
|
|
165
165
|
"bot_name": "DeepScientist",
|
|
166
166
|
"command_prefix": "/",
|
|
167
167
|
"bot_token": None,
|
|
168
|
-
"bot_token_env":
|
|
168
|
+
"bot_token_env": None,
|
|
169
169
|
"application_id": None,
|
|
170
170
|
"dm_policy": "pairing",
|
|
171
171
|
"allow_from": [],
|
|
@@ -183,10 +183,10 @@ def default_connectors() -> dict:
|
|
|
183
183
|
"bot_name": "DeepScientist",
|
|
184
184
|
"command_prefix": "/",
|
|
185
185
|
"bot_token": None,
|
|
186
|
-
"bot_token_env":
|
|
186
|
+
"bot_token_env": None,
|
|
187
187
|
"bot_user_id": None,
|
|
188
188
|
"app_token": None,
|
|
189
|
-
"app_token_env":
|
|
189
|
+
"app_token_env": None,
|
|
190
190
|
"dm_policy": "pairing",
|
|
191
191
|
"allow_from": [],
|
|
192
192
|
"group_policy": "open",
|
|
@@ -203,7 +203,7 @@ def default_connectors() -> dict:
|
|
|
203
203
|
"command_prefix": "/",
|
|
204
204
|
"app_id": None,
|
|
205
205
|
"app_secret": None,
|
|
206
|
-
"app_secret_env":
|
|
206
|
+
"app_secret_env": None,
|
|
207
207
|
"api_base_url": "https://open.feishu.cn",
|
|
208
208
|
"dm_policy": "pairing",
|
|
209
209
|
"allow_from": [],
|
|
@@ -30,6 +30,7 @@ from ..qq_profiles import (
|
|
|
30
30
|
qq_profile_label,
|
|
31
31
|
)
|
|
32
32
|
from ..network import urlopen_with_proxy as urlopen
|
|
33
|
+
from ..runners.runtime_overrides import apply_codex_runtime_overrides, apply_runners_runtime_overrides
|
|
33
34
|
from ..shared import read_json, read_text, read_yaml, resolve_runner_binary, run_command, sha256_text, utc_now, which, write_text, write_yaml
|
|
34
35
|
from .models import (
|
|
35
36
|
CONFIG_NAMES,
|
|
@@ -91,6 +92,9 @@ class ConfigManager:
|
|
|
91
92
|
def load_named_normalized(self, name: str, create_optional: bool = False) -> dict:
|
|
92
93
|
return self._normalize_named_payload(name, self.load_named(name, create_optional=create_optional))
|
|
93
94
|
|
|
95
|
+
def load_runners_config(self) -> dict:
|
|
96
|
+
return apply_runners_runtime_overrides(self.load_named_normalized("runners"))
|
|
97
|
+
|
|
94
98
|
def load_named_text(self, name: str, create_optional: bool = False) -> str:
|
|
95
99
|
path = self.path_for(name)
|
|
96
100
|
if create_optional and name in OPTIONAL_CONFIG_NAMES and not path.exists():
|
|
@@ -1039,6 +1043,7 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1039
1043
|
)
|
|
1040
1044
|
|
|
1041
1045
|
def _probe_codex_runner(self, config: dict) -> dict:
|
|
1046
|
+
config = apply_codex_runtime_overrides(config)
|
|
1042
1047
|
checked_at = utc_now()
|
|
1043
1048
|
binary = str(config.get("binary") or "codex").strip() or "codex"
|
|
1044
1049
|
resolved_binary = resolve_runner_binary(binary, runner_name="codex")
|
|
@@ -10,6 +10,13 @@ from .shared import slugify
|
|
|
10
10
|
PROFILEABLE_CONNECTOR_NAMES = ("telegram", "discord", "slack", "feishu", "whatsapp")
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _normalize_secret_pair(payload: dict[str, Any], direct_key: str, env_key: str) -> None:
|
|
14
|
+
direct = _as_text(payload.get(direct_key))
|
|
15
|
+
env_name = _as_text(payload.get(env_key))
|
|
16
|
+
payload[direct_key] = direct
|
|
17
|
+
payload[env_key] = None if direct else env_name
|
|
18
|
+
|
|
19
|
+
|
|
13
20
|
CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
14
21
|
"telegram": {
|
|
15
22
|
"profile_id_prefix": "telegram-profile",
|
|
@@ -35,7 +42,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
35
42
|
"transport": "polling",
|
|
36
43
|
"bot_name": "DeepScientist",
|
|
37
44
|
"bot_token": None,
|
|
38
|
-
"bot_token_env":
|
|
45
|
+
"bot_token_env": None,
|
|
39
46
|
},
|
|
40
47
|
"profile_fields": (
|
|
41
48
|
"enabled",
|
|
@@ -47,6 +54,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
47
54
|
"migration_keys": ("bot_token",),
|
|
48
55
|
"label_fields": ("bot_name",),
|
|
49
56
|
"id_fields": ("bot_name",),
|
|
57
|
+
"secret_pairs": (("bot_token", "bot_token_env"),),
|
|
50
58
|
},
|
|
51
59
|
"discord": {
|
|
52
60
|
"profile_id_prefix": "discord-profile",
|
|
@@ -74,7 +82,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
74
82
|
"transport": "gateway",
|
|
75
83
|
"bot_name": "DeepScientist",
|
|
76
84
|
"bot_token": None,
|
|
77
|
-
"bot_token_env":
|
|
85
|
+
"bot_token_env": None,
|
|
78
86
|
"application_id": None,
|
|
79
87
|
},
|
|
80
88
|
"profile_fields": (
|
|
@@ -88,6 +96,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
88
96
|
"migration_keys": ("bot_token", "application_id"),
|
|
89
97
|
"label_fields": ("bot_name", "application_id"),
|
|
90
98
|
"id_fields": ("application_id", "bot_name"),
|
|
99
|
+
"secret_pairs": (("bot_token", "bot_token_env"),),
|
|
91
100
|
},
|
|
92
101
|
"slack": {
|
|
93
102
|
"profile_id_prefix": "slack-profile",
|
|
@@ -116,10 +125,10 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
116
125
|
"transport": "socket_mode",
|
|
117
126
|
"bot_name": "DeepScientist",
|
|
118
127
|
"bot_token": None,
|
|
119
|
-
"bot_token_env":
|
|
128
|
+
"bot_token_env": None,
|
|
120
129
|
"bot_user_id": None,
|
|
121
130
|
"app_token": None,
|
|
122
|
-
"app_token_env":
|
|
131
|
+
"app_token_env": None,
|
|
123
132
|
},
|
|
124
133
|
"profile_fields": (
|
|
125
134
|
"enabled",
|
|
@@ -134,6 +143,10 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
134
143
|
"migration_keys": ("bot_token", "bot_user_id", "app_token"),
|
|
135
144
|
"label_fields": ("bot_name", "bot_user_id"),
|
|
136
145
|
"id_fields": ("bot_user_id", "bot_name"),
|
|
146
|
+
"secret_pairs": (
|
|
147
|
+
("bot_token", "bot_token_env"),
|
|
148
|
+
("app_token", "app_token_env"),
|
|
149
|
+
),
|
|
137
150
|
},
|
|
138
151
|
"feishu": {
|
|
139
152
|
"profile_id_prefix": "feishu-profile",
|
|
@@ -162,7 +175,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
162
175
|
"bot_name": "DeepScientist",
|
|
163
176
|
"app_id": None,
|
|
164
177
|
"app_secret": None,
|
|
165
|
-
"app_secret_env":
|
|
178
|
+
"app_secret_env": None,
|
|
166
179
|
"api_base_url": "https://open.feishu.cn",
|
|
167
180
|
},
|
|
168
181
|
"profile_fields": (
|
|
@@ -177,6 +190,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
177
190
|
"migration_keys": ("app_id", "app_secret"),
|
|
178
191
|
"label_fields": ("bot_name", "app_id"),
|
|
179
192
|
"id_fields": ("app_id", "bot_name"),
|
|
193
|
+
"secret_pairs": (("app_secret", "app_secret_env"),),
|
|
180
194
|
},
|
|
181
195
|
"whatsapp": {
|
|
182
196
|
"profile_id_prefix": "whatsapp-profile",
|
|
@@ -213,6 +227,7 @@ CONNECTOR_PROFILE_SPECS: dict[str, dict[str, Any]] = {
|
|
|
213
227
|
"migration_keys": ("session_dir",),
|
|
214
228
|
"label_fields": ("bot_name",),
|
|
215
229
|
"id_fields": ("bot_name",),
|
|
230
|
+
"secret_pairs": (),
|
|
216
231
|
},
|
|
217
232
|
}
|
|
218
233
|
|
|
@@ -270,10 +285,17 @@ def normalize_connector_config(connector_name: str, config: dict[str, Any] | Non
|
|
|
270
285
|
if key in payload
|
|
271
286
|
}
|
|
272
287
|
shared["profiles"] = []
|
|
288
|
+
for direct_key, env_key in spec.get("secret_pairs", ()):
|
|
289
|
+
_normalize_secret_pair(shared, direct_key, env_key)
|
|
273
290
|
|
|
274
291
|
raw_profiles = payload.get("profiles")
|
|
275
292
|
items = list(raw_profiles) if isinstance(raw_profiles, list) else []
|
|
276
|
-
|
|
293
|
+
has_direct_migration_value = any(_as_text(payload.get(key)) for key in spec["migration_keys"])
|
|
294
|
+
has_env_only_secret = bool(payload.get("enabled")) and any(
|
|
295
|
+
_as_text(payload.get(env_key))
|
|
296
|
+
for _, env_key in spec.get("secret_pairs", ())
|
|
297
|
+
)
|
|
298
|
+
if not items and (has_direct_migration_value or has_env_only_secret):
|
|
277
299
|
items = [{key: payload.get(key) for key in spec["profile_fields"]}]
|
|
278
300
|
|
|
279
301
|
used_ids: set[str] = set()
|
|
@@ -297,6 +319,8 @@ def normalize_connector_config(connector_name: str, config: dict[str, Any] | Non
|
|
|
297
319
|
current["transport"] = infer_connector_transport(connector_name, current)
|
|
298
320
|
if "mode" in spec["profile_defaults"] or current.get("mode") is not None:
|
|
299
321
|
current["mode"] = _as_text(current.get("mode")) or str(spec["profile_defaults"].get("mode") or "")
|
|
322
|
+
for direct_key, env_key in spec.get("secret_pairs", ()):
|
|
323
|
+
_normalize_secret_pair(current, direct_key, env_key)
|
|
300
324
|
current["profile_id"] = _unique_profile_id(
|
|
301
325
|
_profile_seed(connector_name, current, index=index),
|
|
302
326
|
prefix=str(spec["profile_id_prefix"]),
|
|
@@ -309,6 +333,10 @@ def normalize_connector_config(connector_name: str, config: dict[str, Any] | Non
|
|
|
309
333
|
if len(profiles) == 1:
|
|
310
334
|
for key in spec["profile_fields"]:
|
|
311
335
|
shared[key] = profiles[0].get(key)
|
|
336
|
+
elif len(profiles) > 1:
|
|
337
|
+
for direct_key, env_key in spec.get("secret_pairs", ()):
|
|
338
|
+
shared[direct_key] = None
|
|
339
|
+
shared[env_key] = None
|
|
312
340
|
return shared
|
|
313
341
|
|
|
314
342
|
|
|
@@ -220,6 +220,9 @@ npm --prefix src/ui run build</pre>
|
|
|
220
220
|
def qq_inbound(self, body: dict) -> dict:
|
|
221
221
|
return self.app.handle_qq_inbound(body)
|
|
222
222
|
|
|
223
|
+
def connector_profile_delete(self, connector: str, profile_id: str) -> dict | tuple[int, dict]:
|
|
224
|
+
return self.app.delete_connector_profile(connector, profile_id)
|
|
225
|
+
|
|
223
226
|
def connector_inbound(self, connector: str, body: dict) -> dict:
|
|
224
227
|
return self.app.handle_connector_inbound(connector, body)
|
|
225
228
|
|
|
@@ -1188,7 +1191,7 @@ npm --prefix src/ui run build</pre>
|
|
|
1188
1191
|
def run_create(self, quest_id: str, body: dict) -> dict:
|
|
1189
1192
|
quest_root = self.app.quest_service._quest_root(quest_id)
|
|
1190
1193
|
config = self.app.config_manager.load_named("config")
|
|
1191
|
-
runners = self.app.config_manager.
|
|
1194
|
+
runners = self.app.config_manager.load_runners_config()
|
|
1192
1195
|
snapshot = self.app.quest_service.snapshot(quest_id)
|
|
1193
1196
|
runner_name = str(body.get("runner") or snapshot.get("runner") or config.get("default_runner", "codex")).strip().lower()
|
|
1194
1197
|
runner_cfg = runners.get(runner_name, {})
|
|
@@ -1354,6 +1357,8 @@ npm --prefix src/ui run build</pre>
|
|
|
1354
1357
|
result = self.app.config_manager.save_named_text(name, body.get("content", ""))
|
|
1355
1358
|
if result.get("ok") and name == "connectors":
|
|
1356
1359
|
result["runtime_reload"] = self.app.reload_connectors_config()
|
|
1360
|
+
if result.get("ok") and name == "runners":
|
|
1361
|
+
result["runtime_reload"] = self.app.reload_runners_config()
|
|
1357
1362
|
return result
|
|
1358
1363
|
|
|
1359
1364
|
def config_validate(self, body: dict | None = None) -> dict:
|
|
@@ -17,6 +17,7 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
|
|
|
17
17
|
("GET", re.compile(r"^/api/connectors/availability$"), "connectors_availability"),
|
|
18
18
|
("GET", re.compile(r"^/api/connectors/qq/bindings$"), "qq_bindings"),
|
|
19
19
|
("POST", re.compile(r"^/api/connectors/qq/inbound$"), "qq_inbound"),
|
|
20
|
+
("DELETE", re.compile(r"^/api/connectors/(?P<connector>[^/]+)/profiles/(?P<profile_id>[^/]+)$"), "connector_profile_delete"),
|
|
20
21
|
("GET", re.compile(r"^/api/connectors/(?P<connector>[^/]+)/bindings$"), "connector_bindings"),
|
|
21
22
|
("POST", re.compile(r"^/api/connectors/(?P<connector>[^/]+)/inbound$"), "connector_inbound"),
|
|
22
23
|
("GET", re.compile(r"^/api/baselines$"), "baselines"),
|