@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
package/bin/ds.js
CHANGED
|
@@ -13,6 +13,7 @@ const pyprojectToml = fs.readFileSync(path.join(repoRoot, 'pyproject.toml'), 'ut
|
|
|
13
13
|
const pythonCandidates = process.platform === 'win32' ? ['python', 'py'] : ['python3', 'python'];
|
|
14
14
|
const requiredPythonSpec = parseRequiredPythonSpec(pyprojectToml);
|
|
15
15
|
const minimumPythonVersion = parseMinimumPythonVersion(requiredPythonSpec);
|
|
16
|
+
const launcherWrapperCommands = ['ds', 'ds-cli', 'research', 'resear'];
|
|
16
17
|
const pythonCommands = new Set([
|
|
17
18
|
'init',
|
|
18
19
|
'new',
|
|
@@ -34,7 +35,6 @@ const pythonCommands = new Set([
|
|
|
34
35
|
]);
|
|
35
36
|
const UPDATE_PACKAGE_NAME = String(packageJson.name || '@researai/deepscientist').trim() || '@researai/deepscientist';
|
|
36
37
|
const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
|
|
37
|
-
const UPDATE_PROMPT_TTL_MS = 12 * 60 * 60 * 1000;
|
|
38
38
|
|
|
39
39
|
const optionsWithValues = new Set(['--home', '--host', '--port', '--quest-id', '--mode']);
|
|
40
40
|
|
|
@@ -46,6 +46,9 @@ Usage:
|
|
|
46
46
|
ds update
|
|
47
47
|
ds update --check
|
|
48
48
|
ds update --yes
|
|
49
|
+
ds migrate /data/DeepScientist
|
|
50
|
+
ds --here
|
|
51
|
+
ds --here doctor
|
|
49
52
|
ds --tui
|
|
50
53
|
ds --both
|
|
51
54
|
ds --host 0.0.0.0 --port 21000
|
|
@@ -67,6 +70,7 @@ Launcher flags:
|
|
|
67
70
|
--stop Stop the managed daemon
|
|
68
71
|
--restart Restart the managed daemon
|
|
69
72
|
--home <path> Use a custom DeepScientist home
|
|
73
|
+
--here Use the current working directory as DeepScientist home
|
|
70
74
|
--quest-id <id> Open the TUI on one quest directly
|
|
71
75
|
|
|
72
76
|
Update:
|
|
@@ -74,6 +78,10 @@ Update:
|
|
|
74
78
|
ds update --check Print structured update status
|
|
75
79
|
ds update --yes Install the latest npm release immediately
|
|
76
80
|
|
|
81
|
+
Migration:
|
|
82
|
+
ds migrate <target> Move the DeepScientist home/install root to a new absolute path
|
|
83
|
+
ds migrate <target> --yes --restart
|
|
84
|
+
|
|
77
85
|
Runtime:
|
|
78
86
|
DeepScientist uses uv to manage a locked local Python runtime.
|
|
79
87
|
If uv is missing, ds bootstraps a local copy under the DeepScientist home automatically.
|
|
@@ -176,10 +184,7 @@ function detectInstallMode(rootPath = repoRoot) {
|
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
function updateManualCommand(installMode) {
|
|
179
|
-
|
|
180
|
-
return `npm install -g ${UPDATE_PACKAGE_NAME}@latest`;
|
|
181
|
-
}
|
|
182
|
-
return 'git pull && bash install.sh';
|
|
187
|
+
return `npm install -g ${UPDATE_PACKAGE_NAME}@latest`;
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
function updateSupportSummary(installMode, npmBinary, launcherPath) {
|
|
@@ -194,14 +199,14 @@ function updateSupportSummary(installMode, npmBinary, launcherPath) {
|
|
|
194
199
|
return {
|
|
195
200
|
canCheck: true,
|
|
196
201
|
canSelfUpdate: false,
|
|
197
|
-
reason: '
|
|
202
|
+
reason: 'Self-update is disabled for this installation. Use the npm command below to install the latest release build.',
|
|
198
203
|
};
|
|
199
204
|
}
|
|
200
205
|
if (!launcherPath || !fs.existsSync(launcherPath)) {
|
|
201
206
|
return {
|
|
202
207
|
canCheck: true,
|
|
203
208
|
canSelfUpdate: false,
|
|
204
|
-
reason: '
|
|
209
|
+
reason: 'Self-update is disabled because the launcher entrypoint could not be resolved. Use the npm command below instead.',
|
|
205
210
|
};
|
|
206
211
|
}
|
|
207
212
|
return {
|
|
@@ -279,14 +284,17 @@ function buildUpdateStatus(home, statePatch = {}) {
|
|
|
279
284
|
const support = updateSupportSummary(installMode, npmBinary, launcherPath);
|
|
280
285
|
const currentVersion = normalizeVersion(state.current_version || packageJson.version);
|
|
281
286
|
const latestVersion = normalizeVersion(state.latest_version || '');
|
|
287
|
+
const promptedVersion = normalizeVersion(state.last_prompted_version || '');
|
|
282
288
|
const updateAvailable = Boolean(latestVersion) && compareVersions(latestVersion, currentVersion) > 0;
|
|
283
289
|
const skippedVersion = normalizeVersion(state.last_skipped_version || '');
|
|
290
|
+
const promptedCurrentTarget = Boolean(updateAvailable && promptedVersion && promptedVersion === latestVersion);
|
|
284
291
|
const skippedCurrentTarget = Boolean(updateAvailable && skippedVersion && skippedVersion === latestVersion);
|
|
285
292
|
const promptRecommended =
|
|
286
293
|
Boolean(updateAvailable)
|
|
287
294
|
&& !Boolean(state.busy)
|
|
295
|
+
&& !promptedCurrentTarget
|
|
288
296
|
&& !skippedCurrentTarget
|
|
289
|
-
|
|
297
|
+
;
|
|
290
298
|
|
|
291
299
|
return {
|
|
292
300
|
ok: true,
|
|
@@ -302,6 +310,7 @@ function buildUpdateStatus(home, statePatch = {}) {
|
|
|
302
310
|
last_checked_at: state.last_checked_at || null,
|
|
303
311
|
last_check_error: state.last_check_error || null,
|
|
304
312
|
last_prompted_at: state.last_prompted_at || null,
|
|
313
|
+
last_prompted_version: promptedVersion || null,
|
|
305
314
|
last_deferred_at: state.last_deferred_at || null,
|
|
306
315
|
last_skipped_version: skippedVersion || null,
|
|
307
316
|
last_update_started_at: state.last_update_started_at || null,
|
|
@@ -345,10 +354,12 @@ function checkForUpdates(home, { force = false, timeoutMs = 3500 } = {}) {
|
|
|
345
354
|
}
|
|
346
355
|
|
|
347
356
|
function markUpdateDeferred(home, version) {
|
|
357
|
+
const normalizedVersion = normalizeVersion(version || readUpdateState(home).latest_version || '');
|
|
348
358
|
const patched = mergeUpdateState(home, {
|
|
349
359
|
last_prompted_at: new Date().toISOString(),
|
|
350
360
|
last_deferred_at: new Date().toISOString(),
|
|
351
|
-
|
|
361
|
+
last_prompted_version: normalizedVersion || null,
|
|
362
|
+
latest_version: normalizedVersion || null,
|
|
352
363
|
});
|
|
353
364
|
return buildUpdateStatus(home, patched);
|
|
354
365
|
}
|
|
@@ -357,6 +368,7 @@ function markUpdateSkipped(home, version) {
|
|
|
357
368
|
const normalized = normalizeVersion(version);
|
|
358
369
|
const patched = mergeUpdateState(home, {
|
|
359
370
|
last_prompted_at: new Date().toISOString(),
|
|
371
|
+
last_prompted_version: normalized || null,
|
|
360
372
|
last_skipped_version: normalized || null,
|
|
361
373
|
});
|
|
362
374
|
return buildUpdateStatus(home, patched);
|
|
@@ -384,6 +396,9 @@ function resolveHome(args) {
|
|
|
384
396
|
if (index >= 0 && index + 1 < args.length) {
|
|
385
397
|
return path.resolve(args[index + 1]);
|
|
386
398
|
}
|
|
399
|
+
if (args.includes('--here')) {
|
|
400
|
+
return process.cwd();
|
|
401
|
+
}
|
|
387
402
|
if (process.env.DEEPSCIENTIST_HOME) {
|
|
388
403
|
return path.resolve(process.env.DEEPSCIENTIST_HOME);
|
|
389
404
|
}
|
|
@@ -554,12 +569,14 @@ function renderLaunchHints({ home, url, bindUrl, pythonSelection }) {
|
|
|
554
569
|
renderKeyValueRows([
|
|
555
570
|
['ds --port 21000', 'Change the web port'],
|
|
556
571
|
['ds --host 0.0.0.0 --port 21000', 'Bind on all interfaces'],
|
|
572
|
+
['ds --here', 'Use the current directory as home'],
|
|
557
573
|
['ds --both', 'Start web + TUI together'],
|
|
558
574
|
['ds --tui', 'Start the terminal workspace only'],
|
|
559
575
|
['ds --no-browser', 'Do not auto-open the browser'],
|
|
560
576
|
['ds --status', 'Show daemon health as JSON'],
|
|
561
577
|
['ds --restart', 'Restart the managed daemon'],
|
|
562
578
|
['ds --stop', 'Stop the managed daemon'],
|
|
579
|
+
['ds migrate /data/DeepScientist', 'Move the full home/install root safely'],
|
|
563
580
|
['ds --help', 'Show the full launcher help'],
|
|
564
581
|
]);
|
|
565
582
|
console.log('');
|
|
@@ -630,6 +647,7 @@ function printLaunchCard({
|
|
|
630
647
|
console.log(centerText(browserLine, width));
|
|
631
648
|
console.log(centerText(nextStep, width));
|
|
632
649
|
console.log(centerText('Run ds --stop to stop the managed daemon.', width));
|
|
650
|
+
console.log(centerText('Need to move this installation later? Use ds migrate /new/path.', width));
|
|
633
651
|
console.log('');
|
|
634
652
|
renderLaunchHints({ home, url, bindUrl, pythonSelection });
|
|
635
653
|
}
|
|
@@ -894,6 +912,22 @@ Flags:
|
|
|
894
912
|
`);
|
|
895
913
|
}
|
|
896
914
|
|
|
915
|
+
function printMigrateHelp() {
|
|
916
|
+
console.log(`DeepScientist migrate
|
|
917
|
+
|
|
918
|
+
Usage:
|
|
919
|
+
ds migrate /absolute/target/path
|
|
920
|
+
ds migrate /absolute/target/path --yes
|
|
921
|
+
ds migrate /absolute/target/path --restart
|
|
922
|
+
ds migrate /absolute/target/path --home /current/source/path
|
|
923
|
+
|
|
924
|
+
Flags:
|
|
925
|
+
--yes Skip the interactive double-confirmation prompt
|
|
926
|
+
--restart Start the managed daemon again from the migrated home
|
|
927
|
+
--home <path> Override the current DeepScientist source home/root
|
|
928
|
+
`);
|
|
929
|
+
}
|
|
930
|
+
|
|
897
931
|
function parseUpdateArgs(argv) {
|
|
898
932
|
const args = [...argv];
|
|
899
933
|
if (args[0] === 'update') {
|
|
@@ -950,6 +984,40 @@ function parseUpdateArgs(argv) {
|
|
|
950
984
|
};
|
|
951
985
|
}
|
|
952
986
|
|
|
987
|
+
function parseMigrateArgs(argv) {
|
|
988
|
+
const args = [...argv];
|
|
989
|
+
if (args[0] === 'migrate') {
|
|
990
|
+
args.shift();
|
|
991
|
+
}
|
|
992
|
+
let home = null;
|
|
993
|
+
let target = null;
|
|
994
|
+
let yes = false;
|
|
995
|
+
let restart = false;
|
|
996
|
+
|
|
997
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
998
|
+
const arg = args[index];
|
|
999
|
+
if (arg === '--yes') yes = true;
|
|
1000
|
+
else if (arg === '--restart') restart = true;
|
|
1001
|
+
else if (arg === '--home' && args[index + 1]) home = path.resolve(expandUserPath(args[++index]));
|
|
1002
|
+
else if (arg === '--help' || arg === '-h') return { help: true };
|
|
1003
|
+
else if (arg.startsWith('--')) return null;
|
|
1004
|
+
else if (!target) target = path.resolve(expandUserPath(arg));
|
|
1005
|
+
else return null;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (!target) {
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return {
|
|
1013
|
+
help: false,
|
|
1014
|
+
home,
|
|
1015
|
+
target,
|
|
1016
|
+
yes,
|
|
1017
|
+
restart,
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
|
|
953
1021
|
function findFirstPositionalArg(args) {
|
|
954
1022
|
for (let index = 0; index < args.length; index += 1) {
|
|
955
1023
|
const arg = args[index];
|
|
@@ -973,6 +1041,188 @@ function realpathOrSelf(targetPath) {
|
|
|
973
1041
|
}
|
|
974
1042
|
}
|
|
975
1043
|
|
|
1044
|
+
function isPathEqual(left, right) {
|
|
1045
|
+
return realpathOrSelf(path.resolve(left)) === realpathOrSelf(path.resolve(right));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function isPathInside(candidatePath, parentPath) {
|
|
1049
|
+
const candidate = realpathOrSelf(path.resolve(candidatePath));
|
|
1050
|
+
const parent = realpathOrSelf(path.resolve(parentPath));
|
|
1051
|
+
if (candidate === parent) {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
const relative = path.relative(parent, candidate);
|
|
1055
|
+
return Boolean(relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function buildInstalledWrapperScript() {
|
|
1059
|
+
return [
|
|
1060
|
+
'#!/usr/bin/env bash',
|
|
1061
|
+
'set -euo pipefail',
|
|
1062
|
+
'SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"',
|
|
1063
|
+
'HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"',
|
|
1064
|
+
'if [ -z "${DEEPSCIENTIST_HOME:-}" ]; then',
|
|
1065
|
+
' export DEEPSCIENTIST_HOME="$HOME_DIR"',
|
|
1066
|
+
'fi',
|
|
1067
|
+
'NODE_BIN="${DEEPSCIENTIST_NODE:-node}"',
|
|
1068
|
+
'exec "$NODE_BIN" "$SCRIPT_DIR/ds.js" "$@"',
|
|
1069
|
+
'',
|
|
1070
|
+
].join('\n');
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function buildGlobalWrapperScript({ installDir, home, commandName }) {
|
|
1074
|
+
return [
|
|
1075
|
+
'#!/usr/bin/env bash',
|
|
1076
|
+
'set -euo pipefail',
|
|
1077
|
+
'if [ -z "${DEEPSCIENTIST_HOME:-}" ]; then',
|
|
1078
|
+
` export DEEPSCIENTIST_HOME="${home}"`,
|
|
1079
|
+
'fi',
|
|
1080
|
+
`exec "${path.join(installDir, 'bin', commandName)}" "$@"`,
|
|
1081
|
+
'',
|
|
1082
|
+
].join('\n');
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function writeExecutableScript(targetPath, content) {
|
|
1086
|
+
ensureDir(path.dirname(targetPath));
|
|
1087
|
+
fs.writeFileSync(targetPath, content, { encoding: 'utf8', mode: 0o755 });
|
|
1088
|
+
fs.chmodSync(targetPath, 0o755);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function repairMigratedInstallWrappers(targetHome) {
|
|
1092
|
+
const installBinDir = path.join(targetHome, 'cli', 'bin');
|
|
1093
|
+
if (!fs.existsSync(installBinDir)) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
const content = buildInstalledWrapperScript();
|
|
1097
|
+
for (const commandName of launcherWrapperCommands) {
|
|
1098
|
+
const wrapperPath = path.join(installBinDir, commandName);
|
|
1099
|
+
if (!fs.existsSync(wrapperPath)) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
writeExecutableScript(wrapperPath, content);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function candidateWrapperPathsForCommand(commandName) {
|
|
1107
|
+
const directories = String(process.env.PATH || '')
|
|
1108
|
+
.split(path.delimiter)
|
|
1109
|
+
.filter(Boolean);
|
|
1110
|
+
const candidates = [];
|
|
1111
|
+
for (const directory of directories) {
|
|
1112
|
+
candidates.push(path.join(directory, commandName));
|
|
1113
|
+
if (process.platform === 'win32') {
|
|
1114
|
+
candidates.push(path.join(directory, `${commandName}.cmd`));
|
|
1115
|
+
candidates.push(path.join(directory, `${commandName}.ps1`));
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return candidates;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function rewriteLauncherWrappersIfPointingAtSource({ sourceHome, targetHome }) {
|
|
1122
|
+
if (process.platform === 'win32') {
|
|
1123
|
+
return [];
|
|
1124
|
+
}
|
|
1125
|
+
const rewritten = [];
|
|
1126
|
+
const sourceInstallDir = path.join(sourceHome, 'cli');
|
|
1127
|
+
const targetInstallDir = path.join(targetHome, 'cli');
|
|
1128
|
+
for (const commandName of launcherWrapperCommands) {
|
|
1129
|
+
for (const candidate of candidateWrapperPathsForCommand(commandName)) {
|
|
1130
|
+
if (!fs.existsSync(candidate) || !fs.statSync(candidate).isFile()) {
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
let text = '';
|
|
1134
|
+
try {
|
|
1135
|
+
text = fs.readFileSync(candidate, 'utf8');
|
|
1136
|
+
} catch {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (!text.includes(sourceInstallDir) && !text.includes(sourceHome)) {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
writeExecutableScript(
|
|
1143
|
+
candidate,
|
|
1144
|
+
buildGlobalWrapperScript({
|
|
1145
|
+
installDir: targetInstallDir,
|
|
1146
|
+
home: targetHome,
|
|
1147
|
+
commandName,
|
|
1148
|
+
})
|
|
1149
|
+
);
|
|
1150
|
+
rewritten.push(candidate);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return rewritten;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function scheduleDeferredSourceCleanup({ sourceHome, targetHome }) {
|
|
1157
|
+
const logPath = path.join(targetHome, 'logs', 'migrate-cleanup.log');
|
|
1158
|
+
ensureDir(path.dirname(logPath));
|
|
1159
|
+
const helperScript = [
|
|
1160
|
+
"const fs = require('node:fs');",
|
|
1161
|
+
"const { setTimeout: sleep } = require('node:timers/promises');",
|
|
1162
|
+
'const parentPid = Number(process.argv[1]);',
|
|
1163
|
+
'const sourceHome = process.argv[2];',
|
|
1164
|
+
'const logPath = process.argv[3];',
|
|
1165
|
+
'(async () => {',
|
|
1166
|
+
' for (let attempt = 0; attempt < 300; attempt += 1) {',
|
|
1167
|
+
' try {',
|
|
1168
|
+
' process.kill(parentPid, 0);',
|
|
1169
|
+
' await sleep(100);',
|
|
1170
|
+
' continue;',
|
|
1171
|
+
' } catch {',
|
|
1172
|
+
' break;',
|
|
1173
|
+
' }',
|
|
1174
|
+
' }',
|
|
1175
|
+
' try {',
|
|
1176
|
+
' fs.rmSync(sourceHome, { recursive: true, force: true });',
|
|
1177
|
+
' } catch (error) {',
|
|
1178
|
+
" fs.appendFileSync(logPath, `[${new Date().toISOString()}] ${error instanceof Error ? error.message : String(error)}\\n`, 'utf8');",
|
|
1179
|
+
' process.exit(1);',
|
|
1180
|
+
' }',
|
|
1181
|
+
'})();',
|
|
1182
|
+
].join('\n');
|
|
1183
|
+
const child = spawn(process.execPath, ['-e', helperScript, String(process.pid), sourceHome, logPath], {
|
|
1184
|
+
detached: true,
|
|
1185
|
+
stdio: 'ignore',
|
|
1186
|
+
env: process.env,
|
|
1187
|
+
});
|
|
1188
|
+
child.unref();
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
async function promptMigrationConfirmation({ sourceHome, targetHome }) {
|
|
1192
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1193
|
+
throw new Error('DeepScientist migration needs a TTY for confirmation. Re-run with `--yes` to continue non-interactively.');
|
|
1194
|
+
}
|
|
1195
|
+
console.log('');
|
|
1196
|
+
console.log('DeepScientist home migration');
|
|
1197
|
+
console.log('');
|
|
1198
|
+
console.log(`From: ${sourceHome}`);
|
|
1199
|
+
console.log(`To: ${targetHome}`);
|
|
1200
|
+
console.log('');
|
|
1201
|
+
console.log('This will stop the managed daemon, copy the full DeepScientist root, verify the copy, update launcher wrappers, and delete the old path after success.');
|
|
1202
|
+
const ask = (question) => new Promise((resolve) => {
|
|
1203
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1204
|
+
rl.question(question, (answer) => {
|
|
1205
|
+
rl.close();
|
|
1206
|
+
resolve(String(answer || '').trim());
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
const first = await ask('Type YES to continue: ');
|
|
1210
|
+
if (first !== 'YES') {
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
const second = await ask('Type MIGRATE to confirm old-path deletion after a successful copy: ');
|
|
1214
|
+
return second === 'MIGRATE';
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function printMigrationSummary({ sourceHome, targetHome, restart }) {
|
|
1218
|
+
console.log('');
|
|
1219
|
+
console.log('DeepScientist migrate');
|
|
1220
|
+
console.log('');
|
|
1221
|
+
console.log(`Source: ${sourceHome}`);
|
|
1222
|
+
console.log(`Target: ${targetHome}`);
|
|
1223
|
+
console.log(`Restart: ${restart ? 'yes' : 'no'}`);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
976
1226
|
function pythonMeetsMinimum(probe) {
|
|
977
1227
|
if (!probe || typeof probe.major !== 'number' || typeof probe.minor !== 'number') {
|
|
978
1228
|
return false;
|
|
@@ -1784,6 +2034,9 @@ function normalizePythonCliArgs(args, home) {
|
|
|
1784
2034
|
index += 1;
|
|
1785
2035
|
continue;
|
|
1786
2036
|
}
|
|
2037
|
+
if (arg === '--here') {
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
1787
2040
|
normalized.push(arg);
|
|
1788
2041
|
}
|
|
1789
2042
|
return ['--home', home, ...normalized];
|
|
@@ -2242,96 +2495,11 @@ function runNpmInstallLatest(home, npmBinary) {
|
|
|
2242
2495
|
};
|
|
2243
2496
|
}
|
|
2244
2497
|
|
|
2245
|
-
async function promptUpdateAction(status) {
|
|
2246
|
-
const options = [
|
|
2247
|
-
{
|
|
2248
|
-
value: 'update',
|
|
2249
|
-
label: status.can_self_update ? 'Update now' : 'Show manual update',
|
|
2250
|
-
},
|
|
2251
|
-
{ value: 'later', label: 'Remind me later' },
|
|
2252
|
-
{ value: 'skip', label: 'Skip this version' },
|
|
2253
|
-
];
|
|
2254
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2255
|
-
return 'later';
|
|
2256
|
-
}
|
|
2257
|
-
return new Promise((resolve) => {
|
|
2258
|
-
let selected = 1;
|
|
2259
|
-
const lines = [
|
|
2260
|
-
'',
|
|
2261
|
-
'A new DeepScientist version is available.',
|
|
2262
|
-
'',
|
|
2263
|
-
`Current: ${status.current_version}`,
|
|
2264
|
-
`Latest: ${status.latest_version || 'unknown'}`,
|
|
2265
|
-
'',
|
|
2266
|
-
status.can_self_update
|
|
2267
|
-
? 'What do you want to do?'
|
|
2268
|
-
: 'Self-update is not available for this installation. Choose an action:',
|
|
2269
|
-
];
|
|
2270
|
-
|
|
2271
|
-
const cleanup = () => {
|
|
2272
|
-
process.stdin.off('keypress', onKeypress);
|
|
2273
|
-
if (process.stdin.isTTY) {
|
|
2274
|
-
process.stdin.setRawMode(false);
|
|
2275
|
-
}
|
|
2276
|
-
process.stdin.pause();
|
|
2277
|
-
console.log('');
|
|
2278
|
-
};
|
|
2279
|
-
|
|
2280
|
-
const render = () => {
|
|
2281
|
-
console.clear();
|
|
2282
|
-
for (const line of lines) {
|
|
2283
|
-
console.log(line);
|
|
2284
|
-
}
|
|
2285
|
-
for (let index = 0; index < options.length; index += 1) {
|
|
2286
|
-
const option = options[index];
|
|
2287
|
-
console.log(`${index === selected ? '>' : ' '} ${option.label}`);
|
|
2288
|
-
}
|
|
2289
|
-
console.log('');
|
|
2290
|
-
console.log('Use ↑/↓ and Enter.');
|
|
2291
|
-
};
|
|
2292
|
-
|
|
2293
|
-
const onKeypress = (_str, key) => {
|
|
2294
|
-
if (key?.name === 'up') {
|
|
2295
|
-
selected = (selected - 1 + options.length) % options.length;
|
|
2296
|
-
render();
|
|
2297
|
-
return;
|
|
2298
|
-
}
|
|
2299
|
-
if (key?.name === 'down') {
|
|
2300
|
-
selected = (selected + 1) % options.length;
|
|
2301
|
-
render();
|
|
2302
|
-
return;
|
|
2303
|
-
}
|
|
2304
|
-
if (key?.name === 'return') {
|
|
2305
|
-
const choice = options[selected].value;
|
|
2306
|
-
cleanup();
|
|
2307
|
-
resolve(choice);
|
|
2308
|
-
return;
|
|
2309
|
-
}
|
|
2310
|
-
if (key?.ctrl && key?.name === 'c') {
|
|
2311
|
-
cleanup();
|
|
2312
|
-
resolve('later');
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
2315
|
-
|
|
2316
|
-
readline.emitKeypressEvents(process.stdin);
|
|
2317
|
-
process.stdin.setRawMode(true);
|
|
2318
|
-
process.stdin.resume();
|
|
2319
|
-
process.stdin.on('keypress', onKeypress);
|
|
2320
|
-
render();
|
|
2321
|
-
});
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
2498
|
function printUpdateStatus(status, { compact = false } = {}) {
|
|
2325
2499
|
if (compact) {
|
|
2326
2500
|
if (status.update_available) {
|
|
2327
|
-
console.log(
|
|
2328
|
-
|
|
2329
|
-
);
|
|
2330
|
-
if (status.can_self_update) {
|
|
2331
|
-
console.log('Run `ds update --yes` to install it.');
|
|
2332
|
-
} else {
|
|
2333
|
-
console.log(`Manual update: ${status.manual_update_command}`);
|
|
2334
|
-
}
|
|
2501
|
+
console.log(`DeepScientist update available: ${status.current_version} -> ${status.latest_version}`);
|
|
2502
|
+
console.log(`Update with: ${status.manual_update_command}`);
|
|
2335
2503
|
return;
|
|
2336
2504
|
}
|
|
2337
2505
|
console.log(`DeepScientist is up to date (${status.current_version}).`);
|
|
@@ -2344,16 +2512,15 @@ function printUpdateStatus(status, { compact = false } = {}) {
|
|
|
2344
2512
|
['Latest', status.latest_version || 'unknown'],
|
|
2345
2513
|
['Available', status.update_available ? 'yes' : 'no'],
|
|
2346
2514
|
['Install mode', status.install_mode],
|
|
2347
|
-
['Self-update', status.can_self_update ? 'supported' : 'manual-only'],
|
|
2348
2515
|
['Last checked', status.last_checked_at || 'never'],
|
|
2349
2516
|
]);
|
|
2350
2517
|
if (status.last_check_error) {
|
|
2351
2518
|
console.log('');
|
|
2352
2519
|
console.log(`Version check error: ${status.last_check_error}`);
|
|
2353
2520
|
}
|
|
2354
|
-
if (
|
|
2521
|
+
if (status.update_available || status.manual_update_command) {
|
|
2355
2522
|
console.log('');
|
|
2356
|
-
console.log(`
|
|
2523
|
+
console.log(`Update command: ${status.manual_update_command}`);
|
|
2357
2524
|
if (status.reason) {
|
|
2358
2525
|
console.log(status.reason);
|
|
2359
2526
|
}
|
|
@@ -2381,26 +2548,6 @@ function spawnDetachedNode(args, options = {}) {
|
|
|
2381
2548
|
return child;
|
|
2382
2549
|
}
|
|
2383
2550
|
|
|
2384
|
-
async function restartIntoUpdatedLauncher(rawArgs) {
|
|
2385
|
-
const launcherPath = resolveLauncherPath();
|
|
2386
|
-
if (!launcherPath) {
|
|
2387
|
-
throw new Error('Could not resolve the DeepScientist launcher after the update.');
|
|
2388
|
-
}
|
|
2389
|
-
const args = [launcherPath, '--skip-update-check', ...rawArgs.filter((item) => item !== '--skip-update-check')];
|
|
2390
|
-
const child = spawn(process.execPath, args, {
|
|
2391
|
-
cwd: repoRoot,
|
|
2392
|
-
stdio: 'inherit',
|
|
2393
|
-
env: process.env,
|
|
2394
|
-
});
|
|
2395
|
-
await new Promise((resolve, reject) => {
|
|
2396
|
-
child.on('error', reject);
|
|
2397
|
-
child.on('exit', (code) => {
|
|
2398
|
-
process.exit(code ?? 0);
|
|
2399
|
-
resolve();
|
|
2400
|
-
});
|
|
2401
|
-
});
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
2551
|
async function performSelfUpdate(home, options = {}) {
|
|
2405
2552
|
const status = checkForUpdates(home, { force: true });
|
|
2406
2553
|
if (!status.update_available) {
|
|
@@ -2566,42 +2713,8 @@ async function maybeHandleStartupUpdate(home, rawArgs, options = {}) {
|
|
|
2566
2713
|
return false;
|
|
2567
2714
|
}
|
|
2568
2715
|
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
mergeUpdateState(home, {
|
|
2572
|
-
last_prompted_at: new Date().toISOString(),
|
|
2573
|
-
});
|
|
2574
|
-
return false;
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
const action = await promptUpdateAction(status);
|
|
2578
|
-
if (action === 'later') {
|
|
2579
|
-
markUpdateDeferred(home, status.latest_version);
|
|
2580
|
-
return false;
|
|
2581
|
-
}
|
|
2582
|
-
if (action === 'skip') {
|
|
2583
|
-
markUpdateSkipped(home, status.latest_version);
|
|
2584
|
-
return false;
|
|
2585
|
-
}
|
|
2586
|
-
if (action === 'update' && !status.can_self_update) {
|
|
2587
|
-
console.log(`Manual update required: ${status.manual_update_command}`);
|
|
2588
|
-
if (status.reason) {
|
|
2589
|
-
console.log(status.reason);
|
|
2590
|
-
}
|
|
2591
|
-
markUpdateDeferred(home, status.latest_version);
|
|
2592
|
-
return false;
|
|
2593
|
-
}
|
|
2594
|
-
if (action === 'update') {
|
|
2595
|
-
console.log(`Updating DeepScientist ${status.current_version} -> ${status.latest_version} ...`);
|
|
2596
|
-
const result = await performSelfUpdate(home, { restartDaemon: false });
|
|
2597
|
-
if (!result.ok) {
|
|
2598
|
-
console.error(result.message);
|
|
2599
|
-
return false;
|
|
2600
|
-
}
|
|
2601
|
-
console.log(result.message);
|
|
2602
|
-
await restartIntoUpdatedLauncher(rawArgs);
|
|
2603
|
-
return true;
|
|
2604
|
-
}
|
|
2716
|
+
printUpdateStatus(status, { compact: true });
|
|
2717
|
+
markUpdateDeferred(home, status.latest_version);
|
|
2605
2718
|
return false;
|
|
2606
2719
|
}
|
|
2607
2720
|
|
|
@@ -2968,32 +3081,137 @@ async function updateMain(rawArgs) {
|
|
|
2968
3081
|
printUpdateStatus(status, { compact: true });
|
|
2969
3082
|
process.exit(0);
|
|
2970
3083
|
}
|
|
3084
|
+
printUpdateStatus(status, { compact: true });
|
|
3085
|
+
process.exit(0);
|
|
3086
|
+
}
|
|
2971
3087
|
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
process.exit(
|
|
3088
|
+
async function migrateMain(rawArgs) {
|
|
3089
|
+
const options = parseMigrateArgs(rawArgs);
|
|
3090
|
+
if (!options) {
|
|
3091
|
+
printMigrateHelp();
|
|
3092
|
+
process.exit(1);
|
|
2977
3093
|
}
|
|
2978
|
-
if (
|
|
2979
|
-
|
|
2980
|
-
console.log(`Skipped ${status.latest_version}.`);
|
|
3094
|
+
if (options.help) {
|
|
3095
|
+
printMigrateHelp();
|
|
2981
3096
|
process.exit(0);
|
|
2982
3097
|
}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3098
|
+
|
|
3099
|
+
const sourceHome = realpathOrSelf(options.home || resolveHome(rawArgs));
|
|
3100
|
+
const targetHome = path.resolve(options.target);
|
|
3101
|
+
if (!fs.existsSync(sourceHome)) {
|
|
3102
|
+
console.error(`DeepScientist source path does not exist: ${sourceHome}`);
|
|
3103
|
+
process.exit(1);
|
|
3104
|
+
}
|
|
3105
|
+
if (isPathEqual(sourceHome, targetHome)) {
|
|
3106
|
+
console.error('DeepScientist source and target paths are identical. Choose a different migration target.');
|
|
3107
|
+
process.exit(1);
|
|
3108
|
+
}
|
|
3109
|
+
if (isPathInside(targetHome, sourceHome) || isPathInside(sourceHome, targetHome)) {
|
|
3110
|
+
console.error('DeepScientist migration requires two separate sibling paths. Do not nest one path inside the other.');
|
|
3111
|
+
process.exit(1);
|
|
3112
|
+
}
|
|
3113
|
+
if (fs.existsSync(targetHome)) {
|
|
3114
|
+
console.error(`DeepScientist target path already exists: ${targetHome}`);
|
|
3115
|
+
process.exit(1);
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
printMigrationSummary({ sourceHome, targetHome, restart: options.restart });
|
|
3119
|
+
if (!options.yes) {
|
|
3120
|
+
const confirmed = await promptMigrationConfirmation({ sourceHome, targetHome });
|
|
3121
|
+
if (!confirmed) {
|
|
3122
|
+
console.log('DeepScientist migration cancelled.');
|
|
3123
|
+
process.exit(1);
|
|
2987
3124
|
}
|
|
2988
|
-
process.exit(0);
|
|
2989
3125
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3126
|
+
|
|
3127
|
+
const state = readDaemonState(sourceHome);
|
|
3128
|
+
const configured = readConfiguredUiAddressFromFile(sourceHome);
|
|
3129
|
+
const url = state?.url || browserUiUrl(configured.host, configured.port);
|
|
3130
|
+
const health = await fetchHealth(url);
|
|
3131
|
+
if (state || healthMatchesHome({ health, home: sourceHome })) {
|
|
3132
|
+
await stopDaemon(sourceHome);
|
|
3133
|
+
} else if (health && health.status === 'ok') {
|
|
3134
|
+
console.log(`Skipping daemon stop because ${url} belongs to another DeepScientist home.`);
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
const pythonRuntime = ensurePythonRuntime(sourceHome);
|
|
3138
|
+
const runtimePython = pythonRuntime.runtimePython;
|
|
3139
|
+
const result = runPythonCli(
|
|
3140
|
+
runtimePython,
|
|
3141
|
+
['--home', sourceHome, 'migrate', targetHome],
|
|
3142
|
+
{ capture: true, allowFailure: true }
|
|
3143
|
+
);
|
|
3144
|
+
let payload = null;
|
|
3145
|
+
try {
|
|
3146
|
+
payload = JSON.parse(String(result.stdout || '{}'));
|
|
3147
|
+
} catch {
|
|
3148
|
+
payload = null;
|
|
3149
|
+
}
|
|
3150
|
+
if (result.status !== 0 || !payload || payload.ok !== true) {
|
|
3151
|
+
if (result.stdout) {
|
|
3152
|
+
process.stdout.write(result.stdout);
|
|
3153
|
+
if (!String(result.stdout).endsWith('\n')) {
|
|
3154
|
+
process.stdout.write('\n');
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
if (result.stderr) {
|
|
3158
|
+
process.stderr.write(result.stderr);
|
|
3159
|
+
if (!String(result.stderr).endsWith('\n')) {
|
|
3160
|
+
process.stderr.write('\n');
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
console.error('DeepScientist migration failed.');
|
|
3164
|
+
process.exit(result.status ?? 1);
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
repairMigratedInstallWrappers(targetHome);
|
|
3168
|
+
const rewrittenWrappers = rewriteLauncherWrappersIfPointingAtSource({ sourceHome, targetHome });
|
|
3169
|
+
|
|
3170
|
+
const sourceContainsCurrentInstall = isPathEqual(repoRoot, path.join(sourceHome, 'cli')) || isPathInside(repoRoot, sourceHome);
|
|
3171
|
+
if (sourceContainsCurrentInstall) {
|
|
3172
|
+
scheduleDeferredSourceCleanup({ sourceHome, targetHome });
|
|
3173
|
+
} else {
|
|
3174
|
+
fs.rmSync(sourceHome, { recursive: true, force: true });
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
let restartMessage = 'Restart skipped.';
|
|
3178
|
+
if (options.restart) {
|
|
3179
|
+
const migratedLauncher = path.join(targetHome, 'cli', 'bin', 'ds.js');
|
|
3180
|
+
if (!fs.existsSync(migratedLauncher)) {
|
|
3181
|
+
restartMessage = `Migration succeeded, but restart was skipped because the migrated launcher is missing: ${migratedLauncher}`;
|
|
3182
|
+
} else {
|
|
3183
|
+
const child = spawn(
|
|
3184
|
+
process.execPath,
|
|
3185
|
+
[migratedLauncher, '--home', targetHome, '--daemon-only', '--no-browser', '--skip-update-check'],
|
|
3186
|
+
{
|
|
3187
|
+
cwd: path.join(targetHome, 'cli'),
|
|
3188
|
+
detached: true,
|
|
3189
|
+
stdio: 'ignore',
|
|
3190
|
+
env: process.env,
|
|
3191
|
+
}
|
|
3192
|
+
);
|
|
3193
|
+
child.unref();
|
|
3194
|
+
restartMessage = 'Managed daemon restart scheduled from the migrated home.';
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
console.log('');
|
|
3199
|
+
console.log('DeepScientist migration completed.');
|
|
3200
|
+
console.log(`New home: ${targetHome}`);
|
|
3201
|
+
if (payload.summary) {
|
|
3202
|
+
console.log(payload.summary);
|
|
3203
|
+
}
|
|
3204
|
+
if (rewrittenWrappers.length > 0) {
|
|
3205
|
+
console.log(`Updated wrappers: ${rewrittenWrappers.join(', ')}`);
|
|
3206
|
+
}
|
|
3207
|
+
console.log(restartMessage);
|
|
3208
|
+
if (sourceContainsCurrentInstall) {
|
|
3209
|
+
console.log(`Old path cleanup has been scheduled: ${sourceHome}`);
|
|
3210
|
+
} else {
|
|
3211
|
+
console.log(`Old path removed: ${sourceHome}`);
|
|
3212
|
+
}
|
|
3213
|
+
console.log(`Use \`ds --home ${targetHome}\` if you want to override the default explicitly.`);
|
|
3214
|
+
process.exit(0);
|
|
2997
3215
|
}
|
|
2998
3216
|
|
|
2999
3217
|
async function launcherMain(rawArgs) {
|
|
@@ -3097,6 +3315,10 @@ async function main() {
|
|
|
3097
3315
|
await updateMain(args);
|
|
3098
3316
|
return;
|
|
3099
3317
|
}
|
|
3318
|
+
if (positional && positional.value === 'migrate') {
|
|
3319
|
+
await migrateMain(args);
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3100
3322
|
if (args.length === 0 || args[0] === 'ui' || (!positional && args[0]?.startsWith('--'))) {
|
|
3101
3323
|
await launcherMain(args);
|
|
3102
3324
|
return;
|
|
@@ -3144,9 +3366,11 @@ module.exports = {
|
|
|
3144
3366
|
legacyVenvRootPath,
|
|
3145
3367
|
resolveUvBinary,
|
|
3146
3368
|
resolveHome,
|
|
3369
|
+
parseMigrateArgs,
|
|
3147
3370
|
useEditableProjectInstall,
|
|
3148
3371
|
compareVersions,
|
|
3149
3372
|
detectInstallMode,
|
|
3373
|
+
updateManualCommand,
|
|
3150
3374
|
buildUpdateStatus,
|
|
3151
3375
|
},
|
|
3152
3376
|
};
|