ework-aio 0.5.8 → 0.5.10
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 +2 -1
- package/src/cli.ts +7 -7
- package/src/commands/lifecycle.ts +21 -5
- package/src/commands/upgrade.ts +1 -1
- package/src/config.ts +1 -0
- package/src/paths.ts +12 -1
- package/src/preflight.ts +9 -1
- package/src/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ework-aio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
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",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"ework-daemon": "^0.4.1",
|
|
50
|
+
"ework-router": "^0.1.0",
|
|
50
51
|
"ework-web": "^0.10.1",
|
|
51
52
|
"opencode-ework": "^0.1.0",
|
|
52
53
|
"zod": "^3.23.0"
|
package/src/cli.ts
CHANGED
|
@@ -62,12 +62,12 @@ Commands:
|
|
|
62
62
|
config get <KEY> Print current value of one key
|
|
63
63
|
config set <KEY> <VALUE> Set a key, then restart affected service
|
|
64
64
|
(unless --no-restart is given)
|
|
65
|
-
config restart <web|daemon|both>
|
|
66
|
-
Restart one or
|
|
65
|
+
config restart <web|daemon|router|both>
|
|
66
|
+
Restart one or all services
|
|
67
67
|
|
|
68
|
-
start [web|daemon|both]
|
|
69
|
-
stop [web|daemon|both]
|
|
70
|
-
restart [web|daemon|both]
|
|
68
|
+
start [web|daemon|router|both] Start services in PID-file mode (default both)
|
|
69
|
+
stop [web|daemon|router|both] Stop services (SIGTERM, 5s grace, then SIGKILL)
|
|
70
|
+
restart [web|daemon|router|both] Stop + start
|
|
71
71
|
add-daemon [port] Start an additional daemon instance
|
|
72
72
|
ps Show PID-file mode status (alias for 'status')
|
|
73
73
|
|
|
@@ -306,9 +306,9 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
306
306
|
|
|
307
307
|
function parseServiceTarget(arg: string | undefined): ServiceTarget {
|
|
308
308
|
if (arg === undefined || arg === "") return "both";
|
|
309
|
-
if (arg === "web" || arg === "daemon" || arg === "both") return arg;
|
|
309
|
+
if (arg === "web" || arg === "daemon" || arg === "router" || arg === "both") return arg;
|
|
310
310
|
throw new InstallError(
|
|
311
|
-
`Invalid service target '${arg}'. Expected: web | daemon | both`,
|
|
311
|
+
`Invalid service target '${arg}'. Expected: web | daemon | router | both`,
|
|
312
312
|
);
|
|
313
313
|
}
|
|
314
314
|
|
|
@@ -36,7 +36,7 @@ export interface ServicePaths {
|
|
|
36
36
|
portKey: string | null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function servicePaths(paths: PathConfig, svc: "web" | "daemon"): ServicePaths {
|
|
39
|
+
function servicePaths(paths: PathConfig, svc: "web" | "daemon" | "router"): ServicePaths {
|
|
40
40
|
if (svc === "web") {
|
|
41
41
|
return {
|
|
42
42
|
bin: "ework-web",
|
|
@@ -49,6 +49,18 @@ function servicePaths(paths: PathConfig, svc: "web" | "daemon"): ServicePaths {
|
|
|
49
49
|
portKey: "WORK_PORT",
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
if (svc === "router") {
|
|
53
|
+
return {
|
|
54
|
+
bin: "ework-router",
|
|
55
|
+
pkg: "ework-router",
|
|
56
|
+
binRelPath: "bin/ework-router.js",
|
|
57
|
+
dataDir: paths.routerDataDir,
|
|
58
|
+
envFile: paths.routerEnvFile,
|
|
59
|
+
pidFile: paths.routerPidFile,
|
|
60
|
+
logFile: paths.routerLogFile,
|
|
61
|
+
portKey: "ROUTER_PORT",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
52
64
|
return {
|
|
53
65
|
bin: "ework-daemon-server",
|
|
54
66
|
pkg: "ework-daemon",
|
|
@@ -95,7 +107,7 @@ async function readLogTail(logFile: string, maxLines: number): Promise<string |
|
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
async function startOne(
|
|
98
|
-
svc: "web" | "daemon",
|
|
110
|
+
svc: "web" | "daemon" | "router",
|
|
99
111
|
paths: PathConfig,
|
|
100
112
|
logger: Logger,
|
|
101
113
|
): Promise<boolean> {
|
|
@@ -140,7 +152,7 @@ export async function startFromSp(
|
|
|
140
152
|
return true;
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
async function stopOne(svc: "web" | "daemon", paths: PathConfig, logger: Logger): Promise<boolean> {
|
|
155
|
+
async function stopOne(svc: "web" | "daemon" | "router", paths: PathConfig, logger: Logger): Promise<boolean> {
|
|
144
156
|
const sp = servicePaths(paths, svc);
|
|
145
157
|
return stopFromSp(sp, svc, logger);
|
|
146
158
|
}
|
|
@@ -194,8 +206,9 @@ function iterDaemonInstances(
|
|
|
194
206
|
}));
|
|
195
207
|
}
|
|
196
208
|
|
|
197
|
-
function targets(target: ServiceTarget): Array<"web" | "daemon"> {
|
|
198
|
-
|
|
209
|
+
function targets(target: ServiceTarget): Array<"web" | "daemon" | "router"> {
|
|
210
|
+
if (target === "both") return ["web", "daemon", "router"];
|
|
211
|
+
return [target];
|
|
199
212
|
}
|
|
200
213
|
|
|
201
214
|
function iterTargets(
|
|
@@ -206,6 +219,8 @@ function iterTargets(
|
|
|
206
219
|
for (const svc of targets(target)) {
|
|
207
220
|
if (svc === "web") {
|
|
208
221
|
out.push({ sp: servicePaths(paths, "web"), label: "web" });
|
|
222
|
+
} else if (svc === "router") {
|
|
223
|
+
out.push({ sp: servicePaths(paths, "router"), label: "router" });
|
|
209
224
|
} else {
|
|
210
225
|
const insts = iterDaemonInstances(paths);
|
|
211
226
|
if (insts.length === 0) {
|
|
@@ -269,6 +284,7 @@ export async function runStatus(opts: GlobalOptions, logger: Logger): Promise<St
|
|
|
269
284
|
|
|
270
285
|
const probeTargets: Array<{ sp: ServicePaths; label: string }> = [
|
|
271
286
|
{ sp: servicePaths(paths, "web"), label: "web" },
|
|
287
|
+
{ sp: servicePaths(paths, "router"), label: "router" },
|
|
272
288
|
];
|
|
273
289
|
const daemonInsts = iterDaemonInstances(paths);
|
|
274
290
|
if (daemonInsts.length === 0) {
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { GlobalOptions } from "../types.ts";
|
|
|
6
6
|
function readInstalledVersion(): string {
|
|
7
7
|
try {
|
|
8
8
|
const pkg = JSON.parse(
|
|
9
|
-
readFileSync(new URL("
|
|
9
|
+
readFileSync(new URL("../../package.json", import.meta.url), "utf8")
|
|
10
10
|
);
|
|
11
11
|
return pkg.version ?? "unknown";
|
|
12
12
|
} catch {
|
package/src/config.ts
CHANGED
|
@@ -81,6 +81,7 @@ export const DAEMON_ENV_KEYS: readonly EnvKeySpec[] = [
|
|
|
81
81
|
{ envVar: "DAEMON_ENV", file: "daemon", generate: () => "production" },
|
|
82
82
|
{ envVar: "DAEMON_PORT", file: "daemon", generate: (c) => String(c.daemonPort) },
|
|
83
83
|
{ envVar: "DAEMON_HOST", file: "daemon", generate: () => "127.0.0.1" },
|
|
84
|
+
{ envVar: "DAEMON_ENDPOINT", file: "daemon", generate: (c) => `127.0.0.1:${c.daemonPort}` },
|
|
84
85
|
{ envVar: "DAEMON_DB_PATH", file: "daemon", generate: (c) => c.paths.daemonDbPath },
|
|
85
86
|
{ envVar: "GITEA_URL", file: "daemon", generate: (c) => `http://127.0.0.1:${c.workPort}` },
|
|
86
87
|
// These tokens come from the bot bootstrap flow — empty placeholder here,
|
package/src/paths.ts
CHANGED
|
@@ -24,8 +24,10 @@ export interface PathConfig {
|
|
|
24
24
|
dataDir: string;
|
|
25
25
|
webDataDir: string;
|
|
26
26
|
daemonDataDir: string;
|
|
27
|
+
routerDataDir: string;
|
|
27
28
|
webEnvFile: string;
|
|
28
29
|
daemonEnvFile: string;
|
|
30
|
+
routerEnvFile: string;
|
|
29
31
|
runDir: string;
|
|
30
32
|
botTokenFile: string;
|
|
31
33
|
opencodeWorkdir: string;
|
|
@@ -35,10 +37,13 @@ export interface PathConfig {
|
|
|
35
37
|
webAttachmentRoot: string;
|
|
36
38
|
webPidFile: string;
|
|
37
39
|
daemonPidFile: string;
|
|
40
|
+
routerPidFile: string;
|
|
38
41
|
webLogFile: string;
|
|
39
42
|
daemonLogFile: string;
|
|
40
|
-
|
|
43
|
+
routerLogFile: string;
|
|
44
|
+
webUnitFile: string | null;
|
|
41
45
|
daemonUnitFile: string | null;
|
|
46
|
+
routerUnitFile: string | null;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export interface ResolvePathsOptions {
|
|
@@ -63,6 +68,7 @@ export function resolvePaths(opts: ResolvePathsOptions): PathConfig {
|
|
|
63
68
|
const dataDir = opts.dataDir || path.join(xdgDataHome, "ework-aio");
|
|
64
69
|
const webDataDir = path.join(dataDir, "ework-web");
|
|
65
70
|
const daemonDataDir = path.join(dataDir, "ework-daemon");
|
|
71
|
+
const routerDataDir = path.join(dataDir, "ework-router");
|
|
66
72
|
const runDir = path.join(dataDir, "run");
|
|
67
73
|
|
|
68
74
|
// Unit file location depends on scope and whether systemd is opted-in
|
|
@@ -77,8 +83,10 @@ export function resolvePaths(opts: ResolvePathsOptions): PathConfig {
|
|
|
77
83
|
dataDir,
|
|
78
84
|
webDataDir,
|
|
79
85
|
daemonDataDir,
|
|
86
|
+
routerDataDir,
|
|
80
87
|
webEnvFile: path.join(webDataDir, ".env"),
|
|
81
88
|
daemonEnvFile: path.join(daemonDataDir, ".env"),
|
|
89
|
+
routerEnvFile: path.join(routerDataDir, ".env"),
|
|
82
90
|
runDir,
|
|
83
91
|
botTokenFile: path.join(dataDir, "bot-token"),
|
|
84
92
|
opencodeWorkdir: path.join(dataDir, "opencode-workdir"),
|
|
@@ -88,10 +96,13 @@ export function resolvePaths(opts: ResolvePathsOptions): PathConfig {
|
|
|
88
96
|
webAttachmentRoot: path.join(webDataDir, "attachments"),
|
|
89
97
|
webPidFile: path.join(runDir, "web.pid"),
|
|
90
98
|
daemonPidFile: path.join(runDir, "daemon.pid"),
|
|
99
|
+
routerPidFile: path.join(runDir, "router.pid"),
|
|
91
100
|
webLogFile: path.join(runDir, "web.log"),
|
|
92
101
|
daemonLogFile: path.join(runDir, "daemon.log"),
|
|
102
|
+
routerLogFile: path.join(runDir, "router.log"),
|
|
93
103
|
webUnitFile: unitDir ? path.join(unitDir, "ework-web.service") : null,
|
|
94
104
|
daemonUnitFile: unitDir ? path.join(unitDir, "ework-daemon.service") : null,
|
|
105
|
+
routerUnitFile: unitDir ? path.join(unitDir, "ework-router.service") : null,
|
|
95
106
|
};
|
|
96
107
|
}
|
|
97
108
|
|
package/src/preflight.ts
CHANGED
|
@@ -41,7 +41,15 @@ export function resolveCommand(cmd: string): string | null {
|
|
|
41
41
|
// the user to "install ework-web first" even though it was already bundled
|
|
42
42
|
// (B-1). Returns the absolute bin path, or null if not bundled.
|
|
43
43
|
export function resolveBundledBin(pkgName: string, binRelPath: string): string | null {
|
|
44
|
-
if (isDevRepo())
|
|
44
|
+
if (isDevRepo()) {
|
|
45
|
+
const r = spawnSync("npm", ["root", "-g"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], env: process.env });
|
|
46
|
+
const npmRoot = (r.stdout ?? "").trim();
|
|
47
|
+
if (npmRoot) {
|
|
48
|
+
const candidate = path.join(npmRoot, "ework-aio", "node_modules", pkgName, binRelPath);
|
|
49
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
45
53
|
const root = process.env.AIO_PACKAGE_ROOT || DEFAULT_PACKAGE_ROOT;
|
|
46
54
|
const candidate = path.join(root, "node_modules", pkgName, binRelPath);
|
|
47
55
|
return fs.existsSync(candidate) ? candidate : null;
|
package/src/types.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type Scope = "user" | "system";
|
|
|
10
10
|
|
|
11
11
|
// Which service(s) a command targets. `ps` and `status` use "both";
|
|
12
12
|
// `start web`/`stop daemon` use a single target.
|
|
13
|
-
export type ServiceTarget = "web" | "daemon" | "both";
|
|
13
|
+
export type ServiceTarget = "web" | "daemon" | "router" | "both";
|
|
14
14
|
|
|
15
15
|
export interface GlobalOptions {
|
|
16
16
|
// Install-time knobs (defaults live in src/types.ts DEFAULTS).
|