oh-my-customcode 0.42.3 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -9376,7 +9376,9 @@ var require_src = __commonJS((exports, module) => {
9376
9376
  });
9377
9377
 
9378
9378
  // src/cli/index.ts
9379
+ import { existsSync as existsSync3 } from "node:fs";
9379
9380
  import { createRequire as createRequire2 } from "node:module";
9381
+ import { join as join14 } from "node:path";
9380
9382
 
9381
9383
  // node_modules/.bun/commander@14.0.2/node_modules/commander/esm.mjs
9382
9384
  var import__ = __toESM(require_commander(), 1);
@@ -14207,7 +14209,7 @@ async function doctorCommand(options = {}) {
14207
14209
  }
14208
14210
 
14209
14211
  // src/cli/init.ts
14210
- import { join as join9 } from "node:path";
14212
+ import { join as join10 } from "node:path";
14211
14213
 
14212
14214
  // src/core/installer.ts
14213
14215
  init_fs();
@@ -15063,6 +15065,83 @@ async function checkUvAvailable() {
15063
15065
  // src/cli/init.ts
15064
15066
  init_fs();
15065
15067
 
15068
+ // src/cli/serve.ts
15069
+ import { spawn } from "node:child_process";
15070
+ import { existsSync as existsSync2 } from "node:fs";
15071
+ import { readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
15072
+ import { join as join9 } from "node:path";
15073
+ var DEFAULT_PORT = 4321;
15074
+ var PID_FILE = join9(process.env.HOME ?? "~", ".omcustom-serve.pid");
15075
+ function findServeBuildDir(projectRoot) {
15076
+ const localBuild = join9(projectRoot, "packages", "serve", "build");
15077
+ if (existsSync2(join9(localBuild, "index.js")))
15078
+ return localBuild;
15079
+ const npmBuild = join9(import.meta.dirname, "..", "..", "packages", "serve", "build");
15080
+ if (existsSync2(join9(npmBuild, "index.js")))
15081
+ return npmBuild;
15082
+ return null;
15083
+ }
15084
+ async function isServeRunning() {
15085
+ try {
15086
+ const raw = await readFile2(PID_FILE, "utf-8");
15087
+ const pid = Number(raw.trim());
15088
+ if (!Number.isFinite(pid) || pid <= 0) {
15089
+ await cleanupPidFile();
15090
+ return false;
15091
+ }
15092
+ process.kill(pid, 0);
15093
+ return true;
15094
+ } catch {
15095
+ await cleanupPidFile();
15096
+ return false;
15097
+ }
15098
+ }
15099
+ async function startServeBackground(projectRoot, port = DEFAULT_PORT) {
15100
+ if (await isServeRunning()) {
15101
+ return;
15102
+ }
15103
+ const buildDir = findServeBuildDir(projectRoot);
15104
+ if (buildDir === null) {
15105
+ return;
15106
+ }
15107
+ const child = spawn("node", [join9(buildDir, "index.js")], {
15108
+ env: {
15109
+ ...process.env,
15110
+ OMCUSTOM_PORT: String(port),
15111
+ OMCUSTOM_HOST: "localhost",
15112
+ OMCUSTOM_ORIGIN: `http://localhost:${port}`,
15113
+ OMCUSTOM_PROJECT_ROOT: projectRoot
15114
+ },
15115
+ stdio: "ignore",
15116
+ detached: true
15117
+ });
15118
+ child.unref();
15119
+ if (child.pid !== undefined) {
15120
+ await writeFile2(PID_FILE, String(child.pid), "utf-8");
15121
+ }
15122
+ }
15123
+ async function stopServe() {
15124
+ try {
15125
+ const raw = await readFile2(PID_FILE, "utf-8");
15126
+ const pid = Number(raw.trim());
15127
+ if (!Number.isFinite(pid) || pid <= 0) {
15128
+ await cleanupPidFile();
15129
+ return false;
15130
+ }
15131
+ process.kill(pid, "SIGTERM");
15132
+ await cleanupPidFile();
15133
+ return true;
15134
+ } catch {
15135
+ await cleanupPidFile();
15136
+ return false;
15137
+ }
15138
+ }
15139
+ async function cleanupPidFile() {
15140
+ try {
15141
+ await unlink(PID_FILE);
15142
+ } catch {}
15143
+ }
15144
+
15066
15145
  // node_modules/.bun/@clack+core@1.1.0/node_modules/@clack/core/dist/index.mjs
15067
15146
  import { styleText as D } from "node:util";
15068
15147
  import { stdout as R, stdin as q } from "node:process";
@@ -16040,7 +16119,7 @@ async function runInitWizard(options) {
16040
16119
  // src/cli/init.ts
16041
16120
  async function checkExistingInstallation(targetDir) {
16042
16121
  const layout = getProviderLayout();
16043
- const rootDir = join9(targetDir, layout.rootDir);
16122
+ const rootDir = join10(targetDir, layout.rootDir);
16044
16123
  return fileExists(rootDir);
16045
16124
  }
16046
16125
  var PROVIDER_SUBDIR_COMPONENTS = new Set([
@@ -16054,13 +16133,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
16054
16133
  function componentToPath(targetDir, component) {
16055
16134
  if (component === "entry-md") {
16056
16135
  const layout = getProviderLayout();
16057
- return join9(targetDir, layout.entryFile);
16136
+ return join10(targetDir, layout.entryFile);
16058
16137
  }
16059
16138
  if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
16060
16139
  const layout = getProviderLayout();
16061
- return join9(targetDir, layout.rootDir, component);
16140
+ return join10(targetDir, layout.rootDir, component);
16062
16141
  }
16063
- return join9(targetDir, component);
16142
+ return join10(targetDir, component);
16064
16143
  }
16065
16144
  function buildInstalledPaths(targetDir, components) {
16066
16145
  return components.map((component) => componentToPath(targetDir, component));
@@ -16162,6 +16241,8 @@ async function initCommand(options) {
16162
16241
  console.log(" /plugin install context7");
16163
16242
  console.log("");
16164
16243
  console.log('See CLAUDE.md "외부 의존성" section for details.');
16244
+ await startServeBackground(targetDir).catch(() => {});
16245
+ console.log(`Web UI: http://127.0.0.1:${DEFAULT_PORT}`);
16165
16246
  return {
16166
16247
  success: true,
16167
16248
  message: i18n.t("cli.init.success"),
@@ -16175,7 +16256,7 @@ async function initCommand(options) {
16175
16256
  }
16176
16257
 
16177
16258
  // src/cli/list.ts
16178
- import { basename as basename3, dirname as dirname3, join as join10, relative as relative3 } from "node:path";
16259
+ import { basename as basename3, dirname as dirname3, join as join11, relative as relative3 } from "node:path";
16179
16260
  init_fs();
16180
16261
  var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
16181
16262
  function parseKeyValue(line) {
@@ -16240,12 +16321,12 @@ function extractAgentTypeFromFilename(filename) {
16240
16321
  return prefixMap[prefix] || "unknown";
16241
16322
  }
16242
16323
  function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
16243
- const relativePath = relative3(join10(baseDir, rootDir, "skills"), skillPath);
16324
+ const relativePath = relative3(join11(baseDir, rootDir, "skills"), skillPath);
16244
16325
  const parts = relativePath.split("/").filter(Boolean);
16245
16326
  return parts[0] || "unknown";
16246
16327
  }
16247
16328
  function extractGuideCategoryFromPath(guidePath, baseDir) {
16248
- const relativePath = relative3(join10(baseDir, "guides"), guidePath);
16329
+ const relativePath = relative3(join11(baseDir, "guides"), guidePath);
16249
16330
  const parts = relativePath.split("/").filter(Boolean);
16250
16331
  return parts[0] || "unknown";
16251
16332
  }
@@ -16339,7 +16420,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
16339
16420
  }
16340
16421
  }
16341
16422
  async function getAgents(targetDir, rootDir = ".claude", config) {
16342
- const agentsDir = join10(targetDir, rootDir, "agents");
16423
+ const agentsDir = join11(targetDir, rootDir, "agents");
16343
16424
  if (!await fileExists(agentsDir))
16344
16425
  return [];
16345
16426
  try {
@@ -16367,7 +16448,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
16367
16448
  }
16368
16449
  }
16369
16450
  async function getSkills(targetDir, rootDir = ".claude", config) {
16370
- const skillsDir = join10(targetDir, rootDir, "skills");
16451
+ const skillsDir = join11(targetDir, rootDir, "skills");
16371
16452
  if (!await fileExists(skillsDir))
16372
16453
  return [];
16373
16454
  try {
@@ -16377,7 +16458,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
16377
16458
  const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
16378
16459
  const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
16379
16460
  const skillDir = dirname3(skillMdPath);
16380
- const indexYamlPath = join10(skillDir, "index.yaml");
16461
+ const indexYamlPath = join11(skillDir, "index.yaml");
16381
16462
  const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
16382
16463
  const relativePath = relative3(targetDir, skillDir);
16383
16464
  return {
@@ -16396,7 +16477,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
16396
16477
  }
16397
16478
  }
16398
16479
  async function getGuides(targetDir, config) {
16399
- const guidesDir = join10(targetDir, "guides");
16480
+ const guidesDir = join11(targetDir, "guides");
16400
16481
  if (!await fileExists(guidesDir))
16401
16482
  return [];
16402
16483
  try {
@@ -16423,7 +16504,7 @@ async function getGuides(targetDir, config) {
16423
16504
  }
16424
16505
  var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
16425
16506
  async function getRules(targetDir, rootDir = ".claude", config) {
16426
- const rulesDir = join10(targetDir, rootDir, "rules");
16507
+ const rulesDir = join11(targetDir, rootDir, "rules");
16427
16508
  if (!await fileExists(rulesDir))
16428
16509
  return [];
16429
16510
  try {
@@ -16495,7 +16576,7 @@ function formatAsJson(components) {
16495
16576
  console.log(JSON.stringify(components, null, 2));
16496
16577
  }
16497
16578
  async function getHooks(targetDir, rootDir = ".claude") {
16498
- const hooksDir = join10(targetDir, rootDir, "hooks");
16579
+ const hooksDir = join11(targetDir, rootDir, "hooks");
16499
16580
  if (!await fileExists(hooksDir))
16500
16581
  return [];
16501
16582
  try {
@@ -16513,7 +16594,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
16513
16594
  }
16514
16595
  }
16515
16596
  async function getContexts(targetDir, rootDir = ".claude") {
16516
- const contextsDir = join10(targetDir, rootDir, "contexts");
16597
+ const contextsDir = join11(targetDir, rootDir, "contexts");
16517
16598
  if (!await fileExists(contextsDir))
16518
16599
  return [];
16519
16600
  try {
@@ -16901,9 +16982,73 @@ async function securityCommand(_options = {}) {
16901
16982
  };
16902
16983
  }
16903
16984
 
16985
+ // src/cli/serve-commands.ts
16986
+ import { execFile, spawnSync as spawnSync2 } from "node:child_process";
16987
+ import { join as join12 } from "node:path";
16988
+ async function serveCommand(options) {
16989
+ const port = options.port !== undefined ? Number(options.port) : DEFAULT_PORT;
16990
+ if (!Number.isFinite(port) || port < 1 || port > 65535) {
16991
+ console.error(`Invalid port: ${options.port}`);
16992
+ process.exit(1);
16993
+ }
16994
+ const cwd = process.cwd();
16995
+ if (options.foreground === true) {
16996
+ runForeground(cwd, port);
16997
+ return;
16998
+ }
16999
+ await startServeBackground(cwd, port);
17000
+ const running = await isServeRunning();
17001
+ if (running) {
17002
+ console.log(`Web UI started: http://127.0.0.1:${port}`);
17003
+ if (options.open === true) {
17004
+ openBrowser(port);
17005
+ }
17006
+ } else {
17007
+ console.error("Failed to start Web UI server");
17008
+ process.exit(1);
17009
+ }
17010
+ }
17011
+ async function serveStopCommand() {
17012
+ const stopped = await stopServe();
17013
+ if (stopped) {
17014
+ console.log("Web UI server stopped");
17015
+ } else {
17016
+ console.log("Web UI server is not running");
17017
+ }
17018
+ }
17019
+ function runForeground(projectRoot, port) {
17020
+ const buildDir = findServeBuildDir(projectRoot);
17021
+ if (buildDir === null) {
17022
+ console.error("Web UI build not found. Run: cd packages/serve && bun run build");
17023
+ process.exit(1);
17024
+ }
17025
+ console.log(`Web UI: http://127.0.0.1:${port}`);
17026
+ spawnSync2("node", [join12(buildDir, "index.js")], {
17027
+ env: {
17028
+ ...process.env,
17029
+ OMCUSTOM_PORT: String(port),
17030
+ OMCUSTOM_HOST: "localhost",
17031
+ OMCUSTOM_ORIGIN: `http://localhost:${port}`,
17032
+ OMCUSTOM_PROJECT_ROOT: projectRoot
17033
+ },
17034
+ stdio: "inherit"
17035
+ });
17036
+ }
17037
+ function openBrowser(port) {
17038
+ const url = `http://127.0.0.1:${port}`;
17039
+ const platform = process.platform;
17040
+ if (platform === "darwin") {
17041
+ execFile("open", [url], () => {});
17042
+ } else if (platform === "win32") {
17043
+ execFile("cmd", ["/c", "start", url], () => {});
17044
+ } else {
17045
+ execFile("xdg-open", [url], () => {});
17046
+ }
17047
+ }
17048
+
16904
17049
  // src/core/updater.ts
16905
17050
  init_fs();
16906
- import { join as join11 } from "node:path";
17051
+ import { join as join13 } from "node:path";
16907
17052
 
16908
17053
  // src/core/entry-merger.ts
16909
17054
  var MANAGED_START = "<!-- omcustom:start -->";
@@ -17152,7 +17297,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
17152
17297
  }
17153
17298
  async function updateEntryDoc(targetDir, config, options) {
17154
17299
  const layout = getProviderLayout();
17155
- const entryPath = join11(targetDir, layout.entryFile);
17300
+ const entryPath = join13(targetDir, layout.entryFile);
17156
17301
  const templateName = getEntryTemplateName2(config.language);
17157
17302
  const templatePath = resolveTemplatePath(templateName);
17158
17303
  if (!await fileExists(templatePath)) {
@@ -17298,7 +17443,7 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
17298
17443
  }
17299
17444
  const protectedRelative = await findProtectedFilesInDir(srcPath, componentPath);
17300
17445
  const path3 = await import("node:path");
17301
- const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join11(destPath, p)));
17446
+ const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join13(destPath, p)));
17302
17447
  return { skipPaths, warnedPaths: protectedRelative };
17303
17448
  }
17304
17449
  function isEntryProtected(relPath, componentRelativePrefix) {
@@ -17339,7 +17484,7 @@ async function updateComponent(targetDir, component, customizations, options, co
17339
17484
  const preservedFiles = [];
17340
17485
  const componentPath = getComponentPath2(component);
17341
17486
  const srcPath = resolveTemplatePath(componentPath);
17342
- const destPath = join11(targetDir, componentPath);
17487
+ const destPath = join13(targetDir, componentPath);
17343
17488
  const customComponents = config.customComponents || [];
17344
17489
  const skipPaths = [];
17345
17490
  if (customizations && !options.forceOverwriteAll) {
@@ -17370,7 +17515,7 @@ async function updateComponent(targetDir, component, customizations, options, co
17370
17515
  }
17371
17516
  skipPaths.push(...protectedSkipPaths);
17372
17517
  const path3 = await import("node:path");
17373
- const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join11(targetDir, p)));
17518
+ const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join13(targetDir, p)));
17374
17519
  const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
17375
17520
  await copyDirectory(srcPath, destPath, {
17376
17521
  overwrite: true,
@@ -17392,12 +17537,12 @@ async function syncRootLevelFiles(targetDir, options) {
17392
17537
  const layout = getProviderLayout();
17393
17538
  const synced = [];
17394
17539
  for (const fileName of ROOT_LEVEL_FILES) {
17395
- const srcPath = resolveTemplatePath(join11(layout.rootDir, fileName));
17540
+ const srcPath = resolveTemplatePath(join13(layout.rootDir, fileName));
17396
17541
  if (!await fileExists(srcPath)) {
17397
17542
  continue;
17398
17543
  }
17399
- const destPath = join11(targetDir, layout.rootDir, fileName);
17400
- await ensureDirectory(join11(destPath, ".."));
17544
+ const destPath = join13(targetDir, layout.rootDir, fileName);
17545
+ await ensureDirectory(join13(destPath, ".."));
17401
17546
  await fs3.copyFile(srcPath, destPath);
17402
17547
  if (fileName.endsWith(".sh")) {
17403
17548
  await fs3.chmod(destPath, 493);
@@ -17432,7 +17577,7 @@ async function removeDeprecatedFiles(targetDir, options) {
17432
17577
  });
17433
17578
  continue;
17434
17579
  }
17435
- const fullPath = join11(targetDir, entry.path);
17580
+ const fullPath = join13(targetDir, entry.path);
17436
17581
  if (await fileExists(fullPath)) {
17437
17582
  await fs3.unlink(fullPath);
17438
17583
  removed.push(entry.path);
@@ -17456,26 +17601,26 @@ function getComponentPath2(component) {
17456
17601
  }
17457
17602
  async function backupInstallation(targetDir) {
17458
17603
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
17459
- const backupDir = join11(targetDir, `.omcustom-backup-${timestamp}`);
17604
+ const backupDir = join13(targetDir, `.omcustom-backup-${timestamp}`);
17460
17605
  const fs3 = await import("node:fs/promises");
17461
17606
  await ensureDirectory(backupDir);
17462
17607
  const layout = getProviderLayout();
17463
17608
  const dirsToBackup = [layout.rootDir, "guides"];
17464
17609
  for (const dir2 of dirsToBackup) {
17465
- const srcPath = join11(targetDir, dir2);
17610
+ const srcPath = join13(targetDir, dir2);
17466
17611
  if (await fileExists(srcPath)) {
17467
- const destPath = join11(backupDir, dir2);
17612
+ const destPath = join13(backupDir, dir2);
17468
17613
  await copyDirectory(srcPath, destPath, { overwrite: true });
17469
17614
  }
17470
17615
  }
17471
- const entryPath = join11(targetDir, layout.entryFile);
17616
+ const entryPath = join13(targetDir, layout.entryFile);
17472
17617
  if (await fileExists(entryPath)) {
17473
- await fs3.copyFile(entryPath, join11(backupDir, layout.entryFile));
17618
+ await fs3.copyFile(entryPath, join13(backupDir, layout.entryFile));
17474
17619
  }
17475
17620
  return backupDir;
17476
17621
  }
17477
17622
  async function loadCustomizationManifest(targetDir) {
17478
- const manifestPath = join11(targetDir, CUSTOMIZATION_MANIFEST_FILE);
17623
+ const manifestPath = join13(targetDir, CUSTOMIZATION_MANIFEST_FILE);
17479
17624
  if (await fileExists(manifestPath)) {
17480
17625
  return readJsonFile(manifestPath);
17481
17626
  }
@@ -17585,6 +17730,12 @@ function createProgram() {
17585
17730
  const result = await securityCommand(options);
17586
17731
  process.exitCode = result.success ? 0 : 1;
17587
17732
  });
17733
+ program2.command("serve").description("Start the web UI server").option("-p, --port <port>", "Port number", "4321").option("--open", "Open browser automatically").option("--foreground", "Run in foreground (not detached)").action(async (options) => {
17734
+ await serveCommand(options);
17735
+ });
17736
+ program2.command("serve-stop").description("Stop the web UI server").action(async () => {
17737
+ await serveStopCommand();
17738
+ });
17588
17739
  program2.hook("preAction", async (thisCommand, actionCommand) => {
17589
17740
  const opts = thisCommand.optsWithGlobals();
17590
17741
  const skipCheck = opts.skipVersionCheck || false;
@@ -17600,6 +17751,13 @@ function createProgram() {
17600
17751
  console.warn(warnings);
17601
17752
  console.warn("");
17602
17753
  }
17754
+ const commandName = actionCommand.name();
17755
+ if (commandName !== "serve-stop" && commandName !== "serve") {
17756
+ const cwd = process.cwd();
17757
+ if (existsSync3(join14(cwd, ".claude"))) {
17758
+ startServeBackground(cwd).catch(() => {});
17759
+ }
17760
+ }
17603
17761
  });
17604
17762
  return program2;
17605
17763
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
3
  "workspaces": ["packages/*"],
4
- "version": "0.42.3",
4
+ "version": "0.43.1",
5
5
  "description": "Batteries-included agent harness for Claude Code",
6
6
  "type": "module",
7
7
  "bin": {
@@ -115,6 +115,16 @@
115
115
  }
116
116
  ],
117
117
  "description": "Check codex CLI and Agent Teams availability at session start"
118
+ },
119
+ {
120
+ "matcher": "*",
121
+ "hooks": [
122
+ {
123
+ "type": "command",
124
+ "command": "bash .claude/hooks/scripts/serve-autostart.sh"
125
+ }
126
+ ],
127
+ "description": "Auto-start Web UI (packages/serve) in background if not already running"
118
128
  }
119
129
  ],
120
130
  "SubagentStart": [
@@ -0,0 +1,71 @@
1
+ #!/bin/bash
2
+ # Web UI Auto-start Hook
3
+ # Trigger: SessionStart
4
+ # Purpose: Start packages/serve in the background if not already running
5
+ # Protocol: stdin JSON -> stdout pass-through, exit 0 always (never blocks session start)
6
+
7
+ input=$(cat)
8
+
9
+ PID_FILE="$HOME/.omcustom-serve.pid"
10
+
11
+ # Check if already running
12
+ if [ -f "$PID_FILE" ]; then
13
+ EXISTING_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
14
+ if [ -n "$EXISTING_PID" ] && kill -0 "$EXISTING_PID" 2>/dev/null; then
15
+ # Server already running — silently pass through
16
+ echo "$input"
17
+ exit 0
18
+ else
19
+ # Stale PID file — clean it up
20
+ rm -f "$PID_FILE"
21
+ fi
22
+ fi
23
+
24
+ # Resolve project root: script lives at .claude/hooks/scripts/ -> 3 levels up
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
27
+
28
+ # Prefer git-based root detection if available
29
+ if command -v git >/dev/null 2>&1; then
30
+ GIT_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "")
31
+ if [ -n "$GIT_ROOT" ]; then
32
+ PROJECT_ROOT="$GIT_ROOT"
33
+ fi
34
+ fi
35
+
36
+ # Locate the build artifact
37
+ BUILD_FILE="${PROJECT_ROOT}/packages/serve/build/index.js"
38
+
39
+ if [ ! -f "$BUILD_FILE" ]; then
40
+ # Build not present — skip silently (advisory only)
41
+ echo "[Hook] Web UI build not found, skipping auto-start (${BUILD_FILE})" >&2
42
+ echo "$input"
43
+ exit 0
44
+ fi
45
+
46
+ # Ensure node is available
47
+ if ! command -v node >/dev/null 2>&1; then
48
+ echo "[Hook] node not found, skipping Web UI auto-start" >&2
49
+ echo "$input"
50
+ exit 0
51
+ fi
52
+
53
+ # Start server fully detached from current process group
54
+ OMCUSTOM_PORT="${OMCUSTOM_PORT:-4321}"
55
+ OMCUSTOM_HOST="${OMCUSTOM_HOST:-localhost}"
56
+ PORT="$OMCUSTOM_PORT"
57
+ HOST="$OMCUSTOM_HOST"
58
+
59
+ nohup env OMCUSTOM_PORT="$OMCUSTOM_PORT" OMCUSTOM_HOST="$OMCUSTOM_HOST" OMCUSTOM_ORIGIN="http://localhost:${PORT}" node "$BUILD_FILE" \
60
+ >"$HOME/.omcustom-serve.log" 2>&1 \
61
+ </dev/null &
62
+ SERVER_PID=$!
63
+ disown "$SERVER_PID"
64
+
65
+ echo "$SERVER_PID" > "$PID_FILE"
66
+
67
+ echo "[Hook] Web UI started (PID ${SERVER_PID}): http://${HOST}:${PORT}" >&2
68
+
69
+ # Pass through stdin JSON
70
+ echo "$input"
71
+ exit 0
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.42.3",
2
+ "version": "0.43.1",
3
3
  "lastUpdated": "2026-03-16T00:00:00.000Z",
4
4
  "components": [
5
5
  {