everything-dev 1.7.1 → 1.8.0

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.
Files changed (136) hide show
  1. package/dist/api.cjs +1 -1
  2. package/dist/api.mjs +1 -1
  3. package/dist/app.cjs +82 -51
  4. package/dist/app.cjs.map +1 -1
  5. package/dist/app.mjs +82 -51
  6. package/dist/app.mjs.map +1 -1
  7. package/dist/cli/upgrade.cjs +70 -10
  8. package/dist/cli/upgrade.cjs.map +1 -1
  9. package/dist/cli/upgrade.mjs +71 -11
  10. package/dist/cli/upgrade.mjs.map +1 -1
  11. package/dist/components/dev-view.cjs +6 -3
  12. package/dist/components/dev-view.cjs.map +1 -1
  13. package/dist/components/dev-view.mjs +6 -3
  14. package/dist/components/dev-view.mjs.map +1 -1
  15. package/dist/components/streaming-view.cjs +5 -2
  16. package/dist/components/streaming-view.cjs.map +1 -1
  17. package/dist/components/streaming-view.mjs +5 -2
  18. package/dist/components/streaming-view.mjs.map +1 -1
  19. package/dist/config.cjs +28 -5
  20. package/dist/config.cjs.map +1 -1
  21. package/dist/config.d.cts.map +1 -1
  22. package/dist/config.d.mts.map +1 -1
  23. package/dist/config.mjs +28 -5
  24. package/dist/config.mjs.map +1 -1
  25. package/dist/contract.cjs +1 -0
  26. package/dist/contract.cjs.map +1 -1
  27. package/dist/contract.d.cts +14 -6
  28. package/dist/contract.d.cts.map +1 -1
  29. package/dist/contract.d.mts +14 -6
  30. package/dist/contract.d.mts.map +1 -1
  31. package/dist/contract.mjs +1 -0
  32. package/dist/contract.mjs.map +1 -1
  33. package/dist/dev-logs.cjs +6 -2
  34. package/dist/dev-logs.cjs.map +1 -1
  35. package/dist/dev-logs.mjs +7 -2
  36. package/dist/dev-logs.mjs.map +1 -1
  37. package/dist/dev-session.cjs +27 -23
  38. package/dist/dev-session.cjs.map +1 -1
  39. package/dist/dev-session.mjs +27 -24
  40. package/dist/dev-session.mjs.map +1 -1
  41. package/dist/federation.server.cjs +1 -1
  42. package/dist/federation.server.mjs +1 -1
  43. package/dist/host.cjs +4 -3
  44. package/dist/host.cjs.map +1 -1
  45. package/dist/host.d.cts.map +1 -1
  46. package/dist/host.d.mts.map +1 -1
  47. package/dist/host.mjs +4 -3
  48. package/dist/host.mjs.map +1 -1
  49. package/dist/integrity.cjs +68 -2
  50. package/dist/integrity.cjs.map +1 -1
  51. package/dist/integrity.d.cts +14 -1
  52. package/dist/integrity.d.cts.map +1 -1
  53. package/dist/integrity.d.mts +14 -1
  54. package/dist/integrity.d.mts.map +1 -1
  55. package/dist/integrity.mjs +66 -3
  56. package/dist/integrity.mjs.map +1 -1
  57. package/dist/mf.cjs +32 -0
  58. package/dist/mf.cjs.map +1 -1
  59. package/dist/mf.d.cts +3 -1
  60. package/dist/mf.d.cts.map +1 -1
  61. package/dist/mf.d.mts +3 -1
  62. package/dist/mf.d.mts.map +1 -1
  63. package/dist/mf.mjs +32 -1
  64. package/dist/mf.mjs.map +1 -1
  65. package/dist/orchestrator.cjs +167 -317
  66. package/dist/orchestrator.cjs.map +1 -1
  67. package/dist/orchestrator.d.cts +24 -21
  68. package/dist/orchestrator.d.cts.map +1 -1
  69. package/dist/orchestrator.d.mts +24 -21
  70. package/dist/orchestrator.d.mts.map +1 -1
  71. package/dist/orchestrator.mjs +168 -316
  72. package/dist/orchestrator.mjs.map +1 -1
  73. package/dist/plugin.cjs +38 -107
  74. package/dist/plugin.cjs.map +1 -1
  75. package/dist/plugin.d.cts +19 -5
  76. package/dist/plugin.d.cts.map +1 -1
  77. package/dist/plugin.d.mts +19 -5
  78. package/dist/plugin.d.mts.map +1 -1
  79. package/dist/plugin.mjs +39 -108
  80. package/dist/plugin.mjs.map +1 -1
  81. package/dist/service-descriptor.cjs +188 -0
  82. package/dist/service-descriptor.cjs.map +1 -0
  83. package/dist/service-descriptor.d.cts +107 -0
  84. package/dist/service-descriptor.d.cts.map +1 -0
  85. package/dist/service-descriptor.d.mts +107 -0
  86. package/dist/service-descriptor.d.mts.map +1 -0
  87. package/dist/service-descriptor.mjs +182 -0
  88. package/dist/service-descriptor.mjs.map +1 -0
  89. package/dist/types.cjs +8 -1
  90. package/dist/types.cjs.map +1 -1
  91. package/dist/types.d.cts +18 -3
  92. package/dist/types.d.cts.map +1 -1
  93. package/dist/types.d.mts +18 -3
  94. package/dist/types.d.mts.map +1 -1
  95. package/dist/types.mjs +8 -1
  96. package/dist/types.mjs.map +1 -1
  97. package/dist/ui/index.cjs +1 -0
  98. package/dist/ui/index.d.cts +2 -2
  99. package/dist/ui/index.d.mts +2 -2
  100. package/dist/ui/index.mjs +2 -2
  101. package/dist/ui/runtime.cjs +4 -0
  102. package/dist/ui/runtime.cjs.map +1 -1
  103. package/dist/ui/runtime.d.cts +2 -1
  104. package/dist/ui/runtime.d.cts.map +1 -1
  105. package/dist/ui/runtime.d.mts +2 -1
  106. package/dist/ui/runtime.d.mts.map +1 -1
  107. package/dist/ui/runtime.mjs +4 -1
  108. package/dist/ui/runtime.mjs.map +1 -1
  109. package/package.json +12 -4
  110. package/skills/dev-workflow/SKILL.md +105 -0
  111. package/skills/publish-sync/SKILL.md +130 -0
  112. package/src/app.ts +98 -204
  113. package/src/cli/upgrade.ts +113 -13
  114. package/src/components/dev-view.tsx +8 -3
  115. package/src/components/streaming-view.ts +7 -2
  116. package/src/config.ts +40 -8
  117. package/src/contract.ts +1 -0
  118. package/src/dev-logs.ts +8 -1
  119. package/src/dev-session.ts +56 -79
  120. package/src/host.ts +4 -3
  121. package/src/integrity.ts +96 -10
  122. package/src/mf.ts +42 -0
  123. package/src/orchestrator.ts +232 -411
  124. package/src/plugin.ts +48 -136
  125. package/src/service-descriptor.ts +258 -0
  126. package/src/types.ts +8 -1
  127. package/src/ui/runtime.ts +5 -0
  128. package/dist/process-registry.cjs +0 -120
  129. package/dist/process-registry.cjs.map +0 -1
  130. package/dist/process-registry.d.cts +0 -25
  131. package/dist/process-registry.d.cts.map +0 -1
  132. package/dist/process-registry.d.mts +0 -25
  133. package/dist/process-registry.d.mts.map +0 -1
  134. package/dist/process-registry.mjs +0 -119
  135. package/dist/process-registry.mjs.map +0 -1
  136. package/src/process-registry.ts +0 -154
@@ -3,6 +3,7 @@ const require_cli_init = require('./init.cjs');
3
3
  const require_sync = require('./sync.cjs');
4
4
  let node_fs = require("node:fs");
5
5
  let node_path = require("node:path");
6
+ let glob = require("glob");
6
7
 
7
8
  //#region src/cli/upgrade.ts
8
9
  const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
@@ -28,18 +29,75 @@ function readInstalledVersion(projectDir, packageName) {
28
29
  if (!version) return void 0;
29
30
  return version.replace(/^[\^~>=]+/, "");
30
31
  }
31
- function updatePackageVersion(projectDir, packageName, newVersion) {
32
- const pkgPath = (0, node_path.join)(projectDir, "package.json");
33
- const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
34
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
35
- const deps = pkg.dependencies;
36
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
32
+ function isBumpedableVersion(value) {
33
+ if (!value) return false;
34
+ if (value === "workspace:*") return false;
35
+ if (value.startsWith("catalog:")) return false;
36
+ return true;
37
+ }
38
+ function bumpDepField(field, packageName, newVersion) {
39
+ if (!field) return false;
40
+ if (!(packageName in field)) return false;
41
+ const current = field[packageName];
42
+ if (!isBumpedableVersion(current)) return false;
43
+ field[packageName] = `^${newVersion}`;
44
+ return true;
45
+ }
46
+ function bumpCatalog(catalog, packageName, newVersion) {
47
+ if (!catalog) return false;
48
+ if (!(packageName in catalog)) return false;
49
+ const current = catalog[packageName];
50
+ if (!isBumpedableVersion(current)) return false;
51
+ catalog[packageName] = `^${newVersion}`;
52
+ return true;
53
+ }
54
+ function bumpPackageJson(pkg, packageName, newVersion) {
55
+ const fields = [];
56
+ for (const fieldName of [
57
+ "dependencies",
58
+ "devDependencies",
59
+ "peerDependencies"
60
+ ]) {
61
+ const field = pkg[fieldName];
62
+ if (bumpDepField(field, packageName, newVersion)) fields.push(fieldName);
37
63
  }
38
- if (pkg.devDependencies && typeof pkg.devDependencies === "object") {
39
- const deps = pkg.devDependencies;
40
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
64
+ const workspaces = pkg.workspaces;
65
+ if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) fields.push("workspaces.catalog");
66
+ return {
67
+ modified: fields.length > 0,
68
+ fields
69
+ };
70
+ }
71
+ function updatePackageVersionInFile(filePath, packageName, newVersion) {
72
+ const pkg = JSON.parse((0, node_fs.readFileSync)(filePath, "utf-8"));
73
+ const result = bumpPackageJson(pkg, packageName, newVersion);
74
+ if (result.modified) (0, node_fs.writeFileSync)(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
75
+ return result.modified;
76
+ }
77
+ function updatePackageVersion(projectDir, packageName, newVersion) {
78
+ return updatePackageVersionInFile((0, node_path.join)(projectDir, "package.json"), packageName, newVersion);
79
+ }
80
+ async function findWorkspacePackageJsons(projectDir) {
81
+ const rootPkgPath = (0, node_path.join)(projectDir, "package.json");
82
+ if (!(0, node_fs.existsSync)(rootPkgPath)) return [];
83
+ const workspaceConfig = JSON.parse((0, node_fs.readFileSync)(rootPkgPath, "utf-8")).workspaces;
84
+ const patterns = [];
85
+ if (Array.isArray(workspaceConfig)) patterns.push(...workspaceConfig);
86
+ else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) patterns.push(...workspaceConfig.packages);
87
+ if (patterns.length === 0) return [];
88
+ const pkgPaths = [];
89
+ for (const pattern of patterns) {
90
+ const matches = await (0, glob.glob)(pattern, {
91
+ cwd: projectDir,
92
+ dot: false,
93
+ absolute: false
94
+ });
95
+ for (const match of matches) {
96
+ const pkgPath = (0, node_path.join)(projectDir, match, "package.json");
97
+ if ((0, node_fs.existsSync)(pkgPath) && (0, node_fs.statSync)(pkgPath).isFile()) pkgPaths.push(pkgPath);
98
+ }
41
99
  }
42
- (0, node_fs.writeFileSync)(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
100
+ return [...new Set(pkgPaths)];
43
101
  }
44
102
  function buildChangelogUrl(oldVersion, newVersion, parentConfig) {
45
103
  if (!oldVersion || oldVersion === newVersion) return void 0;
@@ -93,6 +151,8 @@ async function upgradeTemplate(projectDir, options) {
93
151
  };
94
152
  }
95
153
  for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersion(projectDir, pkg.name, pkg.to);
154
+ const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
155
+ for (const pkgPath of workspacePkgPaths) for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);
96
156
  if (hasUpdates && !options.noInstall) await require_cli_init.runBunInstall(projectDir);
97
157
  let syncResult;
98
158
  if (!options.noSync) syncResult = await require_sync.syncTemplate(projectDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.cjs","names":["runBunInstall","syncTemplate"],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\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 updatePackageVersion(projectDir: string, packageName: string, newVersion: string): void {\n const pkgPath = join(projectDir, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.dependencies && typeof pkg.dependencies === \"object\") {\n const deps = pkg.dependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\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 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":";;;;;;;;AAOA,MAAM,qBAAqB,CAAC,kBAAkB,eAAe;AAM7D,eAAe,sBAAsB,aAA6C;AAChF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU;GAC/E,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;AACF,MAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UADc,MAAM,SAAS,MAAM,EACvB;SACN;AACN,SAAO;;;AAIX,SAAS,qBAAqB,YAAoB,aAAyC;CACzF,MAAM,8BAAe,YAAY,eAAe;AAChD,KAAI,yBAAY,QAAQ,CAAE,QAAO;CACjC,MAAM,MAAM,KAAK,gCAAmB,SAAS,QAAQ,CAAC;CACtD,MAAM,OAAQ,IAAI,gBAAgB,EAAE;CACpC,MAAM,UAAW,IAAI,mBAAmB,EAAE;CAC1C,MAAM,UAAU,KAAK,gBAAgB,QAAQ;AAC7C,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,QAAQ,QAAQ,aAAa,GAAG;;AAGzC,SAAS,oBAAoB,OAAoC;AAC/D,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,UAAU,cAAe,QAAO;AACpC,KAAI,MAAM,WAAW,WAAW,CAAE,QAAO;AACzC,QAAO;;AAGT,SAAS,aACP,OACA,aACA,YACS;AACT,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,EAAE,eAAe,OAAQ,QAAO;CACpC,MAAM,UAAU,MAAM;AACtB,KAAI,CAAC,oBAAoB,QAAQ,CAAE,QAAO;AAC1C,OAAM,eAAe,IAAI;AACzB,QAAO;;AAGT,SAAS,YACP,SACA,aACA,YACS;AACT,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,EAAE,eAAe,SAAU,QAAO;CACtC,MAAM,UAAU,QAAQ;AACxB,KAAI,CAAC,oBAAoB,QAAQ,CAAE,QAAO;AAC1C,SAAQ,eAAe,IAAI;AAC3B,QAAO;;AAQT,SAAS,gBACP,KACA,aACA,YACY;CACZ,MAAM,SAAmB,EAAE;AAE3B,MAAK,MAAM,aAAa;EAAC;EAAgB;EAAmB;EAAmB,EAAW;EACxF,MAAM,QAAQ,IAAI;AAClB,MAAI,aAAa,OAAO,aAAa,WAAW,CAC9C,QAAO,KAAK,UAAU;;CAI1B,MAAM,aAAa,IAAI;AACvB,KAAI,YAAY,WAAW,YAAY,WAAW,SAAS,aAAa,WAAW,CACjF,QAAO,KAAK,qBAAqB;AAGnC,QAAO;EAAE,UAAU,OAAO,SAAS;EAAG;EAAQ;;AAGhD,SAAS,2BACP,UACA,aACA,YACS;CACT,MAAM,MAAM,KAAK,gCAAmB,UAAU,QAAQ,CAAC;CACvD,MAAM,SAAS,gBAAgB,KAAK,aAAa,WAAW;AAC5D,KAAI,OAAO,SACT,4BAAc,UAAU,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC,IAAI;AAE9D,QAAO,OAAO;;AAGhB,SAAS,qBACP,YACA,aACA,YACS;AACT,QAAO,+CAAgC,YAAY,eAAe,EAAE,aAAa,WAAW;;AAG9F,eAAe,0BAA0B,YAAuC;CAC9E,MAAM,kCAAmB,YAAY,eAAe;AACpD,KAAI,yBAAY,YAAY,CAAE,QAAO,EAAE;CAGvC,MAAM,kBADU,KAAK,gCAAmB,aAAa,QAAQ,CAAC,CAC9B;CAEhC,MAAM,WAAqB,EAAE;AAC7B,KAAI,MAAM,QAAQ,gBAAgB,CAChC,UAAS,KAAK,GAAG,gBAAgB;UACxB,iBAAiB,YAAY,MAAM,QAAQ,gBAAgB,SAAS,CAC7E,UAAS,KAAK,GAAG,gBAAgB,SAAS;AAG5C,KAAI,SAAS,WAAW,EAAG,QAAO,EAAE;CAEpC,MAAM,WAAqB,EAAE;AAC7B,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,UAAU,qBAAW,SAAS;GAAE,KAAK;GAAY,KAAK;GAAO,UAAU;GAAO,CAAC;AACrF,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,8BAAe,YAAY,OAAO,eAAe;AACvD,+BAAe,QAAQ,0BAAa,QAAQ,CAAC,QAAQ,CACnD,UAAS,KAAK,QAAQ;;;AAK5B,QAAO,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;;AAG/B,SAAS,kBACP,YACA,YACA,cACoB;AACpB,KAAI,CAAC,cAAc,eAAe,WAAY,QAAO;CACrD,MAAM,UAAU,cAAc;AAC9B,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,cAAc,QAAQ,MAAM,wDAAwD;AAC1F,KAAI,CAAC,YAAa,QAAO;CAEzB,MAAM,GAAG,OAAO,QAAQ;AACxB,QAAO,sBAAsB,MAAM,GAAG,KAAK,YAAY,WAAW,MAAM;;AAG1E,eAAsB,gBACpB,YACA,SACwB;AAExB,KAAI,6CADiB,YAAY,eAAe,CACxB,CACtB,QAAO;EACL,QAAQ;EACR,UAAU,EAAE;EACZ,OAAO;EACR;CAGH,MAAM,WAAsC,EAAE;AAE9C,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,YAAY,qBAAqB,YAAY,KAAK;EACxD,MAAM,SAAS,MAAM,sBAAsB,KAAK;AAEhD,MAAI,CAAC,QAAQ;AACX,YAAS,KAAK;IAAE;IAAM,MAAM;IAAW,IAAI,aAAa;IAAW,CAAC;AACpE;;AAGF,WAAS,KAAK;GAAE;GAAM,MAAM;GAAW,IAAI;GAAQ,CAAC;;CAGtD,MAAM,aAAa,SAAS,MAAM,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,OAAU;AAEhF,KAAI,QAAQ,QAAQ;EAClB,IAAI;AACJ,MAAI,YAAY;GACd,MAAM,iCAAkB,YAAY,kBAAkB;GACtD,IAAI,eAA+C;AACnD,+BAAe,WAAW,CACxB,KAAI;AACF,mBAAe,KAAK,gCAAmB,YAAY,QAAQ,CAAC;WACtD;GAEV,MAAM,UAAU,SAAS,MAAM,MAAM,EAAE,SAAS,iBAAiB;AACjE,OAAI,SAAS,QAAQ,QAAQ,SAAS,QAAQ,GAC5C,gBAAe,kBAAkB,QAAQ,MAAM,QAAQ,IAAI,aAAa;;AAI5E,SAAO;GACL,QAAQ;GACR;GACA;GACD;;AAGH,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,UAAa,IAAI,SAAS,IAAI,GAC7C,sBAAqB,YAAY,IAAI,MAAM,IAAI,GAAG;CAItD,MAAM,oBAAoB,MAAM,0BAA0B,WAAW;AACrE,MAAK,MAAM,WAAW,kBACpB,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,UAAa,IAAI,SAAS,IAAI,GAC7C,4BAA2B,SAAS,IAAI,MAAM,IAAI,GAAG;AAK3D,KAAI,cAAc,CAAC,QAAQ,UACzB,OAAMA,+BAAc,WAAW;CAGjC,IAAI;AACJ,KAAI,CAAC,QAAQ,OACX,cAAa,MAAMC,0BAAa,YAAY;EAC1C,QAAQ;EACR,OAAO,QAAQ;EACf,WAAW;EACZ,CAAC;CAGJ,IAAI;CACJ,MAAM,UAAU,SAAS,MAAM,MAAM,EAAE,SAAS,iBAAiB;AACjE,KAAI,SAAS,QAAQ,QAAQ,SAAS,QAAQ,IAAI;EAChD,MAAM,iCAAkB,YAAY,kBAAkB;EACtD,IAAI,eAA+C;AACnD,8BAAe,WAAW,CACxB,KAAI;AACF,kBAAe,KAAK,gCAAmB,YAAY,QAAQ,CAAC;UACtD;AAEV,iBAAe,kBAAkB,QAAQ,MAAM,QAAQ,IAAI,aAAa;;AAG1E,QAAO;EACL,QAAQ;EACR;EACA,MAAM;EACN;EACD"}
@@ -1,7 +1,8 @@
1
1
  import { runBunInstall } from "./init.mjs";
2
2
  import { syncTemplate } from "./sync.mjs";
3
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import { glob } from "glob";
5
6
 
6
7
  //#region src/cli/upgrade.ts
7
8
  const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
@@ -27,18 +28,75 @@ function readInstalledVersion(projectDir, packageName) {
27
28
  if (!version) return void 0;
28
29
  return version.replace(/^[\^~>=]+/, "");
29
30
  }
30
- function updatePackageVersion(projectDir, packageName, newVersion) {
31
- const pkgPath = join(projectDir, "package.json");
32
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
33
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
34
- const deps = pkg.dependencies;
35
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
31
+ function isBumpedableVersion(value) {
32
+ if (!value) return false;
33
+ if (value === "workspace:*") return false;
34
+ if (value.startsWith("catalog:")) return false;
35
+ return true;
36
+ }
37
+ function bumpDepField(field, packageName, newVersion) {
38
+ if (!field) return false;
39
+ if (!(packageName in field)) return false;
40
+ const current = field[packageName];
41
+ if (!isBumpedableVersion(current)) return false;
42
+ field[packageName] = `^${newVersion}`;
43
+ return true;
44
+ }
45
+ function bumpCatalog(catalog, packageName, newVersion) {
46
+ if (!catalog) return false;
47
+ if (!(packageName in catalog)) return false;
48
+ const current = catalog[packageName];
49
+ if (!isBumpedableVersion(current)) return false;
50
+ catalog[packageName] = `^${newVersion}`;
51
+ return true;
52
+ }
53
+ function bumpPackageJson(pkg, packageName, newVersion) {
54
+ const fields = [];
55
+ for (const fieldName of [
56
+ "dependencies",
57
+ "devDependencies",
58
+ "peerDependencies"
59
+ ]) {
60
+ const field = pkg[fieldName];
61
+ if (bumpDepField(field, packageName, newVersion)) fields.push(fieldName);
36
62
  }
37
- if (pkg.devDependencies && typeof pkg.devDependencies === "object") {
38
- const deps = pkg.devDependencies;
39
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
63
+ const workspaces = pkg.workspaces;
64
+ if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) fields.push("workspaces.catalog");
65
+ return {
66
+ modified: fields.length > 0,
67
+ fields
68
+ };
69
+ }
70
+ function updatePackageVersionInFile(filePath, packageName, newVersion) {
71
+ const pkg = JSON.parse(readFileSync(filePath, "utf-8"));
72
+ const result = bumpPackageJson(pkg, packageName, newVersion);
73
+ if (result.modified) writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
74
+ return result.modified;
75
+ }
76
+ function updatePackageVersion(projectDir, packageName, newVersion) {
77
+ return updatePackageVersionInFile(join(projectDir, "package.json"), packageName, newVersion);
78
+ }
79
+ async function findWorkspacePackageJsons(projectDir) {
80
+ const rootPkgPath = join(projectDir, "package.json");
81
+ if (!existsSync(rootPkgPath)) return [];
82
+ const workspaceConfig = JSON.parse(readFileSync(rootPkgPath, "utf-8")).workspaces;
83
+ const patterns = [];
84
+ if (Array.isArray(workspaceConfig)) patterns.push(...workspaceConfig);
85
+ else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) patterns.push(...workspaceConfig.packages);
86
+ if (patterns.length === 0) return [];
87
+ const pkgPaths = [];
88
+ for (const pattern of patterns) {
89
+ const matches = await glob(pattern, {
90
+ cwd: projectDir,
91
+ dot: false,
92
+ absolute: false
93
+ });
94
+ for (const match of matches) {
95
+ const pkgPath = join(projectDir, match, "package.json");
96
+ if (existsSync(pkgPath) && statSync(pkgPath).isFile()) pkgPaths.push(pkgPath);
97
+ }
40
98
  }
41
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
99
+ return [...new Set(pkgPaths)];
42
100
  }
43
101
  function buildChangelogUrl(oldVersion, newVersion, parentConfig) {
44
102
  if (!oldVersion || oldVersion === newVersion) return void 0;
@@ -92,6 +150,8 @@ async function upgradeTemplate(projectDir, options) {
92
150
  };
93
151
  }
94
152
  for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersion(projectDir, pkg.name, pkg.to);
153
+ const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
154
+ for (const pkgPath of workspacePkgPaths) for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);
95
155
  if (hasUpdates && !options.noInstall) await runBunInstall(projectDir);
96
156
  let syncResult;
97
157
  if (!options.noSync) syncResult = await syncTemplate(projectDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.mjs","names":[],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\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 updatePackageVersion(projectDir: string, packageName: string, newVersion: string): void {\n const pkgPath = join(projectDir, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.dependencies && typeof pkg.dependencies === \"object\") {\n const deps = pkg.dependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\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 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 portStr = proc.port > 0 ? `:${proc.port}` : "";
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
- proc.port > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, {
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 portStr = proc.port > 0 ? `:${proc.port}` : "";
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
- proc.port > 0 && /* @__PURE__ */ jsxs(Text, {
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 statusText = status === "ready" ? "ready" : status === "starting" ? "starting" : status === "error" ? "failed" : "waiting";
105
- const portStr = proc.port > 0 && status === "ready" ? ` :${proc.port}` : "";
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 statusText = status === "ready" ? "ready" : status === "starting" ? "starting" : status === "error" ? "failed" : "waiting";
103
- const portStr = proc.port > 0 && status === "ready" ? ` :${proc.port}` : "";
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
  };