@xopcai/xopc 0.0.83 → 0.0.84
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/README.md +2 -0
- package/README.zh-CN.md +3 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agents-CrpYTHJS.js → agents-tR-nNP04.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-1mcKh5Rh.js → apps-page-BDw6SP-d.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-zd6QNKPx.js → channels-settings-DEFd-jj1.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-uRAuhiUo.js → channels-status-swr-DI5FHdGe.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-O2Q_ruV6.js → cron-api-BSqY8LwW.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-By09AQD-.js → cron-page-D7lVDjcR.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-BpQxde0t.js → dist-CqNMNhJM.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CY27wj_p.js → extension-debug-page-gf2L0kY_.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C-Ed5ZmP.js → extension-page-CQo2Xsmg.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-raLux7E7.js → extension-settings-page-CZf0WoZg.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-fa_hiQcX.js → field-primitives-DTtlp-l8.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BVl5VHvL.js → heartbeat-config-api-B0drdQEJ.js} +1 -1
- package/dist/gateway/static/root/assets/{index-Y-iqo-gL.js → index-0Gt3TG4j.js} +3 -3
- package/dist/gateway/static/root/assets/{logs-page-BdH2n7ZW.js → logs-page-DMuORLfC.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-Vpchzdp-.js → sessions-page-_UO8g6NN.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Kk1yAGBl.js → settings-form-section-DkmHkknc.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-KBm0u6Dz.js → settings-page-Cz8FoW_A.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BjeXXaOn.js → skills-page-HrUOxF7H.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-DpTxN4AF.js → utils-BFwcR6pL.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CwO8Cf01.js → voice-api-key-field-JF8-aqc5.js} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/src/cli/command-catalog.js +110 -8
- package/dist/src/cli/command-catalog.js.map +1 -1
- package/dist/src/cli/command-loaders.js +2 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +9 -1
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/config.js +70 -19
- package/dist/src/cli/commands/config.js.map +1 -1
- package/dist/src/cli/commands/cron-cli.d.ts +2 -0
- package/dist/src/cli/commands/cron-cli.js +15 -0
- package/dist/src/cli/commands/cron-cli.js.map +1 -0
- package/dist/src/cli/commands/cron.d.ts +4 -1
- package/dist/src/cli/commands/cron.js +76 -41
- package/dist/src/cli/commands/cron.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/gateway-health.js +2 -2
- package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/gateway-service.js +2 -2
- package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +3 -3
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
- package/dist/src/cli/commands/gateway/index.d.ts +1 -1
- package/dist/src/cli/commands/gateway/index.js +2 -2
- package/dist/src/cli/commands/gateway/service.d.ts +4 -0
- package/dist/src/cli/commands/gateway/service.js +17 -2
- package/dist/src/cli/commands/gateway/service.js.map +1 -1
- package/dist/src/cli/commands/gateway/subcommands.js +1 -4
- package/dist/src/cli/commands/gateway/subcommands.js.map +1 -1
- package/dist/src/cli/commands/init.js +27 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/models.d.ts +4 -1
- package/dist/src/cli/commands/models.js +86 -74
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +2 -0
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/profile.d.ts +3 -5
- package/dist/src/cli/commands/profile.js +31 -31
- package/dist/src/cli/commands/profile.js.map +1 -1
- package/dist/src/cli/commands/setup.js +6 -1
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/gateway-run-argv.js +15 -5
- package/dist/src/cli/gateway-run-argv.js.map +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/service.js +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,62 +1,92 @@
|
|
|
1
1
|
import { formatExamples, register } from "../registry.js";
|
|
2
|
+
import { withCronService } from "./cron-cli.js";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
//#region src/cli/commands/cron.ts
|
|
4
5
|
function createCronCommand(_ctx) {
|
|
5
6
|
const cmd = new Command("cron").description("Manage scheduled tasks").addHelpText("after", formatExamples([
|
|
6
7
|
"xopc cron list # List all tasks",
|
|
7
8
|
"xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"",
|
|
9
|
+
"xopc cron enable <job-id> # Enable a task",
|
|
10
|
+
"xopc cron disable <job-id> # Disable a task",
|
|
11
|
+
"xopc cron run <job-id> # Run a task now",
|
|
8
12
|
"xopc cron remove <job-id> # Remove a task"
|
|
9
13
|
]));
|
|
10
14
|
cmd.addCommand(new Command("list").description("List all scheduled tasks").action(async () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
await cronService.stop();
|
|
15
|
+
await withCronService(async (cronService) => {
|
|
16
|
+
const jobs = await cronService.listJobs();
|
|
17
|
+
if (jobs.length === 0) {
|
|
18
|
+
console.log("No scheduled tasks.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log("Scheduled Tasks:\n");
|
|
22
|
+
const { getCronPayloadText } = await import("../../cron/job-content.js");
|
|
23
|
+
for (const job of jobs) {
|
|
24
|
+
const state = job.enabled ? "enabled " : "disabled";
|
|
25
|
+
console.log(` ${job.id} [${state}] - ${job.schedule}`);
|
|
26
|
+
console.log(` ${getCronPayloadText({ payload: job.payload })}`);
|
|
27
|
+
console.log(` Next: ${job.next_run || "N/A"}`);
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
28
31
|
}));
|
|
29
32
|
cmd.addCommand(new Command("add").description("Add a scheduled task").option("--name <text>", "Task name").option("--schedule <cron>", "Cron expression (e.g., \"0 9 * * *\")").option("--message <text>", "Message to send").action(async (options) => {
|
|
30
33
|
if (!options.schedule || !options.message) {
|
|
31
34
|
console.error("Error: --schedule and --message are required");
|
|
32
35
|
process.exit(1);
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
37
|
+
await withCronService(async (cronService) => {
|
|
38
|
+
const result = await cronService.addJob(options.schedule, {
|
|
39
|
+
name: options.name,
|
|
40
|
+
payload: {
|
|
41
|
+
kind: "systemEvent",
|
|
42
|
+
text: options.message
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
console.log(`✅ Added job ${result.id}`);
|
|
46
|
+
console.log(` Schedule: ${result.schedule}`);
|
|
43
47
|
});
|
|
44
|
-
console.log(`✅ Added job ${result.id}`);
|
|
45
|
-
console.log(` Schedule: ${result.schedule}`);
|
|
46
|
-
await cronService.stop();
|
|
47
48
|
}));
|
|
48
49
|
cmd.addCommand(new Command("remove").description("Remove a scheduled task").argument("<id>", "Job ID").action(async (id) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
console.error(`Job ${id} not found`);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
50
|
+
await withCronService(async (cronService) => {
|
|
51
|
+
if (await cronService.removeJob(id)) console.log(`✅ Removed job ${id}`);
|
|
52
|
+
else {
|
|
53
|
+
console.error(`Job ${id} not found`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
59
57
|
}));
|
|
58
|
+
cmd.addCommand(new Command("enable").description("Enable a scheduled task").argument("<id>", "Job ID").action(async (id) => {
|
|
59
|
+
await withCronService(async (cronService) => {
|
|
60
|
+
if (await cronService.toggleJob(id, true)) console.log(`✅ Enabled job ${id}`);
|
|
61
|
+
else {
|
|
62
|
+
console.error(`Job ${id} not found`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}));
|
|
67
|
+
cmd.addCommand(new Command("disable").description("Disable a scheduled task").argument("<id>", "Job ID").action(async (id) => {
|
|
68
|
+
await withCronService(async (cronService) => {
|
|
69
|
+
if (await cronService.toggleJob(id, false)) console.log(`✅ Disabled job ${id}`);
|
|
70
|
+
else {
|
|
71
|
+
console.error(`Job ${id} not found`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
const runNowAction = async (id) => {
|
|
77
|
+
await withCronService(async (cronService) => {
|
|
78
|
+
try {
|
|
79
|
+
await cronService.runJobNow(id);
|
|
80
|
+
console.log(`✅ Triggered job ${id}`);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
console.error(message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
cmd.addCommand(new Command("run").description("Run a scheduled task immediately").argument("<id>", "Job ID").action(runNowAction));
|
|
89
|
+
cmd.addCommand(new Command("trigger").description("Alias for `cron run`").argument("<id>", "Job ID").action(runNowAction));
|
|
60
90
|
return cmd;
|
|
61
91
|
}
|
|
62
92
|
register({
|
|
@@ -66,10 +96,15 @@ register({
|
|
|
66
96
|
factory: createCronCommand,
|
|
67
97
|
metadata: {
|
|
68
98
|
category: "utility",
|
|
69
|
-
examples: [
|
|
99
|
+
examples: [
|
|
100
|
+
"xopc cron list",
|
|
101
|
+
"xopc cron add --schedule \"0 9 * * *\" --message \"Hello\"",
|
|
102
|
+
"xopc cron enable abc12345",
|
|
103
|
+
"xopc cron run abc12345"
|
|
104
|
+
]
|
|
70
105
|
}
|
|
71
106
|
});
|
|
72
107
|
//#endregion
|
|
73
|
-
export {};
|
|
108
|
+
export { createCronCommand };
|
|
74
109
|
|
|
75
110
|
//# sourceMappingURL=cron.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.js","names":[],"sources":["../../../../src/cli/commands/cron.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\n\nfunction createCronCommand(_ctx: CLIContext): Command {\n const cmd = new Command('cron')\n .description('Manage scheduled tasks')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc cron list # List all tasks',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"',\n 'xopc cron remove <job-id> # Remove a task',\n ])
|
|
1
|
+
{"version":3,"file":"cron.js","names":[],"sources":["../../../../src/cli/commands/cron.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport { withCronService } from './cron-cli.js';\n\nfunction createCronCommand(_ctx: CLIContext): Command {\n const cmd = new Command('cron')\n .description('Manage scheduled tasks')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc cron list # List all tasks',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Good morning\"',\n 'xopc cron enable <job-id> # Enable a task',\n 'xopc cron disable <job-id> # Disable a task',\n 'xopc cron run <job-id> # Run a task now',\n 'xopc cron remove <job-id> # Remove a task',\n ]),\n );\n\n cmd.addCommand(\n new Command('list')\n .description('List all scheduled tasks')\n .action(async () => {\n await withCronService(async (cronService) => {\n const jobs = await cronService.listJobs();\n\n if (jobs.length === 0) {\n console.log('No scheduled tasks.');\n return;\n }\n\n console.log('Scheduled Tasks:\\n');\n const { getCronPayloadText } = await import('../../cron/job-content.js');\n for (const job of jobs) {\n const state = job.enabled ? 'enabled ' : 'disabled';\n console.log(` ${job.id} [${state}] - ${job.schedule}`);\n console.log(` ${getCronPayloadText({ payload: job.payload })}`);\n console.log(` Next: ${job.next_run || 'N/A'}`);\n console.log();\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('add')\n .description('Add a scheduled task')\n .option('--name <text>', 'Task name')\n .option('--schedule <cron>', 'Cron expression (e.g., \"0 9 * * *\")')\n .option('--message <text>', 'Message to send')\n .action(async (options) => {\n if (!options.schedule || !options.message) {\n console.error('Error: --schedule and --message are required');\n process.exit(1);\n }\n\n await withCronService(async (cronService) => {\n const result = await cronService.addJob(options.schedule, {\n name: options.name,\n payload: { kind: 'systemEvent', text: options.message },\n });\n\n console.log(`✅ Added job ${result.id}`);\n console.log(` Schedule: ${result.schedule}`);\n });\n }),\n );\n\n cmd.addCommand(\n new Command('remove')\n .description('Remove a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.removeJob(id);\n if (success) {\n console.log(`✅ Removed job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('enable')\n .description('Enable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, true);\n if (success) {\n console.log(`✅ Enabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n cmd.addCommand(\n new Command('disable')\n .description('Disable a scheduled task')\n .argument('<id>', 'Job ID')\n .action(async (id) => {\n await withCronService(async (cronService) => {\n const success = await cronService.toggleJob(id, false);\n if (success) {\n console.log(`✅ Disabled job ${id}`);\n } else {\n console.error(`Job ${id} not found`);\n process.exit(1);\n }\n });\n }),\n );\n\n const runNowAction = async (id: string) => {\n await withCronService(async (cronService) => {\n try {\n await cronService.runJobNow(id);\n console.log(`✅ Triggered job ${id}`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(message);\n process.exit(1);\n }\n });\n };\n\n cmd.addCommand(\n new Command('run')\n .description('Run a scheduled task immediately')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n cmd.addCommand(\n new Command('trigger')\n .description('Alias for `cron run`')\n .argument('<id>', 'Job ID')\n .action(runNowAction),\n );\n\n return cmd;\n}\n\nregister({\n id: 'cron',\n name: 'cron',\n description: 'Manage scheduled tasks',\n factory: createCronCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc cron list',\n 'xopc cron add --schedule \"0 9 * * *\" --message \"Hello\"',\n 'xopc cron enable abc12345',\n 'xopc cron run abc12345',\n ],\n },\n});\n\nexport { createCronCommand };\n"],"mappings":"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"}
|
|
@@ -50,7 +50,7 @@ function checkWeixin(cfg) {
|
|
|
50
50
|
const hints = [];
|
|
51
51
|
if ((wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : []).length === 0) {
|
|
52
52
|
messages.push("Weixin is enabled but no accounts are defined in config.");
|
|
53
|
-
hints.push("Run: xopc channels weixin
|
|
53
|
+
hints.push("Run: xopc channels login --channel weixin (or add channels.weixin.accounts).");
|
|
54
54
|
}
|
|
55
55
|
return {
|
|
56
56
|
ok: messages.length === 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-config.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ntype TelegramCfg = {\n enabled?: boolean;\n accounts?: Record<string, { botToken?: string; dmPolicy?: string; enabled?: boolean }>;\n dmPolicy?: string;\n};\n\ntype WeixinCfg = {\n enabled?: boolean;\n accounts?: Record<string, unknown>;\n};\n\nfunction checkTelegram(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const tg = cfg.channels?.telegram as TelegramCfg | undefined;\n if (!tg) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const defaultAcc = tg.accounts?.default;\n const token = defaultAcc?.botToken?.trim() ?? '';\n const enabled = tg.enabled === true || token.length > 0;\n\n if (!enabled) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n\n if (!token) {\n messages.push('Telegram is enabled but no bot token is set.');\n hints.push('Set channels.telegram.accounts.default.botToken.');\n }\n\n const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || 'pairing';\n if (!['pairing', 'allowlist', 'open', 'disabled'].includes(dm)) {\n messages.push(`Telegram dmPolicy \"${dm}\" is not valid.`);\n }\n if (dm === 'open') {\n messages.push('Telegram DM policy is \"open\" (any user can message the bot).');\n hints.push('Consider \"pairing\" or \"allowlist\" for stricter access.');\n }\n\n return {\n ok: messages.length === 0,\n messages,\n hints,\n };\n}\n\nfunction checkWeixin(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const wx = cfg.channels?.weixin as WeixinCfg | undefined;\n if (!wx || wx.enabled !== true) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n const accountKeys = wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : [];\n if (accountKeys.length === 0) {\n messages.push('Weixin is enabled but no accounts are defined in config.');\n hints.push('Run: xopc channels weixin
|
|
1
|
+
{"version":3,"file":"channel-config.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ntype TelegramCfg = {\n enabled?: boolean;\n accounts?: Record<string, { botToken?: string; dmPolicy?: string; enabled?: boolean }>;\n dmPolicy?: string;\n};\n\ntype WeixinCfg = {\n enabled?: boolean;\n accounts?: Record<string, unknown>;\n};\n\nfunction checkTelegram(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const tg = cfg.channels?.telegram as TelegramCfg | undefined;\n if (!tg) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const defaultAcc = tg.accounts?.default;\n const token = defaultAcc?.botToken?.trim() ?? '';\n const enabled = tg.enabled === true || token.length > 0;\n\n if (!enabled) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n\n if (!token) {\n messages.push('Telegram is enabled but no bot token is set.');\n hints.push('Set channels.telegram.accounts.default.botToken.');\n }\n\n const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || 'pairing';\n if (!['pairing', 'allowlist', 'open', 'disabled'].includes(dm)) {\n messages.push(`Telegram dmPolicy \"${dm}\" is not valid.`);\n }\n if (dm === 'open') {\n messages.push('Telegram DM policy is \"open\" (any user can message the bot).');\n hints.push('Consider \"pairing\" or \"allowlist\" for stricter access.');\n }\n\n return {\n ok: messages.length === 0,\n messages,\n hints,\n };\n}\n\nfunction checkWeixin(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const wx = cfg.channels?.weixin as WeixinCfg | undefined;\n if (!wx || wx.enabled !== true) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n const accountKeys = wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : [];\n if (accountKeys.length === 0) {\n messages.push('Weixin is enabled but no accounts are defined in config.');\n hints.push('Run: xopc channels login --channel weixin (or add channels.weixin.accounts).');\n }\n\n return { ok: messages.length === 0, messages, hints };\n}\n\nexport async function checkChannelConfig(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const tg = checkTelegram(cfg);\n const wx = checkWeixin(cfg);\n const allMsg = [...tg.messages, ...wx.messages];\n const allHints = [...tg.hints, ...wx.hints];\n\n const tgEnabled =\n (cfg.channels?.telegram as TelegramCfg | undefined)?.enabled === true ||\n Boolean((cfg.channels?.telegram as TelegramCfg | undefined)?.accounts?.default?.botToken?.trim());\n const wxOn = (cfg.channels?.weixin as WeixinCfg | undefined)?.enabled === true;\n if (!tgEnabled && !wxOn) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No channels enabled; skipped.',\n hints: [],\n };\n }\n\n if (allMsg.length === 0) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'pass',\n message: 'Enabled channel configuration looks valid.',\n hints: [],\n };\n }\n\n const hasFail = allMsg.some((m) => m.includes('no bot token') || m.includes('no accounts'));\n return {\n id: 'channel-config',\n label: 'Channels',\n status: hasFail ? 'fail' : 'warn',\n message: allMsg.join(' '),\n hints: allHints,\n };\n}\n"],"mappings":";;;aAE0D;AAe1D,SAAS,cAAc,KAAmE;CACxF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,GACH,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,aAAa,GAAG,UAAU;CAChC,MAAM,QAAQ,YAAY,UAAU,MAAM,IAAI;AAG9C,KAAI,EAFY,GAAG,YAAY,QAAQ,MAAM,SAAS,GAGpD,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,OAAO;AACV,WAAS,KAAK,+CAA+C;AAC7D,QAAM,KAAK,mDAAmD;;CAGhE,MAAM,MAAM,YAAY,YAAY,GAAG,aAAa;AACpD,KAAI,CAAC;EAAC;EAAW;EAAa;EAAQ;EAAW,CAAC,SAAS,GAAG,CAC5D,UAAS,KAAK,sBAAsB,GAAG,iBAAiB;AAE1D,KAAI,OAAO,QAAQ;AACjB,WAAS,KAAK,iEAA+D;AAC7E,QAAM,KAAK,6DAAyD;;AAGtE,QAAO;EACL,IAAI,SAAS,WAAW;EACxB;EACA;EACD;;AAGH,SAAS,YAAY,KAAmE;CACtF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,MAAM,GAAG,YAAY,KACxB,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,MADoB,GAAG,WAAW,OAAO,KAAK,GAAG,SAAS,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,EACvE,WAAW,GAAG;AAC5B,WAAS,KAAK,2DAA2D;AACzE,QAAM,KAAK,+EAA+E;;AAG5F,QAAO;EAAE,IAAI,SAAS,WAAW;EAAG;EAAU;EAAO;;AAGvD,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,KAAK,cAAc,IAAI;CAC7B,MAAM,KAAK,YAAY,IAAI;CAC3B,MAAM,SAAS,CAAC,GAAG,GAAG,UAAU,GAAG,GAAG,SAAS;CAC/C,MAAM,WAAW,CAAC,GAAG,GAAG,OAAO,GAAG,GAAG,MAAM;CAE3C,MAAM,aACH,IAAI,UAAU,WAAsC,YAAY,QACjE,SAAS,IAAI,UAAU,WAAsC,UAAU,SAAS,UAAU,MAAM,CAAC;CACnG,MAAM,QAAQ,IAAI,UAAU,SAAkC,YAAY;AAC1E,KAAI,CAAC,aAAa,CAAC,KACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAGH,KAAI,OAAO,WAAW,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAIH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAJc,OAAO,MAAM,MAAM,EAAE,SAAS,eAAe,IAAI,EAAE,SAAS,cAAc,CAIzE,GAAG,SAAS;EAC3B,SAAS,OAAO,KAAK,IAAI;EACzB,OAAO;EACR"}
|
|
@@ -33,7 +33,7 @@ async function checkConfigHealth(ctx) {
|
|
|
33
33
|
label: "Config",
|
|
34
34
|
status: "fail",
|
|
35
35
|
message: "Config file not found.",
|
|
36
|
-
hints: [`Run: xopc init`, `Or: xopc doctor --fix`]
|
|
36
|
+
hints: [`Run: xopc init (or xopc setup)`, `Or: xopc doctor --fix`]
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
let raw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/config-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\nimport { loadConfig, saveConfig } from '../../../../config/loader.js';\nimport { ConfigSchema } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkConfigHealth(ctx: DoctorContext): Promise<CheckResult> {\n const path = ctx.configPath;\n\n if (!existsSync(path)) {\n if (ctx.options.fix) {\n try {\n const dir = dirname(path);\n mkdirSync(dir, { recursive: true });\n const defaults = loadConfig(path);\n await saveConfig(defaults, path);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Created default config file.',\n hints: [path],\n fixed: true,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Config file missing and could not create default: ${msg}`,\n hints: [path],\n };\n }\n }\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file not found.',\n hints: [`Run: xopc init`, `Or: xopc doctor --fix`],\n };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Cannot read config file: ${msg}`,\n hints: [path],\n };\n }\n\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file is not valid JSON.',\n hints: ['Fix syntax or restore from backup (.bak).', path],\n };\n }\n\n const parsed = ConfigSchema.safeParse(json);\n if (!parsed.success) {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config does not match the expected schema.',\n hints: parsed.error.issues.slice(0, 5).map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`),\n };\n }\n\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Config file exists and validates.',\n hints: [path],\n };\n}\n"],"mappings":";;;;;aAIsE;aACV;AAG5D,eAAsB,kBAAkB,KAA0C;CAChF,MAAM,OAAO,IAAI;AAEjB,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,MAAI,IAAI,QAAQ,IACd,KAAI;AAEF,aADY,QAAQ,KACP,EAAE,EAAE,WAAW,MAAM,CAAC;AAEnC,SAAM,WADW,WAAW,KACH,EAAE,KAAK;AAChC,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,KAAK;IACb,OAAO;IACR;WACM,GAAG;AAEV,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS,qDALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IAMpD,OAAO,CAAC,KAAK;IACd;;AAGL,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"config-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/config-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { mkdirSync } from 'node:fs';\n\nimport { loadConfig, saveConfig } from '../../../../config/loader.js';\nimport { ConfigSchema } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkConfigHealth(ctx: DoctorContext): Promise<CheckResult> {\n const path = ctx.configPath;\n\n if (!existsSync(path)) {\n if (ctx.options.fix) {\n try {\n const dir = dirname(path);\n mkdirSync(dir, { recursive: true });\n const defaults = loadConfig(path);\n await saveConfig(defaults, path);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Created default config file.',\n hints: [path],\n fixed: true,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Config file missing and could not create default: ${msg}`,\n hints: [path],\n };\n }\n }\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file not found.',\n hints: [`Run: xopc init (or xopc setup)`, `Or: xopc doctor --fix`],\n };\n }\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: `Cannot read config file: ${msg}`,\n hints: [path],\n };\n }\n\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config file is not valid JSON.',\n hints: ['Fix syntax or restore from backup (.bak).', path],\n };\n }\n\n const parsed = ConfigSchema.safeParse(json);\n if (!parsed.success) {\n return {\n id: 'config-health',\n label: 'Config',\n status: 'fail',\n message: 'Config does not match the expected schema.',\n hints: parsed.error.issues.slice(0, 5).map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`),\n };\n }\n\n return {\n id: 'config-health',\n label: 'Config',\n status: 'pass',\n message: 'Config file exists and validates.',\n hints: [path],\n };\n}\n"],"mappings":";;;;;aAIsE;aACV;AAG5D,eAAsB,kBAAkB,KAA0C;CAChF,MAAM,OAAO,IAAI;AAEjB,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,MAAI,IAAI,QAAQ,IACd,KAAI;AAEF,aADY,QAAQ,KACP,EAAE,EAAE,WAAW,MAAM,CAAC;AAEnC,SAAM,WADW,WAAW,KACH,EAAE,KAAK;AAChC,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,KAAK;IACb,OAAO;IACR;WACM,GAAG;AAEV,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS,qDALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IAMpD,OAAO,CAAC,KAAK;IACd;;AAGL,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,kCAAkC,wBAAwB;GACnE;;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,aAAa,MAAM,QAAQ;UAC1B,GAAG;AAEV,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,4BALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;GAMpD,OAAO,CAAC,KAAK;GACd;;CAGH,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,6CAA6C,KAAK;GAC3D;;CAGH,MAAM,SAAS,aAAa,UAAU,KAAK;AAC3C,KAAI,CAAC,OAAO,QACV,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,OAAO,MAAM,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE,UAAU;EACnG;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,KAAK;EACd"}
|
|
@@ -38,7 +38,7 @@ async function checkCronHealth(ctx) {
|
|
|
38
38
|
label: "Cron",
|
|
39
39
|
status: "warn",
|
|
40
40
|
message: "Cron directory does not exist.",
|
|
41
|
-
hints: [cronDir, "Run: xopc init"]
|
|
41
|
+
hints: [cronDir, "Run: xopc init (creates cron directory)"]
|
|
42
42
|
};
|
|
43
43
|
const jobsPath = resolveCronJobsPath();
|
|
44
44
|
if (!existsSync(jobsPath)) return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/cron-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport { resolveCronDir, resolveCronJobsPath } from '../../../../config/paths.js';\nimport { JobDataSchema } from '../../../../cron/validation.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkCronHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n if (cfg.cron?.enabled === false) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Cron is disabled in config; skipped.',\n hints: [],\n };\n }\n\n const cronDir = resolveCronDir();\n if (!existsSync(cronDir)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron directory does not exist.',\n hints: [cronDir, 'Run: xopc init'],\n };\n }\n\n const jobsPath = resolveCronJobsPath();\n if (!existsSync(jobsPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is missing.',\n hints: [jobsPath],\n };\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(jobsPath, 'utf-8'));\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is not valid JSON.',\n hints: [jobsPath],\n };\n }\n\n if (!raw || typeof raw !== 'object' || !('jobs' in raw) || !Array.isArray((raw as { jobs: unknown }).jobs)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file has invalid structure (expected { jobs: [] }).',\n hints: [jobsPath],\n };\n }\n\n const jobs = (raw as { jobs: unknown[] }).jobs;\n const hints: string[] = [];\n let valid = 0;\n let enabled = 0;\n let scheduleMissing = 0;\n\n for (const j of jobs) {\n const r = JobDataSchema.safeParse(j);\n if (r.success) {\n valid++;\n if (r.data.enabled) {\n enabled++;\n const sched = r.data.schedule?.trim();\n if (!sched) {\n scheduleMissing++;\n hints.push(`Job \"${r.data.name || r.data.id}\" is enabled but has no schedule.`);\n }\n }\n } else {\n hints.push('One or more job entries failed validation (check jobs.json).');\n break;\n }\n }\n\n if (valid !== jobs.length) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Some cron jobs are invalid or could not be validated.',\n hints: hints.length ? hints.slice(0, 5) : [jobsPath],\n };\n }\n\n if (scheduleMissing > 0) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: `${scheduleMissing} enabled job(s) are missing a schedule.`,\n hints: hints.slice(0, 5),\n };\n }\n\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'pass',\n message: `Cron jobs file is valid (${enabled} enabled, ${jobs.length} total).`,\n hints: [jobsPath],\n };\n}\n"],"mappings":";;;;;aAE0D;YAEwB;AAIlF,eAAsB,gBAAgB,KAA0C;AAC9E,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;AAGH,KAAI,IAAI,MAAM,YAAY,MACxB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,MAAM,UAAU,gBAAgB;AAChC,KAAI,CAAC,WAAW,QAAQ,CACtB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"cron-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/cron-health.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport { resolveCronDir, resolveCronJobsPath } from '../../../../config/paths.js';\nimport { JobDataSchema } from '../../../../cron/validation.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkCronHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n if (cfg.cron?.enabled === false) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'skip',\n message: 'Cron is disabled in config; skipped.',\n hints: [],\n };\n }\n\n const cronDir = resolveCronDir();\n if (!existsSync(cronDir)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron directory does not exist.',\n hints: [cronDir, 'Run: xopc init (creates cron directory)'],\n };\n }\n\n const jobsPath = resolveCronJobsPath();\n if (!existsSync(jobsPath)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is missing.',\n hints: [jobsPath],\n };\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(jobsPath, 'utf-8'));\n } catch {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file is not valid JSON.',\n hints: [jobsPath],\n };\n }\n\n if (!raw || typeof raw !== 'object' || !('jobs' in raw) || !Array.isArray((raw as { jobs: unknown }).jobs)) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Cron jobs file has invalid structure (expected { jobs: [] }).',\n hints: [jobsPath],\n };\n }\n\n const jobs = (raw as { jobs: unknown[] }).jobs;\n const hints: string[] = [];\n let valid = 0;\n let enabled = 0;\n let scheduleMissing = 0;\n\n for (const j of jobs) {\n const r = JobDataSchema.safeParse(j);\n if (r.success) {\n valid++;\n if (r.data.enabled) {\n enabled++;\n const sched = r.data.schedule?.trim();\n if (!sched) {\n scheduleMissing++;\n hints.push(`Job \"${r.data.name || r.data.id}\" is enabled but has no schedule.`);\n }\n }\n } else {\n hints.push('One or more job entries failed validation (check jobs.json).');\n break;\n }\n }\n\n if (valid !== jobs.length) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: 'Some cron jobs are invalid or could not be validated.',\n hints: hints.length ? hints.slice(0, 5) : [jobsPath],\n };\n }\n\n if (scheduleMissing > 0) {\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'warn',\n message: `${scheduleMissing} enabled job(s) are missing a schedule.`,\n hints: hints.slice(0, 5),\n };\n }\n\n return {\n id: 'cron-health',\n label: 'Cron',\n status: 'pass',\n message: `Cron jobs file is valid (${enabled} enabled, ${jobs.length} total).`,\n hints: [jobsPath],\n };\n}\n"],"mappings":";;;;;aAE0D;YAEwB;AAIlF,eAAsB,gBAAgB,KAA0C;AAC9E,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;AAGH,KAAI,IAAI,MAAM,YAAY,MACxB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,MAAM,UAAU,gBAAgB;AAChC,KAAI,CAAC,WAAW,QAAQ,CACtB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS,0CAA0C;EAC5D;CAGH,MAAM,WAAW,qBAAqB;AACtC,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS;EAClB;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,KAAK,MAAM,aAAa,UAAU,QAAQ,CAAC;SAC3C;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,CAAC,SAAS;GAClB;;AAGH,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,EAAE,UAAU,QAAQ,CAAC,MAAM,QAAS,IAA0B,KAAK,CACxG,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,SAAS;EAClB;CAGH,MAAM,OAAQ,IAA4B;CAC1C,MAAM,QAAkB,EAAE;CAC1B,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI,kBAAkB;AAEtB,MAAK,MAAM,KAAK,MAAM;EACpB,MAAM,IAAI,cAAc,UAAU,EAAE;AACpC,MAAI,EAAE,SAAS;AACb;AACA,OAAI,EAAE,KAAK,SAAS;AAClB;AAEA,QAAI,CADU,EAAE,KAAK,UAAU,MAAM,EACzB;AACV;AACA,WAAM,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,GAAG,mCAAmC;;;SAG9E;AACL,SAAM,KAAK,+DAA+D;AAC1E;;;AAIJ,KAAI,UAAU,KAAK,OACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,MAAM,SAAS,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,SAAS;EACrD;AAGH,KAAI,kBAAkB,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,GAAG,gBAAgB;EAC5B,OAAO,MAAM,MAAM,GAAG,EAAE;EACzB;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,4BAA4B,QAAQ,YAAY,KAAK,OAAO;EACrE,OAAO,CAAC,SAAS;EAClB"}
|
|
@@ -46,7 +46,7 @@ async function checkGatewayHealth(ctx) {
|
|
|
46
46
|
label: "Gateway HTTP",
|
|
47
47
|
status: "warn",
|
|
48
48
|
message: `Gateway returned HTTP ${res.status} at ${url}.`,
|
|
49
|
-
hints: ["Start the gateway: xopc gateway start"]
|
|
49
|
+
hints: ["Start the gateway: xopc gateway service start (or xopc gateway for foreground)"]
|
|
50
50
|
};
|
|
51
51
|
} catch (e) {
|
|
52
52
|
clearTimeout(timer);
|
|
@@ -55,7 +55,7 @@ async function checkGatewayHealth(ctx) {
|
|
|
55
55
|
label: "Gateway HTTP",
|
|
56
56
|
status: "warn",
|
|
57
57
|
message: e instanceof Error && e.name === "AbortError" ? `Gateway did not respond within ${FETCH_TIMEOUT_MS / 1e3}s (${url}).` : `Gateway not reachable (${url}).`,
|
|
58
|
-
hints: ["Start the gateway: xopc gateway start", `Configured base: ${base}`]
|
|
58
|
+
hints: ["Start the gateway: xopc gateway service start (or xopc gateway for foreground)", `Configured base: ${base}`]
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/gateway-health.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport { resolveGatewayLocalClientHost } from '../../../../config/gateway-bind.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nfunction resolveGatewayBaseUrl(cfg: Config): string {\n const host = resolveGatewayLocalClientHost(cfg);\n const port = cfg.gateway?.port ?? 18790;\n return `http://${host}:${port}`;\n}\n\nconst FETCH_TIMEOUT_MS = 5000;\n\nexport async function checkGatewayHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const base = resolveGatewayBaseUrl(cfg);\n const url = `${base.replace(/\\/$/, '')}/health`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n if (res.ok) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'pass',\n message: `Gateway responded OK at ${url}.`,\n hints: [],\n };\n }\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: `Gateway returned HTTP ${res.status} at ${url}.`,\n hints: ['Start the gateway: xopc gateway start'],\n };\n } catch (e) {\n clearTimeout(timer);\n const isAbort = e instanceof Error && e.name === 'AbortError';\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: isAbort\n ? `Gateway did not respond within ${FETCH_TIMEOUT_MS / 1000}s (${url}).`\n : `Gateway not reachable (${url}).`,\n hints: ['Start the gateway: xopc gateway start', `Configured base: ${base}`],\n };\n }\n}\n"],"mappings":";;;;aAE0D;AAK1D,SAAS,sBAAsB,KAAqB;AAGlD,QAAO,UAFM,8BAA8B,IAEtB,CAAC,GADT,IAAI,SAAS,QAAQ;;AAIpC,MAAM,mBAAmB;AAEzB,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,OAAO,sBAAsB,IAAI;CACvC,MAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,CAAC;CACvC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,iBAAiB;AAEpE,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC;AAC3D,eAAa,MAAM;AACnB,MAAI,IAAI,GACN,QAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,2BAA2B,IAAI;GACxC,OAAO,EAAE;GACV;AAEH,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,yBAAyB,IAAI,OAAO,MAAM,IAAI;GACvD,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"gateway-health.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/gateway-health.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport { resolveGatewayLocalClientHost } from '../../../../config/gateway-bind.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nfunction resolveGatewayBaseUrl(cfg: Config): string {\n const host = resolveGatewayLocalClientHost(cfg);\n const port = cfg.gateway?.port ?? 18790;\n return `http://${host}:${port}`;\n}\n\nconst FETCH_TIMEOUT_MS = 5000;\n\nexport async function checkGatewayHealth(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const base = resolveGatewayBaseUrl(cfg);\n const url = `${base.replace(/\\/$/, '')}/health`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n if (res.ok) {\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'pass',\n message: `Gateway responded OK at ${url}.`,\n hints: [],\n };\n }\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: `Gateway returned HTTP ${res.status} at ${url}.`,\n hints: ['Start the gateway: xopc gateway service start (or xopc gateway for foreground)'],\n };\n } catch (e) {\n clearTimeout(timer);\n const isAbort = e instanceof Error && e.name === 'AbortError';\n return {\n id: 'gateway-health',\n label: 'Gateway HTTP',\n status: 'warn',\n message: isAbort\n ? `Gateway did not respond within ${FETCH_TIMEOUT_MS / 1000}s (${url}).`\n : `Gateway not reachable (${url}).`,\n hints: ['Start the gateway: xopc gateway service start (or xopc gateway for foreground)', `Configured base: ${base}`],\n };\n }\n}\n"],"mappings":";;;;aAE0D;AAK1D,SAAS,sBAAsB,KAAqB;AAGlD,QAAO,UAFM,8BAA8B,IAEtB,CAAC,GADT,IAAI,SAAS,QAAQ;;AAIpC,MAAM,mBAAmB;AAEzB,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,OAAO,sBAAsB,IAAI;CACvC,MAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,CAAC;CACvC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,iBAAiB;AAEpE,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC;AAC3D,eAAa,MAAM;AACnB,MAAI,IAAI,GACN,QAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,2BAA2B,IAAI;GACxC,OAAO,EAAE;GACV;AAEH,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,yBAAyB,IAAI,OAAO,MAAM,IAAI;GACvD,OAAO,CAAC,iFAAiF;GAC1F;UACM,GAAG;AACV,eAAa,MAAM;AAEnB,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SALc,aAAa,SAAS,EAAE,SAAS,eAM3C,kCAAkC,mBAAmB,IAAK,KAAK,IAAI,MACnE,0BAA0B,IAAI;GAClC,OAAO,CAAC,kFAAkF,oBAAoB,OAAO;GACtH"}
|
|
@@ -40,7 +40,7 @@ async function checkGatewayService(ctx) {
|
|
|
40
40
|
label: "Gateway service",
|
|
41
41
|
status: "warn",
|
|
42
42
|
message: "Gateway is not installed as a system service.",
|
|
43
|
-
hints: ["Install: xopc gateway install
|
|
43
|
+
hints: ["Install: xopc gateway service install"]
|
|
44
44
|
};
|
|
45
45
|
const runtime = await service.readRuntime(env);
|
|
46
46
|
if (runtime.status === "running" && runtime.pid) return {
|
|
@@ -55,7 +55,7 @@ async function checkGatewayService(ctx) {
|
|
|
55
55
|
label: "Gateway service",
|
|
56
56
|
status: "warn",
|
|
57
57
|
message: `Gateway service is installed but not running (status: ${runtime.status}).`,
|
|
58
|
-
hints: ["Start: xopc gateway start"]
|
|
58
|
+
hints: ["Start: xopc gateway service start"]
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-service.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/gateway-service.ts"],"sourcesContent":["import { loadConfig } from '../../../../config/loader.js';\nimport { existsSync } from 'node:fs';\nimport { resolveGatewayService } from '../../../../daemon/service.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkGatewayService(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n try {\n loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n let service;\n try {\n service = await resolveGatewayService();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: `Service backend unavailable: ${msg}`,\n hints: [],\n };\n }\n\n const env: Record<string, string | undefined> = { ...process.env };\n const loaded = await service.isLoaded({ env });\n if (!loaded) {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'warn',\n message: 'Gateway is not installed as a system service.',\n hints: ['Install: xopc gateway install
|
|
1
|
+
{"version":3,"file":"gateway-service.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/gateway-service.ts"],"sourcesContent":["import { loadConfig } from '../../../../config/loader.js';\nimport { existsSync } from 'node:fs';\nimport { resolveGatewayService } from '../../../../daemon/service.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkGatewayService(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n try {\n loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n let service;\n try {\n service = await resolveGatewayService();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'skip',\n message: `Service backend unavailable: ${msg}`,\n hints: [],\n };\n }\n\n const env: Record<string, string | undefined> = { ...process.env };\n const loaded = await service.isLoaded({ env });\n if (!loaded) {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'warn',\n message: 'Gateway is not installed as a system service.',\n hints: ['Install: xopc gateway service install'],\n };\n }\n\n const runtime = await service.readRuntime(env);\n if (runtime.status === 'running' && runtime.pid) {\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'pass',\n message: `Gateway service is running (PID ${runtime.pid}).`,\n hints: [service.label],\n };\n }\n\n return {\n id: 'gateway-service',\n label: 'Gateway service',\n status: 'warn',\n message: `Gateway service is installed but not running (status: ${runtime.status}).`,\n hints: ['Start: xopc gateway service start'],\n };\n}\n"],"mappings":";;;;aAA0D;AAK1D,eAAsB,oBAAoB,KAA0C;AAClF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAGH,KAAI;AACF,aAAW,IAAI,WAAW;SACpB;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,uBAAuB;UAChC,GAAG;AAEV,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS,gCALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;GAMpD,OAAO,EAAE;GACV;;CAGH,MAAM,MAA0C,EAAE,GAAG,QAAQ,KAAK;AAElE,KAAI,CAAC,MADgB,QAAQ,SAAS,EAAE,KAAK,CAAC,CAE5C,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,wCAAwC;EACjD;CAGH,MAAM,UAAU,MAAM,QAAQ,YAAY,IAAI;AAC9C,KAAI,QAAQ,WAAW,aAAa,QAAQ,IAC1C,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,mCAAmC,QAAQ,IAAI;EACxD,OAAO,CAAC,QAAQ,MAAM;EACvB;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,yDAAyD,QAAQ,OAAO;EACjF,OAAO,CAAC,oCAAoC;EAC7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-integrity.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/state-integrity.ts"],"sourcesContent":["import { existsSync, mkdirSync, chmodSync, accessSync, constants, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nfunction isWritable(dir: string): boolean {\n try {\n accessSync(dir, constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkStateIntegrity(ctx: DoctorContext): Promise<CheckResult> {\n const root = ctx.stateDir;\n const hints: string[] = [];\n\n if (!existsSync(root)) {\n if (ctx.options.fix) {\n try {\n mkdirSync(root, { recursive: true, mode: 0o700 });\n if (process.platform !== 'win32') {\n chmodSync(root, 0o700);\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'Created state directory with safe permissions.',\n hints: [root],\n fixed: true,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: `State directory missing and could not create: ${msg}`,\n hints: [root],\n };\n }\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: 'State directory does not exist.',\n hints: [`Expected: ${root}`, 'Run: xopc init', 'Or: xopc doctor --fix'],\n };\n }\n\n if (!isWritable(root)) {\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: 'State directory is not writable.',\n hints: [root],\n };\n }\n\n if (process.platform !== 'win32') {\n try {\n const mode = statSync(root).mode & 0o777;\n if (mode !== 0o700) {\n if (ctx.options.fix) {\n chmodSync(root, 0o700);\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'State directory permissions set to 700.',\n hints: [root],\n fixed: true,\n };\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'warn',\n message: 'State directory permissions are not 700 (recommended for privacy).',\n hints: [root, 'Run: xopc doctor --fix'],\n };\n }\n } catch {\n /* ignore stat errors */\n }\n }\n\n const agentsDir = join(root, 'agents');\n if (!existsSync(agentsDir) && ctx.options.fix) {\n try {\n mkdirSync(agentsDir, { recursive: true, mode: 0o700 });\n hints.push(`Created: ${agentsDir}`);\n } catch {\n /* best-effort */\n }\n }\n\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'State directory exists and is usable.',\n hints: hints.length ? hints : [root],\n };\n}\n"],"mappings":";;;AAKA,SAAS,WAAW,KAAsB;AACxC,KAAI;AACF,aAAW,KAAK,UAAU,KAAK;AAC/B,SAAO;SACD;AACN,SAAO;;;AAIX,eAAsB,oBAAoB,KAA0C;CAClF,MAAM,OAAO,IAAI;CACjB,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,MAAI,IAAI,QAAQ,IACd,KAAI;AACF,aAAU,MAAM;IAAE,WAAW;IAAM,MAAM;IAAO,CAAC;AACjD,OAAI,QAAQ,aAAa,QACvB,WAAU,MAAM,IAAM;AAExB,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,KAAK;IACb,OAAO;IACR;WACM,GAAG;AAEV,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS,iDALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IAMpD,OAAO,CAAC,KAAK;IACd;;AAGL,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO;IAAC,aAAa;IAAQ;
|
|
1
|
+
{"version":3,"file":"state-integrity.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/state-integrity.ts"],"sourcesContent":["import { existsSync, mkdirSync, chmodSync, accessSync, constants, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nfunction isWritable(dir: string): boolean {\n try {\n accessSync(dir, constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkStateIntegrity(ctx: DoctorContext): Promise<CheckResult> {\n const root = ctx.stateDir;\n const hints: string[] = [];\n\n if (!existsSync(root)) {\n if (ctx.options.fix) {\n try {\n mkdirSync(root, { recursive: true, mode: 0o700 });\n if (process.platform !== 'win32') {\n chmodSync(root, 0o700);\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'Created state directory with safe permissions.',\n hints: [root],\n fixed: true,\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: `State directory missing and could not create: ${msg}`,\n hints: [root],\n };\n }\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: 'State directory does not exist.',\n hints: [`Expected: ${root}`, 'Run: xopc init (or xopc setup)', 'Or: xopc doctor --fix'],\n };\n }\n\n if (!isWritable(root)) {\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'fail',\n message: 'State directory is not writable.',\n hints: [root],\n };\n }\n\n if (process.platform !== 'win32') {\n try {\n const mode = statSync(root).mode & 0o777;\n if (mode !== 0o700) {\n if (ctx.options.fix) {\n chmodSync(root, 0o700);\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'State directory permissions set to 700.',\n hints: [root],\n fixed: true,\n };\n }\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'warn',\n message: 'State directory permissions are not 700 (recommended for privacy).',\n hints: [root, 'Run: xopc doctor --fix'],\n };\n }\n } catch {\n /* ignore stat errors */\n }\n }\n\n const agentsDir = join(root, 'agents');\n if (!existsSync(agentsDir) && ctx.options.fix) {\n try {\n mkdirSync(agentsDir, { recursive: true, mode: 0o700 });\n hints.push(`Created: ${agentsDir}`);\n } catch {\n /* best-effort */\n }\n }\n\n return {\n id: 'state-integrity',\n label: 'State directory',\n status: 'pass',\n message: 'State directory exists and is usable.',\n hints: hints.length ? hints : [root],\n };\n}\n"],"mappings":";;;AAKA,SAAS,WAAW,KAAsB;AACxC,KAAI;AACF,aAAW,KAAK,UAAU,KAAK;AAC/B,SAAO;SACD;AACN,SAAO;;;AAIX,eAAsB,oBAAoB,KAA0C;CAClF,MAAM,OAAO,IAAI;CACjB,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,MAAI,IAAI,QAAQ,IACd,KAAI;AACF,aAAU,MAAM;IAAE,WAAW;IAAM,MAAM;IAAO,CAAC;AACjD,OAAI,QAAQ,aAAa,QACvB,WAAU,MAAM,IAAM;AAExB,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,KAAK;IACb,OAAO;IACR;WACM,GAAG;AAEV,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS,iDALC,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IAMpD,OAAO,CAAC,KAAK;IACd;;AAGL,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO;IAAC,aAAa;IAAQ;IAAkC;IAAwB;GACxF;;AAGH,KAAI,CAAC,WAAW,KAAK,CACnB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,KAAK;EACd;AAGH,KAAI,QAAQ,aAAa,QACvB,KAAI;AAEF,OADa,SAAS,KAAK,CAAC,OAAO,SACtB,KAAO;AAClB,OAAI,IAAI,QAAQ,KAAK;AACnB,cAAU,MAAM,IAAM;AACtB,WAAO;KACL,IAAI;KACJ,OAAO;KACP,QAAQ;KACR,SAAS;KACT,OAAO,CAAC,KAAK;KACb,OAAO;KACR;;AAEH,UAAO;IACL,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO,CAAC,MAAM,yBAAyB;IACxC;;SAEG;CAKV,MAAM,YAAY,KAAK,MAAM,SAAS;AACtC,KAAI,CAAC,WAAW,UAAU,IAAI,IAAI,QAAQ,IACxC,KAAI;AACF,YAAU,WAAW;GAAE,WAAW;GAAM,MAAM;GAAO,CAAC;AACtD,QAAM,KAAK,YAAY,YAAY;SAC7B;AAKV,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,MAAM,SAAS,QAAQ,CAAC,KAAK;EACrC"}
|
|
@@ -37,14 +37,14 @@ async function checkWorkspaceStatus(ctx) {
|
|
|
37
37
|
label: "Workspace",
|
|
38
38
|
status: "warn",
|
|
39
39
|
message: "Agent workspace directory is missing.",
|
|
40
|
-
hints: [root, "Run: xopc onboard"]
|
|
40
|
+
hints: [root, "Run: xopc init or xopc onboard"]
|
|
41
41
|
};
|
|
42
42
|
if (!existsSync(profileRoot)) return {
|
|
43
43
|
id: "workspace-status",
|
|
44
44
|
label: "Workspace",
|
|
45
45
|
status: "warn",
|
|
46
46
|
message: "Agent profile directory is missing.",
|
|
47
|
-
hints: [profileRoot, "Run: xopc onboard"]
|
|
47
|
+
hints: [profileRoot, "Run: xopc init or xopc onboard"]
|
|
48
48
|
};
|
|
49
49
|
const missing = [];
|
|
50
50
|
if (!hasProfileFile(WORKSPACE_FILES.SOUL)) missing.push(WORKSPACE_FILES.SOUL);
|
|
@@ -57,7 +57,7 @@ async function checkWorkspaceStatus(ctx) {
|
|
|
57
57
|
hints: [
|
|
58
58
|
profileRoot,
|
|
59
59
|
root,
|
|
60
|
-
"Run: xopc onboard"
|
|
60
|
+
"Run: xopc init or xopc onboard"
|
|
61
61
|
]
|
|
62
62
|
};
|
|
63
63
|
if (!hasProfileFile(WORKSPACE_FILES.USER)) hints.push(`${WORKSPACE_FILES.USER} is optional; add a user profile for better context.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-status.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/workspace-status.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveDefaultAgentId, resolveAgentProfileDir, resolveAgentWorkspaceDir } from '../../../../agent/agent-scope.js';\nimport { loadConfig } from '../../../../config/loader.js';\nimport { WORKSPACE_FILES } from '../../../../config/paths.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkWorkspaceStatus(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let config;\n try {\n config = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const agentId = resolveDefaultAgentId(config);\n const root = resolveAgentWorkspaceDir(config, agentId);\n const profileRoot = resolveAgentProfileDir(config, agentId);\n const hints: string[] = [];\n\n const hasProfileFile = (name: string): boolean => existsSync(join(profileRoot, name));\n\n if (!existsSync(root)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: 'Agent workspace directory is missing.',\n hints: [root, 'Run: xopc onboard'],\n };\n }\n\n if (!existsSync(profileRoot)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: 'Agent profile directory is missing.',\n hints: [profileRoot, 'Run: xopc onboard'],\n };\n }\n\n const missing: string[] = [];\n if (!hasProfileFile(WORKSPACE_FILES.SOUL)) missing.push(WORKSPACE_FILES.SOUL);\n if (!hasProfileFile(WORKSPACE_FILES.IDENTITY)) missing.push(WORKSPACE_FILES.IDENTITY);\n\n if (missing.length > 0) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: `Essential profile files missing: ${missing.join(', ')}.`,\n hints: [profileRoot, root, 'Run: xopc onboard'],\n };\n }\n\n if (!hasProfileFile(WORKSPACE_FILES.USER)) {\n hints.push(`${WORKSPACE_FILES.USER} is optional; add a user profile for better context.`);\n }\n if (!hasProfileFile(WORKSPACE_FILES.TOOLS)) {\n hints.push(`${WORKSPACE_FILES.TOOLS} is optional; add tool notes if you use many tools.`);\n }\n\n if (!existsSync(join(root, '.git'))) {\n hints.push('No .git in workspace; version control is recommended for backup and history.');\n }\n\n if (hints.length > 0) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'pass',\n message: 'Markdown workspace and essential profile files are present.',\n hints,\n };\n }\n\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'pass',\n message: 'Markdown workspace and essential profile files look good.',\n hints: [profileRoot, root],\n };\n}\n"],"mappings":";;;;;;kBAG2H;aACjE;YACI;AAG9D,eAAsB,qBAAqB,KAA0C;AACnF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,WAAS,WAAW,IAAI,WAAW;SAC7B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,UAAU,sBAAsB,OAAO;CAC7C,MAAM,OAAO,yBAAyB,QAAQ,QAAQ;CACtD,MAAM,cAAc,uBAAuB,QAAQ,QAAQ;CAC3D,MAAM,QAAkB,EAAE;CAE1B,MAAM,kBAAkB,SAA0B,WAAW,KAAK,aAAa,KAAK,CAAC;AAErF,KAAI,CAAC,WAAW,KAAK,CACnB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"workspace-status.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/workspace-status.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveDefaultAgentId, resolveAgentProfileDir, resolveAgentWorkspaceDir } from '../../../../agent/agent-scope.js';\nimport { loadConfig } from '../../../../config/loader.js';\nimport { WORKSPACE_FILES } from '../../../../config/paths.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\nexport async function checkWorkspaceStatus(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let config;\n try {\n config = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const agentId = resolveDefaultAgentId(config);\n const root = resolveAgentWorkspaceDir(config, agentId);\n const profileRoot = resolveAgentProfileDir(config, agentId);\n const hints: string[] = [];\n\n const hasProfileFile = (name: string): boolean => existsSync(join(profileRoot, name));\n\n if (!existsSync(root)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: 'Agent workspace directory is missing.',\n hints: [root, 'Run: xopc init or xopc onboard'],\n };\n }\n\n if (!existsSync(profileRoot)) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: 'Agent profile directory is missing.',\n hints: [profileRoot, 'Run: xopc init or xopc onboard'],\n };\n }\n\n const missing: string[] = [];\n if (!hasProfileFile(WORKSPACE_FILES.SOUL)) missing.push(WORKSPACE_FILES.SOUL);\n if (!hasProfileFile(WORKSPACE_FILES.IDENTITY)) missing.push(WORKSPACE_FILES.IDENTITY);\n\n if (missing.length > 0) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'warn',\n message: `Essential profile files missing: ${missing.join(', ')}.`,\n hints: [profileRoot, root, 'Run: xopc init or xopc onboard'],\n };\n }\n\n if (!hasProfileFile(WORKSPACE_FILES.USER)) {\n hints.push(`${WORKSPACE_FILES.USER} is optional; add a user profile for better context.`);\n }\n if (!hasProfileFile(WORKSPACE_FILES.TOOLS)) {\n hints.push(`${WORKSPACE_FILES.TOOLS} is optional; add tool notes if you use many tools.`);\n }\n\n if (!existsSync(join(root, '.git'))) {\n hints.push('No .git in workspace; version control is recommended for backup and history.');\n }\n\n if (hints.length > 0) {\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'pass',\n message: 'Markdown workspace and essential profile files are present.',\n hints,\n };\n }\n\n return {\n id: 'workspace-status',\n label: 'Workspace',\n status: 'pass',\n message: 'Markdown workspace and essential profile files look good.',\n hints: [profileRoot, root],\n };\n}\n"],"mappings":";;;;;;kBAG2H;aACjE;YACI;AAG9D,eAAsB,qBAAqB,KAA0C;AACnF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,WAAS,WAAW,IAAI,WAAW;SAC7B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,UAAU,sBAAsB,OAAO;CAC7C,MAAM,OAAO,yBAAyB,QAAQ,QAAQ;CACtD,MAAM,cAAc,uBAAuB,QAAQ,QAAQ;CAC3D,MAAM,QAAkB,EAAE;CAE1B,MAAM,kBAAkB,SAA0B,WAAW,KAAK,aAAa,KAAK,CAAC;AAErF,KAAI,CAAC,WAAW,KAAK,CACnB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,MAAM,iCAAiC;EAChD;AAGH,KAAI,CAAC,WAAW,YAAY,CAC1B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,CAAC,aAAa,iCAAiC;EACvD;CAGH,MAAM,UAAoB,EAAE;AAC5B,KAAI,CAAC,eAAe,gBAAgB,KAAK,CAAE,SAAQ,KAAK,gBAAgB,KAAK;AAC7E,KAAI,CAAC,eAAe,gBAAgB,SAAS,CAAE,SAAQ,KAAK,gBAAgB,SAAS;AAErF,KAAI,QAAQ,SAAS,EACnB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS,oCAAoC,QAAQ,KAAK,KAAK,CAAC;EAChE,OAAO;GAAC;GAAa;GAAM;GAAiC;EAC7D;AAGH,KAAI,CAAC,eAAe,gBAAgB,KAAK,CACvC,OAAM,KAAK,GAAG,gBAAgB,KAAK,sDAAsD;AAE3F,KAAI,CAAC,eAAe,gBAAgB,MAAM,CACxC,OAAM,KAAK,GAAG,gBAAgB,MAAM,qDAAqD;AAG3F,KAAI,CAAC,WAAW,KAAK,MAAM,OAAO,CAAC,CACjC,OAAM,KAAK,+EAA+E;AAG5F,KAAI,MAAM,SAAS,EACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT;EACD;AAGH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACN,SAAS;EACT,OAAO,CAAC,aAAa,KAAK;EAC7B"}
|
|
@@ -6,4 +6,4 @@ export { createProbeCommand } from './probe.js';
|
|
|
6
6
|
export { createStopCommand } from './stop.js';
|
|
7
7
|
export { createRestartCommand } from './restart.js';
|
|
8
8
|
export { createLogsCommand } from './logs.js';
|
|
9
|
-
export { createInstallCommand, createUninstallCommand, createServiceStartCommand, createServiceStatusCommand, } from './service.js';
|
|
9
|
+
export { createInstallCommand, createUninstallCommand, createServiceStartCommand, createServiceStatusCommand, createServiceCommand, } from './service.js';
|
|
@@ -6,5 +6,5 @@ import { createProbeCommand } from "./probe.js";
|
|
|
6
6
|
import { createStopCommand } from "./stop.js";
|
|
7
7
|
import { createRestartCommand } from "./restart.js";
|
|
8
8
|
import { createLogsCommand } from "./logs.js";
|
|
9
|
-
import { createInstallCommand, createServiceStartCommand, createServiceStatusCommand, createUninstallCommand } from "./service.js";
|
|
10
|
-
export { createCallCommand, createHealthCommand, createInstallCommand, createLogsCommand, createProbeCommand, createRestartCommand, createServiceStartCommand, createServiceStatusCommand, createStatusCommand, createStopCommand, createTokenCommand, createUninstallCommand };
|
|
9
|
+
import { createInstallCommand, createServiceCommand, createServiceStartCommand, createServiceStatusCommand, createUninstallCommand } from "./service.js";
|
|
10
|
+
export { createCallCommand, createHealthCommand, createInstallCommand, createLogsCommand, createProbeCommand, createRestartCommand, createServiceCommand, createServiceStartCommand, createServiceStatusCommand, createStatusCommand, createStopCommand, createTokenCommand, createUninstallCommand };
|
|
@@ -15,3 +15,7 @@ export declare function createServiceStartCommand(): Command;
|
|
|
15
15
|
* Create service status subcommand
|
|
16
16
|
*/
|
|
17
17
|
export declare function createServiceStatusCommand(): Command;
|
|
18
|
+
/**
|
|
19
|
+
* Gateway OS service command group (`xopc gateway service …`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function createServiceCommand(): Command;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { init_paths, resolveConfigPath } from "../../../config/paths.js";
|
|
2
2
|
import { getContextWithOpts } from "../../context.js";
|
|
3
|
+
import { createStopCommand } from "./stop.js";
|
|
4
|
+
import { createRestartCommand } from "./restart.js";
|
|
3
5
|
import { Command } from "commander";
|
|
4
6
|
//#region src/cli/commands/gateway/service.ts
|
|
5
7
|
init_paths();
|
|
@@ -122,7 +124,7 @@ function createServiceStartCommand() {
|
|
|
122
124
|
* Create service status subcommand
|
|
123
125
|
*/
|
|
124
126
|
function createServiceStatusCommand() {
|
|
125
|
-
return new Command("
|
|
127
|
+
return new Command("status").description("Show OS service status").option("--json", "Output JSON").action(async (options) => {
|
|
126
128
|
const [{ resolveGatewayService, isDaemonAvailableAsync, getPlatformName }] = await Promise.all([import("../../../daemon/service.js")]);
|
|
127
129
|
if (!await isDaemonAvailableAsync()) {
|
|
128
130
|
if (options.json) console.log(JSON.stringify({
|
|
@@ -165,7 +167,20 @@ function createServiceStatusCommand() {
|
|
|
165
167
|
}
|
|
166
168
|
});
|
|
167
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Gateway OS service command group (`xopc gateway service …`).
|
|
172
|
+
*/
|
|
173
|
+
function createServiceCommand() {
|
|
174
|
+
const cmd = new Command("service").description("Manage gateway OS service (LaunchAgent / systemd / Task)");
|
|
175
|
+
cmd.addCommand(createInstallCommand());
|
|
176
|
+
cmd.addCommand(createUninstallCommand());
|
|
177
|
+
cmd.addCommand(createServiceStartCommand());
|
|
178
|
+
cmd.addCommand(createServiceStatusCommand());
|
|
179
|
+
cmd.addCommand(createStopCommand());
|
|
180
|
+
cmd.addCommand(createRestartCommand());
|
|
181
|
+
return cmd;
|
|
182
|
+
}
|
|
168
183
|
//#endregion
|
|
169
|
-
export { createInstallCommand, createServiceStartCommand, createServiceStatusCommand, createUninstallCommand };
|
|
184
|
+
export { createInstallCommand, createServiceCommand, createServiceStartCommand, createServiceStatusCommand, createUninstallCommand };
|
|
170
185
|
|
|
171
186
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../../../../src/cli/commands/gateway/service.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { resolveConfigPath } from '../../../config/paths.js';\nimport { getContextWithOpts } from '../../context.js';\n\n/**\n * Create service install subcommand - actually installs the OS service\n */\nexport function createInstallCommand(): Command {\n return new Command('install')\n .description('Install gateway as OS service (LaunchAgent / systemd / Task)')\n .option('--port <port>', 'Gateway port')\n .option('--token <token>', 'Gateway auth token')\n .option('--force', 'Force reinstall if already installed')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const ctx = getContextWithOpts();\n const configPath = ctx.configPath || resolveConfigPath();\n\n const [{ loadConfig }, { resolveGatewayService, isDaemonAvailableAsync, getPlatformName }, { buildGatewayInstallArgs }] =\n await Promise.all([\n import('../../../config/index.js'),\n import('../../../daemon/service.js'),\n import('../../../daemon/install-plan.js'),\n ]);\n\n const config = loadConfig(configPath);\n const port = options.port ? parseInt(options.port, 10) : (config?.gateway?.port || 18790);\n const bind = config?.gateway?.bind ?? 'loopback';\n const token = options.token || config?.gateway?.auth?.token;\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n if (options.json) {\n console.log(JSON.stringify({ ok: false, error: 'Daemon not available' }));\n } else {\n console.error(`❌ OS service not available on ${getPlatformName()}`);\n }\n process.exit(1);\n }\n\n const service = await resolveGatewayService();\n\n // Check if already installed\n const loaded = await service.isLoaded({ env: process.env });\n if (loaded && !options.force) {\n if (options.json) {\n console.log(JSON.stringify({ ok: true, result: 'already-installed' }));\n } else {\n console.log('ℹ️ Service already installed. Use --force to reinstall.');\n }\n return;\n }\n\n // Uninstall first if force\n if (loaded && options.force) {\n try {\n await service.uninstall({ env: process.env });\n } catch {\n // Best-effort\n }\n }\n\n // Build install args and install\n const installArgs = buildGatewayInstallArgs({ port, bind, token });\n installArgs.stdout = process.stdout;\n\n try {\n await service.install(installArgs);\n\n if (options.json) {\n console.log(JSON.stringify({ ok: true, result: 'installed', label: service.label, port }));\n } else {\n console.log('');\n console.log(`✅ Gateway service installed`);\n console.log(` Service: ${service.label}`);\n console.log(` Port: ${port}`);\n console.log('');\n console.log('💡 The service will start automatically on login.');\n console.log(' Use `xopc gateway status` to verify.');\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (options.json) {\n console.log(JSON.stringify({ ok: false, error: message }));\n } else {\n console.error(`❌ Failed to install service: ${message}`);\n }\n process.exit(1);\n }\n });\n}\n\n/**\n * Create service uninstall subcommand\n */\nexport function createUninstallCommand(): Command {\n return new Command('uninstall')\n .description('Uninstall gateway OS service')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const { executeDaemonUninstall } = await import('./lifecycle-core.js');\n await executeDaemonUninstall(options);\n });\n}\n\n/**\n * Create service start subcommand - starts via daemon service manager\n */\nexport function createServiceStartCommand(): Command {\n return new Command('start')\n .description('Start gateway via OS service manager')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const [{ resolveGatewayService, startGatewayService, isDaemonAvailableAsync }] =\n await Promise.all([import('../../../daemon/service.js')]);\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n console.error('❌ Daemon not available on this platform');\n process.exit(1);\n }\n\n const service = await resolveGatewayService();\n const result = await startGatewayService({ service });\n\n if (options.json) {\n console.log(JSON.stringify({ ok: result.outcome === 'started', outcome: result.outcome }));\n return;\n }\n\n switch (result.outcome) {\n case 'started':\n console.log('✅ Gateway service started');\n break;\n case 'missing-install':\n console.error('❌ Service not installed. Run: xopc gateway service install');\n process.exit(1);\n break;\n case 'repair-required':\n console.error('⚠️ Service needs repair:');\n for (const issue of result.issues || []) {\n console.error(` - ${issue.message}`);\n }\n console.log('💡 Run: xopc gateway service install --force');\n process.exit(1);\n break;\n default:\n console.log(`ℹ️ Service start outcome: ${result.outcome}`);\n }\n });\n}\n\n/**\n * Create service status subcommand\n */\nexport function createServiceStatusCommand(): Command {\n return new Command('service-status')\n .description('Show OS service status')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const [{ resolveGatewayService, isDaemonAvailableAsync, getPlatformName }] =\n await Promise.all([import('../../../daemon/service.js')]);\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n if (options.json) {\n console.log(JSON.stringify({ available: false, platform: process.platform }));\n } else {\n console.log(`ℹ️ Daemon not available on ${getPlatformName()}`);\n }\n return;\n }\n\n const service = await resolveGatewayService();\n const [loaded, runtime, command] = await Promise.all([\n service.isLoaded({ env: process.env }),\n service.readRuntime(),\n service.readCommand(),\n ]);\n\n if (options.json) {\n console.log(JSON.stringify({ available: true, loaded, runtime, command, label: service.label }, null, 2));\n return;\n }\n\n console.log('📊 Gateway Service Status');\n console.log('');\n console.log(` Platform: ${getPlatformName()}`);\n console.log(` Service: ${loaded ? service.loadedText : service.notLoadedText}`);\n console.log(` Runtime: ${runtime.status}${runtime.pid ? ` (pid ${runtime.pid})` : ''}`);\n\n if (command) {\n const portMatch = command.programArguments.join(' ').match(/--port\\s+(\\d+)/);\n if (portMatch) {\n console.log(` Port: ${portMatch[1]}`);\n }\n const version = command.environment?.XOPC_SERVICE_VERSION;\n if (version) {\n console.log(` Version: ${version}`);\n }\n }\n\n if (!loaded) {\n console.log('');\n console.log('💡 Install with: xopc gateway service install');\n }\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../../../../src/cli/commands/gateway/service.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { resolveConfigPath } from '../../../config/paths.js';\nimport { getContextWithOpts } from '../../context.js';\nimport { createRestartCommand } from './restart.js';\nimport { createStopCommand } from './stop.js';\n\n/**\n * Create service install subcommand - actually installs the OS service\n */\nexport function createInstallCommand(): Command {\n return new Command('install')\n .description('Install gateway as OS service (LaunchAgent / systemd / Task)')\n .option('--port <port>', 'Gateway port')\n .option('--token <token>', 'Gateway auth token')\n .option('--force', 'Force reinstall if already installed')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const ctx = getContextWithOpts();\n const configPath = ctx.configPath || resolveConfigPath();\n\n const [{ loadConfig }, { resolveGatewayService, isDaemonAvailableAsync, getPlatformName }, { buildGatewayInstallArgs }] =\n await Promise.all([\n import('../../../config/index.js'),\n import('../../../daemon/service.js'),\n import('../../../daemon/install-plan.js'),\n ]);\n\n const config = loadConfig(configPath);\n const port = options.port ? parseInt(options.port, 10) : (config?.gateway?.port || 18790);\n const bind = config?.gateway?.bind ?? 'loopback';\n const token = options.token || config?.gateway?.auth?.token;\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n if (options.json) {\n console.log(JSON.stringify({ ok: false, error: 'Daemon not available' }));\n } else {\n console.error(`❌ OS service not available on ${getPlatformName()}`);\n }\n process.exit(1);\n }\n\n const service = await resolveGatewayService();\n\n // Check if already installed\n const loaded = await service.isLoaded({ env: process.env });\n if (loaded && !options.force) {\n if (options.json) {\n console.log(JSON.stringify({ ok: true, result: 'already-installed' }));\n } else {\n console.log('ℹ️ Service already installed. Use --force to reinstall.');\n }\n return;\n }\n\n // Uninstall first if force\n if (loaded && options.force) {\n try {\n await service.uninstall({ env: process.env });\n } catch {\n // Best-effort\n }\n }\n\n // Build install args and install\n const installArgs = buildGatewayInstallArgs({ port, bind, token });\n installArgs.stdout = process.stdout;\n\n try {\n await service.install(installArgs);\n\n if (options.json) {\n console.log(JSON.stringify({ ok: true, result: 'installed', label: service.label, port }));\n } else {\n console.log('');\n console.log(`✅ Gateway service installed`);\n console.log(` Service: ${service.label}`);\n console.log(` Port: ${port}`);\n console.log('');\n console.log('💡 The service will start automatically on login.');\n console.log(' Use `xopc gateway status` to verify.');\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (options.json) {\n console.log(JSON.stringify({ ok: false, error: message }));\n } else {\n console.error(`❌ Failed to install service: ${message}`);\n }\n process.exit(1);\n }\n });\n}\n\n/**\n * Create service uninstall subcommand\n */\nexport function createUninstallCommand(): Command {\n return new Command('uninstall')\n .description('Uninstall gateway OS service')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const { executeDaemonUninstall } = await import('./lifecycle-core.js');\n await executeDaemonUninstall(options);\n });\n}\n\n/**\n * Create service start subcommand - starts via daemon service manager\n */\nexport function createServiceStartCommand(): Command {\n return new Command('start')\n .description('Start gateway via OS service manager')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const [{ resolveGatewayService, startGatewayService, isDaemonAvailableAsync }] =\n await Promise.all([import('../../../daemon/service.js')]);\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n console.error('❌ Daemon not available on this platform');\n process.exit(1);\n }\n\n const service = await resolveGatewayService();\n const result = await startGatewayService({ service });\n\n if (options.json) {\n console.log(JSON.stringify({ ok: result.outcome === 'started', outcome: result.outcome }));\n return;\n }\n\n switch (result.outcome) {\n case 'started':\n console.log('✅ Gateway service started');\n break;\n case 'missing-install':\n console.error('❌ Service not installed. Run: xopc gateway service install');\n process.exit(1);\n break;\n case 'repair-required':\n console.error('⚠️ Service needs repair:');\n for (const issue of result.issues || []) {\n console.error(` - ${issue.message}`);\n }\n console.log('💡 Run: xopc gateway service install --force');\n process.exit(1);\n break;\n default:\n console.log(`ℹ️ Service start outcome: ${result.outcome}`);\n }\n });\n}\n\n/**\n * Create service status subcommand\n */\nexport function createServiceStatusCommand(): Command {\n return new Command('status')\n .description('Show OS service status')\n .option('--json', 'Output JSON')\n .action(async (options) => {\n const [{ resolveGatewayService, isDaemonAvailableAsync, getPlatformName }] =\n await Promise.all([import('../../../daemon/service.js')]);\n\n const available = await isDaemonAvailableAsync();\n if (!available) {\n if (options.json) {\n console.log(JSON.stringify({ available: false, platform: process.platform }));\n } else {\n console.log(`ℹ️ Daemon not available on ${getPlatformName()}`);\n }\n return;\n }\n\n const service = await resolveGatewayService();\n const [loaded, runtime, command] = await Promise.all([\n service.isLoaded({ env: process.env }),\n service.readRuntime(),\n service.readCommand(),\n ]);\n\n if (options.json) {\n console.log(JSON.stringify({ available: true, loaded, runtime, command, label: service.label }, null, 2));\n return;\n }\n\n console.log('📊 Gateway Service Status');\n console.log('');\n console.log(` Platform: ${getPlatformName()}`);\n console.log(` Service: ${loaded ? service.loadedText : service.notLoadedText}`);\n console.log(` Runtime: ${runtime.status}${runtime.pid ? ` (pid ${runtime.pid})` : ''}`);\n\n if (command) {\n const portMatch = command.programArguments.join(' ').match(/--port\\s+(\\d+)/);\n if (portMatch) {\n console.log(` Port: ${portMatch[1]}`);\n }\n const version = command.environment?.XOPC_SERVICE_VERSION;\n if (version) {\n console.log(` Version: ${version}`);\n }\n }\n\n if (!loaded) {\n console.log('');\n console.log('💡 Install with: xopc gateway service install');\n }\n });\n}\n\n/**\n * Gateway OS service command group (`xopc gateway service …`).\n */\nexport function createServiceCommand(): Command {\n const cmd = new Command('service').description(\n 'Manage gateway OS service (LaunchAgent / systemd / Task)',\n );\n cmd.addCommand(createInstallCommand());\n cmd.addCommand(createUninstallCommand());\n cmd.addCommand(createServiceStartCommand());\n cmd.addCommand(createServiceStatusCommand());\n cmd.addCommand(createStopCommand());\n cmd.addCommand(createRestartCommand());\n return cmd;\n}\n"],"mappings":"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"}
|