@moda-ai/cli 1.1.1 → 1.2.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 +36 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.js +477 -0
- package/dist/analyst-tui.d.ts +81 -0
- package/dist/analyst-tui.js +314 -0
- package/dist/api-client.d.ts +4 -0
- package/dist/api-client.js +20 -3
- package/dist/auth.d.ts +22 -3
- package/dist/auth.js +230 -66
- package/dist/cli.d.ts +20 -3
- package/dist/cli.js +768 -93
- package/dist/command-context.d.ts +34 -0
- package/dist/command-context.js +38 -0
- package/dist/command-registry.d.ts +22 -0
- package/dist/command-registry.js +15 -0
- package/dist/config.d.ts +40 -1
- package/dist/config.js +70 -3
- package/dist/doctor.d.ts +79 -0
- package/dist/doctor.js +1102 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +9 -0
- package/dist/harness-report.d.ts +138 -0
- package/dist/harness-report.js +1464 -0
- package/dist/harness.d.ts +233 -0
- package/dist/harness.js +3264 -0
- package/dist/init/index.d.ts +30 -1
- package/dist/init/index.js +383 -23
- package/dist/init/rules.d.ts +5 -1
- package/dist/init/rules.js +22 -16
- package/dist/init/sdk-guide.d.ts +3 -0
- package/dist/init/sdk-guide.js +397 -0
- package/dist/init/tenant.js +12 -3
- package/dist/keys.d.ts +47 -0
- package/dist/keys.js +136 -0
- package/dist/output.d.ts +118 -0
- package/dist/output.js +228 -0
- package/dist/production-intelligence.d.ts +134 -0
- package/dist/production-intelligence.js +1113 -0
- package/dist/profile-commands.d.ts +10 -0
- package/dist/profile-commands.js +252 -0
- package/dist/profiles.d.ts +90 -0
- package/dist/profiles.js +432 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.js +368 -0
- package/dist/schemas.d.ts +4 -4
- package/dist/skills.d.ts +25 -0
- package/dist/skills.js +468 -0
- package/dist/sync.d.ts +5 -0
- package/dist/sync.js +27 -0
- package/dist/telemetry.d.ts +18 -0
- package/dist/telemetry.js +63 -0
- package/dist/tui.d.ts +40 -0
- package/dist/tui.js +387 -0
- package/dist/workspace.d.ts +12 -0
- package/dist/workspace.js +1294 -0
- package/hooks/moda-snapshot.sh +41 -0
- package/package.json +10 -7
- package/skills/moda-cli/SKILL.md +48 -4
- package/skills/integrate-moda/SKILL.md +0 -63
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CommandContext } from './command-context.js';
|
|
2
|
+
type CommandResult = {
|
|
3
|
+
exitCode: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function runAuthCommand(context: CommandContext): Promise<CommandResult>;
|
|
6
|
+
export declare function runProfilesCommand(context: CommandContext): Promise<CommandResult>;
|
|
7
|
+
export declare function runConfigCommand(context: CommandContext): Promise<CommandResult>;
|
|
8
|
+
export declare function runKeysCommand(context: CommandContext): Promise<CommandResult>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=profile-commands.d.ts.map
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { authFetch, clearAuthSession, loadAuthSession, login } from './auth.js';
|
|
2
|
+
import { KEY_LIFECYCLE_ENDPOINT_REQUIREMENTS, createLocalKeyDescriptor, loadLocalKeyDescriptors, upsertLocalKeyDescriptor, writeLocalApiKeySecret, } from './keys.js';
|
|
3
|
+
import { DEFAULT_INGEST_URL, PROD_BASE_URL, ensureSecretFilePermissions, getActiveProfile, getModaPaths, loadProfileConfig, resolveApiKeyFromActiveProfile, resolveSecretReference, secretFilePathFromRef, setActiveProfile, upsertProfile, } from './profiles.js';
|
|
4
|
+
import { CliInputError } from './errors.js';
|
|
5
|
+
function subcommand(context) {
|
|
6
|
+
return context.positionals[0];
|
|
7
|
+
}
|
|
8
|
+
function requirePositional(context, index, usage) {
|
|
9
|
+
const value = context.positionals[index];
|
|
10
|
+
if (!value)
|
|
11
|
+
throw new CliInputError('Missing required argument.', usage);
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function flag(context, name) {
|
|
15
|
+
const value = context.flags[name];
|
|
16
|
+
return value && value !== 'true' ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
function boolFlag(context, name) {
|
|
19
|
+
return context.flags[name] === 'true';
|
|
20
|
+
}
|
|
21
|
+
function normalizeEnvironment(value) {
|
|
22
|
+
if (value === 'local' || value === 'dev' || value === 'staging' || value === 'prod') {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
return 'prod';
|
|
26
|
+
}
|
|
27
|
+
function defaultBaseUrl(environment) {
|
|
28
|
+
if (environment === 'local')
|
|
29
|
+
return 'http://localhost:3000';
|
|
30
|
+
if (environment === 'staging')
|
|
31
|
+
return 'https://staging.moda.dev';
|
|
32
|
+
return PROD_BASE_URL;
|
|
33
|
+
}
|
|
34
|
+
function defaultIngestUrl(environment) {
|
|
35
|
+
if (environment === 'local')
|
|
36
|
+
return 'http://localhost:8787';
|
|
37
|
+
return DEFAULT_INGEST_URL;
|
|
38
|
+
}
|
|
39
|
+
function redactSecret(value) {
|
|
40
|
+
if (!value)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (value.length <= 12)
|
|
43
|
+
return '<redacted>';
|
|
44
|
+
return `${value.slice(0, 8)}...${value.slice(-4)}`;
|
|
45
|
+
}
|
|
46
|
+
function serializeProfile(profile, activeProfile) {
|
|
47
|
+
return {
|
|
48
|
+
name: profile.name,
|
|
49
|
+
active: profile.name === activeProfile,
|
|
50
|
+
environment: profile.environment,
|
|
51
|
+
base_url: profile.base_url,
|
|
52
|
+
ingest_url: profile.ingest_url,
|
|
53
|
+
tenant_id: profile.tenant_id,
|
|
54
|
+
workspace_id: profile.workspace_id,
|
|
55
|
+
api_key_ref: profile.api_key_ref,
|
|
56
|
+
auth_token_ref: profile.auth_token_ref,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export async function runAuthCommand(context) {
|
|
60
|
+
switch (subcommand(context)) {
|
|
61
|
+
case 'login': {
|
|
62
|
+
const screen = flag(context, 'screen') === 'signup' ? 'signup' : 'login';
|
|
63
|
+
await login(screen);
|
|
64
|
+
context.output.writeData({
|
|
65
|
+
status: 'authenticated',
|
|
66
|
+
profile: context.profile ?? process.env.MODA_PROFILE ?? null,
|
|
67
|
+
});
|
|
68
|
+
return { exitCode: 0 };
|
|
69
|
+
}
|
|
70
|
+
case 'logout': {
|
|
71
|
+
clearAuthSession({ profile: context.profile, env: context.env });
|
|
72
|
+
context.output.writeData({
|
|
73
|
+
status: 'logged_out',
|
|
74
|
+
profile: context.profile ?? process.env.MODA_PROFILE ?? null,
|
|
75
|
+
});
|
|
76
|
+
return { exitCode: 0 };
|
|
77
|
+
}
|
|
78
|
+
case 'whoami': {
|
|
79
|
+
const session = loadAuthSession({ profile: context.profile, env: context.env });
|
|
80
|
+
if (!session) {
|
|
81
|
+
throw new CliInputError('Not authenticated.', 'Run "moda auth login" or "moda init".');
|
|
82
|
+
}
|
|
83
|
+
const response = await authFetch('/api/cli/context', {}, { profile: context.profile, env: context.env });
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
const body = await response.text();
|
|
86
|
+
throw new Error(`Failed to fetch auth context (${response.status}): ${body}`);
|
|
87
|
+
}
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
context.output.writeData({
|
|
90
|
+
profile: context.profile ?? process.env.MODA_PROFILE ?? null,
|
|
91
|
+
session: {
|
|
92
|
+
expires_at: session.expires_at,
|
|
93
|
+
token_type: session.token_type ?? 'bearer',
|
|
94
|
+
},
|
|
95
|
+
context: data,
|
|
96
|
+
});
|
|
97
|
+
return { exitCode: 0 };
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
throw new CliInputError('Unknown auth command.', 'Usage: moda auth <login|logout|whoami>');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export async function runProfilesCommand(context) {
|
|
104
|
+
switch (subcommand(context)) {
|
|
105
|
+
case 'list': {
|
|
106
|
+
const config = loadProfileConfig({ env: context.env });
|
|
107
|
+
context.output.writeData({
|
|
108
|
+
active_profile: config.active_profile,
|
|
109
|
+
profiles: Object.values(config.profiles).map((profile) => serializeProfile(profile, config.active_profile)),
|
|
110
|
+
migrations: config.migrations,
|
|
111
|
+
});
|
|
112
|
+
return { exitCode: 0 };
|
|
113
|
+
}
|
|
114
|
+
case 'use': {
|
|
115
|
+
const name = requirePositional(context, 1, 'Usage: moda profiles use <name>');
|
|
116
|
+
const config = setActiveProfile(name, { env: context.env });
|
|
117
|
+
context.output.writeData({
|
|
118
|
+
active_profile: config.active_profile,
|
|
119
|
+
profile: serializeProfile(config.profiles[name], config.active_profile),
|
|
120
|
+
});
|
|
121
|
+
return { exitCode: 0 };
|
|
122
|
+
}
|
|
123
|
+
case 'create': {
|
|
124
|
+
const name = requirePositional(context, 1, 'Usage: moda profiles create <name>');
|
|
125
|
+
const environment = normalizeEnvironment(flag(context, 'environment') ?? name);
|
|
126
|
+
const profile = {
|
|
127
|
+
name,
|
|
128
|
+
environment,
|
|
129
|
+
base_url: flag(context, 'base-url') ?? defaultBaseUrl(environment),
|
|
130
|
+
ingest_url: flag(context, 'ingest-url') ?? defaultIngestUrl(environment),
|
|
131
|
+
tenant_id: flag(context, 'tenant-id'),
|
|
132
|
+
workspace_id: flag(context, 'workspace-id'),
|
|
133
|
+
api_key_ref: flag(context, 'api-key-ref'),
|
|
134
|
+
metadata: {
|
|
135
|
+
created_at: new Date().toISOString(),
|
|
136
|
+
source: 'profiles.create',
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const config = upsertProfile(profile, { env: context.env });
|
|
140
|
+
const next = boolFlag(context, 'use')
|
|
141
|
+
? setActiveProfile(name, { env: context.env })
|
|
142
|
+
: config;
|
|
143
|
+
context.output.writeData({
|
|
144
|
+
active_profile: next.active_profile,
|
|
145
|
+
profile: serializeProfile(next.profiles[name], next.active_profile),
|
|
146
|
+
});
|
|
147
|
+
return { exitCode: 0 };
|
|
148
|
+
}
|
|
149
|
+
case 'doctor': {
|
|
150
|
+
const requested = context.positionals[1];
|
|
151
|
+
const config = loadProfileConfig({ env: context.env });
|
|
152
|
+
const activeName = requested ?? context.profile ?? config.active_profile;
|
|
153
|
+
const profile = config.profiles[activeName];
|
|
154
|
+
if (!profile)
|
|
155
|
+
throw new CliInputError(`Moda profile '${activeName}' does not exist.`);
|
|
156
|
+
const secretPath = profile.api_key_ref ? secretFilePathFromRef(profile.api_key_ref) : undefined;
|
|
157
|
+
const permission = secretPath ? ensureSecretFilePermissions(secretPath) : undefined;
|
|
158
|
+
context.output.writeData({
|
|
159
|
+
profile: serializeProfile(profile, config.active_profile),
|
|
160
|
+
checks: [
|
|
161
|
+
{ id: 'profile_exists', status: 'pass' },
|
|
162
|
+
{
|
|
163
|
+
id: 'api_key_available',
|
|
164
|
+
status: resolveSecretReference(profile.api_key_ref) ? 'pass' : 'warning',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'tenant_selected',
|
|
168
|
+
status: profile.tenant_id ? 'pass' : 'warning',
|
|
169
|
+
},
|
|
170
|
+
...(permission
|
|
171
|
+
? [{
|
|
172
|
+
id: 'secret_file_permissions',
|
|
173
|
+
status: permission.secure ? 'pass' : 'fail',
|
|
174
|
+
details: permission,
|
|
175
|
+
}]
|
|
176
|
+
: []),
|
|
177
|
+
],
|
|
178
|
+
});
|
|
179
|
+
return { exitCode: 0 };
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
throw new CliInputError('Unknown profiles command.', 'Usage: moda profiles <list|use|create|doctor>');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export async function runConfigCommand(context) {
|
|
186
|
+
if (subcommand(context) !== 'show') {
|
|
187
|
+
throw new CliInputError('Unknown config command.', 'Usage: moda config show');
|
|
188
|
+
}
|
|
189
|
+
const config = loadProfileConfig({ env: context.env });
|
|
190
|
+
const profile = getActiveProfile({ profile: context.profile, env: context.env });
|
|
191
|
+
context.output.writeData({
|
|
192
|
+
moda_dir: getModaPaths({ env: context.env }).dir,
|
|
193
|
+
active_profile: profile.name,
|
|
194
|
+
profile: serializeProfile(profile, config.active_profile),
|
|
195
|
+
resolved: {
|
|
196
|
+
api_key: redactSecret(resolveApiKeyFromActiveProfile({ profile: profile.name, env: context.env })),
|
|
197
|
+
base_url: profile.base_url,
|
|
198
|
+
ingest_url: profile.ingest_url,
|
|
199
|
+
tenant_id: profile.tenant_id,
|
|
200
|
+
workspace_id: profile.workspace_id,
|
|
201
|
+
},
|
|
202
|
+
migrations: config.migrations,
|
|
203
|
+
});
|
|
204
|
+
return { exitCode: 0 };
|
|
205
|
+
}
|
|
206
|
+
export async function runKeysCommand(context) {
|
|
207
|
+
switch (subcommand(context)) {
|
|
208
|
+
case 'list': {
|
|
209
|
+
const profile = context.profile ?? loadProfileConfig({ env: context.env }).active_profile;
|
|
210
|
+
const keys = loadLocalKeyDescriptors({ env: context.env })
|
|
211
|
+
.filter((key) => !profile || key.profile === profile);
|
|
212
|
+
context.output.writeData({
|
|
213
|
+
profile,
|
|
214
|
+
keys,
|
|
215
|
+
endpoint_requirements: KEY_LIFECYCLE_ENDPOINT_REQUIREMENTS,
|
|
216
|
+
});
|
|
217
|
+
return { exitCode: 0 };
|
|
218
|
+
}
|
|
219
|
+
case 'create': {
|
|
220
|
+
const active = getActiveProfile({ profile: context.profile, env: context.env });
|
|
221
|
+
const name = flag(context, 'name') ?? `${active.name}-${Date.now()}`;
|
|
222
|
+
const descriptor = createLocalKeyDescriptor({
|
|
223
|
+
profile: active.name,
|
|
224
|
+
environment: active.environment,
|
|
225
|
+
name,
|
|
226
|
+
purpose: 'data_api',
|
|
227
|
+
tenant_id: active.tenant_id,
|
|
228
|
+
workspace_id: active.workspace_id,
|
|
229
|
+
});
|
|
230
|
+
const value = flag(context, 'value');
|
|
231
|
+
const secretPath = value
|
|
232
|
+
? writeLocalApiKeySecret(descriptor, value, { env: context.env })
|
|
233
|
+
: undefined;
|
|
234
|
+
upsertLocalKeyDescriptor(descriptor, { env: context.env });
|
|
235
|
+
context.output.writeData({
|
|
236
|
+
key: descriptor,
|
|
237
|
+
secret_path: secretPath,
|
|
238
|
+
endpoint_requirements: KEY_LIFECYCLE_ENDPOINT_REQUIREMENTS.create,
|
|
239
|
+
});
|
|
240
|
+
return { exitCode: 0 };
|
|
241
|
+
}
|
|
242
|
+
case 'rotate':
|
|
243
|
+
case 'revoke': {
|
|
244
|
+
const action = subcommand(context);
|
|
245
|
+
const keyId = requirePositional(context, 1, `Usage: moda keys ${action} <key_id>`);
|
|
246
|
+
throw new CliInputError(`moda keys ${action} is waiting on Moda Cloud key lifecycle support for ${keyId}.`, `Required endpoint: ${KEY_LIFECYCLE_ENDPOINT_REQUIREMENTS[action]}`);
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
throw new CliInputError('Unknown keys command.', 'Usage: moda keys <list|create|rotate|revoke>');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=profile-commands.js.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const PROFILE_CONFIG_VERSION = 1;
|
|
2
|
+
export declare const PROD_BASE_URL = "https://www.moda.dev";
|
|
3
|
+
export declare const DEFAULT_INGEST_URL = "https://moda-ingest.modas.workers.dev";
|
|
4
|
+
export declare const DEFAULT_PROFILE_NAME = "prod";
|
|
5
|
+
export type ModaEnvironment = 'local' | 'dev' | 'staging' | 'prod';
|
|
6
|
+
export type SecretReference = `env:${string}` | `file:${string}` | 'legacy:config.api_key' | 'legacy:auth.access_token';
|
|
7
|
+
export interface LegacyCliConfig {
|
|
8
|
+
api_key?: string;
|
|
9
|
+
ingest_url?: string;
|
|
10
|
+
tenant_id?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ModaProfile {
|
|
13
|
+
name: string;
|
|
14
|
+
environment: ModaEnvironment;
|
|
15
|
+
base_url: string;
|
|
16
|
+
ingest_url?: string;
|
|
17
|
+
tenant_id?: string;
|
|
18
|
+
workspace_id?: string;
|
|
19
|
+
api_key_ref?: SecretReference;
|
|
20
|
+
auth_token_ref?: SecretReference;
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface ProfileMigrationState {
|
|
24
|
+
legacy_config: {
|
|
25
|
+
path: string;
|
|
26
|
+
present: boolean;
|
|
27
|
+
has_api_key: boolean;
|
|
28
|
+
has_ingest_url: boolean;
|
|
29
|
+
has_tenant_id: boolean;
|
|
30
|
+
};
|
|
31
|
+
legacy_auth: {
|
|
32
|
+
path: string;
|
|
33
|
+
present: boolean;
|
|
34
|
+
has_access_token: boolean;
|
|
35
|
+
expires_at?: number;
|
|
36
|
+
expired?: boolean;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface ProfileConfig {
|
|
40
|
+
version: typeof PROFILE_CONFIG_VERSION;
|
|
41
|
+
active_profile: string;
|
|
42
|
+
profiles: Record<string, ModaProfile>;
|
|
43
|
+
migrations: ProfileMigrationState;
|
|
44
|
+
}
|
|
45
|
+
export interface ProfileIOOptions {
|
|
46
|
+
home?: string;
|
|
47
|
+
profile?: string;
|
|
48
|
+
now?: Date;
|
|
49
|
+
env?: NodeJS.ProcessEnv;
|
|
50
|
+
}
|
|
51
|
+
export interface ModaPaths {
|
|
52
|
+
home: string;
|
|
53
|
+
dir: string;
|
|
54
|
+
legacyConfigPath: string;
|
|
55
|
+
legacyAuthPath: string;
|
|
56
|
+
profileConfigPath: string;
|
|
57
|
+
secretsDir: string;
|
|
58
|
+
}
|
|
59
|
+
export interface SecretFilePermissionStatus {
|
|
60
|
+
path: string;
|
|
61
|
+
exists: boolean;
|
|
62
|
+
secure: boolean;
|
|
63
|
+
repaired: boolean;
|
|
64
|
+
mode?: string;
|
|
65
|
+
reason?: string;
|
|
66
|
+
}
|
|
67
|
+
export declare function resolveModaHome(options?: ProfileIOOptions): string;
|
|
68
|
+
export declare function getModaPaths(options?: ProfileIOOptions): ModaPaths;
|
|
69
|
+
export declare function getModaDir(options?: ProfileIOOptions): string;
|
|
70
|
+
export declare function getLegacyConfigPath(options?: ProfileIOOptions): string;
|
|
71
|
+
export declare function loadLegacyCliConfig(options?: ProfileIOOptions): LegacyCliConfig;
|
|
72
|
+
export declare function writeLegacyCliConfig(update: LegacyCliConfig, options?: ProfileIOOptions): LegacyCliConfig;
|
|
73
|
+
export declare function createDefaultProfiles(options?: ProfileIOOptions): Record<string, ModaProfile>;
|
|
74
|
+
export declare function assertValidProfileName(name: string): void;
|
|
75
|
+
export declare function inspectProfileMigrationState(options?: ProfileIOOptions): ProfileMigrationState;
|
|
76
|
+
export declare function loadProfileConfig(options?: ProfileIOOptions): ProfileConfig;
|
|
77
|
+
export declare function saveProfileConfig(config: ProfileConfig, options?: ProfileIOOptions): void;
|
|
78
|
+
export declare function getActiveProfile(options?: ProfileIOOptions): ModaProfile;
|
|
79
|
+
export declare function setActiveProfile(name: string, options?: ProfileIOOptions): ProfileConfig;
|
|
80
|
+
export declare function upsertProfile(profile: ModaProfile, options?: ProfileIOOptions): ProfileConfig;
|
|
81
|
+
export declare function syncLegacyConfigToProfile(legacy: LegacyCliConfig, options?: ProfileIOOptions): ProfileConfig;
|
|
82
|
+
export declare function secretFilePathFromRef(reference: SecretReference, options?: ProfileIOOptions): string | undefined;
|
|
83
|
+
export declare function resolveSecretReference(reference: SecretReference | undefined, options?: ProfileIOOptions): string | undefined;
|
|
84
|
+
export declare function resolveApiKeyFromActiveProfile(options?: ProfileIOOptions): string | undefined;
|
|
85
|
+
export declare function resolveBaseUrl(options?: ProfileIOOptions): string;
|
|
86
|
+
export declare function resolveIngestUrlFromActiveProfile(options?: ProfileIOOptions): string | undefined;
|
|
87
|
+
export declare function resolveTenantIdFromActiveProfile(options?: ProfileIOOptions): string | undefined;
|
|
88
|
+
export declare function ensureSecretFilePermissions(path: string, repair?: boolean): SecretFilePermissionStatus;
|
|
89
|
+
export declare function writeSecretJsonFile(path: string, value: unknown): void;
|
|
90
|
+
//# sourceMappingURL=profiles.d.ts.map
|