ework-aio 0.3.5 → 0.3.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-aio",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "All-in-one installer for ework (issue tracker) + ework-daemon (AI bridge) + opencode-ework (plugin). One command: npm i -g ework-aio && ework-aio install.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  } from "./types.ts";
23
23
  import { runInstall } from "./commands/install.ts";
24
24
  import { runUninstall } from "./commands/uninstall.ts";
25
+ import { runUpgrade } from "./commands/upgrade.ts";
25
26
  import {
26
27
  runStart,
27
28
  runStop,
@@ -48,6 +49,9 @@ Commands:
48
49
  Add 'systemd' to also write+enable systemd
49
50
  units. Without 'systemd', runs in pure
50
51
  PID-file mode (no systemctl calls).
52
+ upgrade Pull latest ework-aio from npm and restart
53
+ services. Use this to update — don't re-run
54
+ 'install' for version bumps.
51
55
  uninstall Stop services and remove units (data preserved)
52
56
  status Show service status (PID-file mode)
53
57
  logs [web|daemon] Tail logs (Ctrl+C to stop)
@@ -430,6 +434,10 @@ export async function main(
430
434
  await runInstall(opts, logger);
431
435
  return 0;
432
436
  }
437
+ case "upgrade": {
438
+ await runUpgrade(opts, logger);
439
+ return 0;
440
+ }
433
441
  case "uninstall": {
434
442
  await runUninstall(opts, logger);
435
443
  return 0;
@@ -83,6 +83,15 @@ export async function runInstall(
83
83
  logger.log(` data dir : ${opts.dataDir ?? "(default ~/.local/share/ework-aio)"}`);
84
84
  logger.hr();
85
85
 
86
+ const preflightPaths = resolvePaths({
87
+ dataDir: opts.dataDir,
88
+ scope: opts.scope,
89
+ useSystemd: opts.useSystemd,
90
+ });
91
+ if (fs.existsSync(preflightPaths.webEnvFile)) {
92
+ logger.warn("existing install detected — use 'ework-aio upgrade' to update versions");
93
+ }
94
+
86
95
  // 1. Preflight: bun/npm/opencode must exist on PATH.
87
96
  const preflight = checkPreflight([...REQUIRED_COMMANDS], {
88
97
  optionalCommands: ["systemctl"],
@@ -17,12 +17,13 @@ import {
17
17
  isProcessRunning,
18
18
  } from "../pidfile.ts";
19
19
  import { parseEnvFile } from "../env.ts";
20
- import { resolveCommand } from "../preflight.ts";
20
+ import { resolveCommand, resolveBundledBin } from "../preflight.ts";
21
21
  import type { GlobalOptions, ServiceTarget } from "../types.ts";
22
22
 
23
23
  interface ServicePaths {
24
24
  bin: string;
25
25
  pkg: string;
26
+ binRelPath: string;
26
27
  dataDir: string;
27
28
  envFile: string;
28
29
  pidFile: string;
@@ -35,6 +36,7 @@ function servicePaths(paths: PathConfig, svc: "web" | "daemon"): ServicePaths {
35
36
  return {
36
37
  bin: "ework-web",
37
38
  pkg: "ework-web",
39
+ binRelPath: "bin/ework-web.js",
38
40
  dataDir: paths.webDataDir,
39
41
  envFile: paths.webEnvFile,
40
42
  pidFile: paths.webPidFile,
@@ -43,14 +45,9 @@ function servicePaths(paths: PathConfig, svc: "web" | "daemon"): ServicePaths {
43
45
  };
44
46
  }
45
47
  return {
46
- // ework-daemon ships two bins; only `ework-daemon-server` actually starts
47
- // the HTTP server. The `ework-daemon` bin is a client CLI that prints help
48
- // and exits — spawning it leaves the daemon "looking dead" with help text
49
- // in the log. See install.ts preflight for the full rationale.
50
48
  bin: "ework-daemon-server",
51
- // npm package name differs from the bin name — the error hint must use the
52
- // package name, not the bin, or the user gets a 404 trying to install.
53
49
  pkg: "ework-daemon",
50
+ binRelPath: "bin/ework-daemon-server.js",
54
51
  dataDir: paths.daemonDataDir,
55
52
  envFile: paths.daemonEnvFile,
56
53
  pidFile: paths.daemonPidFile,
@@ -77,9 +74,9 @@ async function startOne(
77
74
  logger: Logger,
78
75
  ): Promise<boolean> {
79
76
  const sp = servicePaths(paths, svc);
80
- const binPath = resolveCommand(sp.bin);
77
+ const binPath = resolveBundledBin(sp.pkg, sp.binRelPath) ?? resolveCommand(sp.bin);
81
78
  if (!binPath) {
82
- throw new InstallError(`${sp.bin} not found on PATH install with: npm install -g ${sp.pkg}`);
79
+ throw new InstallError(`${sp.bin} not found — update with: npm install -g ework-aio@latest`);
83
80
  }
84
81
  const existingPid = await readPidFile(sp.pidFile);
85
82
  if (existingPid !== null && isProcessRunning(existingPid)) {
@@ -0,0 +1,40 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { Logger, InstallError } from "../log.ts";
3
+ import type { GlobalOptions } from "../types.ts";
4
+
5
+ export async function runUpgrade(opts: GlobalOptions, logger: Logger): Promise<void> {
6
+ logger.hr();
7
+ logger.log("ework-aio upgrade");
8
+ logger.hr();
9
+
10
+ logger.log("pulling latest ework-aio from npm...");
11
+ const result = spawnSync("npm", ["install", "-g", "ework-aio@latest"], {
12
+ encoding: "utf8",
13
+ stdio: ["ignore", "pipe", "pipe"],
14
+ env: process.env,
15
+ });
16
+ if (result.status !== 0) {
17
+ const stderr = result.stderr ?? "(no stderr)";
18
+ throw new InstallError(`npm install -g ework-aio@latest failed:\n${stderr}`);
19
+ }
20
+ logger.ok("ework-aio updated to latest");
21
+
22
+ // Re-exec as a fresh process so the NEW code resolves bundled deps.
23
+ // The current process still holds the old version in memory.
24
+ logger.log("restarting services with new version...");
25
+ const restartResult = spawnSync("ework-aio", ["restart", ...restartFlags(opts)], {
26
+ encoding: "utf8",
27
+ stdio: "inherit",
28
+ env: process.env,
29
+ });
30
+ if (restartResult.status !== 0) {
31
+ throw new InstallError("restart after upgrade failed — run 'ework-aio restart' manually");
32
+ }
33
+ logger.ok("upgrade complete");
34
+ }
35
+
36
+ function restartFlags(opts: GlobalOptions): string[] {
37
+ const flags: string[] = [];
38
+ if (opts.dataDir) flags.push("--data-dir", opts.dataDir);
39
+ return flags;
40
+ }