@mastra/code-sdk 0.1.0-alpha.6 → 0.1.0-alpha.8
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/CHANGELOG.md +58 -0
- package/dist/agents/mastracode-gateway.d.ts +4 -2
- package/dist/agents/mastracode-gateway.d.ts.map +1 -1
- package/dist/agents/mastracode-gateway.js +11 -7
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/model.js +1 -1
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/tools.d.ts +3 -3
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +33 -22
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +51 -19
- package/dist/agents/workspace.js.map +1 -1
- package/dist/index.d.ts +4 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/onboarding/settings.d.ts +6 -2
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +6 -2
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/plugins/dependencies.d.ts.map +1 -1
- package/dist/plugins/dependencies.js +28 -58
- package/dist/plugins/dependencies.js.map +1 -1
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.d.ts.map +1 -0
- package/dist/utils/path-security.js +9 -0
- package/dist/utils/path-security.js.map +1 -0
- package/dist/utils/slash-command-loader.d.ts +5 -1
- package/dist/utils/slash-command-loader.d.ts.map +1 -1
- package/dist/utils/slash-command-loader.js +30 -8
- package/dist/utils/slash-command-loader.js.map +1 -1
- package/package.json +13 -13
|
@@ -2,6 +2,7 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
import { DEFAULT_CONFIG_DIR } from "../constants.js";
|
|
5
|
+
import { isPathWithinRoot } from "./path-security.js";
|
|
5
6
|
async function parseCommandFile(filePath, baseDir) {
|
|
6
7
|
try {
|
|
7
8
|
const content = await fs.readFile(filePath, "utf-8");
|
|
@@ -53,17 +54,32 @@ function extractCommandName(filePath, baseDir) {
|
|
|
53
54
|
const namespace = dirName.replace(/[\\/]/g, ":");
|
|
54
55
|
return `${namespace}:${baseName}`;
|
|
55
56
|
}
|
|
56
|
-
async function scanCommandDirectory(dirPath, rootDir) {
|
|
57
|
+
async function scanCommandDirectory(dirPath, rootDir, options = {}) {
|
|
57
58
|
const baseDir = rootDir ?? dirPath;
|
|
58
59
|
const commands = [];
|
|
60
|
+
const visitedDirectories = options.visitedDirectories ?? /* @__PURE__ */ new Set();
|
|
59
61
|
try {
|
|
62
|
+
const realDirectory = await fs.realpath(dirPath);
|
|
63
|
+
const realAllowedRoot = options.allowedRoot ? await fs.realpath(options.allowedRoot) : void 0;
|
|
64
|
+
if (realAllowedRoot && !isPathWithinRoot(realDirectory, realAllowedRoot)) return commands;
|
|
65
|
+
if (visitedDirectories.has(realDirectory)) return commands;
|
|
66
|
+
visitedDirectories.add(realDirectory);
|
|
60
67
|
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
61
68
|
for (const entry of entries) {
|
|
62
69
|
const fullPath = path.join(dirPath, entry.name);
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
const stats = entry.isSymbolicLink() ? await fs.stat(fullPath).catch(() => null) : entry;
|
|
71
|
+
if (!stats) continue;
|
|
72
|
+
if (stats.isDirectory()) {
|
|
73
|
+
const subCommands = await scanCommandDirectory(fullPath, baseDir, {
|
|
74
|
+
...options,
|
|
75
|
+
visitedDirectories
|
|
76
|
+
});
|
|
65
77
|
commands.push(...subCommands);
|
|
66
|
-
} else if (entry.
|
|
78
|
+
} else if (entry.name.endsWith(".md") && stats.isFile()) {
|
|
79
|
+
if (realAllowedRoot) {
|
|
80
|
+
const realFile = await fs.realpath(fullPath).catch(() => null);
|
|
81
|
+
if (!realFile || !isPathWithinRoot(realFile, realAllowedRoot)) continue;
|
|
82
|
+
}
|
|
67
83
|
const command = await parseCommandFile(fullPath, baseDir);
|
|
68
84
|
if (command) {
|
|
69
85
|
commands.push(command);
|
|
@@ -99,21 +115,27 @@ async function loadCustomCommands(projectDir, configDirName = DEFAULT_CONFIG_DIR
|
|
|
99
115
|
}
|
|
100
116
|
if (projectDir) {
|
|
101
117
|
const opencodeProjectDir = path.join(projectDir, ".opencode", "command");
|
|
102
|
-
const opencodeProjectCommands = await scanCommandDirectory(opencodeProjectDir
|
|
118
|
+
const opencodeProjectCommands = await scanCommandDirectory(opencodeProjectDir, void 0, {
|
|
119
|
+
allowedRoot: projectDir
|
|
120
|
+
});
|
|
103
121
|
addCommands(opencodeProjectCommands);
|
|
104
122
|
}
|
|
105
123
|
if (projectDir) {
|
|
106
124
|
const claudeProjectDir = path.join(projectDir, ".claude", "commands");
|
|
107
|
-
const claudeProjectCommands = await scanCommandDirectory(claudeProjectDir
|
|
125
|
+
const claudeProjectCommands = await scanCommandDirectory(claudeProjectDir, void 0, {
|
|
126
|
+
allowedRoot: projectDir
|
|
127
|
+
});
|
|
108
128
|
addCommands(claudeProjectCommands);
|
|
109
129
|
}
|
|
110
130
|
if (projectDir) {
|
|
111
131
|
const mastraProjectDir = path.join(projectDir, configDirName, "commands");
|
|
112
|
-
const mastraProjectCommands = await scanCommandDirectory(mastraProjectDir
|
|
132
|
+
const mastraProjectCommands = await scanCommandDirectory(mastraProjectDir, void 0, {
|
|
133
|
+
allowedRoot: projectDir
|
|
134
|
+
});
|
|
113
135
|
addCommands(mastraProjectCommands);
|
|
114
136
|
}
|
|
115
137
|
for (const commandsDir of extraCommandDirs) {
|
|
116
|
-
addCommands(await scanCommandDirectory(commandsDir));
|
|
138
|
+
addCommands(await scanCommandDirectory(commandsDir, void 0, { allowedRoot: commandsDir }));
|
|
117
139
|
}
|
|
118
140
|
return Array.from(commandMap.values());
|
|
119
141
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/slash-command-loader.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\n\n/**\n * Metadata for a slash command\n */\nexport interface SlashCommandMetadata {\n /** Command name (e.g., \"git:commit\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** The command template with variables */\n template: string;\n /** Source file path */\n sourcePath: string;\n /** Namespace derived from directory structure */\n namespace?: string;\n /** Whether this command should also be exposed as /goal/<name> */\n goal?: boolean;\n}\n\n/**\n * Parse a command file and extract metadata and template\n * Supports both frontmatter-based and plain markdown files\n */\nexport async function parseCommandFile(filePath: string, baseDir?: string): Promise<SlashCommandMetadata | null> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n const trimmedContent = content.trim();\n\n // Check if file has frontmatter (starts with ---)\n if (!trimmedContent.startsWith('---')) {\n // No frontmatter - treat entire file as template\n // Derive name from file path\n const name = baseDir ? extractCommandName(filePath, baseDir) : path.basename(filePath, '.md');\n\n return {\n name,\n description: '',\n template: content,\n sourcePath: filePath,\n };\n }\n\n // Split frontmatter and template\n const parts = content.split('---');\n if (parts.length < 3) {\n return null;\n }\n\n const frontmatter = parts[1]!.trim();\n const template = parts.slice(2).join('---').trim();\n\n // Parse YAML frontmatter\n const metadata = parseYaml(frontmatter) as Record<string, unknown>;\n\n // Derive name from file path if not specified in frontmatter\n let name: string;\n if (typeof metadata?.name === 'string' && metadata.name) {\n name = metadata.name;\n } else if (baseDir) {\n name = extractCommandName(filePath, baseDir);\n } else {\n name = path.basename(filePath, '.md');\n }\n\n return {\n name,\n description: typeof metadata?.description === 'string' ? metadata.description : '',\n template,\n sourcePath: filePath,\n namespace: typeof metadata?.namespace === 'string' ? metadata.namespace : undefined,\n goal: metadata?.goal === true,\n };\n } catch (error) {\n console.error(`Error parsing command file ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Extract command name from file path\n * Converts path like \"git/commit.md\" to \"git:commit\"\n */\nexport function extractCommandName(filePath: string, baseDir: string): string {\n const relativePath = path.relative(baseDir, filePath);\n const dirName = path.dirname(relativePath);\n const baseName = path.basename(relativePath, '.md');\n\n if (dirName === '.' || dirName === '') {\n return baseName;\n }\n\n // Replace path separators with colons for namespacing\n const namespace = dirName.replace(/[\\\\/]/g, ':');\n return `${namespace}:${baseName}`;\n}\n\n/**\n * Recursively scan a directory for command files.\n * @param dirPath - Current directory to scan\n * @param rootDir - Original root commands directory (used for namespace derivation).\n * When omitted the first call sets it to dirPath.\n */\nexport async function scanCommandDirectory(dirPath: string, rootDir?: string): Promise<SlashCommandMetadata[]> {\n const baseDir = rootDir ?? dirPath;\n const commands: SlashCommandMetadata[] = [];\n\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively scan subdirectories, preserving the root directory for namespace derivation\n const subCommands = await scanCommandDirectory(fullPath, baseDir);\n commands.push(...subCommands);\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n // Parse markdown command files, passing the root commands dir as baseDir for name derivation\n const command = await parseCommandFile(fullPath, baseDir);\n if (command) {\n commands.push(command);\n }\n }\n }\n } catch {\n // Directory doesn't exist or can't be read - silently skip\n }\n\n return commands;\n}\n/**\n * Load custom slash commands from all configured directories\n * Priority: mastra project > claude project > opencode project > mastra user > claude user > opencode user\n */\nexport async function loadCustomCommands(\n projectDir?: string,\n configDirName = DEFAULT_CONFIG_DIR,\n extraCommandDirs: string[] = [],\n): Promise<SlashCommandMetadata[]> {\n // Use a Map so later (higher priority) sources override earlier ones with the same name\n const commandMap = new Map<string, SlashCommandMetadata>();\n\n const addCommands = (newCommands: SlashCommandMetadata[]) => {\n for (const cmd of newCommands) {\n commandMap.set(cmd.name, cmd);\n }\n };\n\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n\n // 1. Load from opencode user directory ~/.opencode/command (lowest priority)\n if (homeDir) {\n const opencodeUserDir = path.join(homeDir, '.opencode', 'command');\n const opencodeUserCommands = await scanCommandDirectory(opencodeUserDir);\n addCommands(opencodeUserCommands);\n }\n\n // 2. Load from claude user directory ~/.claude/commands (Claude Code compat)\n if (homeDir) {\n const claudeUserDir = path.join(homeDir, '.claude', 'commands');\n const claudeUserCommands = await scanCommandDirectory(claudeUserDir);\n addCommands(claudeUserCommands);\n }\n\n // 3. Load from mastra user directory ~/<configDirName>/commands\n if (homeDir) {\n const mastraUserDir = path.join(homeDir, configDirName, 'commands');\n const mastraUserCommands = await scanCommandDirectory(mastraUserDir);\n addCommands(mastraUserCommands);\n }\n\n // 4. Load from opencode project directory .opencode/command\n if (projectDir) {\n const opencodeProjectDir = path.join(projectDir, '.opencode', 'command');\n const opencodeProjectCommands = await scanCommandDirectory(opencodeProjectDir);\n addCommands(opencodeProjectCommands);\n }\n\n // 5. Load from claude project directory .claude/commands (Claude Code compat)\n if (projectDir) {\n const claudeProjectDir = path.join(projectDir, '.claude', 'commands');\n const claudeProjectCommands = await scanCommandDirectory(claudeProjectDir);\n addCommands(claudeProjectCommands);\n }\n\n // 6. Load from mastra project directory <configDirName>/commands\n if (projectDir) {\n const mastraProjectDir = path.join(projectDir, configDirName, 'commands');\n const mastraProjectCommands = await scanCommandDirectory(mastraProjectDir);\n addCommands(mastraProjectCommands);\n }\n\n // 7. Load from active plugin command directories (highest priority)\n for (const commandsDir of extraCommandDirs) {\n addCommands(await scanCommandDirectory(commandsDir));\n }\n\n return Array.from(commandMap.values());\n}\n\n/**\n * Get the commands directory path for a project\n */\nexport function getProjectCommandsDir(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(projectDir, configDirName, 'commands');\n}\n\n/**\n * Initialize a commands directory with an example command\n */\nexport async function initCommandsDirectory(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): Promise<void> {\n const commandsDir = getProjectCommandsDir(projectDir, configDirName);\n\n try {\n await fs.mkdir(commandsDir, { recursive: true });\n\n // Create an example command\n const examplePath = path.join(commandsDir, 'example.md');\n const exampleContent = `---\nname: example\ndescription: An example slash command\n---\n\nThis is an example slash command template.\nYou can use variables like \\$ARGUMENTS or \\$1, \\$2 for positional args.\nYou can also include file content with @filename.\nShell commands with !command will be executed and output included.\n`;\n\n try {\n await fs.access(examplePath);\n // File already exists, don't overwrite\n } catch {\n await fs.writeFile(examplePath, exampleContent, 'utf-8');\n }\n } catch (error) {\n console.error('Error initializing commands directory:', error);\n }\n}\n"],"mappings":"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","names":["name"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/slash-command-loader.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { isPathWithinRoot } from './path-security.js';\n\n/**\n * Metadata for a slash command\n */\nexport interface SlashCommandMetadata {\n /** Command name (e.g., \"git:commit\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** The command template with variables */\n template: string;\n /** Source file path */\n sourcePath: string;\n /** Namespace derived from directory structure */\n namespace?: string;\n /** Whether this command should also be exposed as /goal/<name> */\n goal?: boolean;\n}\n\n/**\n * Parse a command file and extract metadata and template\n * Supports both frontmatter-based and plain markdown files\n */\nexport async function parseCommandFile(filePath: string, baseDir?: string): Promise<SlashCommandMetadata | null> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n const trimmedContent = content.trim();\n\n // Check if file has frontmatter (starts with ---)\n if (!trimmedContent.startsWith('---')) {\n // No frontmatter - treat entire file as template\n // Derive name from file path\n const name = baseDir ? extractCommandName(filePath, baseDir) : path.basename(filePath, '.md');\n\n return {\n name,\n description: '',\n template: content,\n sourcePath: filePath,\n };\n }\n\n // Split frontmatter and template\n const parts = content.split('---');\n if (parts.length < 3) {\n return null;\n }\n\n const frontmatter = parts[1]!.trim();\n const template = parts.slice(2).join('---').trim();\n\n // Parse YAML frontmatter\n const metadata = parseYaml(frontmatter) as Record<string, unknown>;\n\n // Derive name from file path if not specified in frontmatter\n let name: string;\n if (typeof metadata?.name === 'string' && metadata.name) {\n name = metadata.name;\n } else if (baseDir) {\n name = extractCommandName(filePath, baseDir);\n } else {\n name = path.basename(filePath, '.md');\n }\n\n return {\n name,\n description: typeof metadata?.description === 'string' ? metadata.description : '',\n template,\n sourcePath: filePath,\n namespace: typeof metadata?.namespace === 'string' ? metadata.namespace : undefined,\n goal: metadata?.goal === true,\n };\n } catch (error) {\n console.error(`Error parsing command file ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Extract command name from file path\n * Converts path like \"git/commit.md\" to \"git:commit\"\n */\nexport function extractCommandName(filePath: string, baseDir: string): string {\n const relativePath = path.relative(baseDir, filePath);\n const dirName = path.dirname(relativePath);\n const baseName = path.basename(relativePath, '.md');\n\n if (dirName === '.' || dirName === '') {\n return baseName;\n }\n\n // Replace path separators with colons for namespacing\n const namespace = dirName.replace(/[\\\\/]/g, ':');\n return `${namespace}:${baseName}`;\n}\n\n/**\n * Recursively scan a directory for command files.\n * @param dirPath - Current directory to scan\n * @param rootDir - Original root commands directory (used for namespace derivation).\n * When omitted the first call sets it to dirPath.\n */\nexport interface ScanCommandDirectoryOptions {\n allowedRoot?: string;\n visitedDirectories?: Set<string>;\n}\n\nexport async function scanCommandDirectory(\n dirPath: string,\n rootDir?: string,\n options: ScanCommandDirectoryOptions = {},\n): Promise<SlashCommandMetadata[]> {\n const baseDir = rootDir ?? dirPath;\n const commands: SlashCommandMetadata[] = [];\n const visitedDirectories = options.visitedDirectories ?? new Set<string>();\n\n try {\n const realDirectory = await fs.realpath(dirPath);\n const realAllowedRoot = options.allowedRoot ? await fs.realpath(options.allowedRoot) : undefined;\n if (realAllowedRoot && !isPathWithinRoot(realDirectory, realAllowedRoot)) return commands;\n if (visitedDirectories.has(realDirectory)) return commands;\n visitedDirectories.add(realDirectory);\n\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n const stats = entry.isSymbolicLink() ? await fs.stat(fullPath).catch(() => null) : entry;\n if (!stats) continue;\n\n if (stats.isDirectory()) {\n // Recursively scan subdirectories, preserving the root directory for namespace derivation\n const subCommands = await scanCommandDirectory(fullPath, baseDir, {\n ...options,\n visitedDirectories,\n });\n commands.push(...subCommands);\n } else if (entry.name.endsWith('.md') && stats.isFile()) {\n if (realAllowedRoot) {\n const realFile = await fs.realpath(fullPath).catch(() => null);\n if (!realFile || !isPathWithinRoot(realFile, realAllowedRoot)) continue;\n }\n\n // Parse markdown command files, passing the root commands dir as baseDir for name derivation\n const command = await parseCommandFile(fullPath, baseDir);\n if (command) {\n commands.push(command);\n }\n }\n }\n } catch {\n // Directory doesn't exist or can't be read - silently skip\n }\n\n return commands;\n}\n/**\n * Load custom slash commands from all configured directories\n * Priority: mastra project > claude project > opencode project > mastra user > claude user > opencode user\n */\nexport async function loadCustomCommands(\n projectDir?: string,\n configDirName = DEFAULT_CONFIG_DIR,\n extraCommandDirs: string[] = [],\n): Promise<SlashCommandMetadata[]> {\n // Use a Map so later (higher priority) sources override earlier ones with the same name\n const commandMap = new Map<string, SlashCommandMetadata>();\n\n const addCommands = (newCommands: SlashCommandMetadata[]) => {\n for (const cmd of newCommands) {\n commandMap.set(cmd.name, cmd);\n }\n };\n\n const homeDir = process.env.HOME || process.env.USERPROFILE;\n\n // 1. Load from opencode user directory ~/.opencode/command (lowest priority)\n if (homeDir) {\n const opencodeUserDir = path.join(homeDir, '.opencode', 'command');\n const opencodeUserCommands = await scanCommandDirectory(opencodeUserDir);\n addCommands(opencodeUserCommands);\n }\n\n // 2. Load from claude user directory ~/.claude/commands (Claude Code compat)\n if (homeDir) {\n const claudeUserDir = path.join(homeDir, '.claude', 'commands');\n const claudeUserCommands = await scanCommandDirectory(claudeUserDir);\n addCommands(claudeUserCommands);\n }\n\n // 3. Load from mastra user directory ~/<configDirName>/commands\n if (homeDir) {\n const mastraUserDir = path.join(homeDir, configDirName, 'commands');\n const mastraUserCommands = await scanCommandDirectory(mastraUserDir);\n addCommands(mastraUserCommands);\n }\n\n // 4. Load from opencode project directory .opencode/command\n if (projectDir) {\n const opencodeProjectDir = path.join(projectDir, '.opencode', 'command');\n const opencodeProjectCommands = await scanCommandDirectory(opencodeProjectDir, undefined, {\n allowedRoot: projectDir,\n });\n addCommands(opencodeProjectCommands);\n }\n\n // 5. Load from claude project directory .claude/commands (Claude Code compat)\n if (projectDir) {\n const claudeProjectDir = path.join(projectDir, '.claude', 'commands');\n const claudeProjectCommands = await scanCommandDirectory(claudeProjectDir, undefined, {\n allowedRoot: projectDir,\n });\n addCommands(claudeProjectCommands);\n }\n\n // 6. Load from mastra project directory <configDirName>/commands\n if (projectDir) {\n const mastraProjectDir = path.join(projectDir, configDirName, 'commands');\n const mastraProjectCommands = await scanCommandDirectory(mastraProjectDir, undefined, {\n allowedRoot: projectDir,\n });\n addCommands(mastraProjectCommands);\n }\n\n // 7. Load from active plugin command directories (highest priority)\n for (const commandsDir of extraCommandDirs) {\n addCommands(await scanCommandDirectory(commandsDir, undefined, { allowedRoot: commandsDir }));\n }\n\n return Array.from(commandMap.values());\n}\n\n/**\n * Get the commands directory path for a project\n */\nexport function getProjectCommandsDir(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(projectDir, configDirName, 'commands');\n}\n\n/**\n * Initialize a commands directory with an example command\n */\nexport async function initCommandsDirectory(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): Promise<void> {\n const commandsDir = getProjectCommandsDir(projectDir, configDirName);\n\n try {\n await fs.mkdir(commandsDir, { recursive: true });\n\n // Create an example command\n const examplePath = path.join(commandsDir, 'example.md');\n const exampleContent = `---\nname: example\ndescription: An example slash command\n---\n\nThis is an example slash command template.\nYou can use variables like \\$ARGUMENTS or \\$1, \\$2 for positional args.\nYou can also include file content with @filename.\nShell commands with !command will be executed and output included.\n`;\n\n try {\n await fs.access(examplePath);\n // File already exists, don't overwrite\n } catch {\n await fs.writeFile(examplePath, exampleContent, 'utf-8');\n }\n } catch (error) {\n console.error('Error initializing commands directory:', error);\n }\n}\n"],"mappings":"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","names":["name"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/code-sdk",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "Mastra Code SDK: the agent core behind Mastra Code (everything except the TUI) — build your own UIs and surfaces on top of it",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@agentclientprotocol/sdk": "^0.21.0",
|
|
44
|
-
"@ai-sdk/amazon-bedrock": "^3.0.
|
|
45
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
46
|
-
"@ai-sdk/openai": "^3.0.
|
|
47
|
-
"@ai-sdk/openai-compatible": "^2.0.
|
|
44
|
+
"@ai-sdk/amazon-bedrock": "^3.0.105",
|
|
45
|
+
"@ai-sdk/anthropic": "^3.0.96",
|
|
46
|
+
"@ai-sdk/openai": "^3.0.84",
|
|
47
|
+
"@ai-sdk/openai-compatible": "^2.0.59",
|
|
48
48
|
"@ast-grep/napi": "^0.42.0",
|
|
49
49
|
"@aws-sdk/credential-providers": "^3.864.0",
|
|
50
|
-
"ai": "^6.0.
|
|
50
|
+
"ai": "^6.0.224",
|
|
51
51
|
"execa": "^9.6.1",
|
|
52
52
|
"libsql": "^0.5.29",
|
|
53
53
|
"posthog-node": "^5.37.0",
|
|
@@ -56,19 +56,19 @@
|
|
|
56
56
|
"vscode-languageserver-protocol": "^3.17.5",
|
|
57
57
|
"yaml": "^2.7.1",
|
|
58
58
|
"zod": "^4.3.6",
|
|
59
|
-
"@mastra/
|
|
59
|
+
"@mastra/duckdb": "1.5.1",
|
|
60
60
|
"@mastra/agent-browser": "0.4.1",
|
|
61
|
-
"@mastra/fastembed": "1.2.0",
|
|
62
61
|
"@mastra/github-signals": "0.2.2",
|
|
62
|
+
"@mastra/fastembed": "1.2.0",
|
|
63
63
|
"@mastra/libsql": "1.16.0-alpha.0",
|
|
64
|
+
"@mastra/core": "1.51.0-alpha.8",
|
|
64
65
|
"@mastra/mcp": "1.14.0-alpha.0",
|
|
65
|
-
"@mastra/memory": "1.23.0-alpha.3",
|
|
66
|
-
"@mastra/observability": "1.16.1-alpha.0",
|
|
67
|
-
"@mastra/duckdb": "1.5.1",
|
|
68
66
|
"@mastra/pg": "1.15.1",
|
|
67
|
+
"@mastra/memory": "1.23.0-alpha.3",
|
|
68
|
+
"@mastra/schema-compat": "1.3.4-alpha.1",
|
|
69
69
|
"@mastra/stagehand": "0.3.0",
|
|
70
70
|
"@mastra/tavily": "1.1.0",
|
|
71
|
-
"@mastra/
|
|
71
|
+
"@mastra/observability": "1.16.1-alpha.1"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@libsql/client": "^0.17.4",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"tsx": "^4.22.4",
|
|
79
79
|
"typescript": "^6.0.3",
|
|
80
80
|
"typescript-eslint": "^8.57.0",
|
|
81
|
-
"vitest": "4.1.
|
|
81
|
+
"vitest": "4.1.10",
|
|
82
82
|
"@internal/lint": "0.0.113",
|
|
83
83
|
"@internal/types-builder": "0.0.88"
|
|
84
84
|
},
|