#!/usr/bin/env node const fs = require('node:fs'); const path = require('node:path'); const clientRoot = path.resolve(__dirname, '..'); const repoRoot = path.resolve(clientRoot, '../..'); const defaultSgRobotWeb = path.resolve(repoRoot, '../../sgRobot/crates/sgclaw-core/web'); const sourceRoot = path.resolve(process.env.SGROBOT_WEB_DIR || defaultSgRobotWeb); const targetRoot = path.resolve(clientRoot, 'embedded/sgrobot-digital'); const copies = [ ['src/pages/digital', 'src/pages/digital'], ['src/components/digital', 'src/components/digital'], ['src/contexts/RoleContext.tsx', 'src/contexts/RoleContext.tsx'], ['src/lib/api.ts', 'src/lib/api.ts'], ['src/lib/auth.ts', 'src/lib/auth.ts'], ['src/lib/consoleDataAdapter.ts', 'src/lib/consoleDataAdapter.ts'], ['src/lib/i18n.ts', 'src/lib/i18n.ts'], ['src/types/api.ts', 'src/types/api.ts'], ['src/index.css', 'src/index.css'], ['public/digital-assets', 'public/digital-assets'], ['public/logo.png', 'public/logo.png'], ]; function assertExists(location) { if (!fs.existsSync(location)) { throw new Error(`Missing required path: ${location}`); } } function copyPath(from, to) { assertExists(from); fs.rmSync(to, { recursive: true, force: true }); fs.mkdirSync(path.dirname(to), { recursive: true }); fs.cpSync(from, to, { recursive: true }); } function stripTauriWindowImport() { const shellPath = path.join(targetRoot, 'src/pages/digital/DigitalEmployeeShell.tsx'); let source = fs.readFileSync(shellPath, 'utf8'); source = source.replace("import { isTauri } from '@/lib/tauri';\n", ''); source = source.replace( /\n if \(isTauri\(\)\) \{\n void import\('@tauri-apps\/api\/window'\)[\s\S]*?\n return;\n \}/, '', ); fs.writeFileSync(shellPath, source); } function injectQimingclawApiAdapter() { const apiPath = path.join(targetRoot, 'src/lib/api.ts'); let source = fs.readFileSync(apiPath, 'utf8'); if (!source.includes("import { handleQimingclawDigitalApi } from './qimingclawAdapter';")) { source = source.replace( "import { apiOrigin, apiBase } from './basePath';\n", "import { apiOrigin, apiBase } from './basePath';\nimport { handleQimingclawDigitalApi } from './qimingclawAdapter';\n", ); } if (!source.includes('const adapterData = await handleQimingclawDigitalApi(path, options);')) { source = source.replace( "): Promise {\n const { timeoutMs = API_FETCH_TIMEOUT_MS, ...fetchOptions } = options;", "): Promise {\n const adapterData = await handleQimingclawDigitalApi(path, options);\n if (adapterData !== undefined) {\n return adapterData;\n }\n\n const { timeoutMs = API_FETCH_TIMEOUT_MS, ...fetchOptions } = options;", ); } fs.writeFileSync(apiPath, source); } function main() { assertExists(sourceRoot); assertExists(targetRoot); for (const [fromRel, toRel] of copies) { copyPath(path.join(sourceRoot, fromRel), path.join(targetRoot, toRel)); } stripTauriWindowImport(); injectQimingclawApiAdapter(); console.log(`Synced sgRobot digital UI from ${sourceRoot}`); console.log(`Target: ${targetRoot}`); console.log('qimingclaw embedded adapters were preserved.'); } main();