源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
This commit is contained in:
commit
2ed3415fb7
8018 files changed
+1936476
No files matched your search
@@ -0,0 +1,125 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { access, readdir, stat } from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ignoredDirectories = new Set([
|
||||
".git",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"out"
|
||||
]);
|
||||
|
||||
export function isDesktopBundleFresh({ outputMtimeMs, sourceMtimeMs }) {
|
||||
return (
|
||||
Number.isFinite(outputMtimeMs) &&
|
||||
Number.isFinite(sourceMtimeMs) &&
|
||||
outputMtimeMs >= sourceMtimeMs
|
||||
);
|
||||
}
|
||||
|
||||
export async function ensurePreparedDesktopLaunch({ workspaceRoot, log }) {
|
||||
const desktopRoot = join(workspaceRoot, "apps", "desktop");
|
||||
const outputFiles = [
|
||||
join(desktopRoot, "out", "main", "index.js"),
|
||||
join(desktopRoot, "out", "preload", "index.cjs"),
|
||||
join(desktopRoot, "out", "renderer", "index.html")
|
||||
];
|
||||
const sourceMtimeMs = await latestSourceMtimeMs(workspaceRoot);
|
||||
const outputMtimeMs = await oldestMtimeMs(outputFiles);
|
||||
|
||||
if (!isDesktopBundleFresh({ outputMtimeMs, sourceMtimeMs })) {
|
||||
log("building prepared Desktop bundle");
|
||||
await runCommand(
|
||||
"pnpm",
|
||||
["--filter", "@tutti-os/desktop", "build"],
|
||||
workspaceRoot
|
||||
);
|
||||
} else {
|
||||
log("reusing prepared Desktop bundle");
|
||||
}
|
||||
|
||||
const electronExecutable = createRequire(join(desktopRoot, "package.json"))(
|
||||
"electron"
|
||||
);
|
||||
return {
|
||||
args: [desktopRoot],
|
||||
command: electronExecutable,
|
||||
launchMode: "prebuilt"
|
||||
};
|
||||
}
|
||||
|
||||
async function latestSourceMtimeMs(workspaceRoot) {
|
||||
const sourceRoots = [
|
||||
join(workspaceRoot, "apps", "desktop"),
|
||||
join(workspaceRoot, "packages"),
|
||||
join(workspaceRoot, "config"),
|
||||
join(workspaceRoot, "package.json"),
|
||||
join(workspaceRoot, "pnpm-lock.yaml")
|
||||
];
|
||||
let latest = 0;
|
||||
for (const sourceRoot of sourceRoots) {
|
||||
latest = Math.max(latest, await treeMtimeMs(sourceRoot));
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
async function treeMtimeMs(path) {
|
||||
let entry;
|
||||
try {
|
||||
entry = await stat(path);
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") return 0;
|
||||
throw error;
|
||||
}
|
||||
if (!entry.isDirectory()) return entry.mtimeMs;
|
||||
|
||||
let latest = entry.mtimeMs;
|
||||
for (const child of await readdir(path, { withFileTypes: true })) {
|
||||
if (child.isDirectory() && ignoredDirectories.has(child.name)) continue;
|
||||
latest = Math.max(latest, await treeMtimeMs(join(path, child.name)));
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
async function oldestMtimeMs(paths) {
|
||||
let oldest = Number.POSITIVE_INFINITY;
|
||||
for (const path of paths) {
|
||||
try {
|
||||
await access(path);
|
||||
oldest = Math.min(oldest, (await stat(path)).mtimeMs);
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") return 0;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return oldest;
|
||||
}
|
||||
|
||||
function runCommand(command, args, cwd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
cwd,
|
||||
env: process.env,
|
||||
stdio: ["ignore", "pipe", "pipe"]
|
||||
});
|
||||
child.stdout.resume();
|
||||
let stderr = "";
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr += String(chunk);
|
||||
});
|
||||
child.once("error", reject);
|
||||
child.once("exit", (code, signal) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(
|
||||
new Error(
|
||||
`${command} ${args.join(" ")} failed (${code ?? signal ?? "unknown"})${stderr.trim() ? `: ${stderr.trim()}` : ""}`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user