@opentag/cli 0.3.4 → 0.3.5
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/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/index.js +310 -50
- package/dist/index.js.map +1 -1
- package/dist/service.d.ts +14 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/setup/flow.d.ts +1 -0
- package/dist/setup/flow.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type SetupCommandOptions, type SetupFlowDependencies } from "../setup/flow.js";
|
|
2
2
|
import { type StartCommandOptions } from "../start.js";
|
|
3
|
+
import { type ServiceCommandOptions } from "../service.js";
|
|
3
4
|
export type { SetupCommandOptions };
|
|
4
5
|
export type SetupCommandDependencies = Partial<Omit<SetupFlowDependencies, "prompts" | "scanLarkPersonalAgent">> & {
|
|
6
|
+
platform?: NodeJS.Platform;
|
|
5
7
|
prompts?: SetupFlowDependencies["prompts"];
|
|
6
8
|
scanLarkPersonalAgent?: SetupFlowDependencies["scanLarkPersonalAgent"];
|
|
7
9
|
validateLarkCredentials?: SetupFlowDependencies["validateLarkCredentials"];
|
|
8
10
|
startOpenTag?(options: StartCommandOptions): Promise<void>;
|
|
11
|
+
startOpenTagService?(options: ServiceCommandOptions): Promise<void>;
|
|
9
12
|
};
|
|
10
13
|
export declare function runSetupCommand(options: SetupCommandOptions, dependencies?: SetupCommandDependencies): Promise<void>;
|
|
11
14
|
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAOA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI3G,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAOA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI3G,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAwD,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEjH,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAAC,GAAG;IACjH,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC3C,qBAAqB,CAAC,EAAE,qBAAqB,CAAC,uBAAuB,CAAC,CAAC;IACvE,uBAAuB,CAAC,EAAE,qBAAqB,CAAC,yBAAyB,CAAC,CAAC;IAC3E,YAAY,CAAC,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,mBAAmB,CAAC,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE,CAAC;AAwDF,wBAAsB,eAAe,CAAC,OAAO,EAAE,mBAAmB,EAAE,YAAY,GAAE,wBAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8C9H"}
|
package/dist/index.js
CHANGED
|
@@ -2676,6 +2676,19 @@ var launchAgentCliPath = [
|
|
|
2676
2676
|
"/usr/sbin",
|
|
2677
2677
|
"/sbin"
|
|
2678
2678
|
].join(":");
|
|
2679
|
+
function linuxServiceCliPath(home) {
|
|
2680
|
+
return [
|
|
2681
|
+
join3(home, ".local", "bin"),
|
|
2682
|
+
join3(home, ".npm-global", "bin"),
|
|
2683
|
+
join3(home, ".bun", "bin"),
|
|
2684
|
+
"/usr/local/bin",
|
|
2685
|
+
"/usr/bin",
|
|
2686
|
+
"/bin",
|
|
2687
|
+
"/usr/local/sbin",
|
|
2688
|
+
"/usr/sbin",
|
|
2689
|
+
"/sbin"
|
|
2690
|
+
].join(":");
|
|
2691
|
+
}
|
|
2679
2692
|
function loggerFrom(dependencies) {
|
|
2680
2693
|
return dependencies.logger ?? console;
|
|
2681
2694
|
}
|
|
@@ -2688,6 +2701,14 @@ function homeFrom(dependencies) {
|
|
|
2688
2701
|
function uidFrom(dependencies) {
|
|
2689
2702
|
return dependencies.uid ?? (typeof process.getuid === "function" ? process.getuid() : 0);
|
|
2690
2703
|
}
|
|
2704
|
+
function serviceControllerForPlatform(platform = process.platform) {
|
|
2705
|
+
if (platform === "darwin") return "launchd";
|
|
2706
|
+
if (platform === "linux") return "systemd";
|
|
2707
|
+
return "unsupported";
|
|
2708
|
+
}
|
|
2709
|
+
function serviceControllerFrom(dependencies) {
|
|
2710
|
+
return serviceControllerForPlatform(platformFrom(dependencies));
|
|
2711
|
+
}
|
|
2691
2712
|
function servicePaths(options = {}, dependencies = {}) {
|
|
2692
2713
|
const home = homeFrom(dependencies);
|
|
2693
2714
|
const env = dependencies.env ?? process.env;
|
|
@@ -2700,7 +2721,8 @@ function servicePaths(options = {}, dependencies = {}) {
|
|
|
2700
2721
|
logsDir,
|
|
2701
2722
|
plistPath: join3(home, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`),
|
|
2702
2723
|
stdoutPath: join3(logsDir, "opentag.log"),
|
|
2703
|
-
stderrPath: join3(logsDir, "opentag.err.log")
|
|
2724
|
+
stderrPath: join3(logsDir, "opentag.err.log"),
|
|
2725
|
+
unitPath: join3(home, ".config", "systemd", "user", `${SERVICE_LABEL}.service`)
|
|
2704
2726
|
};
|
|
2705
2727
|
}
|
|
2706
2728
|
function escapeXml(value) {
|
|
@@ -2750,6 +2772,36 @@ function buildLaunchAgentPlist(input) {
|
|
|
2750
2772
|
""
|
|
2751
2773
|
].join("\n");
|
|
2752
2774
|
}
|
|
2775
|
+
function systemdQuote(value) {
|
|
2776
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("$", "\\$").replaceAll("%", "%%")}"`;
|
|
2777
|
+
}
|
|
2778
|
+
function systemdPathValue(value) {
|
|
2779
|
+
return value.replaceAll("%", "%%");
|
|
2780
|
+
}
|
|
2781
|
+
function buildSystemdUserService(input) {
|
|
2782
|
+
const environment = Object.entries(input.environment ?? {}).map(
|
|
2783
|
+
([key, value]) => `Environment=${systemdQuote(`${key}=${value}`)}`
|
|
2784
|
+
);
|
|
2785
|
+
return [
|
|
2786
|
+
"[Unit]",
|
|
2787
|
+
"Description=OpenTag local agent",
|
|
2788
|
+
"After=network.target",
|
|
2789
|
+
"",
|
|
2790
|
+
"[Service]",
|
|
2791
|
+
"Type=simple",
|
|
2792
|
+
`WorkingDirectory=${systemdQuote(input.workingDirectory)}`,
|
|
2793
|
+
...environment,
|
|
2794
|
+
`ExecStart=${input.execStart.map(systemdQuote).join(" ")}`,
|
|
2795
|
+
"Restart=always",
|
|
2796
|
+
"RestartSec=3",
|
|
2797
|
+
`StandardOutput=append:${systemdPathValue(input.stdoutPath)}`,
|
|
2798
|
+
`StandardError=append:${systemdPathValue(input.stderrPath)}`,
|
|
2799
|
+
"",
|
|
2800
|
+
"[Install]",
|
|
2801
|
+
"WantedBy=default.target",
|
|
2802
|
+
""
|
|
2803
|
+
].join("\n");
|
|
2804
|
+
}
|
|
2753
2805
|
function launchctlRunner(dependencies) {
|
|
2754
2806
|
if (dependencies.launchctl) return dependencies.launchctl;
|
|
2755
2807
|
return (args) => {
|
|
@@ -2761,6 +2813,17 @@ function launchctlRunner(dependencies) {
|
|
|
2761
2813
|
};
|
|
2762
2814
|
};
|
|
2763
2815
|
}
|
|
2816
|
+
function systemctlRunner(dependencies) {
|
|
2817
|
+
if (dependencies.systemctl) return dependencies.systemctl;
|
|
2818
|
+
return (args) => {
|
|
2819
|
+
const result = spawnSync("systemctl", ["--user", ...args], { encoding: "utf8" });
|
|
2820
|
+
return {
|
|
2821
|
+
status: result.status ?? 1,
|
|
2822
|
+
stdout: result.stdout ?? "",
|
|
2823
|
+
stderr: result.stderr ?? ""
|
|
2824
|
+
};
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2764
2827
|
function launchdDomain(dependencies) {
|
|
2765
2828
|
return `gui/${uidFrom(dependencies)}`;
|
|
2766
2829
|
}
|
|
@@ -2768,12 +2831,14 @@ function launchdServiceTarget(dependencies) {
|
|
|
2768
2831
|
return `${launchdDomain(dependencies)}/${SERVICE_LABEL}`;
|
|
2769
2832
|
}
|
|
2770
2833
|
function unsupportedMessage() {
|
|
2771
|
-
return "OpenTag service management is
|
|
2834
|
+
return "OpenTag service management is supported on macOS and Linux only. Use `opentag start` in the foreground on this platform.";
|
|
2772
2835
|
}
|
|
2773
|
-
function
|
|
2774
|
-
|
|
2836
|
+
function assertSupportedServiceController(dependencies) {
|
|
2837
|
+
const controller = serviceControllerFrom(dependencies);
|
|
2838
|
+
if (controller === "unsupported") {
|
|
2775
2839
|
throw new Error(unsupportedMessage());
|
|
2776
2840
|
}
|
|
2841
|
+
return controller;
|
|
2777
2842
|
}
|
|
2778
2843
|
function runLaunchctlOrThrow(dependencies, args, action) {
|
|
2779
2844
|
const result = launchctlRunner(dependencies)(args);
|
|
@@ -2786,9 +2851,26 @@ function runLaunchctlOrThrow(dependencies, args, action) {
|
|
|
2786
2851
|
function launchctlDetail(result) {
|
|
2787
2852
|
return [result.stderr.trim(), result.stdout.trim()].filter(Boolean).join("\n");
|
|
2788
2853
|
}
|
|
2854
|
+
function systemctlDetail(result) {
|
|
2855
|
+
return [result.stderr.trim(), result.stdout.trim()].filter(Boolean).join("\n");
|
|
2856
|
+
}
|
|
2857
|
+
function runSystemctlOrThrow(dependencies, args, action) {
|
|
2858
|
+
const result = systemctlRunner(dependencies)(args);
|
|
2859
|
+
if (result.status !== 0) {
|
|
2860
|
+
const detail = systemctlDetail(result);
|
|
2861
|
+
throw new Error(`${action} failed${detail ? `: ${detail}` : "."}`);
|
|
2862
|
+
}
|
|
2863
|
+
return result;
|
|
2864
|
+
}
|
|
2789
2865
|
function printLaunchdService(dependencies) {
|
|
2790
2866
|
return launchctlRunner(dependencies)(["print", launchdServiceTarget(dependencies)]);
|
|
2791
2867
|
}
|
|
2868
|
+
function systemdUnitName() {
|
|
2869
|
+
return `${SERVICE_LABEL}.service`;
|
|
2870
|
+
}
|
|
2871
|
+
function printSystemdService(dependencies) {
|
|
2872
|
+
return systemctlRunner(dependencies)(["is-active", systemdUnitName()]);
|
|
2873
|
+
}
|
|
2792
2874
|
function sleepFrom(dependencies) {
|
|
2793
2875
|
return dependencies.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
2794
2876
|
}
|
|
@@ -2810,6 +2892,38 @@ async function waitForLaunchdUnloaded(dependencies, input = {}) {
|
|
|
2810
2892
|
await sleepFrom(dependencies)(intervalMs);
|
|
2811
2893
|
}
|
|
2812
2894
|
}
|
|
2895
|
+
async function waitForSystemdActive(dependencies, input = {}) {
|
|
2896
|
+
const intervalMs = input.intervalMs ?? 100;
|
|
2897
|
+
const deadline = Date.now() + (input.timeoutMs ?? 1500);
|
|
2898
|
+
while (true) {
|
|
2899
|
+
const result = printSystemdService(dependencies);
|
|
2900
|
+
if (result.status === 0 && result.stdout.trim() === "active") return true;
|
|
2901
|
+
if (Date.now() >= deadline) return false;
|
|
2902
|
+
await sleepFrom(dependencies)(intervalMs);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
async function waitForSystemdInactive(dependencies, input = {}) {
|
|
2906
|
+
const intervalMs = input.intervalMs ?? 100;
|
|
2907
|
+
const deadline = Date.now() + (input.timeoutMs ?? 1500);
|
|
2908
|
+
while (true) {
|
|
2909
|
+
const result = printSystemdService(dependencies);
|
|
2910
|
+
if (result.status !== 0 || result.stdout.trim() !== "active") return true;
|
|
2911
|
+
if (Date.now() >= deadline) return false;
|
|
2912
|
+
await sleepFrom(dependencies)(intervalMs);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
async function waitForServiceLoaded(dependencies, input = {}) {
|
|
2916
|
+
const controller = serviceControllerFrom(dependencies);
|
|
2917
|
+
if (controller === "launchd") return waitForLaunchdLoaded(dependencies, input);
|
|
2918
|
+
if (controller === "systemd") return waitForSystemdActive(dependencies, input);
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
async function waitForServiceUnloaded(dependencies, input = {}) {
|
|
2922
|
+
const controller = serviceControllerFrom(dependencies);
|
|
2923
|
+
if (controller === "launchd") return waitForLaunchdUnloaded(dependencies, input);
|
|
2924
|
+
if (controller === "systemd") return waitForSystemdInactive(dependencies, input);
|
|
2925
|
+
return true;
|
|
2926
|
+
}
|
|
2813
2927
|
function serviceWorkingDirectory(configPath) {
|
|
2814
2928
|
const config = readCliConfig(configPath);
|
|
2815
2929
|
return config.daemon.repositories[0]?.checkoutPath ?? dirname2(configPath);
|
|
@@ -2883,46 +2997,77 @@ function formatConnectorReadiness(config) {
|
|
|
2883
2997
|
}
|
|
2884
2998
|
return lines.length > 1 ? lines : [...lines, " none configured"];
|
|
2885
2999
|
}
|
|
2886
|
-
function
|
|
3000
|
+
function serviceCliPath(dependencies) {
|
|
3001
|
+
const controller = serviceControllerFrom(dependencies);
|
|
3002
|
+
return controller === "systemd" ? linuxServiceCliPath(homeFrom(dependencies)) : launchAgentCliPath;
|
|
3003
|
+
}
|
|
3004
|
+
function serviceEnvironment(options, paths, dependencies) {
|
|
2887
3005
|
return {
|
|
2888
3006
|
OPENTAG_CONFIG_PATH: paths.configPath,
|
|
2889
|
-
PATH:
|
|
3007
|
+
PATH: serviceCliPath(dependencies),
|
|
2890
3008
|
...serviceHardeningEnvironment(options)
|
|
2891
3009
|
};
|
|
2892
3010
|
}
|
|
2893
3011
|
function installService(options = {}, dependencies = {}) {
|
|
2894
|
-
|
|
3012
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
2895
3013
|
const paths = servicePaths(options, dependencies);
|
|
2896
3014
|
const workingDirectory = serviceWorkingDirectory(paths.configPath);
|
|
2897
|
-
mkdirSync2(dirname2(paths.plistPath), { recursive: true });
|
|
2898
3015
|
ensurePrivateDirectory(paths.logsDir);
|
|
2899
|
-
|
|
3016
|
+
if (controller === "launchd") {
|
|
3017
|
+
mkdirSync2(dirname2(paths.plistPath), { recursive: true });
|
|
3018
|
+
const plist = buildLaunchAgentPlist({
|
|
3019
|
+
label: paths.label,
|
|
3020
|
+
programArguments: serviceProgramArguments(options, dependencies),
|
|
3021
|
+
runAtLoad: true,
|
|
3022
|
+
keepAlive: true,
|
|
3023
|
+
stdoutPath: paths.stdoutPath,
|
|
3024
|
+
stderrPath: paths.stderrPath,
|
|
3025
|
+
workingDirectory,
|
|
3026
|
+
environment: serviceEnvironment(options, paths, dependencies)
|
|
3027
|
+
});
|
|
3028
|
+
writeFileSync2(paths.plistPath, plist, { mode: 420 });
|
|
3029
|
+
return paths;
|
|
3030
|
+
}
|
|
3031
|
+
mkdirSync2(dirname2(paths.unitPath), { recursive: true });
|
|
3032
|
+
const unit = buildSystemdUserService({
|
|
2900
3033
|
label: paths.label,
|
|
2901
|
-
|
|
2902
|
-
runAtLoad: true,
|
|
2903
|
-
keepAlive: true,
|
|
3034
|
+
execStart: serviceProgramArguments(options, dependencies),
|
|
2904
3035
|
stdoutPath: paths.stdoutPath,
|
|
2905
3036
|
stderrPath: paths.stderrPath,
|
|
2906
3037
|
workingDirectory,
|
|
2907
|
-
environment:
|
|
3038
|
+
environment: serviceEnvironment(options, paths, dependencies)
|
|
2908
3039
|
});
|
|
2909
|
-
writeFileSync2(paths.
|
|
3040
|
+
writeFileSync2(paths.unitPath, unit, { mode: 420 });
|
|
3041
|
+
runSystemctlOrThrow(dependencies, ["daemon-reload"], "systemctl --user daemon-reload");
|
|
3042
|
+
runSystemctlOrThrow(dependencies, ["enable", systemdUnitName()], "systemctl --user enable");
|
|
2910
3043
|
return paths;
|
|
2911
3044
|
}
|
|
2912
|
-
function installed(paths) {
|
|
2913
|
-
return existsSync2(paths.plistPath);
|
|
3045
|
+
function installed(paths, controller) {
|
|
3046
|
+
if (controller === "launchd") return existsSync2(paths.plistPath);
|
|
3047
|
+
if (controller === "systemd") return existsSync2(paths.unitPath);
|
|
3048
|
+
return false;
|
|
2914
3049
|
}
|
|
2915
3050
|
function isNotLoaded(result) {
|
|
2916
3051
|
const text2 = `${result.stderr}
|
|
2917
3052
|
${result.stdout}`.toLowerCase();
|
|
2918
3053
|
return text2.includes("no such process") || text2.includes("could not find service") || text2.includes("service is not loaded");
|
|
2919
3054
|
}
|
|
3055
|
+
function isSystemdNotLoaded(result) {
|
|
3056
|
+
const text2 = `${result.stderr}
|
|
3057
|
+
${result.stdout}`.toLowerCase();
|
|
3058
|
+
return text2.includes("could not be found") || text2.includes("not loaded") || text2.includes("not-found") || text2.includes("no such");
|
|
3059
|
+
}
|
|
2920
3060
|
function startService(options = {}, dependencies = {}) {
|
|
2921
|
-
|
|
3061
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
2922
3062
|
const paths = servicePaths(options, dependencies);
|
|
2923
|
-
if (!installed(paths)) {
|
|
3063
|
+
if (!installed(paths, controller)) {
|
|
2924
3064
|
throw new Error(`OpenTag service is not installed. Run \`opentag service install --config ${paths.configPath}\` first.`);
|
|
2925
3065
|
}
|
|
3066
|
+
if (controller === "systemd") {
|
|
3067
|
+
runSystemctlOrThrow(dependencies, ["daemon-reload"], "systemctl --user daemon-reload");
|
|
3068
|
+
runSystemctlOrThrow(dependencies, ["start", systemdUnitName()], "systemctl --user start");
|
|
3069
|
+
return paths;
|
|
3070
|
+
}
|
|
2926
3071
|
const launchctl = launchctlRunner(dependencies);
|
|
2927
3072
|
const bootstrap = launchctl(["bootstrap", launchdDomain(dependencies), paths.plistPath]);
|
|
2928
3073
|
if (bootstrap.status !== 0) {
|
|
@@ -2940,9 +3085,17 @@ function startService(options = {}, dependencies = {}) {
|
|
|
2940
3085
|
return paths;
|
|
2941
3086
|
}
|
|
2942
3087
|
function stopService(options = {}, dependencies = {}) {
|
|
2943
|
-
|
|
3088
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
2944
3089
|
const paths = servicePaths(options, dependencies);
|
|
2945
|
-
if (!installed(paths)) return paths;
|
|
3090
|
+
if (!installed(paths, controller)) return paths;
|
|
3091
|
+
if (controller === "systemd") {
|
|
3092
|
+
const result = systemctlRunner(dependencies)(["stop", systemdUnitName()]);
|
|
3093
|
+
if (result.status !== 0 && !isSystemdNotLoaded(result)) {
|
|
3094
|
+
const detail = systemctlDetail(result);
|
|
3095
|
+
throw new Error(`systemctl --user stop failed${detail ? `: ${detail}` : "."}`);
|
|
3096
|
+
}
|
|
3097
|
+
return paths;
|
|
3098
|
+
}
|
|
2946
3099
|
const launchctl = launchctlRunner(dependencies);
|
|
2947
3100
|
const first = launchctl(["bootout", launchdServiceTarget(dependencies)]);
|
|
2948
3101
|
if (first.status !== 0) {
|
|
@@ -2956,28 +3109,56 @@ function stopService(options = {}, dependencies = {}) {
|
|
|
2956
3109
|
return paths;
|
|
2957
3110
|
}
|
|
2958
3111
|
function uninstallService(options = {}, dependencies = {}) {
|
|
2959
|
-
|
|
3112
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
2960
3113
|
const paths = stopService(options, dependencies);
|
|
2961
|
-
|
|
3114
|
+
if (controller === "launchd") {
|
|
3115
|
+
rmSync2(paths.plistPath, { force: true });
|
|
3116
|
+
return paths;
|
|
3117
|
+
}
|
|
3118
|
+
const disabled = systemctlRunner(dependencies)(["disable", systemdUnitName()]);
|
|
3119
|
+
if (disabled.status !== 0 && !isSystemdNotLoaded(disabled)) {
|
|
3120
|
+
const detail = systemctlDetail(disabled);
|
|
3121
|
+
throw new Error(`systemctl --user disable failed${detail ? `: ${detail}` : "."}`);
|
|
3122
|
+
}
|
|
3123
|
+
rmSync2(paths.unitPath, { force: true });
|
|
3124
|
+
runSystemctlOrThrow(dependencies, ["daemon-reload"], "systemctl --user daemon-reload");
|
|
2962
3125
|
return paths;
|
|
2963
3126
|
}
|
|
2964
3127
|
function enableServiceAutostart(options = {}, dependencies = {}) {
|
|
2965
|
-
|
|
2966
|
-
const
|
|
3128
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
3129
|
+
const candidate = servicePaths(options, dependencies);
|
|
3130
|
+
const paths = installed(candidate, controller) ? candidate : installService(options, dependencies);
|
|
3131
|
+
if (controller === "systemd") {
|
|
3132
|
+
runSystemctlOrThrow(dependencies, ["enable", systemdUnitName()], "systemctl --user enable");
|
|
3133
|
+
return paths;
|
|
3134
|
+
}
|
|
2967
3135
|
runLaunchctlOrThrow(dependencies, ["enable", launchdServiceTarget(dependencies)], "launchctl enable");
|
|
2968
3136
|
return paths;
|
|
2969
3137
|
}
|
|
2970
3138
|
function disableServiceAutostart(options = {}, dependencies = {}) {
|
|
2971
|
-
|
|
3139
|
+
const controller = assertSupportedServiceController(dependencies);
|
|
2972
3140
|
const paths = servicePaths(options, dependencies);
|
|
2973
|
-
if (installed(paths)) {
|
|
3141
|
+
if (installed(paths, controller)) {
|
|
3142
|
+
if (controller === "systemd") {
|
|
3143
|
+
runSystemctlOrThrow(dependencies, ["disable", systemdUnitName()], "systemctl --user disable");
|
|
3144
|
+
return paths;
|
|
3145
|
+
}
|
|
2974
3146
|
runLaunchctlOrThrow(dependencies, ["disable", launchdServiceTarget(dependencies)], "launchctl disable");
|
|
2975
3147
|
}
|
|
2976
3148
|
return paths;
|
|
2977
3149
|
}
|
|
2978
3150
|
function serviceAutostart(paths, dependencies, isInstalled) {
|
|
2979
3151
|
if (!isInstalled) return "disabled";
|
|
2980
|
-
|
|
3152
|
+
const controller = serviceControllerFrom(dependencies);
|
|
3153
|
+
if (controller === "systemd") {
|
|
3154
|
+
const result2 = systemctlRunner(dependencies)(["is-enabled", systemdUnitName()]);
|
|
3155
|
+
const text2 = `${result2.stdout}
|
|
3156
|
+
${result2.stderr}`.trim().toLowerCase();
|
|
3157
|
+
if (result2.status === 0 && text2.includes("enabled")) return "enabled";
|
|
3158
|
+
if (text2.includes("disabled") || text2.includes("not-found") || text2.includes("could not be found")) return "disabled";
|
|
3159
|
+
return "unknown";
|
|
3160
|
+
}
|
|
3161
|
+
if (controller !== "launchd") return "unknown";
|
|
2981
3162
|
const result = launchctlRunner(dependencies)(["print-disabled", launchdDomain(dependencies)]);
|
|
2982
3163
|
if (result.status !== 0) return "unknown";
|
|
2983
3164
|
const escapedLabel = SERVICE_LABEL.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2989,12 +3170,15 @@ function serviceAutostart(paths, dependencies, isInstalled) {
|
|
|
2989
3170
|
}
|
|
2990
3171
|
function getServiceStatus(options = {}, dependencies = {}) {
|
|
2991
3172
|
const paths = servicePaths(options, dependencies);
|
|
2992
|
-
const controller =
|
|
2993
|
-
const isInstalled = installed(paths);
|
|
3173
|
+
const controller = serviceControllerFrom(dependencies);
|
|
3174
|
+
const isInstalled = installed(paths, controller);
|
|
2994
3175
|
let running = isInstalled ? "stopped" : "unknown";
|
|
2995
3176
|
if (controller === "launchd" && isInstalled) {
|
|
2996
3177
|
const result = launchctlRunner(dependencies)(["print", launchdServiceTarget(dependencies)]);
|
|
2997
3178
|
running = result.status === 0 ? "running" : "stopped";
|
|
3179
|
+
} else if (controller === "systemd" && isInstalled) {
|
|
3180
|
+
const result = systemctlRunner(dependencies)(["is-active", systemdUnitName()]);
|
|
3181
|
+
running = result.status === 0 && result.stdout.trim() === "active" ? "running" : "stopped";
|
|
2998
3182
|
}
|
|
2999
3183
|
let runtimeMode = "unknown";
|
|
3000
3184
|
let relayUrl;
|
|
@@ -3045,10 +3229,34 @@ function readLaunchAgentEnvironment(paths) {
|
|
|
3045
3229
|
})
|
|
3046
3230
|
);
|
|
3047
3231
|
}
|
|
3232
|
+
function unescapeSystemdQuotedValue(value) {
|
|
3233
|
+
return value.replaceAll("%%", "%").replaceAll('\\"', '"').replaceAll("\\$", "$").replaceAll("\\\\", "\\");
|
|
3234
|
+
}
|
|
3235
|
+
function readSystemdEnvironment(paths) {
|
|
3236
|
+
if (!existsSync2(paths.unitPath)) return {};
|
|
3237
|
+
const unit = readFileSync2(paths.unitPath, "utf8");
|
|
3238
|
+
return Object.fromEntries(
|
|
3239
|
+
unit.split(/\r?\n/).flatMap((line) => {
|
|
3240
|
+
const match = line.match(/^Environment=(?:"((?:\\.|[^"])*)"|(.+))$/);
|
|
3241
|
+
const raw = match?.[1] ?? match?.[2];
|
|
3242
|
+
if (!raw) return [];
|
|
3243
|
+
const value = unescapeSystemdQuotedValue(raw);
|
|
3244
|
+
const separator = value.indexOf("=");
|
|
3245
|
+
if (separator <= 0) return [];
|
|
3246
|
+
return [[value.slice(0, separator), value.slice(separator + 1)]];
|
|
3247
|
+
})
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
function readServiceEnvironment(paths) {
|
|
3251
|
+
return {
|
|
3252
|
+
...readLaunchAgentEnvironment(paths),
|
|
3253
|
+
...readSystemdEnvironment(paths)
|
|
3254
|
+
};
|
|
3255
|
+
}
|
|
3048
3256
|
function formatServiceHardening(paths) {
|
|
3049
|
-
const environment =
|
|
3257
|
+
const environment = readServiceEnvironment(paths);
|
|
3050
3258
|
const configured2 = serviceHardeningEnvKeys.filter((key) => environment[key]).map((key) => ` ${key}=${environment[key]}`);
|
|
3051
|
-
return ["Service Hardening:", ...configured2.length ? configured2 : [" dispatcher hardening env not configured in
|
|
3259
|
+
return ["Service Hardening:", ...configured2.length ? configured2 : [" dispatcher hardening env not configured in service definition"]];
|
|
3052
3260
|
}
|
|
3053
3261
|
function doctorCounts(checks) {
|
|
3054
3262
|
return {
|
|
@@ -3138,6 +3346,7 @@ async function getServiceStatusWithRuntimeReadiness(options = {}, dependencies =
|
|
|
3138
3346
|
}
|
|
3139
3347
|
}
|
|
3140
3348
|
function formatServiceStatus(summary) {
|
|
3349
|
+
const definitionLine = summary.controller === "launchd" ? `LaunchAgent: ${summary.plistPath}` : summary.controller === "systemd" ? `Systemd unit: ${summary.unitPath}` : void 0;
|
|
3141
3350
|
return [
|
|
3142
3351
|
`Controller: ${summary.controller}`,
|
|
3143
3352
|
`Installed: ${summary.installed ? "yes" : "no"}`,
|
|
@@ -3153,7 +3362,7 @@ function formatServiceStatus(summary) {
|
|
|
3153
3362
|
...summary.secrets,
|
|
3154
3363
|
...summary.capabilities,
|
|
3155
3364
|
...summary.serviceHardening,
|
|
3156
|
-
|
|
3365
|
+
...definitionLine ? [definitionLine] : [],
|
|
3157
3366
|
`Stdout log: ${summary.stdoutPath}`,
|
|
3158
3367
|
`Stderr log: ${summary.stderrPath}`,
|
|
3159
3368
|
...summary.controller === "unsupported" ? [unsupportedMessage()] : []
|
|
@@ -3203,13 +3412,22 @@ function formatServiceLogs(options = {}, dependencies = {}) {
|
|
|
3203
3412
|
}
|
|
3204
3413
|
async function runServiceInstallCommand(options, dependencies = {}) {
|
|
3205
3414
|
const paths = installService(options, dependencies);
|
|
3206
|
-
|
|
3415
|
+
const controller = serviceControllerFrom(dependencies);
|
|
3416
|
+
loggerFrom(dependencies).log(`OpenTag service installed: ${controller === "systemd" ? paths.unitPath : paths.plistPath}`);
|
|
3207
3417
|
loggerFrom(dependencies).log("It will start at login. Run `opentag service start` to start it now.");
|
|
3208
3418
|
}
|
|
3419
|
+
async function installAndStartService(options = {}, dependencies = {}) {
|
|
3420
|
+
installService(options, dependencies);
|
|
3421
|
+
const paths = startService(options, dependencies);
|
|
3422
|
+
if (!await waitForServiceLoaded(dependencies)) {
|
|
3423
|
+
throw new Error("OpenTag service start did not leave the service manager running. Run `opentag service status` and `opentag service logs` for details.");
|
|
3424
|
+
}
|
|
3425
|
+
return paths;
|
|
3426
|
+
}
|
|
3209
3427
|
async function runServiceStartCommand(options, dependencies = {}) {
|
|
3210
3428
|
const paths = startService(options, dependencies);
|
|
3211
|
-
if (!await
|
|
3212
|
-
throw new Error("OpenTag service start did not leave
|
|
3429
|
+
if (!await waitForServiceLoaded(dependencies)) {
|
|
3430
|
+
throw new Error("OpenTag service start did not leave the service manager running. Run `opentag service status` and `opentag service logs` for details.");
|
|
3213
3431
|
}
|
|
3214
3432
|
loggerFrom(dependencies).log(`OpenTag service started: ${paths.label}`);
|
|
3215
3433
|
}
|
|
@@ -3219,21 +3437,22 @@ async function runServiceStopCommand(options, dependencies = {}) {
|
|
|
3219
3437
|
}
|
|
3220
3438
|
async function runServiceRestartCommand(options, dependencies = {}) {
|
|
3221
3439
|
stopService(options, dependencies);
|
|
3222
|
-
await
|
|
3440
|
+
await waitForServiceUnloaded(dependencies, { timeoutMs: 1e3 });
|
|
3223
3441
|
let paths = startService(options, dependencies);
|
|
3224
|
-
let loaded = await
|
|
3442
|
+
let loaded = await waitForServiceLoaded(dependencies, { timeoutMs: 500 });
|
|
3225
3443
|
if (!loaded) {
|
|
3226
3444
|
paths = startService(options, dependencies);
|
|
3227
|
-
loaded = await
|
|
3445
|
+
loaded = await waitForServiceLoaded(dependencies);
|
|
3228
3446
|
}
|
|
3229
3447
|
if (!loaded) {
|
|
3230
|
-
throw new Error("OpenTag service restart did not leave
|
|
3448
|
+
throw new Error("OpenTag service restart did not leave the service manager running. Run `opentag service status` and `opentag service logs` for details.");
|
|
3231
3449
|
}
|
|
3232
3450
|
loggerFrom(dependencies).log(`OpenTag service restarted: ${paths.label}`);
|
|
3233
3451
|
}
|
|
3234
3452
|
async function runServiceUninstallCommand(options, dependencies = {}) {
|
|
3235
3453
|
const paths = uninstallService(options, dependencies);
|
|
3236
|
-
|
|
3454
|
+
const controller = serviceControllerFrom(dependencies);
|
|
3455
|
+
loggerFrom(dependencies).log(`OpenTag service uninstalled: ${controller === "systemd" ? paths.unitPath : paths.plistPath}`);
|
|
3237
3456
|
}
|
|
3238
3457
|
async function runServiceStatusCommand(options, dependencies = {}) {
|
|
3239
3458
|
const summary = await getServiceStatusWithRuntimeReadiness(options, dependencies);
|
|
@@ -4769,16 +4988,54 @@ async function scanLarkPersonalAgent(input = {}, dependencies = {}) {
|
|
|
4769
4988
|
}
|
|
4770
4989
|
|
|
4771
4990
|
// src/commands/setup.ts
|
|
4772
|
-
function startPromptMessage(language) {
|
|
4773
|
-
return language === "zh-CN" ? "\u73B0\u5728\u542F\u52A8 OpenTag\uFF1F" : "Start OpenTag now?";
|
|
4774
|
-
}
|
|
4775
4991
|
function setupCompleteMessage(language) {
|
|
4776
4992
|
return language === "zh-CN" ? "OpenTag \u8BBE\u7F6E\u5B8C\u6210\u3002" : "OpenTag setup complete.";
|
|
4777
4993
|
}
|
|
4778
4994
|
function startingMessage(language) {
|
|
4779
4995
|
return language === "zh-CN" ? "\u6B63\u5728\u542F\u52A8 OpenTag..." : "Starting OpenTag...";
|
|
4780
4996
|
}
|
|
4997
|
+
function serviceStartingMessage(language) {
|
|
4998
|
+
return language === "zh-CN" ? "\u6B63\u5728\u5B89\u88C5\u5E76\u542F\u52A8 OpenTag \u540E\u53F0\u670D\u52A1..." : "Installing and starting the OpenTag background service...";
|
|
4999
|
+
}
|
|
5000
|
+
function serviceStartedMessage(language) {
|
|
5001
|
+
return language === "zh-CN" ? "OpenTag \u8BBE\u7F6E\u5B8C\u6210\uFF0C\u540E\u53F0\u670D\u52A1\u5DF2\u542F\u52A8\u3002" : "OpenTag setup complete. The background service is running.";
|
|
5002
|
+
}
|
|
5003
|
+
function runModePromptMessage(language) {
|
|
5004
|
+
return language === "zh-CN" ? "OpenTag \u8981\u5982\u4F55\u8FD0\u884C\uFF1F" : "How should OpenTag run?";
|
|
5005
|
+
}
|
|
5006
|
+
function runModeOptions(language, serviceSupported) {
|
|
5007
|
+
if (language === "zh-CN") {
|
|
5008
|
+
return [
|
|
5009
|
+
...serviceSupported ? [{ value: "service", label: "\u5173\u95ED\u8FD9\u4E2A\u7EC8\u7AEF\u540E\u7EE7\u7EED\u8FD0\u884C\uFF08\u63A8\u8350\uFF09" }] : [],
|
|
5010
|
+
{ value: "terminal", label: "\u53EA\u5728\u5F53\u524D\u7EC8\u7AEF\u91CC\u8FD0\u884C" },
|
|
5011
|
+
{ value: "later", label: "\u6682\u65F6\u4E0D\u542F\u52A8" }
|
|
5012
|
+
];
|
|
5013
|
+
}
|
|
5014
|
+
return [
|
|
5015
|
+
...serviceSupported ? [{ value: "service", label: "Keep running after I close this terminal (recommended)" }] : [],
|
|
5016
|
+
{ value: "terminal", label: "Run only in this terminal" },
|
|
5017
|
+
{ value: "later", label: "Do not start now" }
|
|
5018
|
+
];
|
|
5019
|
+
}
|
|
5020
|
+
async function collectRunMode(options, prompts, language, platform) {
|
|
5021
|
+
if (options.service) return "service";
|
|
5022
|
+
if (options.start === true) return "terminal";
|
|
5023
|
+
if (options.start === false || options.yes) return "later";
|
|
5024
|
+
const serviceSupported = serviceControllerForPlatform(platform) !== "unsupported";
|
|
5025
|
+
return prompts.select({
|
|
5026
|
+
message: runModePromptMessage(language),
|
|
5027
|
+
initialValue: serviceSupported ? "service" : "terminal",
|
|
5028
|
+
options: runModeOptions(language, serviceSupported)
|
|
5029
|
+
});
|
|
5030
|
+
}
|
|
4781
5031
|
async function runSetupCommand(options, dependencies = {}) {
|
|
5032
|
+
if (options.service && options.start !== void 0) {
|
|
5033
|
+
throw new Error("--service cannot be combined with --start or --no-start.");
|
|
5034
|
+
}
|
|
5035
|
+
const platform = dependencies.platform ?? process.platform;
|
|
5036
|
+
if (options.service && serviceControllerForPlatform(platform) === "unsupported") {
|
|
5037
|
+
throw new Error("OpenTag background service is not supported on this platform. Use `opentag start` to run OpenTag in this terminal.");
|
|
5038
|
+
}
|
|
4782
5039
|
const env = dependencies.env ?? process.env;
|
|
4783
5040
|
const configPath = options.config ?? defaultConfigPath(env);
|
|
4784
5041
|
if (options.yes && existsSync6(configPath) && !options.force) {
|
|
@@ -4798,11 +5055,14 @@ async function runSetupCommand(options, dependencies = {}) {
|
|
|
4798
5055
|
ensurePrivateDirectory(config.state.worktreeRoot);
|
|
4799
5056
|
writeCliConfigAtomic(configPath, config);
|
|
4800
5057
|
prompts.note(formatSetupComplete(config, configPath));
|
|
4801
|
-
const
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
5058
|
+
const runMode = await collectRunMode(options, prompts, config.preferences?.language, platform);
|
|
5059
|
+
if (runMode === "service") {
|
|
5060
|
+
prompts.note(serviceStartingMessage(config.preferences?.language));
|
|
5061
|
+
await (dependencies.startOpenTagService ?? installAndStartService)({ config: configPath });
|
|
5062
|
+
prompts.outro(serviceStartedMessage(config.preferences?.language));
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
5065
|
+
if (runMode === "terminal") {
|
|
4806
5066
|
prompts.outro(startingMessage(config.preferences?.language));
|
|
4807
5067
|
await (dependencies.startOpenTag ?? runStartCommand)({ config: configPath });
|
|
4808
5068
|
} else {
|
|
@@ -4826,7 +5086,7 @@ function runCliAction(handler) {
|
|
|
4826
5086
|
};
|
|
4827
5087
|
}
|
|
4828
5088
|
program.name(process.env.OPENTAG_CLI_NAME?.trim() || "opentag").description("OpenTag CLI");
|
|
4829
|
-
program.command("setup").description("Create a local OpenTag config").option("--platform <platform>", "Platform to configure").option("--config <path>", "Config file path").option("--project <path>", "Project checkout path").option("--language <language>", "Setup language: en or zh-CN").option("--executor <executor>", "Default executor: echo, codex, claude-code, or hermes").option("--hermes-command <command>", "Hermes CLI command").option("--hermes-profile <profile>", "Hermes profile").option("--hermes-profile-template <template>", "Hermes profile template").option("--agent-profile <profile>", "Executor-neutral agent session profile").option("--agent-profile-template <template>", "Executor-neutral agent session profile template").option("--lark-setup <method>", "Lark setup method: saved, scan, or manual").option("--lark-app-id <id>", "Lark app id").option("--lark-app-secret <secret>", "Lark app secret").option("--tenant <tenant>", "Manual Lark / Feishu tenant: feishu or lark").option("--lark-bot-open-id <openId>", "Lark bot open id for group mentions").option("--slack-mode <mode>", "Slack connection mode: socket_mode or events_api").option("--slack-app-token <token>", "Slack app-level token for Socket Mode").option("--slack-signing-secret <secret>", "Slack signing secret").option("--slack-bot-token <token>", "Slack bot user OAuth token").option("--slack-app-id <id>", "Slack app id").option("--slack-team-id <id>", "Slack team id").option("--slack-channel-id <id>", "Slack channel id").option("--slack-port <port>", "Local Slack Events API port").option("--github-token <token>", "GitHub token for comments and apply-1 pull requests").option("--github-webhook-secret <secret>", "GitHub webhook secret; generated when omitted").option("--github-repository <ownerRepo>", "GitHub repository as owner/repo").option("--github-webhook-path <path>", "GitHub webhook path").option("--github-port <port>", "Local GitHub webhook port").option("--github-auto-create-pr", "Create pull requests immediately after runs").option("--no-github-auto-create-pr", "Use the default apply-1 pull request flow").option("--binding <method>", "Binding method: default_project or bind_later").option("--force", "Overwrite an existing config").option("--start", "Start OpenTag immediately after setup").option("--no-start", "Do not ask to start OpenTag after setup").option("-y, --yes", "Skip setup confirmation").action(runCliAction(runSetupCommand));
|
|
5089
|
+
program.command("setup").description("Create a local OpenTag config").option("--platform <platform>", "Platform to configure").option("--config <path>", "Config file path").option("--project <path>", "Project checkout path").option("--language <language>", "Setup language: en or zh-CN").option("--executor <executor>", "Default executor: echo, codex, claude-code, or hermes").option("--hermes-command <command>", "Hermes CLI command").option("--hermes-profile <profile>", "Hermes profile").option("--hermes-profile-template <template>", "Hermes profile template").option("--agent-profile <profile>", "Executor-neutral agent session profile").option("--agent-profile-template <template>", "Executor-neutral agent session profile template").option("--lark-setup <method>", "Lark setup method: saved, scan, or manual").option("--lark-app-id <id>", "Lark app id").option("--lark-app-secret <secret>", "Lark app secret").option("--tenant <tenant>", "Manual Lark / Feishu tenant: feishu or lark").option("--lark-bot-open-id <openId>", "Lark bot open id for group mentions").option("--slack-mode <mode>", "Slack connection mode: socket_mode or events_api").option("--slack-app-token <token>", "Slack app-level token for Socket Mode").option("--slack-signing-secret <secret>", "Slack signing secret").option("--slack-bot-token <token>", "Slack bot user OAuth token").option("--slack-app-id <id>", "Slack app id").option("--slack-team-id <id>", "Slack team id").option("--slack-channel-id <id>", "Slack channel id").option("--slack-port <port>", "Local Slack Events API port").option("--github-token <token>", "GitHub token for comments and apply-1 pull requests").option("--github-webhook-secret <secret>", "GitHub webhook secret; generated when omitted").option("--github-repository <ownerRepo>", "GitHub repository as owner/repo").option("--github-webhook-path <path>", "GitHub webhook path").option("--github-port <port>", "Local GitHub webhook port").option("--github-auto-create-pr", "Create pull requests immediately after runs").option("--no-github-auto-create-pr", "Use the default apply-1 pull request flow").option("--binding <method>", "Binding method: default_project or bind_later").option("--force", "Overwrite an existing config").option("--start", "Start OpenTag immediately after setup").option("--no-start", "Do not ask to start OpenTag after setup").option("--service", "Install and start OpenTag as a background service after setup").option("-y, --yes", "Skip setup confirmation").action(runCliAction(runSetupCommand));
|
|
4830
5090
|
program.command("pair").description("Pair this local runner with a remote relay").option("--config <path>", "Config file path").option("--relay <url>", "Remote relay dispatcher URL").option("--no-register", "Update config without registering runner and project targets").action(runCliAction(runPairCommand));
|
|
4831
5091
|
program.command("start").description("Start the local OpenTag stack").option("--config <path>", "Config file path").action(runCliAction(runStartCommand));
|
|
4832
5092
|
program.command("status").description("Show the local OpenTag status").option("--config <path>", "Config file path").option("--run <runId>", "Show audit details for one run").option("--channel <provider:account/conversation>", "Show active run and queued follow-ups for one source container").action(runCliAction(runStatusCommand));
|
|
@@ -4835,7 +5095,7 @@ program.command("doctor").description("Check dispatcher, bindings, checkouts, an
|
|
|
4835
5095
|
program.command("ingest").description("Ingest a local external agent progress or completion event").option("--config <path>", "Config file path").requiredOption("--run <runId>", "OpenTag run id").requiredOption("--event <event>", "Event: progress, post_llm_call, before_agent_finalize, agent_end, failed, cancelled, timed_out, or interrupted").option("--source <source>", "External agent runtime source label").option("--message <message>", "Progress or completion summary").option("--type <type>", "Progress event type").option("--idempotency-key <key>", "Stable replay-protection key for retrying the same progress event").option("--result-json <json>", "Complete run with an OpenTagRunResult JSON object").option("--conclusion <conclusion>", "Completion conclusion when --result-json is omitted").option("--summary <summary>", "Completion summary when --result-json is omitted").action(runCliAction(runIngestCommand));
|
|
4836
5096
|
program.command("ingest-template").description("Print a shell template or manifest for local external agent hook ingest").option("--source <source>", "External agent runtime source label").option("--command <command>", "OpenTag CLI command to use in the template").option("--format <format>", "Template format: shell or manifest").action(runCliAction(runIngestTemplateCommand));
|
|
4837
5097
|
var serviceCommand = program.command("service").description("Install and control the OpenTag background service");
|
|
4838
|
-
serviceCommand.command("install").description("Install the OpenTag
|
|
5098
|
+
serviceCommand.command("install").description("Install the OpenTag background service").option("--config <path>", "Config file path").option("--max-request-body-bytes <bytes>", "Persist dispatcher request body limit in the service definition").option("--rate-limit-window-ms <ms>", "Persist dispatcher rate-limit window in the service definition").option("--rate-limit-max-requests <n>", "Persist dispatcher rate-limit max requests in the service definition").option("--rate-limit-disabled", "Persist an explicit disabled dispatcher rate-limit state in the service definition").action(runCliAction(runServiceInstallCommand));
|
|
4839
5099
|
serviceCommand.command("start").description("Start the OpenTag background service").option("--config <path>", "Config file path").action(runCliAction(runServiceStartCommand));
|
|
4840
5100
|
serviceCommand.command("stop").description("Stop the OpenTag background service").option("--config <path>", "Config file path").action(runCliAction(runServiceStopCommand));
|
|
4841
5101
|
serviceCommand.command("restart").description("Restart the OpenTag background service").option("--config <path>", "Config file path").action(runCliAction(runServiceRestartCommand));
|