@matthesketh/fleet 1.8.0 → 1.11.0
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 +186 -16
- package/dist/bin/fleet-agent.d.ts +2 -0
- package/dist/bin/fleet-agent.js +7 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +73 -31
- package/dist/commands/add.d.ts +2 -1
- package/dist/commands/add.js +66 -59
- package/dist/commands/audit.d.ts +1 -0
- package/dist/commands/audit.js +144 -0
- package/dist/commands/backup.d.ts +1 -0
- package/dist/commands/backup.js +510 -0
- package/dist/commands/boot-start.d.ts +3 -1
- package/dist/commands/boot-start.js +39 -47
- package/dist/commands/completions.d.ts +6 -0
- package/dist/commands/completions.js +83 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.js +96 -0
- package/dist/commands/deploy.js +3 -2
- package/dist/commands/deps.js +5 -1
- package/dist/commands/doctor.d.ts +32 -0
- package/dist/commands/doctor.js +186 -0
- package/dist/commands/egress.d.ts +1 -1
- package/dist/commands/egress.js +13 -10
- package/dist/commands/freeze.d.ts +8 -4
- package/dist/commands/freeze.js +77 -59
- package/dist/commands/git.js +2 -2
- package/dist/commands/health.d.ts +2 -1
- package/dist/commands/health.js +38 -56
- package/dist/commands/init.d.ts +2 -1
- package/dist/commands/init.js +83 -73
- package/dist/commands/install-mcp.d.ts +3 -1
- package/dist/commands/install-mcp.js +53 -34
- package/dist/commands/list.d.ts +2 -1
- package/dist/commands/list.js +22 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/notify.d.ts +1 -0
- package/dist/commands/notify.js +51 -0
- package/dist/commands/patch-systemd.d.ts +7 -1
- package/dist/commands/patch-systemd.js +71 -31
- package/dist/commands/remove.d.ts +3 -1
- package/dist/commands/remove.js +37 -26
- package/dist/commands/restart.d.ts +4 -1
- package/dist/commands/restart.js +17 -20
- package/dist/commands/rollback.d.ts +4 -1
- package/dist/commands/rollback.js +33 -42
- package/dist/commands/secrets.js +157 -9
- package/dist/commands/start.d.ts +4 -1
- package/dist/commands/start.js +17 -20
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.js +21 -26
- package/dist/commands/stop.d.ts +4 -1
- package/dist/commands/stop.js +17 -20
- package/dist/commands/testflight.d.ts +1 -0
- package/dist/commands/testflight.js +193 -0
- package/dist/commands/update.d.ts +16 -0
- package/dist/commands/update.js +95 -0
- package/dist/core/audit/cache.d.ts +4 -0
- package/dist/core/audit/cache.js +37 -0
- package/dist/core/audit/config.d.ts +5 -0
- package/dist/core/audit/config.js +35 -0
- package/dist/core/audit/greenlight.d.ts +11 -0
- package/dist/core/audit/greenlight.js +81 -0
- package/dist/core/audit/reporters/cli.d.ts +3 -0
- package/dist/core/audit/reporters/cli.js +68 -0
- package/dist/core/audit/suppress.d.ts +6 -0
- package/dist/core/audit/suppress.js +37 -0
- package/dist/core/audit/target.d.ts +5 -0
- package/dist/core/audit/target.js +26 -0
- package/dist/core/audit/types.d.ts +54 -0
- package/dist/core/audit/types.js +5 -0
- package/dist/core/backup/browser-api.d.ts +66 -0
- package/dist/core/backup/browser-api.js +197 -0
- package/dist/core/backup/browser-server.d.ts +11 -0
- package/dist/core/backup/browser-server.js +241 -0
- package/dist/core/backup/browser-ui.d.ts +5 -0
- package/dist/core/backup/browser-ui.js +268 -0
- package/dist/core/backup/cloudflare.d.ts +7 -0
- package/dist/core/backup/cloudflare.js +82 -0
- package/dist/core/backup/config.d.ts +9 -0
- package/dist/core/backup/config.js +80 -0
- package/dist/core/backup/detect.d.ts +11 -0
- package/dist/core/backup/detect.js +71 -0
- package/dist/core/backup/dump.d.ts +11 -0
- package/dist/core/backup/dump.js +82 -0
- package/dist/core/backup/index.d.ts +9 -0
- package/dist/core/backup/index.js +9 -0
- package/dist/core/backup/repo.d.ts +71 -0
- package/dist/core/backup/repo.js +256 -0
- package/dist/core/backup/schedule.d.ts +17 -0
- package/dist/core/backup/schedule.js +90 -0
- package/dist/core/backup/sensitive.d.ts +5 -0
- package/dist/core/backup/sensitive.js +37 -0
- package/dist/core/backup/status.d.ts +3 -0
- package/dist/core/backup/status.js +29 -0
- package/dist/core/backup/statuspage.d.ts +23 -0
- package/dist/core/backup/statuspage.js +145 -0
- package/dist/core/backup/system.d.ts +24 -0
- package/dist/core/backup/system.js +209 -0
- package/dist/core/backup/totp.d.ts +16 -0
- package/dist/core/backup/totp.js +116 -0
- package/dist/core/backup/types.d.ts +70 -0
- package/dist/core/backup/types.js +7 -0
- package/dist/core/backup/unlock.d.ts +19 -0
- package/dist/core/backup/unlock.js +69 -0
- package/dist/core/boot-refresh.d.ts +1 -1
- package/dist/core/boot-refresh.js +10 -9
- package/dist/core/deps/actors/pr-creator.d.ts +5 -3
- package/dist/core/deps/actors/pr-creator.js +71 -18
- package/dist/core/deps/collectors/fetch-with-timeout.d.ts +7 -0
- package/dist/core/deps/collectors/fetch-with-timeout.js +16 -0
- package/dist/core/deps/collectors/npm.js +3 -1
- package/dist/core/deps/collectors/vulnerability.d.ts +8 -0
- package/dist/core/deps/collectors/vulnerability.js +31 -2
- package/dist/core/deps/config.js +6 -0
- package/dist/core/deps/scanner.js +1 -1
- package/dist/core/deps/types.d.ts +8 -0
- package/dist/core/env.d.ts +3 -0
- package/dist/core/env.js +11 -0
- package/dist/core/exec.d.ts +1 -0
- package/dist/core/exec.js +4 -0
- package/dist/core/file-lock.d.ts +18 -0
- package/dist/core/file-lock.js +44 -0
- package/dist/core/git-onboard.js +10 -13
- package/dist/core/github.d.ts +3 -1
- package/dist/core/github.js +10 -7
- package/dist/core/logs-policy.d.ts +5 -0
- package/dist/core/logs-policy.js +20 -1
- package/dist/core/operator.d.ts +21 -0
- package/dist/core/operator.js +54 -0
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.js +26 -0
- package/dist/core/routines/schema.d.ts +11 -11
- package/dist/core/routines/schema.js +14 -3
- package/dist/core/routines/store.d.ts +8 -8
- package/dist/core/secrets-ops.d.ts +31 -6
- package/dist/core/secrets-ops.js +208 -102
- package/dist/core/secrets-providers.js +2 -2
- package/dist/core/secrets-rotation.d.ts +1 -1
- package/dist/core/secrets-rotation.js +58 -52
- package/dist/core/secrets-v2-cleanup.d.ts +19 -0
- package/dist/core/secrets-v2-cleanup.js +94 -0
- package/dist/core/secrets-v2-creds.d.ts +9 -0
- package/dist/core/secrets-v2-creds.js +44 -0
- package/dist/core/secrets-v2-install.d.ts +13 -0
- package/dist/core/secrets-v2-install.js +76 -0
- package/dist/core/secrets-v2-keypair.d.ts +10 -0
- package/dist/core/secrets-v2-keypair.js +31 -0
- package/dist/core/secrets-v2-migrate.d.ts +29 -0
- package/dist/core/secrets-v2-migrate.js +395 -0
- package/dist/core/secrets-v2-ops.d.ts +36 -0
- package/dist/core/secrets-v2-ops.js +184 -0
- package/dist/core/secrets-v2-protocol.d.ts +19 -0
- package/dist/core/secrets-v2-protocol.js +60 -0
- package/dist/core/secrets-v2-snapshot.d.ts +36 -0
- package/dist/core/secrets-v2-snapshot.js +115 -0
- package/dist/core/secrets-v2.d.ts +21 -0
- package/dist/core/secrets-v2.js +249 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +91 -11
- package/dist/core/self-update.d.ts +32 -11
- package/dist/core/self-update.js +52 -14
- package/dist/core/testflight/asc.d.ts +12 -0
- package/dist/core/testflight/asc.js +101 -0
- package/dist/core/testflight/credentials.d.ts +3 -0
- package/dist/core/testflight/credentials.js +35 -0
- package/dist/core/testflight/eas.d.ts +4 -0
- package/dist/core/testflight/eas.js +38 -0
- package/dist/core/testflight/resolve.d.ts +6 -0
- package/dist/core/testflight/resolve.js +44 -0
- package/dist/core/testflight/types.d.ts +13 -0
- package/dist/core/testflight/types.js +3 -0
- package/dist/core/testflight/workflow.d.ts +17 -0
- package/dist/core/testflight/workflow.js +65 -0
- package/dist/core/validate.d.ts +1 -0
- package/dist/core/validate.js +8 -0
- package/dist/mcp/audit-tools.d.ts +2 -0
- package/dist/mcp/audit-tools.js +94 -0
- package/dist/mcp/git-tools.js +1 -1
- package/dist/mcp/registry-bridge.d.ts +10 -0
- package/dist/mcp/registry-bridge.js +65 -0
- package/dist/mcp/secrets-tools.js +2 -2
- package/dist/mcp/server.js +16 -82
- package/dist/mcp/testflight-tools.d.ts +2 -0
- package/dist/mcp/testflight-tools.js +52 -0
- package/dist/registry/context.d.ts +7 -0
- package/dist/registry/context.js +37 -0
- package/dist/registry/index.d.ts +5 -0
- package/dist/registry/index.js +44 -0
- package/dist/registry/parse-args.d.ts +13 -0
- package/dist/registry/parse-args.js +74 -0
- package/dist/registry/registry.d.ts +24 -0
- package/dist/registry/registry.js +26 -0
- package/dist/registry/render.d.ts +3 -0
- package/dist/registry/render.js +29 -0
- package/dist/registry/types.d.ts +50 -0
- package/dist/registry/types.js +1 -0
- package/dist/templates/agent-unit.d.ts +5 -0
- package/dist/templates/agent-unit.js +40 -0
- package/dist/templates/app-unit-edit.d.ts +2 -0
- package/dist/templates/app-unit-edit.js +46 -0
- package/dist/templates/compose-edit.d.ts +2 -0
- package/dist/templates/compose-edit.js +156 -0
- package/dist/templates/nginx.js +11 -0
- package/dist/templates/systemd.js +6 -0
- package/dist/tui/components/ArgForm.d.ts +7 -0
- package/dist/tui/components/ArgForm.js +64 -0
- package/dist/tui/components/ArgForm.test.d.ts +1 -0
- package/dist/tui/components/ArgForm.test.js +19 -0
- package/dist/tui/components/KeyHint.js +5 -0
- package/dist/tui/hooks/use-secrets.d.ts +8 -8
- package/dist/tui/hooks/use-secrets.js +7 -7
- package/dist/tui/router.d.ts +1 -0
- package/dist/tui/router.js +26 -9
- package/dist/tui/router.test.d.ts +1 -0
- package/dist/tui/router.test.js +13 -0
- package/dist/tui/routines/components/SignalsGrid.test.js +2 -2
- package/dist/tui/routines/tabs/ScaffoldTab.js +1 -1
- package/dist/tui/tests/redaction-rerender.test.d.ts +1 -0
- package/dist/tui/tests/redaction-rerender.test.js +53 -0
- package/dist/tui/tests/scroll-flicker-proof.test.d.ts +1 -0
- package/dist/tui/tests/scroll-flicker-proof.test.js +145 -0
- package/dist/tui/types.d.ts +1 -1
- package/dist/tui/views/CommandPalette.d.ts +5 -0
- package/dist/tui/views/CommandPalette.js +90 -0
- package/dist/tui/views/CommandPalette.test.d.ts +1 -0
- package/dist/tui/views/CommandPalette.test.js +117 -0
- package/dist/tui/views/Dashboard.js +10 -7
- package/dist/tui/views/HealthView.js +14 -5
- package/dist/tui/views/SecretEdit.js +15 -16
- package/dist/tui/views/SecretEdit.test.d.ts +1 -0
- package/dist/tui/views/SecretEdit.test.js +82 -0
- package/dist/tui/views/SecretsView.js +26 -16
- package/package.json +9 -6
package/dist/commands/freeze.js
CHANGED
|
@@ -1,64 +1,82 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { findApp, withRegistry } from '../core/registry.js';
|
|
2
3
|
import { stopService, disableService, enableService, startService } from '../core/systemd.js';
|
|
3
4
|
import { AppNotFoundError } from '../core/errors.js';
|
|
4
|
-
import {
|
|
5
|
-
export function freezeApp(appName, reason) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
export function unfreezeApp(appName) {
|
|
21
|
-
const reg = load();
|
|
22
|
-
const app = findApp(reg, appName);
|
|
23
|
-
if (!app)
|
|
24
|
-
throw new AppNotFoundError(appName);
|
|
25
|
-
if (!app.frozenAt) {
|
|
26
|
-
throw new Error(`App "${appName}" is not frozen`);
|
|
27
|
-
}
|
|
28
|
-
delete app.frozenAt;
|
|
29
|
-
delete app.frozenReason;
|
|
30
|
-
save(reg);
|
|
31
|
-
enableService(app.serviceName);
|
|
32
|
-
startService(app.serviceName);
|
|
5
|
+
import { defineCommand } from '../registry/registry.js';
|
|
6
|
+
export async function freezeApp(appName, reason) {
|
|
7
|
+
await withRegistry(reg => {
|
|
8
|
+
const app = findApp(reg, appName);
|
|
9
|
+
if (!app)
|
|
10
|
+
throw new AppNotFoundError(appName);
|
|
11
|
+
if (app.frozenAt) {
|
|
12
|
+
throw new Error(`App "${appName}" is already frozen (since ${app.frozenAt})`);
|
|
13
|
+
}
|
|
14
|
+
stopService(app.serviceName);
|
|
15
|
+
disableService(app.serviceName);
|
|
16
|
+
app.frozenAt = new Date().toISOString();
|
|
17
|
+
if (reason)
|
|
18
|
+
app.frozenReason = reason;
|
|
19
|
+
return reg;
|
|
20
|
+
});
|
|
33
21
|
}
|
|
34
|
-
export function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
error('Usage: fleet unfreeze <app>');
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
unfreezeApp(appName);
|
|
58
|
-
success(`Unfrozen ${appName} — service enabled and started`);
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
error(err.message);
|
|
62
|
-
process.exit(1);
|
|
22
|
+
export async function unfreezeApp(appName) {
|
|
23
|
+
let serviceName = null;
|
|
24
|
+
await withRegistry(reg => {
|
|
25
|
+
const app = findApp(reg, appName);
|
|
26
|
+
if (!app)
|
|
27
|
+
throw new AppNotFoundError(appName);
|
|
28
|
+
if (!app.frozenAt) {
|
|
29
|
+
throw new Error(`App "${appName}" is not frozen`);
|
|
30
|
+
}
|
|
31
|
+
delete app.frozenAt;
|
|
32
|
+
delete app.frozenReason;
|
|
33
|
+
serviceName = app.serviceName;
|
|
34
|
+
return reg;
|
|
35
|
+
});
|
|
36
|
+
// Service operations run AFTER the lock is released so we don't hold the
|
|
37
|
+
// registry lock while systemctl is starting things up.
|
|
38
|
+
if (serviceName) {
|
|
39
|
+
enableService(serviceName);
|
|
40
|
+
startService(serviceName);
|
|
63
41
|
}
|
|
64
42
|
}
|
|
43
|
+
export const freezeCommand = defineCommand({
|
|
44
|
+
name: 'freeze',
|
|
45
|
+
summary: 'Freeze a crash-looping service (stop + disable)',
|
|
46
|
+
args: z.object({ app: z.string(), reason: z.string().optional(), yes: z.boolean().default(false) }),
|
|
47
|
+
destructive: true,
|
|
48
|
+
async run(args, ctx) {
|
|
49
|
+
if (!args.yes && !(await ctx.confirm(`Freeze ${args.app}? This stops and disables the service.`))) {
|
|
50
|
+
return { ok: false, summary: 'cancelled', data: { app: args.app } };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await freezeApp(args.app, args.reason);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return { ok: false, summary: err instanceof Error ? err.message : String(err), data: { app: args.app } };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
summary: `froze ${args.app}${args.reason ? `: ${args.reason}` : ''}`,
|
|
61
|
+
data: { app: args.app },
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
export const unfreezeCommand = defineCommand({
|
|
66
|
+
name: 'unfreeze',
|
|
67
|
+
summary: 'Unfreeze and restart a frozen service',
|
|
68
|
+
args: z.object({ app: z.string(), yes: z.boolean().default(false) }),
|
|
69
|
+
destructive: true,
|
|
70
|
+
async run(args, ctx) {
|
|
71
|
+
if (!args.yes && !(await ctx.confirm(`Unfreeze ${args.app}? This re-enables and starts the service.`))) {
|
|
72
|
+
return { ok: false, summary: 'cancelled', data: { app: args.app } };
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
await unfreezeApp(args.app);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return { ok: false, summary: err instanceof Error ? err.message : String(err), data: { app: args.app } };
|
|
79
|
+
}
|
|
80
|
+
return { ok: true, summary: `unfroze ${args.app} — service enabled and started`, data: { app: args.app } };
|
|
81
|
+
},
|
|
82
|
+
});
|
package/dist/commands/git.js
CHANGED
|
@@ -105,7 +105,7 @@ async function gitOnboardCmd(args) {
|
|
|
105
105
|
info('cancelled');
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
|
-
const result = executeOnboard(scenario, r, app.name, app.name, status);
|
|
108
|
+
const result = await executeOnboard(scenario, r, app.name, app.name, status);
|
|
109
109
|
heading(`Onboarded: ${app.name}`);
|
|
110
110
|
result.steps.forEach(s => success(s));
|
|
111
111
|
info(`repo: ${result.repoUrl}`);
|
|
@@ -134,7 +134,7 @@ async function gitOnboardAllCmd(args) {
|
|
|
134
134
|
continue;
|
|
135
135
|
}
|
|
136
136
|
try {
|
|
137
|
-
const result = executeOnboard(scenario, r, app.name, app.name, status);
|
|
137
|
+
const result = await executeOnboard(scenario, r, app.name, app.name, status);
|
|
138
138
|
success(`${app.name}: onboarded (${result.scenario})`);
|
|
139
139
|
}
|
|
140
140
|
catch (err) {
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { type HealthResult } from '../core/health.js';
|
|
2
|
+
export declare const healthCommand: import("../registry/types.js").CommandDef<HealthResult[]>;
|
package/dist/commands/health.js
CHANGED
|
@@ -1,60 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { load, findApp } from '../core/registry.js';
|
|
2
3
|
import { checkHealth, checkAllHealth } from '../core/health.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
import { defineCommand } from '../registry/registry.js';
|
|
5
|
+
export const healthCommand = defineCommand({
|
|
6
|
+
name: 'health',
|
|
7
|
+
summary: 'Health checks: systemd + container + HTTP',
|
|
8
|
+
args: z.object({ app: z.string().optional() }),
|
|
9
|
+
tui: { view: 'health' },
|
|
10
|
+
async run(args) {
|
|
11
|
+
let results;
|
|
12
|
+
if (args.app) {
|
|
13
|
+
const app = findApp(load(), args.app);
|
|
14
|
+
if (!app) {
|
|
15
|
+
return { ok: false, summary: `app not found: ${args.app}`, data: [] };
|
|
16
|
+
}
|
|
17
|
+
results = [checkHealth(app)];
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
process.stdout.write(` Systemd: ${sIcon} ${result.systemd.state}\n`);
|
|
21
|
-
for (const ct of result.containers) {
|
|
22
|
-
const cIcon = ct.running ? icon.ok : icon.err;
|
|
23
|
-
process.stdout.write(` Container: ${cIcon} ${ct.name} (${ct.health})\n`);
|
|
19
|
+
else {
|
|
20
|
+
results = checkAllHealth(load().apps);
|
|
24
21
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const cTotal = r.containers.length;
|
|
47
|
-
const httpStatus = r.http
|
|
48
|
-
? (r.http.ok ? `${icon.ok} ${r.http.status}` : `${icon.err} fail`)
|
|
49
|
-
: `${c.dim}-${c.reset}`;
|
|
50
|
-
return [
|
|
51
|
-
`${c.bold}${r.app}${c.reset}`,
|
|
52
|
-
`${sIcon} ${r.systemd.state}`,
|
|
53
|
-
`${cOk}/${cTotal}`,
|
|
54
|
-
httpStatus,
|
|
55
|
-
`${oIcon} ${r.overall}`,
|
|
56
|
-
];
|
|
57
|
-
});
|
|
58
|
-
table(['APP', 'SYSTEMD', 'CONTAINERS', 'HTTP', 'OVERALL'], rows);
|
|
59
|
-
process.stdout.write('\n');
|
|
60
|
-
}
|
|
22
|
+
const healthy = results.filter(r => r.overall === 'healthy').length;
|
|
23
|
+
const degraded = results.filter(r => r.overall === 'degraded').length;
|
|
24
|
+
const down = results.filter(r => r.overall === 'down').length;
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
summary: `${results.length} checked | ${healthy} healthy | ${degraded} degraded | ${down} down`,
|
|
28
|
+
data: results,
|
|
29
|
+
render: {
|
|
30
|
+
kind: 'table',
|
|
31
|
+
columns: ['APP', 'SYSTEMD', 'CONTAINERS', 'HTTP', 'OVERALL'],
|
|
32
|
+
rows: results.map(r => [
|
|
33
|
+
r.app,
|
|
34
|
+
r.systemd.state,
|
|
35
|
+
`${r.containers.filter(ct => ct.running).length}/${r.containers.length}`,
|
|
36
|
+
r.http ? (r.http.ok ? String(r.http.status ?? 'ok') : 'fail') : '—',
|
|
37
|
+
r.overall,
|
|
38
|
+
]),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Registry } from '../core/registry.js';
|
|
2
|
+
export declare const initCommand: import("../registry/types.js").CommandDef<Registry>;
|
package/dist/commands/init.js
CHANGED
|
@@ -1,82 +1,92 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { withRegistry } from '../core/registry.js';
|
|
3
4
|
import { discoverServices, parseServiceFile, readServiceFile } from '../core/systemd.js';
|
|
4
5
|
import { listContainers, getContainersByCompose } from '../core/docker.js';
|
|
5
6
|
import { listSites, readConfig, extractPortFromConfig, extractDomainsFromConfig } from '../core/nginx.js';
|
|
6
|
-
import {
|
|
7
|
+
import { defineCommand } from '../registry/registry.js';
|
|
7
8
|
const SKIP_SERVICES = ['docker-databases'];
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
9
|
+
export const initCommand = defineCommand({
|
|
10
|
+
name: 'init',
|
|
11
|
+
summary: 'Auto-discover all existing apps',
|
|
12
|
+
args: z.object({}),
|
|
13
|
+
async run(_args, ctx) {
|
|
14
|
+
ctx.log({ level: 'info', message: 'fleet init — auto-discovering apps' });
|
|
15
|
+
const services = discoverServices();
|
|
16
|
+
const containers = listContainers();
|
|
17
|
+
const sites = listSites();
|
|
18
|
+
ctx.log({ level: 'info', message: `found ${services.length} compose services, ${containers.length} running containers, ${sites.length} nginx sites` });
|
|
19
|
+
let added = 0;
|
|
20
|
+
// assigned inside the withRegistry callback below, which always runs.
|
|
21
|
+
let discovered;
|
|
22
|
+
await withRegistry(reg => {
|
|
23
|
+
for (const serviceName of services) {
|
|
24
|
+
if (SKIP_SERVICES.includes(serviceName))
|
|
25
|
+
continue;
|
|
26
|
+
const content = readServiceFile(serviceName);
|
|
27
|
+
if (!content)
|
|
28
|
+
continue;
|
|
29
|
+
const parsed = parseServiceFile(content);
|
|
30
|
+
if (!parsed.workingDirectory)
|
|
31
|
+
continue;
|
|
32
|
+
const composePath = parsed.workingDirectory;
|
|
33
|
+
const composeFile = parsed.composeFile;
|
|
34
|
+
const composeContainers = getContainersByCompose(composePath, composeFile);
|
|
35
|
+
const port = detectPort(composePath, composeFile, composeContainers, containers);
|
|
36
|
+
const domains = detectDomains(serviceName, sites, port);
|
|
37
|
+
const usesSharedDb = detectSharedDb(composePath, composeFile);
|
|
38
|
+
const type = detectType(composePath, composeFile, domains);
|
|
39
|
+
const displayName = detectDisplayName(serviceName, content);
|
|
40
|
+
const app = {
|
|
41
|
+
name: serviceName,
|
|
42
|
+
displayName,
|
|
43
|
+
composePath,
|
|
44
|
+
composeFile,
|
|
45
|
+
serviceName,
|
|
46
|
+
domains,
|
|
47
|
+
port,
|
|
48
|
+
usesSharedDb,
|
|
49
|
+
type,
|
|
50
|
+
containers: composeContainers.length > 0 ? composeContainers : [serviceName],
|
|
51
|
+
dependsOnDatabases: parsed.dependsOnDatabases,
|
|
52
|
+
registeredAt: new Date().toISOString(),
|
|
53
|
+
};
|
|
54
|
+
const existing = reg.apps.findIndex(a => a.name === serviceName);
|
|
55
|
+
if (existing >= 0) {
|
|
56
|
+
const prev = reg.apps[existing];
|
|
57
|
+
if (prev.healthPath)
|
|
58
|
+
app.healthPath = prev.healthPath;
|
|
59
|
+
if (prev.gitRepo)
|
|
60
|
+
app.gitRepo = prev.gitRepo;
|
|
61
|
+
if (prev.gitRemoteUrl)
|
|
62
|
+
app.gitRemoteUrl = prev.gitRemoteUrl;
|
|
63
|
+
if (prev.gitOnboardedAt)
|
|
64
|
+
app.gitOnboardedAt = prev.gitOnboardedAt;
|
|
65
|
+
if (prev.secretsManaged)
|
|
66
|
+
app.secretsManaged = prev.secretsManaged;
|
|
67
|
+
reg.apps[existing] = app;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
reg.apps.push(app);
|
|
71
|
+
}
|
|
72
|
+
added++;
|
|
73
|
+
ctx.log({ level: 'info', message: `${serviceName} (${composePath})` });
|
|
74
|
+
}
|
|
75
|
+
discovered = reg;
|
|
76
|
+
return reg;
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
summary: `registered ${added} app${added === 1 ? '' : 's'}`,
|
|
81
|
+
data: discovered,
|
|
82
|
+
render: {
|
|
83
|
+
kind: 'table',
|
|
84
|
+
columns: ['NAME', 'PATH', 'TYPE', 'PORT'],
|
|
85
|
+
rows: discovered.apps.map(a => [a.name, a.composePath, a.type, a.port?.toString() ?? '—']),
|
|
86
|
+
},
|
|
49
87
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const prev = reg.apps[existing];
|
|
53
|
-
if (prev.healthPath)
|
|
54
|
-
app.healthPath = prev.healthPath;
|
|
55
|
-
if (prev.gitRepo)
|
|
56
|
-
app.gitRepo = prev.gitRepo;
|
|
57
|
-
if (prev.gitRemoteUrl)
|
|
58
|
-
app.gitRemoteUrl = prev.gitRemoteUrl;
|
|
59
|
-
if (prev.gitOnboardedAt)
|
|
60
|
-
app.gitOnboardedAt = prev.gitOnboardedAt;
|
|
61
|
-
if (prev.secretsManaged)
|
|
62
|
-
app.secretsManaged = prev.secretsManaged;
|
|
63
|
-
reg.apps[existing] = app;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
reg.apps.push(app);
|
|
67
|
-
}
|
|
68
|
-
added++;
|
|
69
|
-
success(`${serviceName} (${composePath})`);
|
|
70
|
-
}
|
|
71
|
-
save(reg);
|
|
72
|
-
if (json) {
|
|
73
|
-
process.stdout.write(JSON.stringify(reg, null, 2) + '\n');
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
info(`Registered ${added} apps`);
|
|
77
|
-
success('Init complete');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
80
90
|
function detectPort(composePath, composeFile, composeContainers, allContainers) {
|
|
81
91
|
const file = composeFile ?? 'docker-compose.yml';
|
|
82
92
|
const fullPath = `${composePath}/${file}`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { defineCommand } from '../registry/registry.js';
|
|
4
5
|
const FLEET_DIST = resolve(join(import.meta.dirname, '..', '..', 'dist', 'index.js'));
|
|
5
6
|
function getClaudeConfigPath() {
|
|
6
7
|
const home = process.env.HOME || process.env.USERPROFILE || '/root';
|
|
@@ -16,40 +17,58 @@ function loadConfig(path) {
|
|
|
16
17
|
return {};
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
export const installMcpCommand = defineCommand({
|
|
21
|
+
name: 'install-mcp',
|
|
22
|
+
summary: 'Install fleet as a Claude Code MCP server',
|
|
23
|
+
args: z.object({ uninstall: z.boolean().default(false) }),
|
|
24
|
+
cliOnly: true,
|
|
25
|
+
async run(args, ctx) {
|
|
26
|
+
const configPath = getClaudeConfigPath();
|
|
27
|
+
const config = loadConfig(configPath);
|
|
28
|
+
if (args.uninstall) {
|
|
29
|
+
if (config.mcpServers?.fleet) {
|
|
30
|
+
delete config.mcpServers.fleet;
|
|
31
|
+
if (Object.keys(config.mcpServers).length === 0) {
|
|
32
|
+
delete config.mcpServers;
|
|
33
|
+
}
|
|
34
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
summary: 'Removed fleet MCP server from Claude Code',
|
|
38
|
+
data: { installed: false },
|
|
39
|
+
};
|
|
28
40
|
}
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
else {
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
summary: 'fleet MCP server not configured — nothing to remove',
|
|
45
|
+
data: { installed: false },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!existsSync(FLEET_DIST)) {
|
|
50
|
+
ctx.log({ level: 'warn', message: 'dist/index.js not found — run "npm run build" first' });
|
|
51
|
+
}
|
|
52
|
+
config.mcpServers = config.mcpServers || {};
|
|
53
|
+
const existed = !!config.mcpServers.fleet;
|
|
54
|
+
config.mcpServers.fleet = {
|
|
55
|
+
command: 'node',
|
|
56
|
+
args: [FLEET_DIST, 'mcp'],
|
|
57
|
+
};
|
|
58
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
59
|
+
let summary;
|
|
60
|
+
if (existed) {
|
|
61
|
+
summary = 'Updated fleet MCP server in Claude Code';
|
|
31
62
|
}
|
|
32
63
|
else {
|
|
33
|
-
|
|
64
|
+
summary = 'Installed fleet MCP server to Claude Code';
|
|
34
65
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
args: [FLEET_DIST, 'mcp'],
|
|
45
|
-
};
|
|
46
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
47
|
-
if (existed) {
|
|
48
|
-
success('Updated fleet MCP server in Claude Code');
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
success('Installed fleet MCP server to Claude Code');
|
|
52
|
-
}
|
|
53
|
-
info(`Config: ${configPath}`);
|
|
54
|
-
info(`Server: node ${FLEET_DIST} mcp`);
|
|
55
|
-
}
|
|
66
|
+
ctx.log({ level: 'info', message: `Config: ${configPath}` });
|
|
67
|
+
ctx.log({ level: 'info', message: `Server: node ${FLEET_DIST} mcp` });
|
|
68
|
+
return {
|
|
69
|
+
ok: true,
|
|
70
|
+
summary,
|
|
71
|
+
data: { installed: true },
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { AppEntry } from '../core/registry.js';
|
|
2
|
+
export declare const listCommand: import("../registry/types.js").CommandDef<AppEntry[]>;
|
package/dist/commands/list.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { load } from '../core/registry.js';
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
3
|
+
import { defineCommand } from '../registry/registry.js';
|
|
4
|
+
export const listCommand = defineCommand({
|
|
5
|
+
name: 'list',
|
|
6
|
+
summary: 'List registered apps',
|
|
7
|
+
args: z.object({}),
|
|
8
|
+
async run() {
|
|
9
|
+
const reg = load();
|
|
10
|
+
return {
|
|
11
|
+
ok: true,
|
|
12
|
+
summary: `${reg.apps.length} app${reg.apps.length === 1 ? '' : 's'} registered`,
|
|
13
|
+
data: reg.apps,
|
|
14
|
+
render: {
|
|
15
|
+
kind: 'table',
|
|
16
|
+
columns: ['NAME', 'SERVICE', 'PORT', 'TYPE', 'DOMAINS'],
|
|
17
|
+
rows: reg.apps.map(a => [
|
|
18
|
+
a.name, a.serviceName, a.port?.toString() ?? '—', a.type, a.domains.join(', ') || '—',
|
|
19
|
+
]),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
});
|
package/dist/commands/logs.js
CHANGED
|
@@ -53,7 +53,7 @@ function logsMulti(args) {
|
|
|
53
53
|
const tail = parseInt(valOf('--tail') ?? valOf('-n') ?? '50', 10) || 50;
|
|
54
54
|
if (!all && !appsCsv && !containersCsv) {
|
|
55
55
|
error('Usage: fleet logs --all [-f] [--since 15m] [--grep err] [--level warn]');
|
|
56
|
-
error(' fleet logs --apps
|
|
56
|
+
error(' fleet logs --apps poolside,brewco [-f]');
|
|
57
57
|
error(' fleet logs --containers "*-postgres" [-f]');
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function notifyCommand(args: string[]): Promise<void>;
|