@moneysiren/cli 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/apps/cli/src/cli.d.ts +56 -0
- package/dist/apps/cli/src/cli.js +182 -0
- package/dist/apps/cli/src/commands/dashboard.d.ts +3 -0
- package/dist/apps/cli/src/commands/dashboard.js +239 -0
- package/dist/apps/cli/src/commands/doctor.d.ts +3 -0
- package/dist/apps/cli/src/commands/doctor.js +25 -0
- package/dist/apps/cli/src/commands/init.d.ts +3 -0
- package/dist/apps/cli/src/commands/init.js +18 -0
- package/dist/apps/cli/src/commands/install.d.ts +3 -0
- package/dist/apps/cli/src/commands/install.js +116 -0
- package/dist/apps/cli/src/commands/modes.d.ts +3 -0
- package/dist/apps/cli/src/commands/modes.js +65 -0
- package/dist/apps/cli/src/commands/notify.d.ts +3 -0
- package/dist/apps/cli/src/commands/notify.js +430 -0
- package/dist/apps/cli/src/commands/report.d.ts +3 -0
- package/dist/apps/cli/src/commands/report.js +206 -0
- package/dist/apps/cli/src/commands/runtime.d.ts +5 -0
- package/dist/apps/cli/src/commands/runtime.js +133 -0
- package/dist/apps/cli/src/commands/shared.d.ts +9 -0
- package/dist/apps/cli/src/commands/shared.js +29 -0
- package/dist/apps/cli/src/commands/summary.d.ts +3 -0
- package/dist/apps/cli/src/commands/summary.js +15 -0
- package/dist/apps/cli/src/commands/sync.d.ts +3 -0
- package/dist/apps/cli/src/commands/sync.js +393 -0
- package/dist/apps/cli/src/commands/theme.d.ts +3 -0
- package/dist/apps/cli/src/commands/theme.js +181 -0
- package/dist/apps/cli/src/home.d.ts +7 -0
- package/dist/apps/cli/src/home.js +97 -0
- package/dist/apps/cli/src/index.d.ts +3 -0
- package/dist/apps/cli/src/index.js +14 -0
- package/dist/apps/cli/src/install-profile.d.ts +35 -0
- package/dist/apps/cli/src/install-profile.js +124 -0
- package/dist/apps/cli/src/install-selector.d.ts +10 -0
- package/dist/apps/cli/src/install-selector.js +66 -0
- package/dist/apps/cli/src/interactive.d.ts +3 -0
- package/dist/apps/cli/src/interactive.js +32 -0
- package/dist/apps/cli/src/postinstall.d.ts +3 -0
- package/dist/apps/cli/src/postinstall.js +42 -0
- package/dist/apps/cli/src/runtime-adapter.d.ts +24 -0
- package/dist/apps/cli/src/runtime-adapter.js +185 -0
- package/dist/apps/cli/src/slash.d.ts +15 -0
- package/dist/apps/cli/src/slash.js +202 -0
- package/dist/apps/cli/src/summary-model.d.ts +51 -0
- package/dist/apps/cli/src/summary-model.js +136 -0
- package/dist/apps/cli/src/theme.d.ts +18 -0
- package/dist/apps/cli/src/theme.js +118 -0
- package/dist/packages/config/src/index.d.ts +3 -0
- package/dist/packages/config/src/index.js +3 -0
- package/dist/packages/config/src/load.d.ts +3 -0
- package/dist/packages/config/src/load.js +77 -0
- package/dist/packages/config/src/schema.d.ts +46 -0
- package/dist/packages/config/src/schema.js +25 -0
- package/dist/packages/connectors/aws/src/cost-explorer.d.ts +34 -0
- package/dist/packages/connectors/aws/src/cost-explorer.js +43 -0
- package/dist/packages/connectors/aws/src/index.d.ts +35 -0
- package/dist/packages/connectors/aws/src/index.js +67 -0
- package/dist/packages/connectors/aws/src/normalize.d.ts +69 -0
- package/dist/packages/connectors/aws/src/normalize.js +141 -0
- package/dist/packages/connectors/aws/src/sdk-client.d.ts +6 -0
- package/dist/packages/connectors/aws/src/sdk-client.js +21 -0
- package/dist/packages/connectors/cloudflare/src/client.d.ts +23 -0
- package/dist/packages/connectors/cloudflare/src/client.js +107 -0
- package/dist/packages/connectors/cloudflare/src/index.d.ts +33 -0
- package/dist/packages/connectors/cloudflare/src/index.js +81 -0
- package/dist/packages/connectors/cloudflare/src/normalize.d.ts +113 -0
- package/dist/packages/connectors/cloudflare/src/normalize.js +288 -0
- package/dist/packages/connectors/mock/src/index.d.ts +58 -0
- package/dist/packages/connectors/mock/src/index.js +66 -0
- package/dist/packages/connectors/openai/src/index.d.ts +55 -0
- package/dist/packages/connectors/openai/src/index.js +169 -0
- package/dist/packages/connectors/openai/src/normalize.d.ts +91 -0
- package/dist/packages/connectors/openai/src/normalize.js +180 -0
- package/dist/packages/connectors/supabase/src/client.d.ts +22 -0
- package/dist/packages/connectors/supabase/src/client.js +132 -0
- package/dist/packages/connectors/supabase/src/index.d.ts +33 -0
- package/dist/packages/connectors/supabase/src/index.js +87 -0
- package/dist/packages/connectors/supabase/src/normalize.d.ts +106 -0
- package/dist/packages/connectors/supabase/src/normalize.js +266 -0
- package/dist/packages/core/src/collector.d.ts +12 -0
- package/dist/packages/core/src/collector.js +68 -0
- package/dist/packages/core/src/index.d.ts +5 -0
- package/dist/packages/core/src/index.js +4 -0
- package/dist/packages/core/src/provider.d.ts +18 -0
- package/dist/packages/core/src/provider.js +2 -0
- package/dist/packages/core/src/risk-engine.d.ts +9 -0
- package/dist/packages/core/src/risk-engine.js +4 -0
- package/dist/packages/core/src/snapshots.d.ts +49 -0
- package/dist/packages/core/src/snapshots.js +9 -0
- package/dist/packages/db/src/client.d.ts +11 -0
- package/dist/packages/db/src/client.js +14 -0
- package/dist/packages/db/src/index.d.ts +6 -0
- package/dist/packages/db/src/index.js +6 -0
- package/dist/packages/db/src/local-store.d.ts +161 -0
- package/dist/packages/db/src/local-store.js +623 -0
- package/dist/packages/db/src/migrate.d.ts +17 -0
- package/dist/packages/db/src/migrate.js +35 -0
- package/dist/packages/db/src/schema.d.ts +5 -0
- package/dist/packages/db/src/schema.js +120 -0
- package/dist/packages/db/src/sqlite-bin.d.ts +3 -0
- package/dist/packages/db/src/sqlite-bin.js +16 -0
- package/dist/packages/local-api/src/index.d.ts +2 -0
- package/dist/packages/local-api/src/index.js +2 -0
- package/dist/packages/local-api/src/server.d.ts +36 -0
- package/dist/packages/local-api/src/server.js +310 -0
- package/dist/packages/report/src/daily.d.ts +24 -0
- package/dist/packages/report/src/daily.js +9 -0
- package/dist/packages/report/src/index.d.ts +4 -0
- package/dist/packages/report/src/index.js +4 -0
- package/dist/packages/report/src/korean.d.ts +3 -0
- package/dist/packages/report/src/korean.js +62 -0
- package/dist/packages/report/src/slack.d.ts +34 -0
- package/dist/packages/report/src/slack.js +134 -0
- package/dist/packages/runtime/src/index.d.ts +2 -0
- package/dist/packages/runtime/src/index.js +2 -0
- package/dist/packages/runtime/src/runtime.d.ts +26 -0
- package/dist/packages/runtime/src/runtime.js +182 -0
- package/dist/packages/view-model/src/index.d.ts +3 -0
- package/dist/packages/view-model/src/index.js +3 -0
- package/dist/packages/view-model/src/notification-preferences-model.d.ts +47 -0
- package/dist/packages/view-model/src/notification-preferences-model.js +218 -0
- package/dist/packages/view-model/src/notification-preferences.d.ts +6 -0
- package/dist/packages/view-model/src/notification-preferences.js +36 -0
- package/dist/packages/view-model/src/view-model.d.ts +193 -0
- package/dist/packages/view-model/src/view-model.js +684 -0
- package/package.json +49 -0
- package/scripts/postinstall.mjs +11 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCli } from "./cli.js";
|
|
3
|
+
const result = await runCli(process.argv.slice(2), {
|
|
4
|
+
cwd: process.cwd(),
|
|
5
|
+
env: process.env,
|
|
6
|
+
stdin: process.stdin,
|
|
7
|
+
output: process.stdout,
|
|
8
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
9
|
+
stdoutIsTTY: process.stdout.isTTY,
|
|
10
|
+
stdout: (line) => console.log(line),
|
|
11
|
+
stderr: (line) => console.error(line),
|
|
12
|
+
});
|
|
13
|
+
process.exitCode = result.exitCode;
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const INSTALL_SURFACES: readonly ["cli", "web", "hud"];
|
|
2
|
+
export declare const DEFAULT_INSTALL_SURFACES: readonly InstallSurface[];
|
|
3
|
+
export type InstallSurface = (typeof INSTALL_SURFACES)[number];
|
|
4
|
+
export type InstallProfileSource = "cli" | "postinstall";
|
|
5
|
+
export interface InstallProfile {
|
|
6
|
+
version: 1;
|
|
7
|
+
selectedSurfaces: readonly InstallSurface[];
|
|
8
|
+
recommendedDefault: boolean;
|
|
9
|
+
localOnly: true;
|
|
10
|
+
secretsReturned: false;
|
|
11
|
+
source: InstallProfileSource;
|
|
12
|
+
installedAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
}
|
|
15
|
+
export interface InstallProfileFileOptions {
|
|
16
|
+
env?: Record<string, string | undefined>;
|
|
17
|
+
now?: () => Date;
|
|
18
|
+
path?: string;
|
|
19
|
+
platform?: NodeJS.Platform;
|
|
20
|
+
}
|
|
21
|
+
export declare function resolveInstallProfilePath(options?: InstallProfileFileOptions): string;
|
|
22
|
+
export declare function readInstallProfileFile(options?: InstallProfileFileOptions): Promise<InstallProfile | null>;
|
|
23
|
+
export declare function writeInstallProfileFile(input: {
|
|
24
|
+
selectedSurfaces: readonly InstallSurface[];
|
|
25
|
+
source: InstallProfileSource;
|
|
26
|
+
recommendedDefault?: boolean;
|
|
27
|
+
}, options?: InstallProfileFileOptions): Promise<InstallProfile>;
|
|
28
|
+
export declare function parseInstallProfile(value: unknown): InstallProfile | null;
|
|
29
|
+
export declare function parseInstallSurfaces(value: unknown): readonly InstallSurface[];
|
|
30
|
+
export declare function normalizeInstallSurfaces(values: readonly InstallSurface[]): readonly InstallSurface[];
|
|
31
|
+
export declare function isRecommendedInstallSelection(values: readonly InstallSurface[]): boolean;
|
|
32
|
+
export declare function formatInstallSurfaces(values: readonly InstallSurface[]): string;
|
|
33
|
+
export declare function installSurfaceLabel(surface: InstallSurface): string;
|
|
34
|
+
export declare function isInstallSurface(value: unknown): value is InstallSurface;
|
|
35
|
+
//# sourceMappingURL=install-profile.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, posix, win32 } from "node:path";
|
|
4
|
+
export const INSTALL_SURFACES = ["cli", "web", "hud"];
|
|
5
|
+
export const DEFAULT_INSTALL_SURFACES = INSTALL_SURFACES;
|
|
6
|
+
const INSTALL_PROFILE_ENV_KEY = "MONEYSIREN_INSTALL_PROFILE_PATH";
|
|
7
|
+
export function resolveInstallProfilePath(options = {}) {
|
|
8
|
+
if (options.path !== undefined && options.path.trim().length > 0) {
|
|
9
|
+
return resolveInstallPath(options.path, process.cwd());
|
|
10
|
+
}
|
|
11
|
+
const env = options.env ?? process.env;
|
|
12
|
+
const configuredPath = env[INSTALL_PROFILE_ENV_KEY];
|
|
13
|
+
if (configuredPath !== undefined && configuredPath.trim().length > 0) {
|
|
14
|
+
return resolveInstallPath(configuredPath, process.cwd());
|
|
15
|
+
}
|
|
16
|
+
return defaultInstallProfilePath(env, options.platform ?? process.platform);
|
|
17
|
+
}
|
|
18
|
+
export async function readInstallProfileFile(options = {}) {
|
|
19
|
+
try {
|
|
20
|
+
return parseInstallProfile(JSON.parse(await readFile(resolveInstallProfilePath(options), "utf8")));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function writeInstallProfileFile(input, options = {}) {
|
|
27
|
+
const existing = await readInstallProfileFile(options);
|
|
28
|
+
const now = (options.now ?? (() => new Date()))().toISOString();
|
|
29
|
+
const profile = {
|
|
30
|
+
version: 1,
|
|
31
|
+
selectedSurfaces: normalizeInstallSurfaces(input.selectedSurfaces),
|
|
32
|
+
recommendedDefault: input.recommendedDefault ?? isRecommendedInstallSelection(input.selectedSurfaces),
|
|
33
|
+
localOnly: true,
|
|
34
|
+
secretsReturned: false,
|
|
35
|
+
source: input.source,
|
|
36
|
+
installedAt: existing?.installedAt ?? now,
|
|
37
|
+
updatedAt: now,
|
|
38
|
+
};
|
|
39
|
+
const path = resolveInstallProfilePath(options);
|
|
40
|
+
await mkdir(dirname(path), { recursive: true });
|
|
41
|
+
await writeFile(path, `${JSON.stringify(profile, null, 2)}\n`, "utf8");
|
|
42
|
+
return profile;
|
|
43
|
+
}
|
|
44
|
+
export function parseInstallProfile(value) {
|
|
45
|
+
if (!isRecord(value) || value.version !== 1) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const selectedSurfaces = parseInstallSurfaces(value.selectedSurfaces);
|
|
49
|
+
if (selectedSurfaces.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
version: 1,
|
|
54
|
+
selectedSurfaces,
|
|
55
|
+
recommendedDefault: typeof value.recommendedDefault === "boolean"
|
|
56
|
+
? value.recommendedDefault
|
|
57
|
+
: isRecommendedInstallSelection(selectedSurfaces),
|
|
58
|
+
localOnly: true,
|
|
59
|
+
secretsReturned: false,
|
|
60
|
+
source: value.source === "postinstall" ? "postinstall" : "cli",
|
|
61
|
+
installedAt: typeof value.installedAt === "string" ? value.installedAt : "",
|
|
62
|
+
updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : "",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function parseInstallSurfaces(value) {
|
|
66
|
+
if (!Array.isArray(value)) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return normalizeInstallSurfaces(value.filter(isInstallSurface));
|
|
70
|
+
}
|
|
71
|
+
export function normalizeInstallSurfaces(values) {
|
|
72
|
+
const selected = new Set(values);
|
|
73
|
+
return INSTALL_SURFACES.filter((surface) => selected.has(surface));
|
|
74
|
+
}
|
|
75
|
+
export function isRecommendedInstallSelection(values) {
|
|
76
|
+
const selected = normalizeInstallSurfaces(values);
|
|
77
|
+
return selected.length === INSTALL_SURFACES.length &&
|
|
78
|
+
INSTALL_SURFACES.every((surface, index) => selected[index] === surface);
|
|
79
|
+
}
|
|
80
|
+
export function formatInstallSurfaces(values) {
|
|
81
|
+
return normalizeInstallSurfaces(values).map(installSurfaceLabel).join(", ");
|
|
82
|
+
}
|
|
83
|
+
export function installSurfaceLabel(surface) {
|
|
84
|
+
if (surface === "cli") {
|
|
85
|
+
return "CLI";
|
|
86
|
+
}
|
|
87
|
+
if (surface === "web") {
|
|
88
|
+
return "Web dashboard";
|
|
89
|
+
}
|
|
90
|
+
return "HUD";
|
|
91
|
+
}
|
|
92
|
+
export function isInstallSurface(value) {
|
|
93
|
+
return typeof value === "string" && INSTALL_SURFACES.includes(value);
|
|
94
|
+
}
|
|
95
|
+
function defaultInstallProfilePath(env, platform) {
|
|
96
|
+
if (platform === "darwin") {
|
|
97
|
+
return joinForPlatform(platform, resolveHomeDirectory(env), "Library", "Application Support", "MoneySiren", "install-profile.json");
|
|
98
|
+
}
|
|
99
|
+
if (platform === "win32") {
|
|
100
|
+
return joinForPlatform(platform, resolveWindowsAppDataDirectory(env), "MoneySiren", "install-profile.json");
|
|
101
|
+
}
|
|
102
|
+
const configHome = trimToNull(env.XDG_CONFIG_HOME) ?? joinForPlatform(platform, resolveHomeDirectory(env), ".config");
|
|
103
|
+
return joinForPlatform(platform, configHome, "moneysiren", "install-profile.json");
|
|
104
|
+
}
|
|
105
|
+
function resolveInstallPath(path, cwd) {
|
|
106
|
+
return isAbsolute(path) ? path : join(cwd, path);
|
|
107
|
+
}
|
|
108
|
+
function resolveWindowsAppDataDirectory(env) {
|
|
109
|
+
return trimToNull(env.APPDATA) ?? win32.join(resolveHomeDirectory(env), "AppData", "Roaming");
|
|
110
|
+
}
|
|
111
|
+
function resolveHomeDirectory(env) {
|
|
112
|
+
return trimToNull(env.HOME) ?? trimToNull(env.USERPROFILE) ?? homedir();
|
|
113
|
+
}
|
|
114
|
+
function trimToNull(value) {
|
|
115
|
+
const trimmed = value?.trim();
|
|
116
|
+
return trimmed === undefined || trimmed.length === 0 ? null : trimmed;
|
|
117
|
+
}
|
|
118
|
+
function joinForPlatform(platform, ...segments) {
|
|
119
|
+
return platform === "win32" ? win32.join(...segments) : posix.join(...segments);
|
|
120
|
+
}
|
|
121
|
+
function isRecord(value) {
|
|
122
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=install-profile.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type InstallSurface } from "./install-profile.js";
|
|
2
|
+
export interface InstallSelectionPromptOptions {
|
|
3
|
+
stdin: NodeJS.ReadableStream;
|
|
4
|
+
output: NodeJS.WritableStream;
|
|
5
|
+
}
|
|
6
|
+
export declare function promptForInstallSurfaces(options: InstallSelectionPromptOptions): Promise<readonly InstallSurface[]>;
|
|
7
|
+
export declare function parseInstallSurfaceSelection(input: string): readonly InstallSurface[] | null;
|
|
8
|
+
export declare function formatInstallSelectionLine(selectedSurfaces: readonly InstallSurface[]): string;
|
|
9
|
+
export declare function installSelectionHelp(): string;
|
|
10
|
+
//# sourceMappingURL=install-selector.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { DEFAULT_INSTALL_SURFACES, INSTALL_SURFACES, formatInstallSurfaces, installSurfaceLabel, isInstallSurface, normalizeInstallSurfaces, } from "./install-profile.js";
|
|
3
|
+
export async function promptForInstallSurfaces(options) {
|
|
4
|
+
options.output.write([
|
|
5
|
+
"MoneySiren setup",
|
|
6
|
+
"Recommended default: CLI, Web dashboard, HUD",
|
|
7
|
+
"Select components to enable after npm install:",
|
|
8
|
+
" 1. CLI",
|
|
9
|
+
" 2. Web dashboard",
|
|
10
|
+
" 3. HUD",
|
|
11
|
+
"",
|
|
12
|
+
].join("\n"));
|
|
13
|
+
options.output.write("\n");
|
|
14
|
+
const readline = createInterface({
|
|
15
|
+
input: options.stdin,
|
|
16
|
+
output: options.output,
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
const answer = await readline.question("Choose components [1,2,3] (Enter = all recommended): ");
|
|
20
|
+
const selected = parseInstallSurfaceSelection(answer);
|
|
21
|
+
if (selected === null) {
|
|
22
|
+
options.output.write("Invalid selection. Using recommended default: CLI, Web dashboard, HUD\n");
|
|
23
|
+
return DEFAULT_INSTALL_SURFACES;
|
|
24
|
+
}
|
|
25
|
+
return selected;
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
readline.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function parseInstallSurfaceSelection(input) {
|
|
32
|
+
const trimmed = input.trim().toLowerCase();
|
|
33
|
+
if (trimmed.length === 0 || trimmed === "all" || trimmed === "recommended") {
|
|
34
|
+
return DEFAULT_INSTALL_SURFACES;
|
|
35
|
+
}
|
|
36
|
+
const selected = trimmed
|
|
37
|
+
.split(/[\s,;]+/)
|
|
38
|
+
.filter((token) => token.length > 0)
|
|
39
|
+
.map(selectionTokenToSurface);
|
|
40
|
+
if (selected.some((surface) => surface === null)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const normalized = normalizeInstallSurfaces(selected.filter((surface) => surface !== null));
|
|
44
|
+
return normalized.length === 0 ? null : normalized;
|
|
45
|
+
}
|
|
46
|
+
export function formatInstallSelectionLine(selectedSurfaces) {
|
|
47
|
+
return `Selected components: ${formatInstallSurfaces(selectedSurfaces)}`;
|
|
48
|
+
}
|
|
49
|
+
export function installSelectionHelp() {
|
|
50
|
+
return INSTALL_SURFACES
|
|
51
|
+
.map((surface, index) => ` ${index + 1}. ${installSurfaceLabel(surface)}`)
|
|
52
|
+
.join("\n");
|
|
53
|
+
}
|
|
54
|
+
function selectionTokenToSurface(token) {
|
|
55
|
+
if (token === "1") {
|
|
56
|
+
return "cli";
|
|
57
|
+
}
|
|
58
|
+
if (token === "2") {
|
|
59
|
+
return "web";
|
|
60
|
+
}
|
|
61
|
+
if (token === "3") {
|
|
62
|
+
return "hud";
|
|
63
|
+
}
|
|
64
|
+
return isInstallSurface(token) ? token : null;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=install-selector.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { isSlashQuit, parseSlashInput } from "./slash.js";
|
|
3
|
+
export async function runSlashPrompt(context, execute) {
|
|
4
|
+
if (context.stdin === undefined || context.output === undefined) {
|
|
5
|
+
return 0;
|
|
6
|
+
}
|
|
7
|
+
const prompt = context.theme.command("moneysiren> ");
|
|
8
|
+
const readline = createInterface({
|
|
9
|
+
input: context.stdin,
|
|
10
|
+
output: context.output,
|
|
11
|
+
terminal: false,
|
|
12
|
+
historySize: 0,
|
|
13
|
+
});
|
|
14
|
+
context.stdout("Slash prompt ready. Type /help for commands or /quit to exit.");
|
|
15
|
+
try {
|
|
16
|
+
let lastExitCode = 0;
|
|
17
|
+
while (true) {
|
|
18
|
+
const args = parseSlashInput(await readline.question(prompt));
|
|
19
|
+
if (args.length === 0) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
lastExitCode = await execute(args);
|
|
23
|
+
if (isSlashQuit(args)) {
|
|
24
|
+
return lastExitCode;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
readline.close();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=interactive.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { DEFAULT_INSTALL_SURFACES, writeInstallProfileFile } from "./install-profile.js";
|
|
3
|
+
import { formatInstallSelectionLine, promptForInstallSurfaces } from "./install-selector.js";
|
|
4
|
+
const selectedSurfaces = await selectSurfaces();
|
|
5
|
+
try {
|
|
6
|
+
const profile = await writeInstallProfileFile({
|
|
7
|
+
selectedSurfaces,
|
|
8
|
+
source: "postinstall",
|
|
9
|
+
recommendedDefault: selectedSurfaces.length === DEFAULT_INSTALL_SURFACES.length,
|
|
10
|
+
});
|
|
11
|
+
console.log("MoneySiren setup profile saved.");
|
|
12
|
+
console.log(formatInstallSelectionLine(profile.selectedSurfaces));
|
|
13
|
+
console.log("Run `moneysiren install --status` to review or `moneysiren install` to change it.");
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.warn(`MoneySiren setup profile skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
17
|
+
}
|
|
18
|
+
async function selectSurfaces() {
|
|
19
|
+
if (isTruthy(process.env.MONEYSIREN_SKIP_POSTINSTALL)) {
|
|
20
|
+
return DEFAULT_INSTALL_SURFACES;
|
|
21
|
+
}
|
|
22
|
+
if (!shouldPrompt()) {
|
|
23
|
+
return DEFAULT_INSTALL_SURFACES;
|
|
24
|
+
}
|
|
25
|
+
return promptForInstallSurfaces({
|
|
26
|
+
stdin: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function shouldPrompt() {
|
|
31
|
+
return Boolean(process.stdin.isTTY) &&
|
|
32
|
+
Boolean(process.stdout.isTTY) &&
|
|
33
|
+
!isTruthy(process.env.CI);
|
|
34
|
+
}
|
|
35
|
+
function isTruthy(value) {
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const normalized = value.trim().toLowerCase();
|
|
40
|
+
return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=postinstall.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type LocalRuntime } from "../../../packages/runtime/src/index.js";
|
|
2
|
+
import type { CliExecutionContext } from "./cli.js";
|
|
3
|
+
export type { LocalRuntime } from "../../../packages/runtime/src/index.js";
|
|
4
|
+
export interface StartRuntimeOptions {
|
|
5
|
+
openBrowser?: boolean;
|
|
6
|
+
port?: number;
|
|
7
|
+
headless?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type StartRuntimeResult = {
|
|
10
|
+
status: "running" | "started";
|
|
11
|
+
runtime: LocalRuntime;
|
|
12
|
+
} | {
|
|
13
|
+
status: "unavailable";
|
|
14
|
+
reason: string;
|
|
15
|
+
guidance: readonly string[];
|
|
16
|
+
};
|
|
17
|
+
export interface CliLocalRuntimeAdapter {
|
|
18
|
+
findRuntime(): Promise<LocalRuntime | null>;
|
|
19
|
+
assertRuntimeHealthy(runtime: LocalRuntime): Promise<boolean>;
|
|
20
|
+
startRuntime(options: StartRuntimeOptions): Promise<StartRuntimeResult>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createFallbackLocalRuntimeAdapter(context: CliExecutionContext): CliLocalRuntimeAdapter;
|
|
23
|
+
export declare function openUrlInBrowser(url: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=runtime-adapter.d.ts.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { startLocalApiServer } from "../../../packages/local-api/src/index.js";
|
|
4
|
+
import { assertRuntimeHealthy as assertPackageRuntimeHealthy, findRuntime, } from "../../../packages/runtime/src/index.js";
|
|
5
|
+
import { readLocalStore } from "../../../packages/db/src/index.js";
|
|
6
|
+
import { loadCliConfig, resolveDbPath } from "./commands/shared.js";
|
|
7
|
+
let activeLocalApiServer = null;
|
|
8
|
+
let registeredShutdownHandlers = false;
|
|
9
|
+
export function createFallbackLocalRuntimeAdapter(context) {
|
|
10
|
+
return {
|
|
11
|
+
async findRuntime() {
|
|
12
|
+
return findRuntime({
|
|
13
|
+
cwd: context.cwd,
|
|
14
|
+
env: context.env,
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
async assertRuntimeHealthy(runtime) {
|
|
18
|
+
return assertPackageRuntimeHealthy(runtime, {
|
|
19
|
+
fetchImpl: context.fetch,
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
async startRuntime(options) {
|
|
23
|
+
const runtime = await findRuntime({
|
|
24
|
+
cwd: context.cwd,
|
|
25
|
+
env: context.env,
|
|
26
|
+
});
|
|
27
|
+
if (runtime !== null && await assertPackageRuntimeHealthy(runtime, {
|
|
28
|
+
fetchImpl: context.fetch,
|
|
29
|
+
})) {
|
|
30
|
+
return {
|
|
31
|
+
status: "running",
|
|
32
|
+
runtime,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (activeLocalApiServer !== null) {
|
|
36
|
+
return {
|
|
37
|
+
status: "running",
|
|
38
|
+
runtime: activeLocalApiServer.runtime,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const api = await startLocalApiServer({
|
|
42
|
+
...(options.port === undefined ? {} : { port: options.port }),
|
|
43
|
+
runtimeLock: {
|
|
44
|
+
cwd: context.cwd,
|
|
45
|
+
env: context.env,
|
|
46
|
+
},
|
|
47
|
+
viewModel: {
|
|
48
|
+
now: context.now,
|
|
49
|
+
readStore: () => readViewModelStore(context),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
activeLocalApiServer = api;
|
|
53
|
+
registerShutdownHandlers();
|
|
54
|
+
return {
|
|
55
|
+
status: "started",
|
|
56
|
+
runtime: api.runtime,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function openUrlInBrowser(url) {
|
|
62
|
+
const parsedUrl = new URL(url);
|
|
63
|
+
if (!isLoopbackHttpUrl(parsedUrl)) {
|
|
64
|
+
throw new Error("Refusing to open a non-loopback runtime URL.");
|
|
65
|
+
}
|
|
66
|
+
const child = process.platform === "win32"
|
|
67
|
+
? spawn("cmd", ["/c", "start", "", parsedUrl.toString()], {
|
|
68
|
+
detached: true,
|
|
69
|
+
stdio: "ignore",
|
|
70
|
+
windowsHide: true,
|
|
71
|
+
})
|
|
72
|
+
: process.platform === "darwin"
|
|
73
|
+
? spawn("open", [parsedUrl.toString()], {
|
|
74
|
+
detached: true,
|
|
75
|
+
stdio: "ignore",
|
|
76
|
+
})
|
|
77
|
+
: spawn("xdg-open", [parsedUrl.toString()], {
|
|
78
|
+
detached: true,
|
|
79
|
+
stdio: "ignore",
|
|
80
|
+
});
|
|
81
|
+
child.unref();
|
|
82
|
+
}
|
|
83
|
+
function isLoopbackHttpUrl(url) {
|
|
84
|
+
return url.protocol === "http:" &&
|
|
85
|
+
(url.hostname === "127.0.0.1" ||
|
|
86
|
+
url.hostname === "localhost" ||
|
|
87
|
+
url.hostname === "::1" ||
|
|
88
|
+
url.hostname === "[::1]");
|
|
89
|
+
}
|
|
90
|
+
async function readViewModelStore(context) {
|
|
91
|
+
const config = loadCliConfig(context.env);
|
|
92
|
+
const dbPath = resolveDbPath(context.cwd, config.dbPath);
|
|
93
|
+
if (!await pathExists(dbPath)) {
|
|
94
|
+
return emptyViewModelStore();
|
|
95
|
+
}
|
|
96
|
+
return localStoreToViewModelStore(await readLocalStore({ dbPath }));
|
|
97
|
+
}
|
|
98
|
+
function localStoreToViewModelStore(store) {
|
|
99
|
+
return {
|
|
100
|
+
providers: store.providers.map((provider) => ({
|
|
101
|
+
key: provider.key,
|
|
102
|
+
displayName: provider.displayName,
|
|
103
|
+
})),
|
|
104
|
+
usageSnapshots: store.usageSnapshots.map((snapshot) => ({
|
|
105
|
+
providerKey: snapshot.providerKey,
|
|
106
|
+
collectedAt: snapshot.collectedAt,
|
|
107
|
+
service: snapshot.service,
|
|
108
|
+
metric: snapshot.metric,
|
|
109
|
+
unit: snapshot.unit,
|
|
110
|
+
value: snapshot.value,
|
|
111
|
+
})),
|
|
112
|
+
billingSnapshots: store.billingSnapshots.map((snapshot) => ({
|
|
113
|
+
providerKey: snapshot.providerKey,
|
|
114
|
+
collectedAt: snapshot.collectedAt,
|
|
115
|
+
amountMinor: snapshot.amountMinor,
|
|
116
|
+
currency: snapshot.currency,
|
|
117
|
+
status: snapshot.status,
|
|
118
|
+
})),
|
|
119
|
+
serviceHealthSnapshots: store.serviceHealthSnapshots.map((snapshot) => ({
|
|
120
|
+
providerKey: snapshot.providerKey,
|
|
121
|
+
collectedAt: snapshot.collectedAt,
|
|
122
|
+
service: snapshot.service,
|
|
123
|
+
status: snapshot.status,
|
|
124
|
+
...(snapshot.region === undefined ? {} : { region: snapshot.region }),
|
|
125
|
+
...(snapshot.message === undefined ? {} : { message: snapshot.message }),
|
|
126
|
+
})),
|
|
127
|
+
costEstimates: store.costEstimates.map((estimate) => ({
|
|
128
|
+
providerKey: estimate.providerKey,
|
|
129
|
+
collectedAt: estimate.collectedAt,
|
|
130
|
+
estimatedAmountMinor: estimate.estimatedAmountMinor,
|
|
131
|
+
currency: estimate.currency,
|
|
132
|
+
confidence: estimate.confidence,
|
|
133
|
+
})),
|
|
134
|
+
alerts: store.alerts.map((alert) => ({
|
|
135
|
+
...(alert.providerKey === undefined ? {} : { providerKey: alert.providerKey }),
|
|
136
|
+
createdAt: alert.createdAt,
|
|
137
|
+
severity: alert.severity,
|
|
138
|
+
category: alert.category,
|
|
139
|
+
title: alert.title,
|
|
140
|
+
message: alert.message,
|
|
141
|
+
})),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function emptyViewModelStore() {
|
|
145
|
+
return {
|
|
146
|
+
providers: [],
|
|
147
|
+
usageSnapshots: [],
|
|
148
|
+
billingSnapshots: [],
|
|
149
|
+
serviceHealthSnapshots: [],
|
|
150
|
+
costEstimates: [],
|
|
151
|
+
alerts: [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async function pathExists(path) {
|
|
155
|
+
try {
|
|
156
|
+
await stat(path);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function registerShutdownHandlers() {
|
|
164
|
+
if (registeredShutdownHandlers) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
registeredShutdownHandlers = true;
|
|
168
|
+
const closeActiveServer = () => {
|
|
169
|
+
const server = activeLocalApiServer;
|
|
170
|
+
activeLocalApiServer = null;
|
|
171
|
+
if (server !== null) {
|
|
172
|
+
void server.close();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
process.once("beforeExit", closeActiveServer);
|
|
176
|
+
process.once("SIGINT", () => {
|
|
177
|
+
closeActiveServer();
|
|
178
|
+
process.exit(0);
|
|
179
|
+
});
|
|
180
|
+
process.once("SIGTERM", () => {
|
|
181
|
+
closeActiveServer();
|
|
182
|
+
process.exit(0);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=runtime-adapter.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type SlashDispatch = {
|
|
2
|
+
kind: "dispatch";
|
|
3
|
+
args: readonly string[];
|
|
4
|
+
} | {
|
|
5
|
+
kind: "quit";
|
|
6
|
+
} | {
|
|
7
|
+
kind: "error";
|
|
8
|
+
message: string;
|
|
9
|
+
usage?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function parseSlashInput(input: string): readonly string[];
|
|
12
|
+
export declare function isSlashQuit(args: readonly string[]): boolean;
|
|
13
|
+
export declare function resolveSlashCommand(args: readonly string[]): SlashDispatch;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=slash.d.ts.map
|