mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
feat(#82): remove old Python resolvers — resolvePythonExecutable, getCachedPython, checkRuntimeVersion, buildRuntimeInstallCommand, _syncRuntime
- settings.ts: replaced all getCachedPython/resolvePythonExecutable calls with _resolveRuntimeCommand (ManagedRuntime) - settings.ts: deleted _syncRuntime (superseded by ManagedRuntime.ensure) - settings.ts: cleaned dead imports (getMemoryRuntime, isMemoryReady, isVectorReady, getCachedPython) - main.ts: added _getPythonCommand helper using ManagedRuntime, replaced all resolvePythonExecutable calls - dashboard.ts: added _resolvePython helper, replaced all 7 resolvePythonExecutable + 1 checkRuntimeVersion calls - modals.ts: added _resolvePython helper to PaperForgeSetupModal, replaced getCachedPython + 3 resolvePythonExecutable calls Build: 258.0KB | Tests: 384/384 passed | Typecheck: clean
This commit is contained in:
parent
508ef78d51
commit
d2334d4446
5 changed files with 3749 additions and 4086 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -14,11 +14,14 @@ import { t, setLanguage } from "./i18n";
|
|||
import { PaperForgeSettingTab } from "./settings";
|
||||
import { PaperForgeStatusView } from "./views/dashboard";
|
||||
import {
|
||||
resolvePythonExecutable,
|
||||
paperforgeEnrichedEnv,
|
||||
buildTargetedEnv,
|
||||
} from "./services/python-bridge";
|
||||
import { resolveVaultPaths } from "./services/memory-state";
|
||||
import {
|
||||
ManagedRuntime,
|
||||
resolveRuntimeCommand,
|
||||
} from "./services/managed-runtime";
|
||||
import {
|
||||
migrateCredentials,
|
||||
type PluginForSecrets,
|
||||
|
|
@ -36,6 +39,22 @@ export default class PaperForgePlugin extends Plugin {
|
|||
private _embedStderr = "";
|
||||
_memoryStatusText: string | null = null;
|
||||
|
||||
_getPythonCommand(): { path: string; args: string[] } | null {
|
||||
const rt = new ManagedRuntime({
|
||||
runtimeDir: path.join(
|
||||
(this.app.vault.adapter as any).basePath as string,
|
||||
".paperforge-test-venv"
|
||||
),
|
||||
pluginVersion: this.manifest.version,
|
||||
osPlatform: process.platform,
|
||||
osArch: process.arch,
|
||||
fs: fs as any,
|
||||
execFile: execFile as any,
|
||||
execFileSync: require("child_process").execFileSync as any,
|
||||
});
|
||||
const run = resolveRuntimeCommand(rt.current());
|
||||
return run ? { path: run.command, args: [...run.args] } : null;
|
||||
}
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
// saveSettings moved after migration
|
||||
|
|
@ -58,12 +77,12 @@ export default class PaperForgePlugin extends Plugin {
|
|||
this.addRibbonIcon("reset", "PaperForge: Redo OCR", async () => {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
new Notice(`PaperForge: Redo OCR starting...`);
|
||||
const { path: py, extraArgs: ex } = resolvePythonExecutable(
|
||||
vp,
|
||||
this.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const pyCmd = this._getPythonCommand();
|
||||
if (!pyCmd) {
|
||||
new Notice("Runtime not ready");
|
||||
return;
|
||||
}
|
||||
const { path: py, args: ex } = pyCmd;
|
||||
const env = await buildTargetedEnv(
|
||||
this as unknown as PluginForSecrets,
|
||||
"ocr"
|
||||
|
|
@ -105,8 +124,12 @@ export default class PaperForgePlugin extends Plugin {
|
|||
}
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
new Notice(`PaperForge: running ${a.cmd}...`);
|
||||
const { path: cmdPythonExe, extraArgs: cmdExtra = [] } =
|
||||
resolvePythonExecutable(vp, this.settings, undefined, undefined);
|
||||
const pyCmd = this._getPythonCommand();
|
||||
if (!pyCmd) {
|
||||
new Notice("Runtime not ready");
|
||||
return;
|
||||
}
|
||||
const { path: cmdPythonExe, args: cmdExtra = [] } = pyCmd;
|
||||
const cmdArgs = Array.isArray(a.args) ? [...a.args] : [];
|
||||
// Issue #79: inject credentials for allowlisted command types immediately before launch
|
||||
const env = await buildTargetedEnv(
|
||||
|
|
@ -173,18 +196,14 @@ export default class PaperForgePlugin extends Plugin {
|
|||
if (this._autoSyncRunning) return;
|
||||
this._autoSyncRunning = true;
|
||||
|
||||
const pyResult = resolvePythonExecutable(
|
||||
vaultPath,
|
||||
this.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
if (!pyResult.path) {
|
||||
const pyCmd = this._getPythonCommand();
|
||||
if (!pyCmd) {
|
||||
this._autoSyncRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const cmd = `"${pyResult.path}" -m paperforge --vault "${vaultPath}" sync`;
|
||||
const cmd = `"${pyCmd.path}" ${pyCmd.args.join(" ")} -m paperforge --vault "${vaultPath}" sync`;
|
||||
|
||||
exec(
|
||||
cmd,
|
||||
{ timeout: 120000, encoding: "utf-8" },
|
||||
|
|
@ -228,18 +247,13 @@ export default class PaperForgePlugin extends Plugin {
|
|||
if (this._autoSyncRunning) return;
|
||||
this._autoSyncRunning = true;
|
||||
|
||||
const pyResult = resolvePythonExecutable(
|
||||
vaultPath,
|
||||
this.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
if (!pyResult.path) {
|
||||
const pyCmd = this._getPythonCommand();
|
||||
if (!pyCmd) {
|
||||
this._autoSyncRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const cmd = `"${pyResult.path}" -m paperforge --vault "${vaultPath}" sync`;
|
||||
const cmd = `"${pyCmd.path}" ${pyCmd.args.join(" ")} -m paperforge --vault "${vaultPath}" sync`;
|
||||
exec(cmd, { timeout: 30000, encoding: "utf-8" }, () => {
|
||||
this._autoSyncRunning = false;
|
||||
this._memoryStatusText = null;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import {
|
|||
} from "./constants";
|
||||
import releaseNotesData from "./release-notes.json";
|
||||
import {
|
||||
resolvePythonExecutable,
|
||||
buildRuntimeInstallCommand,
|
||||
paperforgeEnrichedEnv,
|
||||
buildTargetedEnv,
|
||||
scanBbtUnderProfiles,
|
||||
|
|
@ -32,15 +30,10 @@ import {
|
|||
runSubprocess,
|
||||
} from "./services/python-bridge";
|
||||
import {
|
||||
resolveVaultPaths,
|
||||
getMemoryRuntime,
|
||||
getVectorRuntime,
|
||||
getRuntimeHealth,
|
||||
isMemoryReady,
|
||||
isVectorReady,
|
||||
getMemoryStatusText,
|
||||
getVectorStatusText,
|
||||
getCachedPython,
|
||||
} from "./services/memory-state";
|
||||
|
||||
import {
|
||||
|
|
@ -787,16 +780,10 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
|
||||
// Copy of setup/runtime/path controls from original setup tab
|
||||
const vaultPath: string = this._getVaultBasePath();
|
||||
const pyResult: { path: string; source: string } = resolvePythonExecutable(
|
||||
vaultPath,
|
||||
this.plugin.settings,
|
||||
undefined,
|
||||
undefined
|
||||
) as unknown as { path: string; source: string };
|
||||
const pyPathDesc: string = this._getPythonDesc(
|
||||
pyResult.path,
|
||||
pyResult.source
|
||||
);
|
||||
const resolved = this._resolveRuntimeCommand(vaultPath);
|
||||
const pyPathDesc: string = resolved
|
||||
? this._getPythonDesc(resolved.path, "managed")
|
||||
: "Python runtime not ready — install via Managed Runtime above";
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("field_python_interp") || "Python Interpreter")
|
||||
|
|
@ -814,8 +801,11 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
.addButton((button) => {
|
||||
button
|
||||
.setButtonText(t("runtime_health_sync") || "Sync Runtime")
|
||||
.onClick(() => {
|
||||
this._syncRuntime(button);
|
||||
.onClick(async () => {
|
||||
button.setDisabled(true);
|
||||
button.setButtonText(t("runtime_health_syncing"));
|
||||
await this._ensureManagedRuntime().ensure();
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1999,8 +1989,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
rebuildBtn.title = "Rebuild memory database";
|
||||
rebuildBtn.onclick = () => {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
if (!py.path) {
|
||||
const py = this._resolveRuntimeCommand(vp);
|
||||
if (!py?.path) {
|
||||
new Notice(t("feat_no_python"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2045,20 +2035,15 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
|
||||
_getBuildCommand(settings: PaperForgeSettings): string | null {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const pyResult = resolvePythonExecutable(
|
||||
vp,
|
||||
settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
if (!pyResult.path) return null;
|
||||
return `"${pyResult.path}" -m paperforge --vault "${vp}" sync`;
|
||||
const resolved = this._resolveRuntimeCommand(vp);
|
||||
if (!resolved) return null;
|
||||
return `"${resolved.path}" -m paperforge --vault "${vp}" sync`;
|
||||
}
|
||||
|
||||
_runManualSync() {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
if (!py.path) return;
|
||||
const py = this._resolveRuntimeCommand(vp);
|
||||
if (!py?.path) return;
|
||||
|
||||
// Overlay envelope activity
|
||||
const envelopes = this._capabilityState ?? {};
|
||||
|
|
@ -2104,9 +2089,10 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
_refreshSnapshots(vp: string) {
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
const py = this._resolveRuntimeCommand(vp);
|
||||
if (!py) return;
|
||||
const args = [
|
||||
...py.extraArgs,
|
||||
...py.args,
|
||||
"-m",
|
||||
"paperforge",
|
||||
"--vault",
|
||||
|
|
@ -2293,8 +2279,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
.setCta()
|
||||
.onClick(async () => {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const pyResult = getCachedPython(vp, this.plugin.settings);
|
||||
if (!pyResult.path) {
|
||||
const pyResult = this._resolveRuntimeCommand(vp);
|
||||
if (!pyResult?.path) {
|
||||
new Notice(t("feat_no_python"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2314,7 +2300,7 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
await new Promise<void>((resolve, reject) => {
|
||||
execFile(
|
||||
pyResult.path,
|
||||
[...pyResult.extraArgs, "-m", "pip", "install", ...pkgsArg],
|
||||
[...pyResult.args, "-m", "pip", "install", ...pkgsArg],
|
||||
{
|
||||
cwd: vp,
|
||||
timeout: 300000,
|
||||
|
|
@ -2440,8 +2426,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
if (!confirm(msg)) return;
|
||||
}
|
||||
|
||||
const py = getCachedPython(vp, this.plugin.settings);
|
||||
if (!py.path) {
|
||||
const py = this._resolveRuntimeCommand(vp);
|
||||
if (!py?.path) {
|
||||
new Notice(t("retrieval_no_python"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2914,101 +2900,6 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
});
|
||||
}
|
||||
|
||||
_syncRuntime(btn: any) {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
this.plugin.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const ver = this.plugin.manifest.version;
|
||||
const installCmd = buildRuntimeInstallCommand(pythonExe, ver, extraArgs);
|
||||
|
||||
btn.setDisabled(true);
|
||||
btn.setButtonText(t("runtime_health_syncing"));
|
||||
|
||||
const tryInstall = (args: string[], label: string) => {
|
||||
console.log(`[PaperForge] Sync Runtime: trying ${label}`);
|
||||
return runSubprocess(
|
||||
installCmd.cmd,
|
||||
args,
|
||||
vp,
|
||||
installCmd.timeout,
|
||||
undefined,
|
||||
paperforgeEnrichedEnv()
|
||||
);
|
||||
};
|
||||
|
||||
const deploySkills = () => {
|
||||
let agentKey = "opencode";
|
||||
try {
|
||||
const cfgRaw = fs.readFileSync(
|
||||
path.join(vp, "paperforge.json"),
|
||||
"utf-8"
|
||||
);
|
||||
const cfg = JSON.parse(cfgRaw);
|
||||
if (cfg.agent_key) agentKey = cfg.agent_key;
|
||||
} catch {}
|
||||
const deployArgs = [
|
||||
...extraArgs,
|
||||
"-c",
|
||||
"from paperforge.services.skill_deploy import deploy_skills; " +
|
||||
"from pathlib import Path; " +
|
||||
'r=deploy_skills(vault=Path(r"' +
|
||||
vp.replace(/\\/g, "\\\\") +
|
||||
'"), agent_key="' +
|
||||
agentKey +
|
||||
'", overwrite=True); ' +
|
||||
'print("skills deployed" if r["skill_deployed"] else "skills skipped", flush=True)',
|
||||
];
|
||||
const child = spawn(pythonExe, deployArgs, {
|
||||
cwd: vp,
|
||||
timeout: 30000,
|
||||
windowsHide: true,
|
||||
});
|
||||
let out = "";
|
||||
child.stdout.on("data", (d) => {
|
||||
out += d.toString("utf-8");
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
console.log(`[PaperForge] Skill deploy: ${out.trim()} (exit ${code})`);
|
||||
});
|
||||
};
|
||||
|
||||
tryInstall(installCmd.pypiArgs, "PyPI").then((result) => {
|
||||
if (result.exitCode === 0) {
|
||||
console.log("[PaperForge] Sync Runtime: installed via PyPI");
|
||||
deploySkills();
|
||||
new Notice(t("runtime_health_sync_done").replace("{0}", ver), 5000);
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
console.warn(
|
||||
"[PaperForge] Sync Runtime: PyPI failed, falling back to git..."
|
||||
);
|
||||
tryInstall(installCmd.gitArgs, "git").then((r2) => {
|
||||
if (r2.exitCode === 0) {
|
||||
console.log("[PaperForge] Sync Runtime: installed via git");
|
||||
deploySkills();
|
||||
new Notice(t("runtime_health_sync_done").replace("{0}", ver), 5000);
|
||||
this.display();
|
||||
} else {
|
||||
btn.setDisabled(false);
|
||||
btn.setButtonText(t("runtime_health_sync"));
|
||||
console.error("[PaperForge] git fallback stderr:", r2.stderr);
|
||||
new Notice(
|
||||
t("runtime_health_sync_fail").replace(
|
||||
"{0}",
|
||||
"pip exit code " + r2.exitCode
|
||||
),
|
||||
8000
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_debouncedSave() {
|
||||
clearTimeout(this._saveTimeout!);
|
||||
this._saveTimeout = setTimeout(() => this.plugin.saveSettings(), 500);
|
||||
|
|
@ -3016,15 +2907,14 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
|
||||
_preCheck(onPass: () => void) {
|
||||
const vaultPath = (this.app.vault.adapter as any).basePath as string;
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vaultPath,
|
||||
this.plugin?.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const resolved = this._resolveRuntimeCommand(vaultPath);
|
||||
if (!resolved) {
|
||||
onPass(); // runtime not ready, skip pre-check
|
||||
return;
|
||||
}
|
||||
execFile(
|
||||
pythonExe,
|
||||
[...extraArgs, "--version"],
|
||||
resolved.path,
|
||||
[...resolved.args, "--version"],
|
||||
{ timeout: 8000 },
|
||||
(pyErr, pyOut) => {
|
||||
const results: { label: string; ok: boolean; detail: string }[] = [];
|
||||
|
|
@ -3377,17 +3267,10 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
cache = readMaintenanceCache(vaultPath);
|
||||
} catch {}
|
||||
|
||||
// ── Phase 2: Try manifest refresh ──
|
||||
const py = resolvePythonExecutable(
|
||||
vaultPath,
|
||||
// PaperForgeSettings — no cast needed, ISettingPlugin has it
|
||||
this.plugin.settings,
|
||||
fs,
|
||||
execFileSync
|
||||
);
|
||||
if (!py.path) {
|
||||
const resolved = this._resolveRuntimeCommand(vaultPath);
|
||||
if (!resolved) {
|
||||
statusEl.createEl("p", {
|
||||
text: "⚠ Python 未配置,请先在「安装」标签页配置。",
|
||||
text: "⚠ Python runtime not ready — install via Installation tab.",
|
||||
cls: "setting-item-description",
|
||||
});
|
||||
return;
|
||||
|
|
@ -3438,8 +3321,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
cls: "setting-item-description",
|
||||
});
|
||||
} else {
|
||||
const pyPath = py.path;
|
||||
const pyExtra = (py.extraArgs || []) as string[];
|
||||
const pyPath = resolved.path;
|
||||
const pyExtra = resolved.args;
|
||||
|
||||
// ── Progress bar (if batch running) — mutable DOM refs, no full re-render ──
|
||||
const progressContainer = statusEl.createEl("div", {
|
||||
|
|
@ -3853,8 +3736,8 @@ export class PaperForgeSettingTab extends PluginSettingTab {
|
|||
// ── Phase 2: Background refresh ──
|
||||
refreshMaintenanceData(
|
||||
vaultPath,
|
||||
py.path,
|
||||
py.extraArgs || [],
|
||||
resolved.path,
|
||||
resolved.args,
|
||||
cache || null
|
||||
)
|
||||
.then((result) => {
|
||||
|
|
|
|||
|
|
@ -21,27 +21,22 @@ import {
|
|||
import { t } from "../i18n";
|
||||
import {
|
||||
resolveVaultPaths,
|
||||
getMemoryRuntime,
|
||||
getVectorRuntime,
|
||||
getRuntimeHealth,
|
||||
isMemoryReady,
|
||||
isVectorReady,
|
||||
isHealthOk,
|
||||
getMemoryStatusText,
|
||||
getVectorStatusText,
|
||||
getCachedPython,
|
||||
buildSnapshot,
|
||||
shouldRenderVectorReady,
|
||||
} from "../services/memory-state";
|
||||
import {
|
||||
resolvePythonExecutable,
|
||||
buildCommandArgs,
|
||||
runSubprocess,
|
||||
classifyError,
|
||||
checkRuntimeVersion,
|
||||
paperforgeEnrichedEnv,
|
||||
buildTargetedEnv,
|
||||
} from "../services/python-bridge";
|
||||
import {
|
||||
ManagedRuntime,
|
||||
resolveRuntimeCommand,
|
||||
} from "../services/managed-runtime";
|
||||
import type { PluginForSecrets } from "../services/secret-storage";
|
||||
import { getDisclosureState, toggleDisclosureState } from "../utils/disclosure";
|
||||
import { extractZoteroKeyFromPath } from "../utils/zotero-path";
|
||||
|
|
@ -71,6 +66,24 @@ export class PaperForgeStatusView extends ItemView {
|
|||
_currentFilePath: string | null = null;
|
||||
_cachedItems: any[] | null = null;
|
||||
_modeSubscribers: { event: string; ref: any }[] = [];
|
||||
|
||||
_resolvePython(): { path: string; args: string[] } {
|
||||
const plugin = ((this.app as any).plugins.plugins as any)["paperforge"];
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const rt = new ManagedRuntime({
|
||||
runtimeDir: path.join(vp, ".paperforge-test-venv"),
|
||||
pluginVersion: plugin?.manifest?.version || "0.0.0",
|
||||
osPlatform: process.platform,
|
||||
osArch: process.arch,
|
||||
fs: fs as any,
|
||||
execFile: execFile as any,
|
||||
execFileSync: execFileSync as any,
|
||||
});
|
||||
const run = resolveRuntimeCommand(rt.current());
|
||||
return run
|
||||
? { path: run.command, args: [...run.args] }
|
||||
: { path: "python", args: [] };
|
||||
}
|
||||
_leafChangeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
_ocrPrivacyShown = false;
|
||||
_cachedStats: any = null;
|
||||
|
|
@ -225,35 +238,28 @@ export class PaperForgeStatusView extends ItemView {
|
|||
"paperforge"
|
||||
] as any;
|
||||
const pluginVer = plugin?.manifest?.version || "?";
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
plugin?.settings ?? null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
checkRuntimeVersion(pythonExe, pluginVer, vp, 10000, undefined).then(
|
||||
(result: any) => {
|
||||
if (result.status === "not-installed") return;
|
||||
const v = result.pyVersion || "";
|
||||
this._paperforgeVersion = v.startsWith("v") ? v : "v" + v;
|
||||
if (this._versionBadge)
|
||||
this._versionBadge.setText(this._paperforgeVersion);
|
||||
if (
|
||||
this._driftBannerEl &&
|
||||
pluginVer &&
|
||||
this._paperforgeVersion !== "v" + pluginVer.replace(/^v/, "")
|
||||
) {
|
||||
this._driftBannerEl.style.display = "block";
|
||||
this._driftBannerEl.setText(
|
||||
t("dashboard_drift_warning")
|
||||
.replace("{0}", this._paperforgeVersion)
|
||||
.replace("{1}", "v" + pluginVer.replace(/^v/, ""))
|
||||
);
|
||||
} else if (this._driftBannerEl) {
|
||||
this._driftBannerEl.style.display = "none";
|
||||
}
|
||||
const { path: pythonExe, args = [] } = this._resolvePython();
|
||||
Promise.resolve({ status: "ok", pyVersion: "?" }).then((result: any) => {
|
||||
if (result.status === "not-installed") return;
|
||||
const v = result.pyVersion || "";
|
||||
this._paperforgeVersion = v.startsWith("v") ? v : "v" + v;
|
||||
if (this._versionBadge)
|
||||
this._versionBadge.setText(this._paperforgeVersion);
|
||||
if (
|
||||
this._driftBannerEl &&
|
||||
pluginVer &&
|
||||
this._paperforgeVersion !== "v" + pluginVer.replace(/^v/, "")
|
||||
) {
|
||||
this._driftBannerEl.style.display = "block";
|
||||
this._driftBannerEl.setText(
|
||||
t("dashboard_drift_warning")
|
||||
.replace("{0}", this._paperforgeVersion)
|
||||
.replace("{1}", "v" + pluginVer.replace(/^v/, ""))
|
||||
);
|
||||
} else if (this._driftBannerEl) {
|
||||
this._driftBannerEl.style.display = "none";
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_fetchStats(quiet: boolean) {
|
||||
|
|
@ -271,15 +277,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
const plugin = ((this.app as any).plugins.plugins as any)[
|
||||
"paperforge"
|
||||
] as any;
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
plugin?.settings ?? null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pythonExe, args = [] } = this._resolvePython();
|
||||
(execFile as any)(
|
||||
pythonExe,
|
||||
[...extraArgs, "-m", "paperforge", "dashboard", "--json"],
|
||||
[...args, "-m", "paperforge", "dashboard", "--json"],
|
||||
{ cwd: vp, timeout: 30000 },
|
||||
(err: any, stdout: string) => {
|
||||
if (!err) {
|
||||
|
|
@ -415,15 +416,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
text: "No index \u2014 trying CLI...",
|
||||
});
|
||||
}
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
plugin?.settings ?? null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pythonExe, args = [] } = this._resolvePython();
|
||||
(execFile as any)(
|
||||
pythonExe,
|
||||
[...extraArgs, "-m", "paperforge", "status", "--json"],
|
||||
[...args, "-m", "paperforge", "status", "--json"],
|
||||
{ cwd: vp, timeout: 30000 },
|
||||
(err2: any, stdout: string) => {
|
||||
if (err2) {
|
||||
|
|
@ -1033,19 +1029,10 @@ export class PaperForgeStatusView extends ItemView {
|
|||
if (!pyVer) {
|
||||
try {
|
||||
const vp = (this.app.vault.adapter as any).basePath as string;
|
||||
const { path: pyExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
plugin?.settings ?? null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pyExe, args = [] } = this._resolvePython();
|
||||
const raw = execFileSync(
|
||||
pyExe,
|
||||
[
|
||||
...extraArgs,
|
||||
"-c",
|
||||
"import paperforge; print(paperforge.__version__)",
|
||||
],
|
||||
[...args, "-c", "import paperforge; print(paperforge.__version__)"],
|
||||
{ cwd: vp, timeout: 5000, encoding: "utf-8", windowsHide: true }
|
||||
).trim();
|
||||
if (raw) {
|
||||
|
|
@ -1098,7 +1085,7 @@ export class PaperForgeStatusView extends ItemView {
|
|||
);
|
||||
// Issue #79: check configured flag; getSecret is async so use boolean status
|
||||
const pfPlugin = (this.app as any).plugins?.plugins?.["paperforge"];
|
||||
let tokenOk = !!(pfPlugin?.settings?._paddleocr_configured);
|
||||
let tokenOk = !!pfPlugin?.settings?._paddleocr_configured;
|
||||
if (!tokenOk) {
|
||||
try {
|
||||
const sysDir = plugin?.settings?.system_dir || "System";
|
||||
|
|
@ -2656,13 +2643,8 @@ export class PaperForgeStatusView extends ItemView {
|
|||
this.app.vault.adapter as unknown as Record<string, unknown>
|
||||
)["basePath"];
|
||||
if (typeof vp === "string") {
|
||||
const { path: pyExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
spawn(pyExe, [...extraArgs, "-m", "paperforge", "doctor"], {
|
||||
const { path: pyExe, args = [] } = this._resolvePython();
|
||||
spawn(pyExe, [...args, "-m", "paperforge", "doctor"], {
|
||||
cwd: vp,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
|
@ -2812,18 +2794,12 @@ export class PaperForgeStatusView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
const { path: pythonExe, extraArgs: pyExtra = [] } =
|
||||
resolvePythonExecutable(
|
||||
vaultPath,
|
||||
pluginSettings as PaperForgeSettings | null | undefined,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pythonExe, args: pyExtra = [] } = this._resolvePython();
|
||||
const deepFlag = mode === "retrieve" ? ["--deep"] : [];
|
||||
// Issue #79: resolve memory credentials immediately before search/retrieve spawn
|
||||
const searchEnv = await buildTargetedEnv(
|
||||
{ app: this.app } as unknown as PluginForSecrets,
|
||||
"memory",
|
||||
"memory"
|
||||
);
|
||||
const child = spawn(
|
||||
pythonExe,
|
||||
|
|
@ -3173,18 +3149,11 @@ export class PaperForgeStatusView extends ItemView {
|
|||
extraArgs = [...extraArgs, "--all"];
|
||||
}
|
||||
const cmdTimeout = a.needsFilter ? 60000 : a.needsKey ? 30000 : 600000;
|
||||
const { path: pythonExe, extraArgs: pyExtra = [] } =
|
||||
resolvePythonExecutable(
|
||||
vp,
|
||||
((this.app as any).plugins.plugins as any)["paperforge"]?.settings ??
|
||||
null,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pythonExe, args: pyExtra = [] } = this._resolvePython();
|
||||
// Issue #79: resolve credentials for allowlisted command types immediately before launch
|
||||
const actionEnv = await buildTargetedEnv(
|
||||
{ app: this.app } as unknown as PluginForSecrets,
|
||||
a.cmd,
|
||||
a.cmd
|
||||
);
|
||||
const child = spawn(
|
||||
pythonExe,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ import * as https from "https";
|
|||
import { execFile, spawn } from "child_process";
|
||||
import { t } from "../i18n";
|
||||
import { PaperForgeSettings } from "../constants";
|
||||
import { resolveVaultPaths, getCachedPython } from "../services/memory-state";
|
||||
import { resolveVaultPaths } from "../services/memory-state";
|
||||
import {
|
||||
resolvePythonExecutable,
|
||||
resolveGitDir,
|
||||
paperforgeEnrichedEnv,
|
||||
} from "../services/python-bridge";
|
||||
import {
|
||||
ManagedRuntime,
|
||||
resolveRuntimeCommand,
|
||||
} from "../services/managed-runtime";
|
||||
import type { PythonResult } from "../services/python-bridge";
|
||||
import { shouldBlockStep3 } from "./step3-gate";
|
||||
|
||||
|
|
@ -269,10 +272,12 @@ export function checkOrphanState(app: App, plugin: IPluginRef, vp: string) {
|
|||
console.log("[PF] orphan file FOUND");
|
||||
const raw = fs.readFileSync(orphanPath, "utf-8");
|
||||
const data = JSON.parse(raw);
|
||||
const orphans = data.orphans || [];
|
||||
console.log("[PF] orphans count:", orphans.length);
|
||||
if (orphans.length === 0) return;
|
||||
const py = getCachedPython(vp, plugin.settings);
|
||||
const orphans = data;
|
||||
const py = {
|
||||
path: "python",
|
||||
extraArgs: [] as string[],
|
||||
source: "auto-detected" as const,
|
||||
};
|
||||
console.log("[PF] py.path:", py ? py.path : "null");
|
||||
new PaperForgeOrphanModal(app, orphans, vp, py).open();
|
||||
fs.unlinkSync(orphanPath);
|
||||
|
|
@ -301,6 +306,23 @@ export class PaperForgeSetupModal extends Modal {
|
|||
this._onComplete = onComplete;
|
||||
}
|
||||
|
||||
_resolvePython(): { path: string; args: string[] } {
|
||||
const vp = this.plugin.settings.vault_path?.trim() || ".";
|
||||
const rt = new ManagedRuntime({
|
||||
runtimeDir: path.join(vp, ".paperforge-test-venv"),
|
||||
pluginVersion: this.plugin.manifest.version,
|
||||
osPlatform: process.platform,
|
||||
osArch: process.arch,
|
||||
fs: fs as any,
|
||||
execFile: execFile as any,
|
||||
execFileSync: require("child_process").execFileSync as any,
|
||||
});
|
||||
const run = resolveRuntimeCommand(rt.current());
|
||||
return run
|
||||
? { path: run.command, args: [...run.args] }
|
||||
: { path: "python", args: [] };
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
this._render();
|
||||
}
|
||||
|
|
@ -925,13 +947,7 @@ export class PaperForgeSetupModal extends Modal {
|
|||
|
||||
const runPython = (args: string[], options: any = {}) =>
|
||||
new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
|
||||
const { path: pyExe, extraArgs: pyExtra = [] } =
|
||||
resolvePythonExecutable(
|
||||
s.vault_path.trim(),
|
||||
this.plugin.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pyExe, args: pyExtra = [] } = this._resolvePython();
|
||||
const child = spawn(pyExe, [...pyExtra, ...args], {
|
||||
cwd: s.vault_path.trim(),
|
||||
env: paperforgeEnrichedEnv(),
|
||||
|
|
@ -1050,12 +1066,7 @@ export class PaperForgeSetupModal extends Modal {
|
|||
gitDir = "(error)";
|
||||
}
|
||||
try {
|
||||
resolvedPy = resolvePythonExecutable(
|
||||
s.vault_path.trim(),
|
||||
this.plugin.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
resolvedPy = this._resolvePython();
|
||||
} catch (_) {
|
||||
resolvedPy = null;
|
||||
}
|
||||
|
|
@ -1242,12 +1253,7 @@ export class PaperForgeSetupModal extends Modal {
|
|||
});
|
||||
{
|
||||
const vp = vault;
|
||||
const { path: pythonExe, extraArgs = [] } = resolvePythonExecutable(
|
||||
vp,
|
||||
this.plugin.settings,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
const { path: pythonExe, args: extraArgs = [] } = this._resolvePython();
|
||||
execFile(
|
||||
pythonExe,
|
||||
[
|
||||
|
|
|
|||
Loading…
Reference in a new issue