@moda-ai/cli 1.0.4 → 1.1.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/.env.example +2 -2
- package/README.md +52 -50
- package/dist/auth.js +39 -67
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +140 -14
- package/dist/config.d.ts +1 -2
- package/dist/config.js +3 -4
- package/dist/init/detect.d.ts +1 -1
- package/dist/init/detect.js +28 -5
- package/dist/init/index.js +26 -68
- package/dist/init/mcp.d.ts +1 -7
- package/dist/init/mcp.js +5 -88
- package/dist/init/rules.d.ts +1 -1
- package/dist/init/rules.js +151 -58
- package/dist/init/tenant.js +4 -42
- package/dist/schemas.d.ts +71 -0
- package/dist/schemas.js +44 -0
- package/dist/transforms.d.ts +25 -0
- package/dist/transforms.js +102 -0
- package/package.json +8 -19
- package/skills/integrate-moda/SKILL.md +63 -0
- package/skills/moda-cli/SKILL.md +323 -0
- package/dist/__tests__/api-client.test.d.ts +0 -2
- package/dist/__tests__/api-client.test.js +0 -73
- package/dist/__tests__/cli.test.d.ts +0 -2
- package/dist/__tests__/cli.test.js +0 -109
- package/dist/__tests__/e2e.test.d.ts +0 -2
- package/dist/__tests__/e2e.test.js +0 -239
- package/dist/__tests__/handlers.test.d.ts +0 -2
- package/dist/__tests__/handlers.test.js +0 -71
- package/dist/__tests__/integration.test.d.ts +0 -2
- package/dist/__tests__/integration.test.js +0 -114
- package/dist/__tests__/schemas.test.d.ts +0 -2
- package/dist/__tests__/schemas.test.js +0 -81
- package/dist/handlers.d.ts +0 -7
- package/dist/handlers.js +0 -117
- package/dist/http-server.d.ts +0 -3
- package/dist/http-server.js +0 -74
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -26
- package/dist/tools.d.ts +0 -260
- package/dist/tools.js +0 -133
- package/dist/worker.d.ts +0 -9
- package/dist/worker.js +0 -24
- package/mcp-config.example.json +0 -12
package/dist/init/index.js
CHANGED
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
import { select } from '@inquirer/prompts';
|
|
3
3
|
import { login, loadToken } from '../auth.js';
|
|
4
4
|
import { selectTenantAndCreateKey } from './tenant.js';
|
|
5
|
-
import {
|
|
6
|
-
import { detectAgents, configureMCP } from './mcp.js';
|
|
5
|
+
import { detectAgents } from './mcp.js';
|
|
7
6
|
import { installRules } from './rules.js';
|
|
8
7
|
export async function runInit(flags) {
|
|
9
8
|
const yes = flags.yes === true || flags.y === true;
|
|
10
|
-
const skipSdk = flags.skip_sdk === true;
|
|
11
|
-
const skipMcp = flags.skip_mcp === true;
|
|
12
9
|
console.log('\n Moda — LLM Observability for Agents\n');
|
|
13
10
|
// Step 1: Authentication
|
|
14
|
-
console.log(' Step 1/
|
|
11
|
+
console.log(' Step 1/3: Authentication');
|
|
15
12
|
let token = loadToken();
|
|
16
13
|
if (!token) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
let action = 'login';
|
|
15
|
+
if (!yes) {
|
|
16
|
+
action = await select({
|
|
17
|
+
message: 'Get started',
|
|
18
|
+
choices: [
|
|
19
|
+
{ name: 'Sign up', value: 'signup' },
|
|
20
|
+
{ name: 'Login', value: 'login' },
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
24
|
console.log(` Opening browser to ${action === 'signup' ? 'sign up' : 'log in'}...`);
|
|
25
25
|
token = await login(action);
|
|
26
26
|
console.log(' Authentication successful');
|
|
@@ -29,66 +29,24 @@ export async function runInit(flags) {
|
|
|
29
29
|
console.log(' Already authenticated (cached token)');
|
|
30
30
|
}
|
|
31
31
|
// Step 2: Tenant + API Key
|
|
32
|
-
console.log('\n Step 2/
|
|
33
|
-
const {
|
|
34
|
-
// Step 3: SDK
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (project.packageManager)
|
|
41
|
-
parts.push(project.packageManager);
|
|
42
|
-
if (project.llmProviders.length > 0) {
|
|
43
|
-
parts.push(project.llmProviders.join(', '));
|
|
44
|
-
}
|
|
45
|
-
console.log(` Detected: ${parts.join(' / ')}`);
|
|
46
|
-
await installSDK(project);
|
|
47
|
-
const snippet = getInitSnippet(project);
|
|
48
|
-
if (snippet) {
|
|
49
|
-
console.log('\n Add this to your entry file:');
|
|
50
|
-
console.log(snippet);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.log(' No project detected, skipping SDK installation');
|
|
55
|
-
console.log(' Install manually: npm install moda-ai (or pip install moda-ai)');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
console.log('\n Step 3/5: Install SDK (skipped)');
|
|
60
|
-
}
|
|
61
|
-
// Step 4: MCP
|
|
62
|
-
if (!skipMcp) {
|
|
63
|
-
console.log('\n Step 4/5: Configure MCP Server');
|
|
64
|
-
const agents = detectAgents();
|
|
65
|
-
if (agents.length > 0) {
|
|
66
|
-
console.log(` Detected: ${agents.map((a) => a.name).join(', ')}`);
|
|
67
|
-
const configured = await configureMCP(agents, apiKey, yes);
|
|
68
|
-
if (configured.length > 0) {
|
|
69
|
-
console.log(` Configured ${configured.length} agent(s): ${configured.join(', ')}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
console.log(' No coding agents detected');
|
|
74
|
-
console.log(' Manual setup: claude mcp add moda -- npx -y @moda-ai/cli');
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
console.log('\n Step 4/5: Configure MCP Server (skipped)');
|
|
79
|
-
}
|
|
80
|
-
// Step 5: Rules
|
|
81
|
-
console.log('\n Step 5/5: Agent Rules (optional)');
|
|
82
|
-
const agentNames = detectAgents().map((a) => a.name);
|
|
83
|
-
if (agentNames.length > 0) {
|
|
84
|
-
await installRules(agentNames, yes);
|
|
32
|
+
console.log('\n Step 2/3: Select Organization');
|
|
33
|
+
const { tenantName } = await selectTenantAndCreateKey();
|
|
34
|
+
// Step 3: Install SDK integration guide for coding agents
|
|
35
|
+
console.log('\n Step 3/3: Install SDK Guide');
|
|
36
|
+
const agents = detectAgents();
|
|
37
|
+
if (agents.length > 0) {
|
|
38
|
+
console.log(` Detected: ${agents.join(', ')}`);
|
|
39
|
+
await installRules(agents);
|
|
85
40
|
}
|
|
86
41
|
else {
|
|
87
|
-
console.log('
|
|
42
|
+
console.log(' No coding agents detected, skipping');
|
|
88
43
|
}
|
|
89
44
|
// Done
|
|
90
|
-
console.log(`\n Setup complete for ${tenantName}
|
|
91
|
-
console.log('
|
|
45
|
+
console.log(`\n Setup complete for ${tenantName}!\n`);
|
|
46
|
+
console.log(' Now paste this into your coding agent to integrate Moda:\n');
|
|
47
|
+
console.log(' Integrate the Moda SDK into this project. Follow the Moda SDK');
|
|
48
|
+
console.log(' integration guide in the project rules.\n');
|
|
49
|
+
console.log(' Dashboard: https://moda.dev');
|
|
92
50
|
console.log('');
|
|
93
51
|
}
|
|
94
52
|
//# sourceMappingURL=index.js.map
|
package/dist/init/mcp.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
name: string;
|
|
3
|
-
configPath: string;
|
|
4
|
-
configKey: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function detectAgents(): DetectedAgent[];
|
|
7
|
-
export declare function configureMCP(agents: DetectedAgent[], apiKey: string, autoYes?: boolean): Promise<string[]>;
|
|
1
|
+
export declare function detectAgents(): string[];
|
|
8
2
|
//# sourceMappingURL=mcp.d.ts.map
|
package/dist/init/mcp.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// src/init/mcp.ts —
|
|
2
|
-
import { existsSync
|
|
3
|
-
import { join
|
|
1
|
+
// src/init/mcp.ts — Coding agent detection
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import { checkbox } from '@inquirer/prompts';
|
|
6
5
|
function isOnPath(cmd) {
|
|
7
6
|
try {
|
|
8
7
|
execSync(`which ${cmd}`, { stdio: 'pipe' });
|
|
@@ -17,94 +16,12 @@ export function detectAgents() {
|
|
|
17
16
|
const agents = [];
|
|
18
17
|
// Claude Code
|
|
19
18
|
if (existsSync(join(cwd, '.claude')) || isOnPath('claude')) {
|
|
20
|
-
agents.push(
|
|
21
|
-
name: 'Claude Code',
|
|
22
|
-
configPath: join(cwd, '.claude', 'settings.local.json'),
|
|
23
|
-
configKey: 'mcpServers',
|
|
24
|
-
});
|
|
19
|
+
agents.push('Claude Code');
|
|
25
20
|
}
|
|
26
21
|
// Cursor
|
|
27
22
|
if (existsSync(join(cwd, '.cursor'))) {
|
|
28
|
-
agents.push(
|
|
29
|
-
name: 'Cursor',
|
|
30
|
-
configPath: join(cwd, '.cursor', 'mcp.json'),
|
|
31
|
-
configKey: 'mcpServers',
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
// VS Code
|
|
35
|
-
if (existsSync(join(cwd, '.vscode'))) {
|
|
36
|
-
agents.push({
|
|
37
|
-
name: 'VS Code',
|
|
38
|
-
configPath: join(cwd, '.vscode', 'mcp.json'),
|
|
39
|
-
configKey: 'servers',
|
|
40
|
-
});
|
|
23
|
+
agents.push('Cursor');
|
|
41
24
|
}
|
|
42
25
|
return agents;
|
|
43
26
|
}
|
|
44
|
-
export async function configureMCP(agents, apiKey, autoYes = false) {
|
|
45
|
-
if (agents.length === 0)
|
|
46
|
-
return [];
|
|
47
|
-
let selected;
|
|
48
|
-
if (autoYes) {
|
|
49
|
-
selected = agents;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
const choices = agents.map((a) => ({
|
|
53
|
-
name: a.name,
|
|
54
|
-
value: a,
|
|
55
|
-
checked: true,
|
|
56
|
-
}));
|
|
57
|
-
selected = await checkbox({
|
|
58
|
-
message: 'Configure MCP server for:',
|
|
59
|
-
choices,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
const configured = [];
|
|
63
|
-
for (const agent of selected) {
|
|
64
|
-
try {
|
|
65
|
-
writeAgentConfig(agent, apiKey);
|
|
66
|
-
configured.push(agent.name);
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
-
console.error(` Warning: Failed to configure ${agent.name}: ${message}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Show manual instructions for skipped agents
|
|
74
|
-
const skipped = agents.filter((a) => !selected.includes(a));
|
|
75
|
-
if (skipped.length > 0) {
|
|
76
|
-
console.log('\n Manual setup for skipped agents:');
|
|
77
|
-
for (const agent of skipped) {
|
|
78
|
-
console.log(` ${agent.name}: Add moda server to ${agent.configPath}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return configured;
|
|
82
|
-
}
|
|
83
|
-
function writeAgentConfig(agent, apiKey) {
|
|
84
|
-
// Ensure directory exists
|
|
85
|
-
const dir = dirname(agent.configPath);
|
|
86
|
-
mkdirSync(dir, { recursive: true });
|
|
87
|
-
// Read existing config
|
|
88
|
-
let config = {};
|
|
89
|
-
if (existsSync(agent.configPath)) {
|
|
90
|
-
try {
|
|
91
|
-
config = JSON.parse(readFileSync(agent.configPath, 'utf8'));
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
config = {};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// Merge moda server entry
|
|
98
|
-
const servers = config[agent.configKey] || {};
|
|
99
|
-
servers['moda'] = {
|
|
100
|
-
command: 'npx',
|
|
101
|
-
args: ['-y', '@moda-ai/cli'],
|
|
102
|
-
env: {
|
|
103
|
-
MODA_API_KEY: apiKey,
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
config[agent.configKey] = servers;
|
|
107
|
-
// Write back
|
|
108
|
-
writeFileSync(agent.configPath, JSON.stringify(config, null, 2) + '\n');
|
|
109
|
-
}
|
|
110
27
|
//# sourceMappingURL=mcp.js.map
|
package/dist/init/rules.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function installRules(detectedAgents: string[]
|
|
1
|
+
export declare function installRules(detectedAgents: string[]): Promise<void>;
|
|
2
2
|
//# sourceMappingURL=rules.d.ts.map
|
package/dist/init/rules.js
CHANGED
|
@@ -1,72 +1,165 @@
|
|
|
1
|
-
// src/init/rules.ts —
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/init/rules.ts — Install Moda agent skills into the user's project.
|
|
2
|
+
//
|
|
3
|
+
// Each entry in SKILLS is a directory under <package>/skills/ that contains a
|
|
4
|
+
// SKILL.md (Claude Code format). At install time we copy each skill into every
|
|
5
|
+
// detected coding agent, translating frontmatter where the agent expects a
|
|
6
|
+
// different shape (e.g. Cursor's .mdc rules).
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
4
10
|
import { confirm } from '@inquirer/prompts';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'
|
|
31
|
-
|
|
11
|
+
// --- Package path resolution ---
|
|
12
|
+
//
|
|
13
|
+
// Resolved relative to this module's URL so it works whether we're running
|
|
14
|
+
// from src/ in dev or from the published dist/ inside node_modules.
|
|
15
|
+
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
16
|
+
const SKILLS_SOURCE_DIR = join(PACKAGE_ROOT, 'skills');
|
|
17
|
+
const SKILLS = [
|
|
18
|
+
{
|
|
19
|
+
id: 'integrate-moda',
|
|
20
|
+
label: 'integrate-moda — instrument this project with the Moda SDK',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'moda-cli',
|
|
24
|
+
label: 'moda-cli — let coding agents query Moda analytics via the CLI',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
const ADAPTERS = {
|
|
28
|
+
'Claude Code': {
|
|
29
|
+
name: 'Claude Code',
|
|
30
|
+
targetFor: (skillId) => ({
|
|
31
|
+
dir: join('.claude', 'skills', skillId),
|
|
32
|
+
filename: 'SKILL.md',
|
|
33
|
+
}),
|
|
34
|
+
transform: (content) => content,
|
|
35
|
+
},
|
|
36
|
+
'Cursor': {
|
|
37
|
+
name: 'Cursor',
|
|
38
|
+
targetFor: (skillId) => ({
|
|
39
|
+
dir: join('.cursor', 'rules'),
|
|
40
|
+
filename: `${skillId}.mdc`,
|
|
41
|
+
}),
|
|
42
|
+
transform: claudeSkillToCursorRule,
|
|
43
|
+
},
|
|
32
44
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
// --- Frontmatter translation ---
|
|
46
|
+
//
|
|
47
|
+
// Cursor's .mdc rules use a different frontmatter shape:
|
|
48
|
+
// ---
|
|
49
|
+
// description: "..."
|
|
50
|
+
// alwaysApply: false
|
|
51
|
+
// ---
|
|
52
|
+
// The body markdown is identical, so we strip the Claude frontmatter, lift
|
|
53
|
+
// the description out of it, and emit Cursor's variant.
|
|
54
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
55
|
+
const DESCRIPTION_RE = /^description:\s*(.+?)\s*$/m;
|
|
56
|
+
function claudeSkillToCursorRule(skillContent) {
|
|
57
|
+
const match = FRONTMATTER_RE.exec(skillContent);
|
|
58
|
+
if (!match) {
|
|
59
|
+
// Body without frontmatter — pass through untouched rather than guess.
|
|
60
|
+
return skillContent;
|
|
61
|
+
}
|
|
62
|
+
const [, frontmatter, body] = match;
|
|
63
|
+
const descMatch = DESCRIPTION_RE.exec(frontmatter);
|
|
64
|
+
const description = descMatch?.[1].trim() ?? '';
|
|
65
|
+
// JSON.stringify gives us a YAML-safe double-quoted string with proper
|
|
66
|
+
// escaping for any quotes or backslashes inside the description.
|
|
67
|
+
const cursorFrontmatter = `---\n` +
|
|
68
|
+
`description: ${JSON.stringify(description)}\n` +
|
|
69
|
+
`alwaysApply: false\n` +
|
|
70
|
+
`---\n`;
|
|
71
|
+
return `${cursorFrontmatter}\n${body.replace(/^\s+/, '')}`;
|
|
72
|
+
}
|
|
73
|
+
// --- I/O helpers ---
|
|
74
|
+
function loadSkillSource(skillId) {
|
|
75
|
+
const path = join(SKILLS_SOURCE_DIR, skillId, 'SKILL.md');
|
|
76
|
+
if (!existsSync(path)) {
|
|
77
|
+
throw new Error(`Skill source missing: ${path}`);
|
|
78
|
+
}
|
|
79
|
+
return readFileSync(path, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
function installSkillForAgent(skill, adapter, content) {
|
|
82
|
+
const { dir, filename } = adapter.targetFor(skill.id);
|
|
83
|
+
const dirPath = join(process.cwd(), dir);
|
|
84
|
+
const filePath = join(dirPath, filename);
|
|
85
|
+
const relativePath = join(dir, filename);
|
|
86
|
+
try {
|
|
87
|
+
mkdirSync(dirPath, { recursive: true });
|
|
88
|
+
if (existsSync(filePath)) {
|
|
89
|
+
return { agent: adapter.name, relativePath, status: 'exists' };
|
|
90
|
+
}
|
|
91
|
+
writeFileSync(filePath, adapter.transform(content));
|
|
92
|
+
return { agent: adapter.name, relativePath, status: 'wrote' };
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
return {
|
|
96
|
+
agent: adapter.name,
|
|
97
|
+
relativePath,
|
|
98
|
+
status: 'failed',
|
|
99
|
+
error: err instanceof Error ? err.message : String(err),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// --- Public entry point ---
|
|
104
|
+
export async function installRules(detectedAgents) {
|
|
105
|
+
const adapters = detectedAgents
|
|
106
|
+
.map((a) => ADAPTERS[a])
|
|
107
|
+
.filter((a) => a !== undefined);
|
|
108
|
+
if (adapters.length === 0) {
|
|
109
|
+
console.log(' No supported coding agents detected');
|
|
37
110
|
return;
|
|
38
111
|
}
|
|
39
|
-
|
|
112
|
+
const agentList = adapters.map((a) => a.name).join(', ');
|
|
113
|
+
const skillList = SKILLS.map((s) => `\n • ${s.label}`).join('');
|
|
114
|
+
const shouldInstall = await confirm({
|
|
115
|
+
message: `Install Moda skills for ${agentList}?${skillList}\n Continue?`,
|
|
116
|
+
default: true,
|
|
117
|
+
});
|
|
40
118
|
if (!shouldInstall) {
|
|
41
|
-
|
|
42
|
-
message: 'Install agent rules for coding assistants?',
|
|
43
|
-
default: true,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
if (!shouldInstall)
|
|
119
|
+
console.log(' Skipped');
|
|
47
120
|
return;
|
|
48
|
-
|
|
49
|
-
|
|
121
|
+
}
|
|
122
|
+
let wrote = 0;
|
|
123
|
+
let skipped = 0;
|
|
124
|
+
let failed = 0;
|
|
125
|
+
for (const skill of SKILLS) {
|
|
126
|
+
let content;
|
|
50
127
|
try {
|
|
51
|
-
|
|
52
|
-
mkdirSync(dir, { recursive: true });
|
|
53
|
-
if (existsSync(filePath)) {
|
|
54
|
-
const content = readFileSync(filePath, 'utf8');
|
|
55
|
-
if (content.includes('## Moda SDK')) {
|
|
56
|
-
console.log(` ${agent}: Moda SDK section already exists in ${AGENT_RULES_FILES[agent]}`);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
appendFileSync(filePath, MODA_RULES_CONTENT);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
writeFileSync(filePath, MODA_RULES_CONTENT.trimStart());
|
|
63
|
-
}
|
|
64
|
-
console.log(` Added Moda SDK reference to ${AGENT_RULES_FILES[agent]}`);
|
|
128
|
+
content = loadSkillSource(skill.id);
|
|
65
129
|
}
|
|
66
130
|
catch (err) {
|
|
67
131
|
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
-
console.error(` Warning:
|
|
132
|
+
console.error(` Warning: ${message}`);
|
|
133
|
+
failed += adapters.length;
|
|
134
|
+
continue;
|
|
69
135
|
}
|
|
136
|
+
for (const adapter of adapters) {
|
|
137
|
+
const outcome = installSkillForAgent(skill, adapter, content);
|
|
138
|
+
switch (outcome.status) {
|
|
139
|
+
case 'wrote':
|
|
140
|
+
console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
|
|
141
|
+
wrote++;
|
|
142
|
+
break;
|
|
143
|
+
case 'exists':
|
|
144
|
+
console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
|
|
145
|
+
skipped++;
|
|
146
|
+
break;
|
|
147
|
+
case 'failed':
|
|
148
|
+
console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
|
|
149
|
+
failed++;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const summary = [
|
|
155
|
+
wrote && `${wrote} written`,
|
|
156
|
+
skipped && `${skipped} already present`,
|
|
157
|
+
failed && `${failed} failed`,
|
|
158
|
+
]
|
|
159
|
+
.filter(Boolean)
|
|
160
|
+
.join(', ');
|
|
161
|
+
if (summary) {
|
|
162
|
+
console.log(` Done — ${summary}`);
|
|
70
163
|
}
|
|
71
164
|
}
|
|
72
165
|
//# sourceMappingURL=rules.js.map
|
package/dist/init/tenant.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// src/init/tenant.ts — Tenant selection and API key provisioning
|
|
2
2
|
import { select } from '@inquirer/prompts';
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
3
|
import { authFetch } from '../auth.js';
|
|
6
4
|
export async function selectTenantAndCreateKey() {
|
|
7
5
|
// 1. Fetch CLI context
|
|
@@ -14,7 +12,7 @@ export async function selectTenantAndCreateKey() {
|
|
|
14
12
|
console.log(` Authenticated as ${ctx.user.email}`);
|
|
15
13
|
// 2. Select tenant
|
|
16
14
|
if (ctx.tenants.length === 0) {
|
|
17
|
-
console.error(' No organizations found. Create one at https://
|
|
15
|
+
console.error(' No organizations found. Create one at https://moda.dev');
|
|
18
16
|
process.exit(1);
|
|
19
17
|
}
|
|
20
18
|
let tenantId;
|
|
@@ -47,45 +45,9 @@ export async function selectTenantAndCreateKey() {
|
|
|
47
45
|
throw new Error(`Failed to create API key (${bootstrapRes.status}): ${body}`);
|
|
48
46
|
}
|
|
49
47
|
const result = (await bootstrapRes.json());
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
ensureGitignore();
|
|
54
|
-
console.log(' API key created and saved to .env');
|
|
48
|
+
console.log(' API key created.\n');
|
|
49
|
+
console.log(` Add this to your environment variables:\n`);
|
|
50
|
+
console.log(` MODA_API_KEY=${result.apiKey}\n`);
|
|
55
51
|
return result;
|
|
56
52
|
}
|
|
57
|
-
function writeEnvFile(apiKey) {
|
|
58
|
-
const envPath = join(process.cwd(), '.env');
|
|
59
|
-
const line = `MODA_API_KEY=${apiKey}`;
|
|
60
|
-
if (existsSync(envPath)) {
|
|
61
|
-
const content = readFileSync(envPath, 'utf8');
|
|
62
|
-
if (content.includes('MODA_API_KEY=')) {
|
|
63
|
-
// Replace existing line
|
|
64
|
-
const updated = content.replace(/^MODA_API_KEY=.*$/m, line);
|
|
65
|
-
writeFileSync(envPath, updated);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
// Append
|
|
69
|
-
const separator = content.endsWith('\n') ? '' : '\n';
|
|
70
|
-
appendFileSync(envPath, `${separator}${line}\n`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
writeFileSync(envPath, `${line}\n`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function ensureGitignore() {
|
|
78
|
-
const gitignorePath = join(process.cwd(), '.gitignore');
|
|
79
|
-
if (existsSync(gitignorePath)) {
|
|
80
|
-
const content = readFileSync(gitignorePath, 'utf8');
|
|
81
|
-
const lines = content.split('\n').map((l) => l.trim());
|
|
82
|
-
if (!lines.includes('.env')) {
|
|
83
|
-
const separator = content.endsWith('\n') ? '' : '\n';
|
|
84
|
-
appendFileSync(gitignorePath, `${separator}.env\n`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
writeFileSync(gitignorePath, '.env\n');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
53
|
//# sourceMappingURL=tenant.js.map
|
package/dist/schemas.d.ts
CHANGED
|
@@ -71,14 +71,20 @@ export declare const FrustrationsSchema: z.ZodObject<{
|
|
|
71
71
|
days_back: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
72
72
|
limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
73
73
|
offset: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
74
|
+
include_window: z.ZodOptional<z.ZodEffects<z.ZodBoolean, boolean, unknown>>;
|
|
75
|
+
window: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
74
76
|
}, "strip", z.ZodTypeAny, {
|
|
75
77
|
days_back?: number | undefined;
|
|
76
78
|
limit?: number | undefined;
|
|
77
79
|
offset?: number | undefined;
|
|
80
|
+
window?: number | undefined;
|
|
81
|
+
include_window?: boolean | undefined;
|
|
78
82
|
}, {
|
|
79
83
|
days_back?: number | undefined;
|
|
80
84
|
limit?: number | undefined;
|
|
81
85
|
offset?: number | undefined;
|
|
86
|
+
window?: number | undefined;
|
|
87
|
+
include_window?: unknown;
|
|
82
88
|
}>;
|
|
83
89
|
export declare const ToolFailuresSchema: z.ZodObject<{
|
|
84
90
|
days_back: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
@@ -93,17 +99,82 @@ export declare const ToolFailureDetailSchema: z.ZodObject<{
|
|
|
93
99
|
days_back: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
94
100
|
limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
95
101
|
offset: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
102
|
+
include_window: z.ZodOptional<z.ZodEffects<z.ZodBoolean, boolean, unknown>>;
|
|
103
|
+
window: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
96
104
|
}, "strip", z.ZodTypeAny, {
|
|
97
105
|
tool_name: string;
|
|
98
106
|
days_back?: number | undefined;
|
|
99
107
|
limit?: number | undefined;
|
|
100
108
|
offset?: number | undefined;
|
|
109
|
+
window?: number | undefined;
|
|
110
|
+
include_window?: boolean | undefined;
|
|
101
111
|
subtype?: string | undefined;
|
|
102
112
|
}, {
|
|
103
113
|
tool_name: string;
|
|
104
114
|
days_back?: number | undefined;
|
|
105
115
|
limit?: number | undefined;
|
|
106
116
|
offset?: number | undefined;
|
|
117
|
+
window?: number | undefined;
|
|
118
|
+
include_window?: unknown;
|
|
107
119
|
subtype?: string | undefined;
|
|
108
120
|
}>;
|
|
121
|
+
/** moda workflows — recurring workflow catalog (read from agent_job_classes). */
|
|
122
|
+
export declare const WorkflowsSchema: z.ZodObject<{
|
|
123
|
+
time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
|
|
124
|
+
user_id: z.ZodOptional<z.ZodString>;
|
|
125
|
+
}, "strip", z.ZodTypeAny, {
|
|
126
|
+
time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
|
|
127
|
+
user_id?: string | undefined;
|
|
128
|
+
}, {
|
|
129
|
+
time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
|
|
130
|
+
user_id?: string | undefined;
|
|
131
|
+
}>;
|
|
132
|
+
/** moda runs — list workflow executions with classified outcomes. */
|
|
133
|
+
export declare const RunsSchema: z.ZodObject<{
|
|
134
|
+
time_range: z.ZodOptional<z.ZodDefault<z.ZodEnum<["all", "1h", "3d", "7d", "24h", "30d", "90d"]>>>;
|
|
135
|
+
outcome: z.ZodOptional<z.ZodEnum<["running", "succeeded", "partial", "failed", "loop", "abandoned", "unknown"]>>;
|
|
136
|
+
/** Filter to one workflow class id (e.g. cron:helpscout-triage-hourly). */
|
|
137
|
+
class_id: z.ZodOptional<z.ZodString>;
|
|
138
|
+
user_id: z.ZodOptional<z.ZodString>;
|
|
139
|
+
limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
140
|
+
offset: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
141
|
+
}, "strip", z.ZodTypeAny, {
|
|
142
|
+
time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
|
|
143
|
+
limit?: number | undefined;
|
|
144
|
+
offset?: number | undefined;
|
|
145
|
+
user_id?: string | undefined;
|
|
146
|
+
outcome?: "running" | "succeeded" | "partial" | "failed" | "loop" | "abandoned" | "unknown" | undefined;
|
|
147
|
+
class_id?: string | undefined;
|
|
148
|
+
}, {
|
|
149
|
+
time_range?: "all" | "1h" | "3d" | "7d" | "24h" | "30d" | "90d" | undefined;
|
|
150
|
+
limit?: number | undefined;
|
|
151
|
+
offset?: number | undefined;
|
|
152
|
+
user_id?: string | undefined;
|
|
153
|
+
outcome?: "running" | "succeeded" | "partial" | "failed" | "loop" | "abandoned" | "unknown" | undefined;
|
|
154
|
+
class_id?: string | undefined;
|
|
155
|
+
}>;
|
|
156
|
+
/** moda run <runId> — single execution detail with side-effect receipts. */
|
|
157
|
+
export declare const RunDetailSchema: z.ZodObject<{
|
|
158
|
+
run_id: z.ZodString;
|
|
159
|
+
}, "strip", z.ZodTypeAny, {
|
|
160
|
+
run_id: string;
|
|
161
|
+
}, {
|
|
162
|
+
run_id: string;
|
|
163
|
+
}>;
|
|
164
|
+
/** moda loops — currently active loop detections. */
|
|
165
|
+
export declare const LoopsSchema: z.ZodObject<{
|
|
166
|
+
limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
167
|
+
}, "strip", z.ZodTypeAny, {
|
|
168
|
+
limit?: number | undefined;
|
|
169
|
+
}, {
|
|
170
|
+
limit?: number | undefined;
|
|
171
|
+
}>;
|
|
172
|
+
/** moda loop <detectionId> — single loop detection detail. */
|
|
173
|
+
export declare const LoopDetailSchema: z.ZodObject<{
|
|
174
|
+
detection_id: z.ZodString;
|
|
175
|
+
}, "strip", z.ZodTypeAny, {
|
|
176
|
+
detection_id: string;
|
|
177
|
+
}, {
|
|
178
|
+
detection_id: string;
|
|
179
|
+
}>;
|
|
109
180
|
//# sourceMappingURL=schemas.d.ts.map
|