everything-dev 1.7.2 → 1.8.1
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/dist/api.cjs +1 -1
- package/dist/api.mjs +1 -1
- package/dist/app.cjs +82 -51
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +82 -51
- package/dist/app.mjs.map +1 -1
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/components/dev-view.cjs +6 -3
- package/dist/components/dev-view.cjs.map +1 -1
- package/dist/components/dev-view.mjs +6 -3
- package/dist/components/dev-view.mjs.map +1 -1
- package/dist/components/streaming-view.cjs +5 -2
- package/dist/components/streaming-view.cjs.map +1 -1
- package/dist/components/streaming-view.mjs +5 -2
- package/dist/components/streaming-view.mjs.map +1 -1
- package/dist/config.cjs +28 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +28 -5
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +1 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +10 -2
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +10 -2
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +1 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-logs.cjs +6 -2
- package/dist/dev-logs.cjs.map +1 -1
- package/dist/dev-logs.mjs +7 -2
- package/dist/dev-logs.mjs.map +1 -1
- package/dist/dev-session.cjs +27 -23
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +27 -24
- package/dist/dev-session.mjs.map +1 -1
- package/dist/federation.server.cjs +1 -1
- package/dist/federation.server.mjs +1 -1
- package/dist/host.cjs +4 -3
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts.map +1 -1
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +4 -3
- package/dist/host.mjs.map +1 -1
- package/dist/integrity.cjs +68 -2
- package/dist/integrity.cjs.map +1 -1
- package/dist/integrity.d.cts +14 -1
- package/dist/integrity.d.cts.map +1 -1
- package/dist/integrity.d.mts +14 -1
- package/dist/integrity.d.mts.map +1 -1
- package/dist/integrity.mjs +66 -3
- package/dist/integrity.mjs.map +1 -1
- package/dist/mf.cjs +32 -0
- package/dist/mf.cjs.map +1 -1
- package/dist/mf.d.cts +3 -1
- package/dist/mf.d.cts.map +1 -1
- package/dist/mf.d.mts +3 -1
- package/dist/mf.d.mts.map +1 -1
- package/dist/mf.mjs +32 -1
- package/dist/mf.mjs.map +1 -1
- package/dist/orchestrator.cjs +167 -317
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.d.cts +24 -21
- package/dist/orchestrator.d.cts.map +1 -1
- package/dist/orchestrator.d.mts +24 -21
- package/dist/orchestrator.d.mts.map +1 -1
- package/dist/orchestrator.mjs +168 -316
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +38 -107
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +16 -2
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +16 -2
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +39 -108
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.cjs +188 -0
- package/dist/service-descriptor.cjs.map +1 -0
- package/dist/service-descriptor.d.cts +107 -0
- package/dist/service-descriptor.d.cts.map +1 -0
- package/dist/service-descriptor.d.mts +107 -0
- package/dist/service-descriptor.d.mts.map +1 -0
- package/dist/service-descriptor.mjs +182 -0
- package/dist/service-descriptor.mjs.map +1 -0
- package/dist/types.cjs +8 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +16 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +16 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +8 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/index.cjs +1 -0
- package/dist/ui/index.d.cts +2 -2
- package/dist/ui/index.d.mts +2 -2
- package/dist/ui/index.mjs +2 -2
- package/dist/ui/runtime.cjs +4 -0
- package/dist/ui/runtime.cjs.map +1 -1
- package/dist/ui/runtime.d.cts +2 -1
- package/dist/ui/runtime.d.cts.map +1 -1
- package/dist/ui/runtime.d.mts +2 -1
- package/dist/ui/runtime.d.mts.map +1 -1
- package/dist/ui/runtime.mjs +4 -1
- package/dist/ui/runtime.mjs.map +1 -1
- package/package.json +12 -4
- package/skills/dev-workflow/SKILL.md +105 -0
- package/skills/publish-sync/SKILL.md +130 -0
- package/src/app.ts +98 -204
- package/src/cli/upgrade.ts +20 -4
- package/src/components/dev-view.tsx +8 -3
- package/src/components/streaming-view.ts +7 -2
- package/src/config.ts +40 -8
- package/src/contract.ts +1 -0
- package/src/dev-logs.ts +8 -1
- package/src/dev-session.ts +56 -79
- package/src/host.ts +4 -3
- package/src/integrity.ts +96 -10
- package/src/mf.ts +42 -0
- package/src/orchestrator.ts +232 -411
- package/src/plugin.ts +48 -136
- package/src/service-descriptor.ts +258 -0
- package/src/types.ts +8 -1
- package/src/ui/runtime.ts +5 -0
- package/dist/process-registry.cjs +0 -120
- package/dist/process-registry.cjs.map +0 -1
- package/dist/process-registry.d.cts +0 -25
- package/dist/process-registry.d.cts.map +0 -1
- package/dist/process-registry.d.mts +0 -25
- package/dist/process-registry.d.mts.map +0 -1
- package/dist/process-registry.mjs +0 -119
- package/dist/process-registry.mjs.map +0 -1
- package/src/process-registry.ts +0 -154
package/dist/cli/upgrade.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.cjs","names":["runBunInstall","syncTemplate"],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(catalog: Record<string, string> | undefined, packageName: string, newVersion: string): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(pkg: Record<string, unknown>, packageName: string, newVersion: string): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(filePath: string, packageName: string, newVersion: string): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"upgrade.cjs","names":["runBunInstall","syncTemplate"],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(\n catalog: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(\n pkg: Record<string, unknown>,\n packageName: string,\n newVersion: string,\n): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(\n filePath: string,\n packageName: string,\n newVersion: string,\n): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(\n projectDir: string,\n packageName: string,\n newVersion: string,\n): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
|
package/dist/cli/upgrade.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.mjs","names":[],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(catalog: Record<string, string> | undefined, packageName: string, newVersion: string): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(pkg: Record<string, unknown>, packageName: string, newVersion: string): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(filePath: string, packageName: string, newVersion: string): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"upgrade.mjs","names":[],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(\n catalog: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(\n pkg: Record<string, unknown>,\n packageName: string,\n newVersion: string,\n): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(\n filePath: string,\n packageName: string,\n newVersion: string,\n): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(\n projectDir: string,\n packageName: string,\n newVersion: string,\n): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
|
|
@@ -61,9 +61,12 @@ function getColumnWidths(processes) {
|
|
|
61
61
|
}
|
|
62
62
|
function ProcessRow({ proc, nameWidth, sourceWidth }) {
|
|
63
63
|
const color = getServiceColor(proc.name);
|
|
64
|
-
const
|
|
64
|
+
const isRemote = proc.source === "remote";
|
|
65
|
+
const isHost = proc.name === "host";
|
|
66
|
+
const showPort = proc.port > 0 && (isHost || !isRemote);
|
|
67
|
+
const portStr = showPort ? `:${proc.port}` : "";
|
|
65
68
|
const sourceLabel = proc.source ? ` (${proc.source})` : "";
|
|
66
|
-
const statusText = proc.status === "pending" ? "waiting" : proc.status === "starting" ? "starting" : proc.status === "ready" ? "running" : "failed";
|
|
69
|
+
const statusText = proc.status === "pending" ? "waiting" : proc.status === "starting" ? "starting" : proc.status === "ready" ? isRemote && !isHost ? "loaded" : "running" : "failed";
|
|
67
70
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, { children: [
|
|
68
71
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, { children: " " }),
|
|
69
72
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIcon, { status: proc.status }),
|
|
@@ -81,7 +84,7 @@ function ProcessRow({ proc, nameWidth, sourceWidth }) {
|
|
|
81
84
|
color: proc.status === "ready" ? "#00ff41" : "gray",
|
|
82
85
|
children: statusText
|
|
83
86
|
}),
|
|
84
|
-
|
|
87
|
+
showPort && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, {
|
|
85
88
|
color: "#00ffff",
|
|
86
89
|
children: [" ", portStr]
|
|
87
90
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-view.cjs","names":["Text","icons","Box","linkify","frames","gradients","colors","divider"],"sources":["../../src/components/dev-view.tsx"],"sourcesContent":["import { Box, render, Text, useApp, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { SourceMode } from \"../types\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, divider, frames, gradients, icons } from \"../utils/theme\";\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: SourceMode;\n}\n\nexport interface LogEntry {\n id: string;\n source: string;\n line: string;\n timestamp: number;\n isError?: boolean;\n}\n\ninterface DevViewProps {\n processes: ProcessState[];\n logs: LogEntry[];\n description: string;\n proxyTarget?: string;\n onExit?: () => Promise<void> | void;\n onExportLogs?: () => Promise<void> | void;\n}\n\nfunction StatusIcon({ status }: { status: ProcessStatus }) {\n switch (status) {\n case \"pending\":\n return <Text color=\"gray\">{icons.pending}</Text>;\n case \"starting\":\n return <Text color=\"#00ffff\">{icons.scan}</Text>;\n case \"ready\":\n return <Text color=\"#00ff41\">{icons.ok}</Text>;\n case \"error\":\n return <Text color=\"#ff3366\">{icons.err}</Text>;\n }\n}\n\nfunction getServiceColor(name: string): string {\n if (name.startsWith(PLUGIN_PREFIX)) return \"#ffaa00\";\n return name === \"host\" ? \"#00ffff\" : name === \"ui\" ? \"#ff00ff\" : \"#0080ff\";\n}\n\nfunction getDisplayName(name: string): string {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n}\n\nfunction isPlugin(name: string): boolean {\n return name.startsWith(PLUGIN_PREFIX);\n}\n\nfunction getSectionedProcesses(processes: ProcessState[]): Array<{\n key: string;\n title: string;\n processes: ProcessState[];\n}> {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n}\n\nfunction getColumnWidths(processes: ProcessState[]): { name: number; source: number } {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));\n return { name, source };\n}\n\nfunction ProcessRow({\n proc,\n nameWidth,\n sourceWidth,\n}: {\n proc: ProcessState;\n nameWidth: number;\n sourceWidth: number;\n}) {\n const color = getServiceColor(proc.name);\n const portStr = proc.port > 0 ? `:${proc.port}` : \"\";\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n\n const statusText =\n proc.status === \"pending\"\n ? \"waiting\"\n : proc.status === \"starting\"\n ? \"starting\"\n : proc.status === \"ready\"\n ? \"running\"\n : \"failed\";\n\n return (\n <Box>\n <Text>{\" \"}</Text>\n <StatusIcon status={proc.status} />\n <Text> </Text>\n <Text color={color} bold>\n {getDisplayName(proc.name).padEnd(nameWidth)}\n </Text>\n <Text color=\"gray\">{sourceLabel.padEnd(sourceWidth)}</Text>\n <Text color={proc.status === \"ready\" ? \"#00ff41\" : \"gray\"}>{statusText}</Text>\n {proc.port > 0 && <Text color=\"#00ffff\"> {portStr}</Text>}\n </Box>\n );\n}\n\nfunction SectionHeader({ title }: { title: string }) {\n return (\n <Box marginBottom={0} marginTop={1}>\n <Text color=\"#00ffff\" bold>\n {title}\n </Text>\n </Box>\n );\n}\n\nfunction LogLine({ entry }: { entry: LogEntry }) {\n const color = getServiceColor(entry.source);\n\n return (\n <Box>\n <Text color={color}>[{entry.source}]</Text>\n <Text color={entry.isError ? \"#ff3366\" : undefined}> {linkify(entry.line)}</Text>\n </Box>\n );\n}\n\nfunction truncateUrl(url: string, maxLen: number): string {\n if (url.length <= maxLen) return url;\n try {\n const parsed = new URL(url);\n const host = parsed.host;\n if (host.length > maxLen - 10) {\n return `${host.slice(0, maxLen - 13)}...`;\n }\n return host;\n } catch {\n return `${url.slice(0, maxLen - 3)}...`;\n }\n}\n\nfunction DevView({\n processes,\n logs,\n description,\n proxyTarget,\n onExit,\n onExportLogs,\n}: DevViewProps) {\n const { exit } = useApp();\n const [isShuttingDown, setIsShuttingDown] = useState(false);\n\n useInput((input, key) => {\n if (isShuttingDown) return;\n\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n setIsShuttingDown(true);\n Promise.resolve(onExit?.()).then(() => {\n exit();\n });\n }\n if (input === \"l\") {\n setIsShuttingDown(true);\n Promise.resolve(onExportLogs?.()).then(() => {\n exit();\n });\n }\n });\n\n const readyCount = processes.filter((p) => p.status === \"ready\").length;\n const total = processes.length;\n const allReady = readyCount === total;\n const hostProcess = processes.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const recentLogs = logs.slice(-12);\n const sectionedProcesses = getSectionedProcesses(processes);\n const columnWidths = getColumnWidths(processes);\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={0}>\n <Text color=\"#00ffff\">{frames.top(52)}</Text>\n </Box>\n <Box>\n <Text>\n {\" \"}\n {icons.run} {gradients.cyber(description.toUpperCase())}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#00ffff\">{frames.bottom(52)}</Text>\n </Box>\n\n {allReady && (\n <Box marginBottom={1} flexDirection=\"column\">\n <Box>\n <Text color=\"#00ff41\">\n {\" \"}\n {icons.app} APP READY\n </Text>\n </Box>\n <Box>\n <Text color=\"#00ff41\" bold>\n {\" \"}\n {icons.arrow} http://localhost:{hostPort}\n </Text>\n </Box>\n </Box>\n )}\n\n {proxyTarget && (\n <Box marginBottom={1}>\n <Text color=\"#ffaa00\">\n {\" \"}\n {icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}\n </Text>\n </Box>\n )}\n\n <Box marginTop={0} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n {sectionedProcesses.map((section) => (\n <Box key={section.key} flexDirection=\"column\">\n <SectionHeader title={section.title} />\n {section.processes.map((proc) => (\n <ProcessRow\n key={proc.name}\n proc={proc}\n nameWidth={columnWidths.name}\n sourceWidth={columnWidths.source}\n />\n ))}\n </Box>\n ))}\n\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n <Box marginTop={0}>\n <Text color={allReady ? \"#00ff41\" : \"#00ffff\"}>\n {\" \"}\n {allReady\n ? `${icons.ok} All ${total} services running`\n : `${icons.scan} ${readyCount}/${total} ready`}\n </Text>\n <Text color=\"gray\">\n {\" \"}\n {icons.dot} q quit {icons.dot} l logs\n </Text>\n </Box>\n\n {recentLogs.length > 0 && (\n <>\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={0}>\n {recentLogs.map((entry) => (\n <LogLine key={entry.id} entry={entry} />\n ))}\n </Box>\n </>\n )}\n </Box>\n );\n}\n\nexport interface DevViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => void;\n}\n\nexport function renderDevView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n onExportLogs?: () => Promise<void> | void,\n): DevViewHandle {\n let processes = [...initialProcesses];\n let logs: LogEntry[] = [];\n let rerender: (() => void) | null = null;\n const proxyTarget = env.API_PROXY;\n let logSeq = 0;\n let lastLogKey: string | null = null;\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));\n rerender?.();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const nextKey = `${source}:${isError ? \"1\" : \"0\"}:${line}`;\n if (nextKey === lastLogKey) return;\n lastLogKey = nextKey;\n\n logs = [\n ...logs,\n { id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },\n ];\n if (logs.length > 100) logs = logs.slice(-100);\n rerender?.();\n };\n\n function DevViewWrapper() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n rerender = () => forceUpdate((n: number) => n + 1);\n return () => {\n rerender = null;\n };\n }, []);\n\n return (\n <DevView\n processes={processes}\n logs={logs}\n description={description}\n proxyTarget={proxyTarget}\n onExit={onExit}\n onExportLogs={onExportLogs}\n />\n );\n }\n\n const { unmount } = render(<DevViewWrapper />);\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-view.cjs","names":["Text","icons","Box","linkify","frames","gradients","colors","divider"],"sources":["../../src/components/dev-view.tsx"],"sourcesContent":["import { Box, render, Text, useApp, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { SourceMode } from \"../types\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, divider, frames, gradients, icons } from \"../utils/theme\";\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: SourceMode;\n}\n\nexport interface LogEntry {\n id: string;\n source: string;\n line: string;\n timestamp: number;\n isError?: boolean;\n}\n\ninterface DevViewProps {\n processes: ProcessState[];\n logs: LogEntry[];\n description: string;\n proxyTarget?: string;\n onExit?: () => Promise<void> | void;\n onExportLogs?: () => Promise<void> | void;\n}\n\nfunction StatusIcon({ status }: { status: ProcessStatus }) {\n switch (status) {\n case \"pending\":\n return <Text color=\"gray\">{icons.pending}</Text>;\n case \"starting\":\n return <Text color=\"#00ffff\">{icons.scan}</Text>;\n case \"ready\":\n return <Text color=\"#00ff41\">{icons.ok}</Text>;\n case \"error\":\n return <Text color=\"#ff3366\">{icons.err}</Text>;\n }\n}\n\nfunction getServiceColor(name: string): string {\n if (name.startsWith(PLUGIN_PREFIX)) return \"#ffaa00\";\n return name === \"host\" ? \"#00ffff\" : name === \"ui\" ? \"#ff00ff\" : \"#0080ff\";\n}\n\nfunction getDisplayName(name: string): string {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n}\n\nfunction isPlugin(name: string): boolean {\n return name.startsWith(PLUGIN_PREFIX);\n}\n\nfunction getSectionedProcesses(processes: ProcessState[]): Array<{\n key: string;\n title: string;\n processes: ProcessState[];\n}> {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n}\n\nfunction getColumnWidths(processes: ProcessState[]): { name: number; source: number } {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));\n return { name, source };\n}\n\nfunction ProcessRow({\n proc,\n nameWidth,\n sourceWidth,\n}: {\n proc: ProcessState;\n nameWidth: number;\n sourceWidth: number;\n}) {\n const color = getServiceColor(proc.name);\n const isRemote = proc.source === \"remote\";\n const isHost = proc.name === \"host\";\n const showPort = proc.port > 0 && (isHost || !isRemote);\n const portStr = showPort ? `:${proc.port}` : \"\";\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n\n const statusText =\n proc.status === \"pending\"\n ? \"waiting\"\n : proc.status === \"starting\"\n ? \"starting\"\n : proc.status === \"ready\"\n ? isRemote && !isHost\n ? \"loaded\"\n : \"running\"\n : \"failed\";\n\n return (\n <Box>\n <Text>{\" \"}</Text>\n <StatusIcon status={proc.status} />\n <Text> </Text>\n <Text color={color} bold>\n {getDisplayName(proc.name).padEnd(nameWidth)}\n </Text>\n <Text color=\"gray\">{sourceLabel.padEnd(sourceWidth)}</Text>\n <Text color={proc.status === \"ready\" ? \"#00ff41\" : \"gray\"}>{statusText}</Text>\n {showPort && <Text color=\"#00ffff\"> {portStr}</Text>}\n </Box>\n );\n}\n\nfunction SectionHeader({ title }: { title: string }) {\n return (\n <Box marginBottom={0} marginTop={1}>\n <Text color=\"#00ffff\" bold>\n {title}\n </Text>\n </Box>\n );\n}\n\nfunction LogLine({ entry }: { entry: LogEntry }) {\n const color = getServiceColor(entry.source);\n\n return (\n <Box>\n <Text color={color}>[{entry.source}]</Text>\n <Text color={entry.isError ? \"#ff3366\" : undefined}> {linkify(entry.line)}</Text>\n </Box>\n );\n}\n\nfunction truncateUrl(url: string, maxLen: number): string {\n if (url.length <= maxLen) return url;\n try {\n const parsed = new URL(url);\n const host = parsed.host;\n if (host.length > maxLen - 10) {\n return `${host.slice(0, maxLen - 13)}...`;\n }\n return host;\n } catch {\n return `${url.slice(0, maxLen - 3)}...`;\n }\n}\n\nfunction DevView({\n processes,\n logs,\n description,\n proxyTarget,\n onExit,\n onExportLogs,\n}: DevViewProps) {\n const { exit } = useApp();\n const [isShuttingDown, setIsShuttingDown] = useState(false);\n\n useInput((input, key) => {\n if (isShuttingDown) return;\n\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n setIsShuttingDown(true);\n Promise.resolve(onExit?.()).then(() => {\n exit();\n });\n }\n if (input === \"l\") {\n setIsShuttingDown(true);\n Promise.resolve(onExportLogs?.()).then(() => {\n exit();\n });\n }\n });\n\n const readyCount = processes.filter((p) => p.status === \"ready\").length;\n const total = processes.length;\n const allReady = readyCount === total;\n const hostProcess = processes.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const recentLogs = logs.slice(-12);\n const sectionedProcesses = getSectionedProcesses(processes);\n const columnWidths = getColumnWidths(processes);\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={0}>\n <Text color=\"#00ffff\">{frames.top(52)}</Text>\n </Box>\n <Box>\n <Text>\n {\" \"}\n {icons.run} {gradients.cyber(description.toUpperCase())}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#00ffff\">{frames.bottom(52)}</Text>\n </Box>\n\n {allReady && (\n <Box marginBottom={1} flexDirection=\"column\">\n <Box>\n <Text color=\"#00ff41\">\n {\" \"}\n {icons.app} APP READY\n </Text>\n </Box>\n <Box>\n <Text color=\"#00ff41\" bold>\n {\" \"}\n {icons.arrow} http://localhost:{hostPort}\n </Text>\n </Box>\n </Box>\n )}\n\n {proxyTarget && (\n <Box marginBottom={1}>\n <Text color=\"#ffaa00\">\n {\" \"}\n {icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}\n </Text>\n </Box>\n )}\n\n <Box marginTop={0} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n {sectionedProcesses.map((section) => (\n <Box key={section.key} flexDirection=\"column\">\n <SectionHeader title={section.title} />\n {section.processes.map((proc) => (\n <ProcessRow\n key={proc.name}\n proc={proc}\n nameWidth={columnWidths.name}\n sourceWidth={columnWidths.source}\n />\n ))}\n </Box>\n ))}\n\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n <Box marginTop={0}>\n <Text color={allReady ? \"#00ff41\" : \"#00ffff\"}>\n {\" \"}\n {allReady\n ? `${icons.ok} All ${total} services running`\n : `${icons.scan} ${readyCount}/${total} ready`}\n </Text>\n <Text color=\"gray\">\n {\" \"}\n {icons.dot} q quit {icons.dot} l logs\n </Text>\n </Box>\n\n {recentLogs.length > 0 && (\n <>\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={0}>\n {recentLogs.map((entry) => (\n <LogLine key={entry.id} entry={entry} />\n ))}\n </Box>\n </>\n )}\n </Box>\n );\n}\n\nexport interface DevViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => void;\n}\n\nexport function renderDevView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n onExportLogs?: () => Promise<void> | void,\n): DevViewHandle {\n let processes = [...initialProcesses];\n let logs: LogEntry[] = [];\n let rerender: (() => void) | null = null;\n const proxyTarget = env.API_PROXY;\n let logSeq = 0;\n let lastLogKey: string | null = null;\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));\n rerender?.();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const nextKey = `${source}:${isError ? \"1\" : \"0\"}:${line}`;\n if (nextKey === lastLogKey) return;\n lastLogKey = nextKey;\n\n logs = [\n ...logs,\n { id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },\n ];\n if (logs.length > 100) logs = logs.slice(-100);\n rerender?.();\n };\n\n function DevViewWrapper() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n rerender = () => forceUpdate((n: number) => n + 1);\n return () => {\n rerender = null;\n };\n }, []);\n\n return (\n <DevView\n processes={processes}\n logs={logs}\n description={description}\n proxyTarget={proxyTarget}\n onExit={onExit}\n onExportLogs={onExportLogs}\n />\n );\n }\n\n const { unmount } = render(<DevViewWrapper />);\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
@@ -60,9 +60,12 @@ function getColumnWidths(processes) {
|
|
|
60
60
|
}
|
|
61
61
|
function ProcessRow({ proc, nameWidth, sourceWidth }) {
|
|
62
62
|
const color = getServiceColor(proc.name);
|
|
63
|
-
const
|
|
63
|
+
const isRemote = proc.source === "remote";
|
|
64
|
+
const isHost = proc.name === "host";
|
|
65
|
+
const showPort = proc.port > 0 && (isHost || !isRemote);
|
|
66
|
+
const portStr = showPort ? `:${proc.port}` : "";
|
|
64
67
|
const sourceLabel = proc.source ? ` (${proc.source})` : "";
|
|
65
|
-
const statusText = proc.status === "pending" ? "waiting" : proc.status === "starting" ? "starting" : proc.status === "ready" ? "running" : "failed";
|
|
68
|
+
const statusText = proc.status === "pending" ? "waiting" : proc.status === "starting" ? "starting" : proc.status === "ready" ? isRemote && !isHost ? "loaded" : "running" : "failed";
|
|
66
69
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
67
70
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
68
71
|
/* @__PURE__ */ jsx(StatusIcon, { status: proc.status }),
|
|
@@ -80,7 +83,7 @@ function ProcessRow({ proc, nameWidth, sourceWidth }) {
|
|
|
80
83
|
color: proc.status === "ready" ? "#00ff41" : "gray",
|
|
81
84
|
children: statusText
|
|
82
85
|
}),
|
|
83
|
-
|
|
86
|
+
showPort && /* @__PURE__ */ jsxs(Text, {
|
|
84
87
|
color: "#00ffff",
|
|
85
88
|
children: [" ", portStr]
|
|
86
89
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-view.mjs","names":[],"sources":["../../src/components/dev-view.tsx"],"sourcesContent":["import { Box, render, Text, useApp, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { SourceMode } from \"../types\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, divider, frames, gradients, icons } from \"../utils/theme\";\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: SourceMode;\n}\n\nexport interface LogEntry {\n id: string;\n source: string;\n line: string;\n timestamp: number;\n isError?: boolean;\n}\n\ninterface DevViewProps {\n processes: ProcessState[];\n logs: LogEntry[];\n description: string;\n proxyTarget?: string;\n onExit?: () => Promise<void> | void;\n onExportLogs?: () => Promise<void> | void;\n}\n\nfunction StatusIcon({ status }: { status: ProcessStatus }) {\n switch (status) {\n case \"pending\":\n return <Text color=\"gray\">{icons.pending}</Text>;\n case \"starting\":\n return <Text color=\"#00ffff\">{icons.scan}</Text>;\n case \"ready\":\n return <Text color=\"#00ff41\">{icons.ok}</Text>;\n case \"error\":\n return <Text color=\"#ff3366\">{icons.err}</Text>;\n }\n}\n\nfunction getServiceColor(name: string): string {\n if (name.startsWith(PLUGIN_PREFIX)) return \"#ffaa00\";\n return name === \"host\" ? \"#00ffff\" : name === \"ui\" ? \"#ff00ff\" : \"#0080ff\";\n}\n\nfunction getDisplayName(name: string): string {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n}\n\nfunction isPlugin(name: string): boolean {\n return name.startsWith(PLUGIN_PREFIX);\n}\n\nfunction getSectionedProcesses(processes: ProcessState[]): Array<{\n key: string;\n title: string;\n processes: ProcessState[];\n}> {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n}\n\nfunction getColumnWidths(processes: ProcessState[]): { name: number; source: number } {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));\n return { name, source };\n}\n\nfunction ProcessRow({\n proc,\n nameWidth,\n sourceWidth,\n}: {\n proc: ProcessState;\n nameWidth: number;\n sourceWidth: number;\n}) {\n const color = getServiceColor(proc.name);\n const portStr = proc.port > 0 ? `:${proc.port}` : \"\";\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n\n const statusText =\n proc.status === \"pending\"\n ? \"waiting\"\n : proc.status === \"starting\"\n ? \"starting\"\n : proc.status === \"ready\"\n ? \"running\"\n : \"failed\";\n\n return (\n <Box>\n <Text>{\" \"}</Text>\n <StatusIcon status={proc.status} />\n <Text> </Text>\n <Text color={color} bold>\n {getDisplayName(proc.name).padEnd(nameWidth)}\n </Text>\n <Text color=\"gray\">{sourceLabel.padEnd(sourceWidth)}</Text>\n <Text color={proc.status === \"ready\" ? \"#00ff41\" : \"gray\"}>{statusText}</Text>\n {proc.port > 0 && <Text color=\"#00ffff\"> {portStr}</Text>}\n </Box>\n );\n}\n\nfunction SectionHeader({ title }: { title: string }) {\n return (\n <Box marginBottom={0} marginTop={1}>\n <Text color=\"#00ffff\" bold>\n {title}\n </Text>\n </Box>\n );\n}\n\nfunction LogLine({ entry }: { entry: LogEntry }) {\n const color = getServiceColor(entry.source);\n\n return (\n <Box>\n <Text color={color}>[{entry.source}]</Text>\n <Text color={entry.isError ? \"#ff3366\" : undefined}> {linkify(entry.line)}</Text>\n </Box>\n );\n}\n\nfunction truncateUrl(url: string, maxLen: number): string {\n if (url.length <= maxLen) return url;\n try {\n const parsed = new URL(url);\n const host = parsed.host;\n if (host.length > maxLen - 10) {\n return `${host.slice(0, maxLen - 13)}...`;\n }\n return host;\n } catch {\n return `${url.slice(0, maxLen - 3)}...`;\n }\n}\n\nfunction DevView({\n processes,\n logs,\n description,\n proxyTarget,\n onExit,\n onExportLogs,\n}: DevViewProps) {\n const { exit } = useApp();\n const [isShuttingDown, setIsShuttingDown] = useState(false);\n\n useInput((input, key) => {\n if (isShuttingDown) return;\n\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n setIsShuttingDown(true);\n Promise.resolve(onExit?.()).then(() => {\n exit();\n });\n }\n if (input === \"l\") {\n setIsShuttingDown(true);\n Promise.resolve(onExportLogs?.()).then(() => {\n exit();\n });\n }\n });\n\n const readyCount = processes.filter((p) => p.status === \"ready\").length;\n const total = processes.length;\n const allReady = readyCount === total;\n const hostProcess = processes.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const recentLogs = logs.slice(-12);\n const sectionedProcesses = getSectionedProcesses(processes);\n const columnWidths = getColumnWidths(processes);\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={0}>\n <Text color=\"#00ffff\">{frames.top(52)}</Text>\n </Box>\n <Box>\n <Text>\n {\" \"}\n {icons.run} {gradients.cyber(description.toUpperCase())}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#00ffff\">{frames.bottom(52)}</Text>\n </Box>\n\n {allReady && (\n <Box marginBottom={1} flexDirection=\"column\">\n <Box>\n <Text color=\"#00ff41\">\n {\" \"}\n {icons.app} APP READY\n </Text>\n </Box>\n <Box>\n <Text color=\"#00ff41\" bold>\n {\" \"}\n {icons.arrow} http://localhost:{hostPort}\n </Text>\n </Box>\n </Box>\n )}\n\n {proxyTarget && (\n <Box marginBottom={1}>\n <Text color=\"#ffaa00\">\n {\" \"}\n {icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}\n </Text>\n </Box>\n )}\n\n <Box marginTop={0} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n {sectionedProcesses.map((section) => (\n <Box key={section.key} flexDirection=\"column\">\n <SectionHeader title={section.title} />\n {section.processes.map((proc) => (\n <ProcessRow\n key={proc.name}\n proc={proc}\n nameWidth={columnWidths.name}\n sourceWidth={columnWidths.source}\n />\n ))}\n </Box>\n ))}\n\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n <Box marginTop={0}>\n <Text color={allReady ? \"#00ff41\" : \"#00ffff\"}>\n {\" \"}\n {allReady\n ? `${icons.ok} All ${total} services running`\n : `${icons.scan} ${readyCount}/${total} ready`}\n </Text>\n <Text color=\"gray\">\n {\" \"}\n {icons.dot} q quit {icons.dot} l logs\n </Text>\n </Box>\n\n {recentLogs.length > 0 && (\n <>\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={0}>\n {recentLogs.map((entry) => (\n <LogLine key={entry.id} entry={entry} />\n ))}\n </Box>\n </>\n )}\n </Box>\n );\n}\n\nexport interface DevViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => void;\n}\n\nexport function renderDevView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n onExportLogs?: () => Promise<void> | void,\n): DevViewHandle {\n let processes = [...initialProcesses];\n let logs: LogEntry[] = [];\n let rerender: (() => void) | null = null;\n const proxyTarget = env.API_PROXY;\n let logSeq = 0;\n let lastLogKey: string | null = null;\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));\n rerender?.();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const nextKey = `${source}:${isError ? \"1\" : \"0\"}:${line}`;\n if (nextKey === lastLogKey) return;\n lastLogKey = nextKey;\n\n logs = [\n ...logs,\n { id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },\n ];\n if (logs.length > 100) logs = logs.slice(-100);\n rerender?.();\n };\n\n function DevViewWrapper() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n rerender = () => forceUpdate((n: number) => n + 1);\n return () => {\n rerender = null;\n };\n }, []);\n\n return (\n <DevView\n processes={processes}\n logs={logs}\n description={description}\n proxyTarget={proxyTarget}\n onExit={onExit}\n onExportLogs={onExportLogs}\n />\n );\n }\n\n const { unmount } = render(<DevViewWrapper />);\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-view.mjs","names":[],"sources":["../../src/components/dev-view.tsx"],"sourcesContent":["import { Box, render, Text, useApp, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { SourceMode } from \"../types\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, divider, frames, gradients, icons } from \"../utils/theme\";\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: SourceMode;\n}\n\nexport interface LogEntry {\n id: string;\n source: string;\n line: string;\n timestamp: number;\n isError?: boolean;\n}\n\ninterface DevViewProps {\n processes: ProcessState[];\n logs: LogEntry[];\n description: string;\n proxyTarget?: string;\n onExit?: () => Promise<void> | void;\n onExportLogs?: () => Promise<void> | void;\n}\n\nfunction StatusIcon({ status }: { status: ProcessStatus }) {\n switch (status) {\n case \"pending\":\n return <Text color=\"gray\">{icons.pending}</Text>;\n case \"starting\":\n return <Text color=\"#00ffff\">{icons.scan}</Text>;\n case \"ready\":\n return <Text color=\"#00ff41\">{icons.ok}</Text>;\n case \"error\":\n return <Text color=\"#ff3366\">{icons.err}</Text>;\n }\n}\n\nfunction getServiceColor(name: string): string {\n if (name.startsWith(PLUGIN_PREFIX)) return \"#ffaa00\";\n return name === \"host\" ? \"#00ffff\" : name === \"ui\" ? \"#ff00ff\" : \"#0080ff\";\n}\n\nfunction getDisplayName(name: string): string {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n}\n\nfunction isPlugin(name: string): boolean {\n return name.startsWith(PLUGIN_PREFIX);\n}\n\nfunction getSectionedProcesses(processes: ProcessState[]): Array<{\n key: string;\n title: string;\n processes: ProcessState[];\n}> {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n}\n\nfunction getColumnWidths(processes: ProcessState[]): { name: number; source: number } {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));\n return { name, source };\n}\n\nfunction ProcessRow({\n proc,\n nameWidth,\n sourceWidth,\n}: {\n proc: ProcessState;\n nameWidth: number;\n sourceWidth: number;\n}) {\n const color = getServiceColor(proc.name);\n const isRemote = proc.source === \"remote\";\n const isHost = proc.name === \"host\";\n const showPort = proc.port > 0 && (isHost || !isRemote);\n const portStr = showPort ? `:${proc.port}` : \"\";\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n\n const statusText =\n proc.status === \"pending\"\n ? \"waiting\"\n : proc.status === \"starting\"\n ? \"starting\"\n : proc.status === \"ready\"\n ? isRemote && !isHost\n ? \"loaded\"\n : \"running\"\n : \"failed\";\n\n return (\n <Box>\n <Text>{\" \"}</Text>\n <StatusIcon status={proc.status} />\n <Text> </Text>\n <Text color={color} bold>\n {getDisplayName(proc.name).padEnd(nameWidth)}\n </Text>\n <Text color=\"gray\">{sourceLabel.padEnd(sourceWidth)}</Text>\n <Text color={proc.status === \"ready\" ? \"#00ff41\" : \"gray\"}>{statusText}</Text>\n {showPort && <Text color=\"#00ffff\"> {portStr}</Text>}\n </Box>\n );\n}\n\nfunction SectionHeader({ title }: { title: string }) {\n return (\n <Box marginBottom={0} marginTop={1}>\n <Text color=\"#00ffff\" bold>\n {title}\n </Text>\n </Box>\n );\n}\n\nfunction LogLine({ entry }: { entry: LogEntry }) {\n const color = getServiceColor(entry.source);\n\n return (\n <Box>\n <Text color={color}>[{entry.source}]</Text>\n <Text color={entry.isError ? \"#ff3366\" : undefined}> {linkify(entry.line)}</Text>\n </Box>\n );\n}\n\nfunction truncateUrl(url: string, maxLen: number): string {\n if (url.length <= maxLen) return url;\n try {\n const parsed = new URL(url);\n const host = parsed.host;\n if (host.length > maxLen - 10) {\n return `${host.slice(0, maxLen - 13)}...`;\n }\n return host;\n } catch {\n return `${url.slice(0, maxLen - 3)}...`;\n }\n}\n\nfunction DevView({\n processes,\n logs,\n description,\n proxyTarget,\n onExit,\n onExportLogs,\n}: DevViewProps) {\n const { exit } = useApp();\n const [isShuttingDown, setIsShuttingDown] = useState(false);\n\n useInput((input, key) => {\n if (isShuttingDown) return;\n\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n setIsShuttingDown(true);\n Promise.resolve(onExit?.()).then(() => {\n exit();\n });\n }\n if (input === \"l\") {\n setIsShuttingDown(true);\n Promise.resolve(onExportLogs?.()).then(() => {\n exit();\n });\n }\n });\n\n const readyCount = processes.filter((p) => p.status === \"ready\").length;\n const total = processes.length;\n const allReady = readyCount === total;\n const hostProcess = processes.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const recentLogs = logs.slice(-12);\n const sectionedProcesses = getSectionedProcesses(processes);\n const columnWidths = getColumnWidths(processes);\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={0}>\n <Text color=\"#00ffff\">{frames.top(52)}</Text>\n </Box>\n <Box>\n <Text>\n {\" \"}\n {icons.run} {gradients.cyber(description.toUpperCase())}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#00ffff\">{frames.bottom(52)}</Text>\n </Box>\n\n {allReady && (\n <Box marginBottom={1} flexDirection=\"column\">\n <Box>\n <Text color=\"#00ff41\">\n {\" \"}\n {icons.app} APP READY\n </Text>\n </Box>\n <Box>\n <Text color=\"#00ff41\" bold>\n {\" \"}\n {icons.arrow} http://localhost:{hostPort}\n </Text>\n </Box>\n </Box>\n )}\n\n {proxyTarget && (\n <Box marginBottom={1}>\n <Text color=\"#ffaa00\">\n {\" \"}\n {icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}\n </Text>\n </Box>\n )}\n\n <Box marginTop={0} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n {sectionedProcesses.map((section) => (\n <Box key={section.key} flexDirection=\"column\">\n <SectionHeader title={section.title} />\n {section.processes.map((proc) => (\n <ProcessRow\n key={proc.name}\n proc={proc}\n nameWidth={columnWidths.name}\n sourceWidth={columnWidths.source}\n />\n ))}\n </Box>\n ))}\n\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n <Box marginTop={0}>\n <Text color={allReady ? \"#00ff41\" : \"#00ffff\"}>\n {\" \"}\n {allReady\n ? `${icons.ok} All ${total} services running`\n : `${icons.scan} ${readyCount}/${total} ready`}\n </Text>\n <Text color=\"gray\">\n {\" \"}\n {icons.dot} q quit {icons.dot} l logs\n </Text>\n </Box>\n\n {recentLogs.length > 0 && (\n <>\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={0}>\n {recentLogs.map((entry) => (\n <LogLine key={entry.id} entry={entry} />\n ))}\n </Box>\n </>\n )}\n </Box>\n );\n}\n\nexport interface DevViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => void;\n}\n\nexport function renderDevView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n onExportLogs?: () => Promise<void> | void,\n): DevViewHandle {\n let processes = [...initialProcesses];\n let logs: LogEntry[] = [];\n let rerender: (() => void) | null = null;\n const proxyTarget = env.API_PROXY;\n let logSeq = 0;\n let lastLogKey: string | null = null;\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));\n rerender?.();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const nextKey = `${source}:${isError ? \"1\" : \"0\"}:${line}`;\n if (nextKey === lastLogKey) return;\n lastLogKey = nextKey;\n\n logs = [\n ...logs,\n { id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },\n ];\n if (logs.length > 100) logs = logs.slice(-100);\n rerender?.();\n };\n\n function DevViewWrapper() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n rerender = () => forceUpdate((n: number) => n + 1);\n return () => {\n rerender = null;\n };\n }, []);\n\n return (\n <DevView\n processes={processes}\n logs={logs}\n description={description}\n proxyTarget={proxyTarget}\n onExit={onExit}\n onExportLogs={onExportLogs}\n />\n );\n }\n\n const { unmount } = render(<DevViewWrapper />);\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
@@ -101,8 +101,11 @@ function renderStreamingView(initialProcesses, description, env, onExit) {
|
|
|
101
101
|
const icon = getStatusIcon(status);
|
|
102
102
|
const displayName = getDisplayName(name).padEnd(columnWidths.name);
|
|
103
103
|
const sourceLabel = proc?.source ? ` (${proc.source})` : "";
|
|
104
|
-
const
|
|
105
|
-
const
|
|
104
|
+
const isRemote = proc?.source === "remote";
|
|
105
|
+
const isHost = name === "host";
|
|
106
|
+
const showPort = proc.port > 0 && (isHost || !isRemote) && status === "ready";
|
|
107
|
+
const statusText = status === "ready" ? isRemote && !isHost ? "loaded" : "running" : status === "starting" ? "starting" : status === "error" ? "failed" : "waiting";
|
|
108
|
+
const portStr = showPort ? ` :${proc.port}` : "";
|
|
106
109
|
write(`${require_theme.colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === "ready" ? require_theme.colors.green(icon) : status === "error" ? require_theme.colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`);
|
|
107
110
|
checkAllReady();
|
|
108
111
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-view.cjs","names":["colors","icons","linkify"],"sources":["../../src/components/streaming-view.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, icons } from \"../utils/theme\";\nimport type { ProcessState, ProcessStatus } from \"./dev-view\";\n\nconst orange = chalk.hex(\"#ffaa00\");\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport interface StreamingViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => Promise<void> | void;\n}\n\nconst getTimestamp = (): string => {\n const now = new Date();\n return `${now.getHours().toString().padStart(2, \"0\")}:${now.getMinutes().toString().padStart(2, \"0\")}:${now.getSeconds().toString().padStart(2, \"0\")}`;\n};\n\nconst write = (text: string) => process.stdout.write(`${text}\\n`);\n\nconst getServiceColor = (name: string): ((s: string) => string) => {\n if (name.startsWith(PLUGIN_PREFIX)) return orange;\n if (name === \"host\") return colors.cyan;\n if (name === \"ui\" || name === \"ui-ssr\") return colors.magenta;\n if (name === \"api\") return colors.blue;\n return colors.white;\n};\n\nconst getDisplayName = (name: string): string => {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n};\n\nconst isPlugin = (name: string): boolean => name.startsWith(PLUGIN_PREFIX);\n\nconst getSectionedProcesses = (processes: ProcessState[]) => {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n};\n\nconst getColumnWidths = (processes: ProcessState[]) => {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? ` (${p.source})`.length : 0)));\n return { name, source };\n};\n\nconst getStatusIcon = (status: ProcessStatus): string => {\n switch (status) {\n case \"pending\":\n return icons.pending;\n case \"starting\":\n return icons.scan;\n case \"ready\":\n return icons.ok;\n case \"error\":\n return icons.err;\n }\n};\n\nexport function renderStreamingView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n): StreamingViewHandle {\n const processes = new Map<string, ProcessState>();\n for (const p of initialProcesses) {\n processes.set(p.name, { ...p });\n }\n\n let allReadyPrinted = false;\n const hostProcess = initialProcesses.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const proxyTarget = env.API_PROXY;\n const sectionedProcesses = getSectionedProcesses(initialProcesses);\n const columnWidths = getColumnWidths(initialProcesses);\n const lastLogBySource = new Map<string, string>();\n\n console.log();\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log(` ${icons.run} ${colors.cyan(description.toUpperCase())}`);\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log();\n\n if (proxyTarget) {\n console.log(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`));\n console.log();\n }\n\n for (const section of sectionedProcesses) {\n console.log(colors.cyan(` ${section.title}`));\n for (const proc of section.processes) {\n const color = getServiceColor(proc.name);\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n console.log(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`,\n );\n }\n console.log();\n }\n\n const checkAllReady = () => {\n if (allReadyPrinted) return;\n const allReady = Array.from(processes.values()).every((p) => p.status === \"ready\");\n if (allReady) {\n allReadyPrinted = true;\n console.log();\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log(colors.green(`${icons.ok} All ${processes.size} services ready`));\n console.log(colors.green(`${icons.arrow} http://localhost:${hostPort}`));\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log();\n }\n };\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n const proc = processes.get(name);\n if (!proc) return;\n\n proc.status = status;\n if (message) proc.message = message;\n\n const color = getServiceColor(name);\n const icon = getStatusIcon(status);\n const displayName = getDisplayName(name).padEnd(columnWidths.name);\n const sourceLabel = proc?.source ? ` (${proc.source})` : \"\";\n const statusText =\n status === \"ready\"\n ? \"ready\"\n : status === \"starting\"\n ? \"starting\"\n : status === \"error\"\n ? \"failed\"\n : \"waiting\";\n const portStr = proc.port > 0 && status === \"ready\" ? ` :${proc.port}` : \"\";\n\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === \"ready\" ? colors.green(icon) : status === \"error\" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`,\n );\n\n checkAllReady();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const lastLine = lastLogBySource.get(source);\n const nextLine = `${isError ? \"ERR\" : \"OUT\"}:${line}`;\n if (lastLine === nextLine) return;\n lastLogBySource.set(source, nextLine);\n\n const color = getServiceColor(source);\n const logColor = isError ? colors.error : colors.dim;\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim(\"│\")} ${logColor(linkify(line))}`,\n );\n };\n\n const unmount = () => onExit?.();\n\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streaming-view.cjs","names":["colors","icons","linkify"],"sources":["../../src/components/streaming-view.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, icons } from \"../utils/theme\";\nimport type { ProcessState, ProcessStatus } from \"./dev-view\";\n\nconst orange = chalk.hex(\"#ffaa00\");\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport interface StreamingViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => Promise<void> | void;\n}\n\nconst getTimestamp = (): string => {\n const now = new Date();\n return `${now.getHours().toString().padStart(2, \"0\")}:${now.getMinutes().toString().padStart(2, \"0\")}:${now.getSeconds().toString().padStart(2, \"0\")}`;\n};\n\nconst write = (text: string) => process.stdout.write(`${text}\\n`);\n\nconst getServiceColor = (name: string): ((s: string) => string) => {\n if (name.startsWith(PLUGIN_PREFIX)) return orange;\n if (name === \"host\") return colors.cyan;\n if (name === \"ui\" || name === \"ui-ssr\") return colors.magenta;\n if (name === \"api\") return colors.blue;\n return colors.white;\n};\n\nconst getDisplayName = (name: string): string => {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n};\n\nconst isPlugin = (name: string): boolean => name.startsWith(PLUGIN_PREFIX);\n\nconst getSectionedProcesses = (processes: ProcessState[]) => {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n};\n\nconst getColumnWidths = (processes: ProcessState[]) => {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? ` (${p.source})`.length : 0)));\n return { name, source };\n};\n\nconst getStatusIcon = (status: ProcessStatus): string => {\n switch (status) {\n case \"pending\":\n return icons.pending;\n case \"starting\":\n return icons.scan;\n case \"ready\":\n return icons.ok;\n case \"error\":\n return icons.err;\n }\n};\n\nexport function renderStreamingView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n): StreamingViewHandle {\n const processes = new Map<string, ProcessState>();\n for (const p of initialProcesses) {\n processes.set(p.name, { ...p });\n }\n\n let allReadyPrinted = false;\n const hostProcess = initialProcesses.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const proxyTarget = env.API_PROXY;\n const sectionedProcesses = getSectionedProcesses(initialProcesses);\n const columnWidths = getColumnWidths(initialProcesses);\n const lastLogBySource = new Map<string, string>();\n\n console.log();\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log(` ${icons.run} ${colors.cyan(description.toUpperCase())}`);\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log();\n\n if (proxyTarget) {\n console.log(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`));\n console.log();\n }\n\n for (const section of sectionedProcesses) {\n console.log(colors.cyan(` ${section.title}`));\n for (const proc of section.processes) {\n const color = getServiceColor(proc.name);\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n console.log(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`,\n );\n }\n console.log();\n }\n\n const checkAllReady = () => {\n if (allReadyPrinted) return;\n const allReady = Array.from(processes.values()).every((p) => p.status === \"ready\");\n if (allReady) {\n allReadyPrinted = true;\n console.log();\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log(colors.green(`${icons.ok} All ${processes.size} services ready`));\n console.log(colors.green(`${icons.arrow} http://localhost:${hostPort}`));\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log();\n }\n };\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n const proc = processes.get(name);\n if (!proc) return;\n\n proc.status = status;\n if (message) proc.message = message;\n\n const color = getServiceColor(name);\n const icon = getStatusIcon(status);\n const displayName = getDisplayName(name).padEnd(columnWidths.name);\n const sourceLabel = proc?.source ? ` (${proc.source})` : \"\";\n const isRemote = proc?.source === \"remote\";\n const isHost = name === \"host\";\n const showPort = proc.port > 0 && (isHost || !isRemote) && status === \"ready\";\n const statusText =\n status === \"ready\"\n ? isRemote && !isHost\n ? \"loaded\"\n : \"running\"\n : status === \"starting\"\n ? \"starting\"\n : status === \"error\"\n ? \"failed\"\n : \"waiting\";\n const portStr = showPort ? ` :${proc.port}` : \"\";\n\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === \"ready\" ? colors.green(icon) : status === \"error\" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`,\n );\n\n checkAllReady();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const lastLine = lastLogBySource.get(source);\n const nextLine = `${isError ? \"ERR\" : \"OUT\"}:${line}`;\n if (lastLine === nextLine) return;\n lastLogBySource.set(source, nextLine);\n\n const color = getServiceColor(source);\n const logColor = isError ? colors.error : colors.dim;\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim(\"│\")} ${logColor(linkify(line))}`,\n );\n };\n\n const unmount = () => onExit?.();\n\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
@@ -99,8 +99,11 @@ function renderStreamingView(initialProcesses, description, env, onExit) {
|
|
|
99
99
|
const icon = getStatusIcon(status);
|
|
100
100
|
const displayName = getDisplayName(name).padEnd(columnWidths.name);
|
|
101
101
|
const sourceLabel = proc?.source ? ` (${proc.source})` : "";
|
|
102
|
-
const
|
|
103
|
-
const
|
|
102
|
+
const isRemote = proc?.source === "remote";
|
|
103
|
+
const isHost = name === "host";
|
|
104
|
+
const showPort = proc.port > 0 && (isHost || !isRemote) && status === "ready";
|
|
105
|
+
const statusText = status === "ready" ? isRemote && !isHost ? "loaded" : "running" : status === "starting" ? "starting" : status === "error" ? "failed" : "waiting";
|
|
106
|
+
const portStr = showPort ? ` :${proc.port}` : "";
|
|
104
107
|
write(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === "ready" ? colors.green(icon) : status === "error" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`);
|
|
105
108
|
checkAllReady();
|
|
106
109
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-view.mjs","names":[],"sources":["../../src/components/streaming-view.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, icons } from \"../utils/theme\";\nimport type { ProcessState, ProcessStatus } from \"./dev-view\";\n\nconst orange = chalk.hex(\"#ffaa00\");\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport interface StreamingViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => Promise<void> | void;\n}\n\nconst getTimestamp = (): string => {\n const now = new Date();\n return `${now.getHours().toString().padStart(2, \"0\")}:${now.getMinutes().toString().padStart(2, \"0\")}:${now.getSeconds().toString().padStart(2, \"0\")}`;\n};\n\nconst write = (text: string) => process.stdout.write(`${text}\\n`);\n\nconst getServiceColor = (name: string): ((s: string) => string) => {\n if (name.startsWith(PLUGIN_PREFIX)) return orange;\n if (name === \"host\") return colors.cyan;\n if (name === \"ui\" || name === \"ui-ssr\") return colors.magenta;\n if (name === \"api\") return colors.blue;\n return colors.white;\n};\n\nconst getDisplayName = (name: string): string => {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n};\n\nconst isPlugin = (name: string): boolean => name.startsWith(PLUGIN_PREFIX);\n\nconst getSectionedProcesses = (processes: ProcessState[]) => {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n};\n\nconst getColumnWidths = (processes: ProcessState[]) => {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? ` (${p.source})`.length : 0)));\n return { name, source };\n};\n\nconst getStatusIcon = (status: ProcessStatus): string => {\n switch (status) {\n case \"pending\":\n return icons.pending;\n case \"starting\":\n return icons.scan;\n case \"ready\":\n return icons.ok;\n case \"error\":\n return icons.err;\n }\n};\n\nexport function renderStreamingView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n): StreamingViewHandle {\n const processes = new Map<string, ProcessState>();\n for (const p of initialProcesses) {\n processes.set(p.name, { ...p });\n }\n\n let allReadyPrinted = false;\n const hostProcess = initialProcesses.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const proxyTarget = env.API_PROXY;\n const sectionedProcesses = getSectionedProcesses(initialProcesses);\n const columnWidths = getColumnWidths(initialProcesses);\n const lastLogBySource = new Map<string, string>();\n\n console.log();\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log(` ${icons.run} ${colors.cyan(description.toUpperCase())}`);\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log();\n\n if (proxyTarget) {\n console.log(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`));\n console.log();\n }\n\n for (const section of sectionedProcesses) {\n console.log(colors.cyan(` ${section.title}`));\n for (const proc of section.processes) {\n const color = getServiceColor(proc.name);\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n console.log(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`,\n );\n }\n console.log();\n }\n\n const checkAllReady = () => {\n if (allReadyPrinted) return;\n const allReady = Array.from(processes.values()).every((p) => p.status === \"ready\");\n if (allReady) {\n allReadyPrinted = true;\n console.log();\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log(colors.green(`${icons.ok} All ${processes.size} services ready`));\n console.log(colors.green(`${icons.arrow} http://localhost:${hostPort}`));\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log();\n }\n };\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n const proc = processes.get(name);\n if (!proc) return;\n\n proc.status = status;\n if (message) proc.message = message;\n\n const color = getServiceColor(name);\n const icon = getStatusIcon(status);\n const displayName = getDisplayName(name).padEnd(columnWidths.name);\n const sourceLabel = proc?.source ? ` (${proc.source})` : \"\";\n const statusText =\n status === \"ready\"\n ? \"ready\"\n : status === \"starting\"\n ? \"starting\"\n : status === \"error\"\n ? \"failed\"\n : \"waiting\";\n const portStr = proc.port > 0 && status === \"ready\" ? ` :${proc.port}` : \"\";\n\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === \"ready\" ? colors.green(icon) : status === \"error\" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`,\n );\n\n checkAllReady();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const lastLine = lastLogBySource.get(source);\n const nextLine = `${isError ? \"ERR\" : \"OUT\"}:${line}`;\n if (lastLine === nextLine) return;\n lastLogBySource.set(source, nextLine);\n\n const color = getServiceColor(source);\n const logColor = isError ? colors.error : colors.dim;\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim(\"│\")} ${logColor(linkify(line))}`,\n );\n };\n\n const unmount = () => onExit?.();\n\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streaming-view.mjs","names":[],"sources":["../../src/components/streaming-view.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, icons } from \"../utils/theme\";\nimport type { ProcessState, ProcessStatus } from \"./dev-view\";\n\nconst orange = chalk.hex(\"#ffaa00\");\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport interface StreamingViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => Promise<void> | void;\n}\n\nconst getTimestamp = (): string => {\n const now = new Date();\n return `${now.getHours().toString().padStart(2, \"0\")}:${now.getMinutes().toString().padStart(2, \"0\")}:${now.getSeconds().toString().padStart(2, \"0\")}`;\n};\n\nconst write = (text: string) => process.stdout.write(`${text}\\n`);\n\nconst getServiceColor = (name: string): ((s: string) => string) => {\n if (name.startsWith(PLUGIN_PREFIX)) return orange;\n if (name === \"host\") return colors.cyan;\n if (name === \"ui\" || name === \"ui-ssr\") return colors.magenta;\n if (name === \"api\") return colors.blue;\n return colors.white;\n};\n\nconst getDisplayName = (name: string): string => {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n};\n\nconst isPlugin = (name: string): boolean => name.startsWith(PLUGIN_PREFIX);\n\nconst getSectionedProcesses = (processes: ProcessState[]) => {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n};\n\nconst getColumnWidths = (processes: ProcessState[]) => {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? ` (${p.source})`.length : 0)));\n return { name, source };\n};\n\nconst getStatusIcon = (status: ProcessStatus): string => {\n switch (status) {\n case \"pending\":\n return icons.pending;\n case \"starting\":\n return icons.scan;\n case \"ready\":\n return icons.ok;\n case \"error\":\n return icons.err;\n }\n};\n\nexport function renderStreamingView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n): StreamingViewHandle {\n const processes = new Map<string, ProcessState>();\n for (const p of initialProcesses) {\n processes.set(p.name, { ...p });\n }\n\n let allReadyPrinted = false;\n const hostProcess = initialProcesses.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const proxyTarget = env.API_PROXY;\n const sectionedProcesses = getSectionedProcesses(initialProcesses);\n const columnWidths = getColumnWidths(initialProcesses);\n const lastLogBySource = new Map<string, string>();\n\n console.log();\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log(` ${icons.run} ${colors.cyan(description.toUpperCase())}`);\n console.log(colors.cyan(`${\"─\".repeat(52)}`));\n console.log();\n\n if (proxyTarget) {\n console.log(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`));\n console.log();\n }\n\n for (const section of sectionedProcesses) {\n console.log(colors.cyan(` ${section.title}`));\n for (const proc of section.processes) {\n const color = getServiceColor(proc.name);\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n console.log(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`,\n );\n }\n console.log();\n }\n\n const checkAllReady = () => {\n if (allReadyPrinted) return;\n const allReady = Array.from(processes.values()).every((p) => p.status === \"ready\");\n if (allReady) {\n allReadyPrinted = true;\n console.log();\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log(colors.green(`${icons.ok} All ${processes.size} services ready`));\n console.log(colors.green(`${icons.arrow} http://localhost:${hostPort}`));\n console.log(colors.dim(`${\"─\".repeat(52)}`));\n console.log();\n }\n };\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n const proc = processes.get(name);\n if (!proc) return;\n\n proc.status = status;\n if (message) proc.message = message;\n\n const color = getServiceColor(name);\n const icon = getStatusIcon(status);\n const displayName = getDisplayName(name).padEnd(columnWidths.name);\n const sourceLabel = proc?.source ? ` (${proc.source})` : \"\";\n const isRemote = proc?.source === \"remote\";\n const isHost = name === \"host\";\n const showPort = proc.port > 0 && (isHost || !isRemote) && status === \"ready\";\n const statusText =\n status === \"ready\"\n ? isRemote && !isHost\n ? \"loaded\"\n : \"running\"\n : status === \"starting\"\n ? \"starting\"\n : status === \"error\"\n ? \"failed\"\n : \"waiting\";\n const portStr = showPort ? ` :${proc.port}` : \"\";\n\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === \"ready\" ? colors.green(icon) : status === \"error\" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`,\n );\n\n checkAllReady();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const lastLine = lastLogBySource.get(source);\n const nextLine = `${isError ? \"ERR\" : \"OUT\"}:${line}`;\n if (lastLine === nextLine) return;\n lastLogBySource.set(source, nextLine);\n\n const color = getServiceColor(source);\n const logColor = isError ? colors.error : colors.dim;\n write(\n `${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim(\"│\")} ${logColor(linkify(line))}`,\n );\n };\n\n const unmount = () => onExit?.();\n\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}
|
package/dist/config.cjs
CHANGED
|
@@ -67,20 +67,38 @@ async function buildRuntimePluginsForConfig(config, baseDir, env) {
|
|
|
67
67
|
const plugins = await resolveRuntimePlugins(config.plugins ?? {}, baseDir, env);
|
|
68
68
|
return Object.keys(plugins).length > 0 ? plugins : void 0;
|
|
69
69
|
}
|
|
70
|
+
function resolveDevelopmentTarget(development, production, baseDir) {
|
|
71
|
+
const devTarget = resolveRuntimeTarget(development, baseDir);
|
|
72
|
+
if (devTarget.source === "local" && (!devTarget.localPath || !(0, node_fs.existsSync)(devTarget.localPath))) return resolveRuntimeTarget(production, baseDir, "remote");
|
|
73
|
+
return devTarget;
|
|
74
|
+
}
|
|
70
75
|
function buildRuntimeConfig(config, baseDir, env, options) {
|
|
71
76
|
const uiConfig = config.app.ui;
|
|
72
77
|
const apiConfig = config.app.api;
|
|
73
78
|
const authConfig = config.app.auth;
|
|
74
|
-
const uiRuntime = env === "development" ?
|
|
75
|
-
const apiRuntime = env === "development" ?
|
|
76
|
-
const authRuntime = authConfig ? env === "development" ?
|
|
79
|
+
const uiRuntime = env === "development" ? resolveDevelopmentTarget(uiConfig.development, uiConfig.production, baseDir) : resolveRuntimeTarget(uiConfig.production, baseDir, "remote");
|
|
80
|
+
const apiRuntime = env === "development" ? resolveDevelopmentTarget(apiConfig.development, apiConfig.production, baseDir) : resolveRuntimeTarget(apiConfig.production, baseDir, "remote");
|
|
81
|
+
const authRuntime = authConfig ? env === "development" ? resolveDevelopmentTarget(authConfig.development, authConfig.production, baseDir) : resolveRuntimeTarget(authConfig.production, baseDir, "remote") : void 0;
|
|
82
|
+
const hostConfig = config.app.host;
|
|
83
|
+
const hostRuntime = env === "development" ? resolveDevelopmentTarget(hostConfig.development, hostConfig.production, baseDir) : resolveRuntimeTarget(hostConfig.production, baseDir, "remote");
|
|
84
|
+
const hostListeningUrl = resolveDevelopmentHostUrl(hostConfig.development);
|
|
77
85
|
return {
|
|
78
86
|
env,
|
|
79
87
|
account: config.account,
|
|
80
88
|
domain: config.domain,
|
|
81
89
|
networkId: require_network.getNetworkIdForAccount(config.account),
|
|
82
90
|
repository: config.repository,
|
|
83
|
-
|
|
91
|
+
host: {
|
|
92
|
+
name: "host",
|
|
93
|
+
url: hostListeningUrl,
|
|
94
|
+
entry: `${hostListeningUrl}/mf-manifest.json`,
|
|
95
|
+
localPath: hostRuntime.localPath,
|
|
96
|
+
port: hostRuntime.port ?? DEFAULT_HOST_PORT,
|
|
97
|
+
secrets: hostConfig.secrets,
|
|
98
|
+
integrity: env === "production" ? hostConfig.integrity : void 0,
|
|
99
|
+
source: hostRuntime.source,
|
|
100
|
+
remoteUrl: hostRuntime.source === "remote" ? hostRuntime.url : void 0
|
|
101
|
+
},
|
|
84
102
|
shared: config.shared,
|
|
85
103
|
ui: {
|
|
86
104
|
name: uiConfig.name,
|
|
@@ -242,10 +260,15 @@ function resolveRuntimeTarget(value, baseDir, defaultSource = "remote") {
|
|
|
242
260
|
if (value.startsWith(LOCAL_PREFIX)) {
|
|
243
261
|
const localTarget = value.slice(6).trim();
|
|
244
262
|
if (!localTarget) throw new Error(`Invalid local development target: ${value}`);
|
|
263
|
+
const localPath = (0, node_path.resolve)(baseDir, localTarget);
|
|
264
|
+
if (!(0, node_fs.existsSync)(localPath)) return {
|
|
265
|
+
source: defaultSource,
|
|
266
|
+
url: ""
|
|
267
|
+
};
|
|
245
268
|
return {
|
|
246
269
|
source: "local",
|
|
247
270
|
url: "",
|
|
248
|
-
localPath
|
|
271
|
+
localPath
|
|
249
272
|
};
|
|
250
273
|
}
|
|
251
274
|
return {
|