chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:44 +08:00
commit 33645b2d9d
144 changed files with 63699 additions and 0 deletions
+192
View File
@@ -0,0 +1,192 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const { execFileSync } = require('child_process');
const REPO_ROOT = path.resolve(__dirname, '..');
const PACKAGE_JSON = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
const PRODUCT_NAME = PACKAGE_JSON.build?.productName || 'Open Generative AI';
const PACKAGE_NAME = 'open-generative-ai';
const COMMAND_NAME = 'open-generative-ai';
const INSTALL_DIR_NAME = PACKAGE_NAME;
const LINUX_DEPENDS = [
'libasound2t64 | libasound2',
'libatk-bridge2.0-0',
'libatk1.0-0',
'libc6',
'libcairo2',
'libcups2t64 | libcups2',
'libdrm2',
'libgbm1',
'libglib2.0-0',
'libgtk-3-0',
'libnspr4',
'libnss3',
'libpango-1.0-0',
'libx11-6',
'libx11-xcb1',
'libxcb-dri3-0',
'libxcomposite1',
'libxdamage1',
'libxext6',
'libxfixes3',
'libxkbcommon0',
'libxrandr2',
'xdg-utils',
].join(', ');
function parseArgs(argv) {
const args = {};
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (!arg.startsWith('--')) continue;
const [rawKey, inlineValue] = arg.slice(2).split('=');
if (inlineValue !== undefined) {
args[rawKey] = inlineValue;
continue;
}
const nextValue = argv[i + 1];
if (nextValue && !nextValue.startsWith('--')) {
args[rawKey] = nextValue;
i += 1;
} else {
args[rawKey] = true;
}
}
return args;
}
function toDebArch(arch) {
if (arch === 'x64') return 'amd64';
if (arch === 'arm64') return 'arm64';
return arch;
}
function getDefaultAppDir(arch) {
const folderName = arch === 'arm64' ? 'linux-arm64-unpacked' : 'linux-unpacked';
return path.join(REPO_ROOT, 'release', folderName);
}
function detectExecutableName(appDir) {
const preferredNames = [PRODUCT_NAME, PACKAGE_JSON.productName, PACKAGE_JSON.name]
.filter(Boolean);
for (const fileName of preferredNames) {
const fullPath = path.join(appDir, fileName);
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
return fileName;
}
}
const denyList = new Set([
'chrome-sandbox',
'chrome_crashpad_handler',
]);
const candidates = fs.readdirSync(appDir, { withFileTypes: true })
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
.filter((name) => !denyList.has(name))
.filter((name) => !name.endsWith('.so') && !name.includes('.so.'))
.filter((name) => (fs.statSync(path.join(appDir, name)).mode & 0o111) !== 0);
if (candidates.length === 0) {
throw new Error(`Could not detect the packaged executable in ${appDir}`);
}
return candidates[0];
}
function writeFile(targetPath, contents, mode) {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, contents);
if (mode) fs.chmodSync(targetPath, mode);
}
function main() {
if (process.platform !== 'linux') {
console.error('This packaging script must be run on Linux.');
process.exit(1);
}
const args = parseArgs(process.argv.slice(2));
const arch = args.arch || process.arch;
const debArch = toDebArch(arch);
const appDir = path.resolve(args['app-dir'] || getDefaultAppDir(arch));
const outputDir = path.resolve(args['output-dir'] || path.join(REPO_ROOT, 'release'));
const version = args.version || PACKAGE_JSON.version;
if (!fs.existsSync(appDir)) {
console.error(`Packaged app directory not found: ${appDir}`);
process.exit(1);
}
const executableName = detectExecutableName(appDir);
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'oga-deb-'));
const packageRoot = path.join(tempRoot, 'pkgroot');
const installDir = path.join(packageRoot, 'opt', INSTALL_DIR_NAME);
const wrapperPath = path.join(packageRoot, 'usr', 'bin', COMMAND_NAME);
const desktopPath = path.join(packageRoot, 'usr', 'share', 'applications', `${PACKAGE_NAME}.desktop`);
const iconPath = path.join(packageRoot, 'usr', 'share', 'pixmaps', `${PACKAGE_NAME}.png`);
const controlPath = path.join(packageRoot, 'DEBIAN', 'control');
const outputPath = path.join(outputDir, `${PACKAGE_NAME}_${version}_${debArch}.deb`);
fs.mkdirSync(outputDir, { recursive: true });
fs.cpSync(appDir, installDir, { recursive: true });
const chromeSandboxPath = path.join(installDir, 'chrome-sandbox');
if (fs.existsSync(chromeSandboxPath)) {
fs.chmodSync(chromeSandboxPath, 0o4755);
}
writeFile(
wrapperPath,
`#!/bin/sh\nexec "/opt/${INSTALL_DIR_NAME}/${executableName}" "$@"\n`,
0o755
);
writeFile(
desktopPath,
`[Desktop Entry]
Name=${PRODUCT_NAME}
Exec=${COMMAND_NAME}
Icon=${PACKAGE_NAME}
Type=Application
Categories=Graphics;Utility;
Terminal=false
StartupNotify=true
`,
0o644
);
fs.mkdirSync(path.dirname(iconPath), { recursive: true });
fs.copyFileSync(path.join(REPO_ROOT, 'public', 'banner.png'), iconPath);
fs.chmodSync(iconPath, 0o644);
writeFile(
controlPath,
`Package: ${PACKAGE_NAME}
Version: ${version}
Section: graphics
Priority: optional
Architecture: ${debArch}
Maintainer: Open Generative AI Team
Depends: ${LINUX_DEPENDS}
Description: Local-first generative AI studio for image, video, and design workflows
`,
0o644
);
execFileSync('dpkg-deb', ['--build', '--root-owner-group', packageRoot, outputPath], {
stdio: 'inherit',
});
fs.rmSync(tempRoot, { recursive: true, force: true });
console.log(`Created ${outputPath}`);
}
main();
+81
View File
@@ -0,0 +1,81 @@
const fs = require('fs');
const path = require('path');
const REQUIRED_FILES = {
darwin: ['sd-cli', 'libstable-diffusion.dylib'],
linux: ['sd-cli', 'libstable-diffusion.so'],
win32: ['sd-cli.exe'],
};
const OPTIONAL_FILES = ['sd-server'];
function resolveSourceBinDir(sourcePath) {
const absoluteSourcePath = path.resolve(sourcePath);
const nestedBinDir = path.join(absoluteSourcePath, 'bin');
if (fs.existsSync(nestedBinDir) && fs.statSync(nestedBinDir).isDirectory()) {
return nestedBinDir;
}
return absoluteSourcePath;
}
function stageLocalAiBinary({ platform, arch, sourcePath }) {
const sourceBinDir = resolveSourceBinDir(sourcePath);
const requiredFiles = REQUIRED_FILES[platform];
if (!requiredFiles) {
throw new Error(`Unsupported platform "${platform}". Expected one of: ${Object.keys(REQUIRED_FILES).join(', ')}`);
}
const missingFiles = requiredFiles.filter((fileName) => !fs.existsSync(path.join(sourceBinDir, fileName)));
if (missingFiles.length > 0) {
throw new Error(`Missing required files in ${sourceBinDir}: ${missingFiles.join(', ')}`);
}
const repoRoot = path.resolve(__dirname, '..');
const stageDir = path.join(repoRoot, 'build', 'local-ai', `${platform}-${arch}`, 'bin');
fs.rmSync(stageDir, { recursive: true, force: true });
fs.mkdirSync(stageDir, { recursive: true });
for (const fileName of [...requiredFiles, ...OPTIONAL_FILES]) {
const sourceFile = path.join(sourceBinDir, fileName);
if (!fs.existsSync(sourceFile)) continue;
const targetFile = path.join(stageDir, fileName);
fs.copyFileSync(sourceFile, targetFile);
if (platform !== 'win32') {
fs.chmodSync(targetFile, 0o755);
}
}
return stageDir;
}
function main() {
const [platform, arch, sourcePath] = process.argv.slice(2);
if (!platform || !arch || !sourcePath) {
console.error('Usage: node scripts/stage-local-ai-binary.js <platform> <arch> <source-path>');
process.exit(1);
}
try {
const stageDir = stageLocalAiBinary({ platform, arch, sourcePath });
console.log(`Staged local AI binaries in ${stageDir}`);
} catch (error) {
console.error(error.message);
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = {
resolveSourceBinDir,
stageLocalAiBinary,
};
+179
View File
@@ -0,0 +1,179 @@
/**
* Test script for MiniMax provider integration.
*
* Verifies that the MiniMax Image 01 model is correctly registered in models.js
* and that the model definition has the expected structure.
*
* Usage:
* node scripts/test_minimax_provider.js
*
* Set MUAPI_KEY env var to run the live API smoke test:
* MUAPI_KEY=your_key node scripts/test_minimax_provider.js
*/
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..");
// ── 1. Model registration check ──────────────────────────────────────────────
const modelsContent = readFileSync(
join(ROOT, "src", "lib", "models.js"),
"utf-8"
);
// Extract the t2iModels JSON array via a simple regex
const t2iMatch = modelsContent.match(/export const t2iModels = (\[[\s\S]*?\]);/);
if (!t2iMatch) {
console.error("FAIL: Could not parse t2iModels from src/lib/models.js");
process.exit(1);
}
let t2iModels;
try {
t2iModels = JSON.parse(t2iMatch[1]);
} catch (err) {
console.error("FAIL: t2iModels is not valid JSON:", err.message);
process.exit(1);
}
const minimaxModel = t2iModels.find((m) => m.id === "minimax-image-01");
if (!minimaxModel) {
console.error(
'FAIL: "minimax-image-01" not found in t2iModels.\n' +
"Expected it to be registered in src/lib/models.js."
);
process.exit(1);
}
// Validate required fields
const required = ["id", "name", "endpoint", "family", "inputs"];
for (const field of required) {
if (!minimaxModel[field]) {
console.error(`FAIL: minimax-image-01 is missing required field: ${field}`);
process.exit(1);
}
}
if (minimaxModel.family !== "minimax") {
console.error(
`FAIL: expected family "minimax", got "${minimaxModel.family}"`
);
process.exit(1);
}
if (!minimaxModel.inputs.prompt) {
console.error("FAIL: minimax-image-01 inputs missing 'prompt' field");
process.exit(1);
}
if (!minimaxModel.inputs.aspect_ratio?.enum?.includes("1:1")) {
console.error(
"FAIL: minimax-image-01 aspect_ratio enum does not include '1:1'"
);
process.exit(1);
}
console.log("PASS: minimax-image-01 is correctly registered in t2iModels");
console.log(
` endpoint=${minimaxModel.endpoint} family=${minimaxModel.family}`
);
console.log(
` aspect ratios: ${minimaxModel.inputs.aspect_ratio.enum.join(", ")}`
);
// ── 2. models_dump.json check ─────────────────────────────────────────────────
const dump = JSON.parse(
readFileSync(join(ROOT, "models_dump.json"), "utf-8")
);
const dumpEntry = dump.t2i?.find((m) => m.id === "minimax-image-01");
if (!dumpEntry) {
console.error(
'FAIL: "minimax-image-01" not found in models_dump.json t2i section'
);
process.exit(1);
}
console.log("PASS: minimax-image-01 found in models_dump.json");
// ── 3. Live API smoke test (optional) ────────────────────────────────────────
const apiKey = process.env.MUAPI_KEY;
if (!apiKey) {
console.log(
"\nINFO: Skipping live API test (set MUAPI_KEY env var to enable)."
);
console.log("\nAll checks passed.");
process.exit(0);
}
console.log("\nRunning live API smoke test against muapi.ai …");
const MUAPI_BASE = "https://api.muapi.ai";
async function testMiniMaxImageGeneration() {
const endpoint = minimaxModel.endpoint;
const url = `${MUAPI_BASE}/api/v1/${endpoint}`;
const payload = {
prompt: "A simple test: a red apple on a white background.",
aspect_ratio: "1:1",
num_images: 1,
};
console.log(`POST ${url}`);
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
body: JSON.stringify(payload),
});
if (!res.ok) {
const text = await res.text();
console.error(`FAIL: API returned ${res.status}: ${text.slice(0, 200)}`);
process.exit(1);
}
const data = await res.json();
const requestId = data.request_id || data.id;
if (!requestId) {
console.error("FAIL: No request_id in response:", JSON.stringify(data));
process.exit(1);
}
console.log(`PASS: Generation queued — request_id=${requestId}`);
// Poll for result (max 60 s)
const pollUrl = `${MUAPI_BASE}/api/v1/predictions/${requestId}/result`;
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 2000));
const poll = await fetch(pollUrl, {
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
});
if (!poll.ok) continue;
const result = await poll.json();
const status = result.status?.toLowerCase();
if (status === "completed" || status === "succeeded" || status === "success") {
const imageUrl =
result.outputs?.[0] || result.url || result.output?.url;
console.log(`PASS: Generation complete — image URL: ${imageUrl}`);
console.log("\nAll checks passed.");
return;
}
if (status === "failed" || status === "error") {
console.error("FAIL: Generation failed:", result.error);
process.exit(1);
}
console.log(` Polling … status=${status}`);
}
console.error("FAIL: Timed out waiting for generation result.");
process.exit(1);
}
testMiniMaxImageGeneration().catch((err) => {
console.error("FAIL: Unexpected error:", err.message);
process.exit(1);
});