@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.2
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 +49 -0
- package/dist/acp/agent.js +155 -167
- package/dist/acp/agent.js.map +1 -1
- package/dist/acp/event-mapper.js +193 -168
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/acp/index.js +60 -49
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/server.js +31 -30
- package/dist/acp/server.js.map +1 -1
- package/dist/acp.js +1 -4
- package/dist/agents/credential-resolver.js +40 -29
- package/dist/agents/credential-resolver.js.map +1 -1
- package/dist/agents/custom-provider-source.js +16 -9
- package/dist/agents/custom-provider-source.js.map +1 -1
- package/dist/agents/instructions.js +27 -36
- package/dist/agents/instructions.js.map +1 -1
- package/dist/agents/mastracode-gateway.js +355 -428
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.js +87 -78
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/model.js +114 -119
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/modes/build.js +12 -13
- package/dist/agents/modes/build.js.map +1 -1
- package/dist/agents/modes/explore.js +14 -13
- package/dist/agents/modes/explore.js.map +1 -1
- package/dist/agents/modes/plan.js +14 -15
- package/dist/agents/modes/plan.js.map +1 -1
- package/dist/agents/prompts/agent-instructions.js +78 -69
- package/dist/agents/prompts/agent-instructions.js.map +1 -1
- package/dist/agents/prompts/build.js +19 -16
- package/dist/agents/prompts/build.js.map +1 -1
- package/dist/agents/prompts/fast.js +8 -4
- package/dist/agents/prompts/fast.js.map +1 -1
- package/dist/agents/prompts/index.js +48 -47
- package/dist/agents/prompts/index.js.map +1 -1
- package/dist/agents/prompts/model.js +6 -5
- package/dist/agents/prompts/model.js.map +1 -1
- package/dist/agents/prompts/plan.js +20 -16
- package/dist/agents/prompts/plan.js.map +1 -1
- package/dist/agents/prompts/tool-guidance.js +77 -109
- package/dist/agents/prompts/tool-guidance.js.map +1 -1
- package/dist/agents/sandbox-filesystem.js +245 -259
- package/dist/agents/sandbox-filesystem.js.map +1 -1
- package/dist/agents/sandbox-reattach.js +9 -11
- package/dist/agents/sandbox-reattach.js.map +1 -1
- package/dist/agents/thread-caveman-state.js +92 -81
- package/dist/agents/thread-caveman-state.js.map +1 -1
- package/dist/agents/tool-availability.js +62 -80
- package/dist/agents/tool-availability.js.map +1 -1
- package/dist/agents/tools.js +99 -139
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.js +232 -225
- package/dist/agents/workspace.js.map +1 -1
- package/dist/analytics.js +139 -128
- package/dist/analytics.js.map +1 -1
- package/dist/auth/authorization-input.js +39 -25
- package/dist/auth/authorization-input.js.map +1 -1
- package/dist/auth/device-code.js +112 -80
- package/dist/auth/device-code.js.map +1 -1
- package/dist/auth/index.js +2 -9
- package/dist/auth/pkce.js +27 -16
- package/dist/auth/pkce.js.map +1 -1
- package/dist/auth/providers/anthropic.js +107 -93
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.js +304 -293
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/openai-codex.js +442 -460
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.js +179 -145
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/auth/storage.js +182 -175
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.js +1 -1
- package/dist/clipboard/index.js +206 -172
- package/dist/clipboard/index.js.map +1 -1
- package/dist/constants.js +11 -16
- package/dist/constants.js.map +1 -1
- package/dist/error-classification.js +19 -11
- package/dist/error-classification.js.map +1 -1
- package/dist/evals/context-builder.js +145 -104
- package/dist/evals/context-builder.js.map +1 -1
- package/dist/evals/index.js +4 -7
- package/dist/evals/scorers/classify-command.js +62 -41
- package/dist/evals/scorers/classify-command.js.map +1 -1
- package/dist/evals/scorers/efficiency.js +239 -181
- package/dist/evals/scorers/efficiency.js.map +1 -1
- package/dist/evals/scorers/extract-tools.js +51 -47
- package/dist/evals/scorers/extract-tools.js.map +1 -1
- package/dist/evals/scorers/index.js +1 -5
- package/dist/evals/scorers/outcome.js +321 -212
- package/dist/evals/scorers/outcome.js.map +1 -1
- package/dist/goal-manager.js +266 -261
- package/dist/goal-manager.js.map +1 -1
- package/dist/goal-signal.js +14 -13
- package/dist/goal-signal.js.map +1 -1
- package/dist/headless/cli.js +153 -154
- package/dist/headless/cli.js.map +1 -1
- package/dist/headless/flags.js +180 -178
- package/dist/headless/flags.js.map +1 -1
- package/dist/headless/format.js +72 -71
- package/dist/headless/format.js.map +1 -1
- package/dist/headless/index.js +5 -31
- package/dist/headless/policy.js +35 -31
- package/dist/headless/policy.js.map +1 -1
- package/dist/headless/run-mc.js +332 -334
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/headless/types.js +16 -7
- package/dist/headless/types.js.map +1 -1
- package/dist/hooks/config.js +56 -61
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/executor.js +118 -116
- package/dist/hooks/executor.js.map +1 -1
- package/dist/hooks/index.js +4 -15
- package/dist/hooks/manager.js +221 -280
- package/dist/hooks/manager.js.map +1 -1
- package/dist/hooks/types.js +5 -4
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +632 -712
- package/dist/index.js.map +1 -1
- package/dist/ipc/ipc-reporter.js +19 -22
- package/dist/ipc/ipc-reporter.js.map +1 -1
- package/dist/lsp/client.js +248 -306
- package/dist/lsp/client.js.map +1 -1
- package/dist/lsp/index.js +1 -6
- package/dist/lsp/language.js +39 -43
- package/dist/lsp/language.js.map +1 -1
- package/dist/lsp/manager.js +78 -93
- package/dist/lsp/manager.js.map +1 -1
- package/dist/lsp/server.js +151 -120
- package/dist/lsp/server.js.map +1 -1
- package/dist/lsp/workspace.js +50 -47
- package/dist/lsp/workspace.js.map +1 -1
- package/dist/mcp/config.js +210 -199
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/index.js +2 -9
- package/dist/mcp/manager.js +482 -475
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.js +1 -1
- package/dist/onboarding/custom-packs.js +14 -17
- package/dist/onboarding/custom-packs.js.map +1 -1
- package/dist/onboarding/custom-providers.js +22 -20
- package/dist/onboarding/custom-providers.js.map +1 -1
- package/dist/onboarding/index.d.ts +1 -1
- package/dist/onboarding/index.d.ts.map +1 -1
- package/dist/onboarding/index.js +3 -21
- package/dist/onboarding/om-settings.d.ts +5 -0
- package/dist/onboarding/om-settings.d.ts.map +1 -1
- package/dist/onboarding/om-settings.js +41 -20
- package/dist/onboarding/om-settings.js.map +1 -1
- package/dist/onboarding/packs.d.ts +4 -6
- package/dist/onboarding/packs.d.ts.map +1 -1
- package/dist/onboarding/packs.js +154 -105
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/onboarding/settings.js +575 -517
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/permissions.js +112 -99
- package/dist/permissions.js.map +1 -1
- package/dist/plugin.js +21 -23
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/dependencies.js +64 -87
- package/dist/plugins/dependencies.js.map +1 -1
- package/dist/plugins/install.js +159 -169
- package/dist/plugins/install.js.map +1 -1
- package/dist/plugins/loader.js +170 -185
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/manager.js +374 -380
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/manifest.js +38 -63
- package/dist/plugins/manifest.js.map +1 -1
- package/dist/plugins/package-link.js +48 -64
- package/dist/plugins/package-link.js.map +1 -1
- package/dist/plugins/paths.js +18 -19
- package/dist/plugins/paths.js.map +1 -1
- package/dist/plugins/registry.js +97 -81
- package/dist/plugins/registry.js.map +1 -1
- package/dist/plugins/scaffold.js +62 -98
- package/dist/plugins/scaffold.js.map +1 -1
- package/dist/plugins/types.js +1 -1
- package/dist/processors/plan-rejection-abort.js +45 -44
- package/dist/processors/plan-rejection-abort.js.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.js +134 -125
- package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
- package/dist/providers/amazon-bedrock.js +68 -47
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/claude-max.js +153 -133
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.js +232 -230
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/openai-codex.js +271 -260
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/xai.js +57 -50
- package/dist/providers/xai.js.map +1 -1
- package/dist/schema.js +91 -89
- package/dist/schema.js.map +1 -1
- package/dist/theme-palette.js +18 -14
- package/dist/theme-palette.js.map +1 -1
- package/dist/tool-names.js +46 -37
- package/dist/tool-names.js.map +1 -1
- package/dist/tools/index.js +2 -8
- package/dist/tools/request-sandbox-access.js +71 -80
- package/dist/tools/request-sandbox-access.js.map +1 -1
- package/dist/tools/utils.js +26 -16
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/web-search.js +51 -55
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/binaries.js +65 -61
- package/dist/utils/binaries.js.map +1 -1
- package/dist/utils/debug-log.js +51 -47
- package/dist/utils/debug-log.js.map +1 -1
- package/dist/utils/errors.js +157 -192
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/gateway-sync.js +49 -37
- package/dist/utils/gateway-sync.js.map +1 -1
- package/dist/utils/path-security.js +7 -6
- package/dist/utils/path-security.js.map +1 -1
- package/dist/utils/plan-diff.js +58 -39
- package/dist/utils/plan-diff.js.map +1 -1
- package/dist/utils/plans.js +92 -74
- package/dist/utils/plans.js.map +1 -1
- package/dist/utils/project.js +318 -256
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/signals-pubsub.js +117 -99
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/slash-command-loader.js +118 -154
- package/dist/utils/slash-command-loader.js.map +1 -1
- package/dist/utils/slash-command-processor.js +92 -83
- package/dist/utils/slash-command-processor.js.map +1 -1
- package/dist/utils/stdin-pipe.js +87 -63
- package/dist/utils/stdin-pipe.js.map +1 -1
- package/dist/utils/storage-factory.js +104 -84
- package/dist/utils/storage-factory.js.map +1 -1
- package/dist/utils/storage-maintenance.js +249 -211
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/dist/utils/thread-lock.js +96 -92
- package/dist/utils/thread-lock.js.map +1 -1
- package/dist/utils/token-estimator.js +12 -12
- package/dist/utils/token-estimator.js.map +1 -1
- package/dist/utils/update-check.d.ts +1 -1
- package/dist/utils/update-check.js +341 -259
- package/dist/utils/update-check.js.map +1 -1
- package/dist/voice/stt-registry.js +131 -113
- package/dist/voice/stt-registry.js.map +1 -1
- package/package.json +11 -11
- package/dist/acp.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/evals/index.js.map +0 -1
- package/dist/evals/scorers/index.js.map +0 -1
- package/dist/headless/index.js.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/lsp/index.js.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/types.js.map +0 -1
- package/dist/onboarding/index.js.map +0 -1
- package/dist/plugins/types.js.map +0 -1
- package/dist/tools/index.js.map +0 -1
package/dist/plugins/manager.js
CHANGED
|
@@ -1,390 +1,384 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { execa } from "execa";
|
|
4
1
|
import { getEntryPackageRoot, installPluginDependenciesForEntry } from "./dependencies.js";
|
|
5
|
-
import { discoverLocalPlugins, installGithubPlugin, installLocalPlugin, NON_INTERACTIVE_GIT_ENV } from "./install.js";
|
|
6
|
-
import { collectActivePluginTools, isInsideDirectory, loadPlugins, resolvePluginEntryPath } from "./loader.js";
|
|
7
|
-
import { ensureMastraCodePackageLink } from "./package-link.js";
|
|
8
2
|
import { getPluginScopePaths } from "./paths.js";
|
|
9
3
|
import { loadPluginRegistry, removePluginRecord, savePluginRegistry, setPluginRecord } from "./registry.js";
|
|
4
|
+
import { collectActivePluginTools, isInsideDirectory, loadPlugins, resolvePluginEntryPath } from "./loader.js";
|
|
5
|
+
import { ensureMastraCodePackageLink } from "./package-link.js";
|
|
6
|
+
import { NON_INTERACTIVE_GIT_ENV, discoverLocalPlugins, installGithubPlugin, installLocalPlugin } from "./install.js";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { execa } from "execa";
|
|
10
|
+
//#region src/plugins/manager.ts
|
|
10
11
|
const GITHUB_PLUGIN_POLL_INTERVAL_MS = 6e4;
|
|
11
12
|
function gitExecOptions(cwd) {
|
|
12
|
-
|
|
13
|
+
return {
|
|
14
|
+
cwd,
|
|
15
|
+
env: NON_INTERACTIVE_GIT_ENV
|
|
16
|
+
};
|
|
13
17
|
}
|
|
14
18
|
function getEntryVersion(entryPath) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
class PluginManager {
|
|
19
|
-
constructor(options) {
|
|
20
|
-
this.options = options;
|
|
21
|
-
}
|
|
22
|
-
options;
|
|
23
|
-
loadedPlugins = [];
|
|
24
|
-
pluginTools = {};
|
|
25
|
-
rawPluginTools = {};
|
|
26
|
-
toolRenderConfigs = /* @__PURE__ */ new Map();
|
|
27
|
-
watchedLocalEntries = /* @__PURE__ */ new Set();
|
|
28
|
-
localEntryVersions = /* @__PURE__ */ new Map();
|
|
29
|
-
githubPollTimer;
|
|
30
|
-
githubPollInFlight;
|
|
31
|
-
reloadInFlight;
|
|
32
|
-
reloadListeners = /* @__PURE__ */ new Set();
|
|
33
|
-
githubUpdateListeners = /* @__PURE__ */ new Set();
|
|
34
|
-
onReload(listener) {
|
|
35
|
-
this.reloadListeners.add(listener);
|
|
36
|
-
return () => this.reloadListeners.delete(listener);
|
|
37
|
-
}
|
|
38
|
-
/** Notified with the display names of GitHub plugins that were updated by the background poll. */
|
|
39
|
-
onGithubPluginsUpdated(listener) {
|
|
40
|
-
this.githubUpdateListeners.add(listener);
|
|
41
|
-
return () => this.githubUpdateListeners.delete(listener);
|
|
42
|
-
}
|
|
43
|
-
async reload() {
|
|
44
|
-
if (this.reloadInFlight) return this.reloadInFlight;
|
|
45
|
-
this.reloadInFlight = (async () => {
|
|
46
|
-
this.loadedPlugins = await loadPlugins(this.options);
|
|
47
|
-
this.updateLocalEntryWatchers(this.loadedPlugins);
|
|
48
|
-
this.updateGithubPoller(this.loadedPlugins);
|
|
49
|
-
this.updatePluginRenderConfigs(this.loadedPlugins);
|
|
50
|
-
this.updatePluginTools(collectActivePluginTools(this.loadedPlugins));
|
|
51
|
-
await this.notifyReloadListeners(this.loadedPlugins);
|
|
52
|
-
return this.loadedPlugins;
|
|
53
|
-
})().finally(() => {
|
|
54
|
-
this.reloadInFlight = void 0;
|
|
55
|
-
});
|
|
56
|
-
return this.reloadInFlight;
|
|
57
|
-
}
|
|
58
|
-
async listPlugins() {
|
|
59
|
-
if (this.loadedPlugins.length === 0) {
|
|
60
|
-
await this.reload();
|
|
61
|
-
}
|
|
62
|
-
return this.loadedPlugins;
|
|
63
|
-
}
|
|
64
|
-
getLoadedPlugins() {
|
|
65
|
-
return this.loadedPlugins;
|
|
66
|
-
}
|
|
67
|
-
getPluginTools() {
|
|
68
|
-
return this.pluginTools;
|
|
69
|
-
}
|
|
70
|
-
getToolRenderConfig(toolName) {
|
|
71
|
-
return this.toolRenderConfigs.get(toolName);
|
|
72
|
-
}
|
|
73
|
-
getPluginSkillPaths() {
|
|
74
|
-
return this.loadedPlugins.flatMap((plugin) => plugin.status === "active" ? plugin.skillPaths ?? [] : []);
|
|
75
|
-
}
|
|
76
|
-
getPluginCommandPaths() {
|
|
77
|
-
return this.loadedPlugins.flatMap((plugin) => plugin.status === "active" ? plugin.commandPaths ?? [] : []);
|
|
78
|
-
}
|
|
79
|
-
getPluginInstructions() {
|
|
80
|
-
return this.loadedPlugins.flatMap(
|
|
81
|
-
(plugin) => plugin.status === "active" && plugin.instructions ? [plugin.instructions] : []
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
async notifyReloadListeners(plugins) {
|
|
85
|
-
await Promise.all([...this.reloadListeners].map((listener) => Promise.resolve(listener(plugins))));
|
|
86
|
-
}
|
|
87
|
-
updatePluginRenderConfigs(plugins) {
|
|
88
|
-
this.toolRenderConfigs.clear();
|
|
89
|
-
for (const plugin of plugins) {
|
|
90
|
-
if (plugin.status !== "active") continue;
|
|
91
|
-
for (const [toolName, renderConfig] of Object.entries(plugin.renderConfigs ?? {})) {
|
|
92
|
-
if (!this.toolRenderConfigs.has(toolName)) {
|
|
93
|
-
this.toolRenderConfigs.set(toolName, renderConfig);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
updatePluginTools(nextTools) {
|
|
99
|
-
for (const name of Object.keys(this.rawPluginTools)) {
|
|
100
|
-
if (!(name in nextTools)) {
|
|
101
|
-
delete this.rawPluginTools[name];
|
|
102
|
-
delete this.pluginTools[name];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
for (const [name, tool] of Object.entries(nextTools)) {
|
|
106
|
-
this.rawPluginTools[name] = tool;
|
|
107
|
-
if (!this.pluginTools[name]) {
|
|
108
|
-
this.pluginTools[name] = this.createLiveToolProxy(name);
|
|
109
|
-
}
|
|
110
|
-
this.syncLiveToolProxy(name, tool);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
createLiveToolProxy(toolName) {
|
|
114
|
-
return {
|
|
115
|
-
execute: async (...args) => {
|
|
116
|
-
await this.reloadChangedLocalPlugins();
|
|
117
|
-
const latestTool = this.rawPluginTools[toolName];
|
|
118
|
-
if (!latestTool?.execute) {
|
|
119
|
-
throw new Error(`Plugin tool "${toolName}" is no longer available`);
|
|
120
|
-
}
|
|
121
|
-
return latestTool.execute(...args);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
syncLiveToolProxy(toolName, tool) {
|
|
126
|
-
const proxy = this.pluginTools[toolName];
|
|
127
|
-
if (!proxy) return;
|
|
128
|
-
const mutableProxy = proxy;
|
|
129
|
-
for (const key of Object.keys(mutableProxy)) {
|
|
130
|
-
delete mutableProxy[key];
|
|
131
|
-
}
|
|
132
|
-
Object.assign(proxy, tool);
|
|
133
|
-
proxy.execute = this.createLiveToolProxy(toolName).execute;
|
|
134
|
-
}
|
|
135
|
-
async reloadChangedLocalPlugins() {
|
|
136
|
-
for (const plugin of this.loadedPlugins) {
|
|
137
|
-
if (plugin.source !== "local" || plugin.status !== "active") continue;
|
|
138
|
-
const entryPath = resolvePluginEntryPath(plugin, this.options);
|
|
139
|
-
const currentVersion = getEntryVersion(entryPath);
|
|
140
|
-
if (this.localEntryVersions.get(entryPath) !== currentVersion) {
|
|
141
|
-
await this.reload();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
updateLocalEntryWatchers(plugins) {
|
|
147
|
-
const nextEntries = /* @__PURE__ */ new Set();
|
|
148
|
-
for (const plugin of plugins) {
|
|
149
|
-
if (plugin.source !== "local" || plugin.status !== "active") continue;
|
|
150
|
-
let entryPath;
|
|
151
|
-
let entryVersion;
|
|
152
|
-
try {
|
|
153
|
-
entryPath = resolvePluginEntryPath(plugin, this.options);
|
|
154
|
-
entryVersion = getEntryVersion(entryPath);
|
|
155
|
-
} catch {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
nextEntries.add(entryPath);
|
|
159
|
-
this.localEntryVersions.set(entryPath, entryVersion);
|
|
160
|
-
if (this.watchedLocalEntries.has(entryPath)) continue;
|
|
161
|
-
const watcher = fs.watchFile(entryPath, { interval: 500 }, (current, previous) => {
|
|
162
|
-
if (current.mtimeMs === previous.mtimeMs) return;
|
|
163
|
-
void this.reload().catch(() => void 0);
|
|
164
|
-
});
|
|
165
|
-
watcher.unref?.();
|
|
166
|
-
this.watchedLocalEntries.add(entryPath);
|
|
167
|
-
}
|
|
168
|
-
for (const entryPath of this.watchedLocalEntries) {
|
|
169
|
-
if (nextEntries.has(entryPath)) continue;
|
|
170
|
-
fs.unwatchFile(entryPath);
|
|
171
|
-
this.watchedLocalEntries.delete(entryPath);
|
|
172
|
-
this.localEntryVersions.delete(entryPath);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
updateGithubPoller(plugins) {
|
|
176
|
-
const hasGithubPlugin = plugins.some(
|
|
177
|
-
(plugin) => plugin.source === "github" && plugin.status !== "inactive" && plugin.status !== "blocked"
|
|
178
|
-
);
|
|
179
|
-
if (hasGithubPlugin && !this.githubPollTimer) {
|
|
180
|
-
this.githubPollTimer = setInterval(() => {
|
|
181
|
-
void this.pollGithubSourcesForUpdates().catch(() => void 0);
|
|
182
|
-
}, GITHUB_PLUGIN_POLL_INTERVAL_MS);
|
|
183
|
-
this.githubPollTimer.unref?.();
|
|
184
|
-
}
|
|
185
|
-
if (!hasGithubPlugin && this.githubPollTimer) {
|
|
186
|
-
clearInterval(this.githubPollTimer);
|
|
187
|
-
this.githubPollTimer = void 0;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
async pollGithubSourcesForUpdates() {
|
|
191
|
-
if (this.githubPollInFlight) return this.githubPollInFlight;
|
|
192
|
-
this.githubPollInFlight = this.pollGithubSourcesForUpdatesOnce().finally(() => {
|
|
193
|
-
this.githubPollInFlight = void 0;
|
|
194
|
-
});
|
|
195
|
-
return this.githubPollInFlight;
|
|
196
|
-
}
|
|
197
|
-
async pollGithubSourcesForUpdatesOnce() {
|
|
198
|
-
const changedCheckouts = /* @__PURE__ */ new Set();
|
|
199
|
-
const seen = /* @__PURE__ */ new Set();
|
|
200
|
-
for (const plugin of this.loadedPlugins) {
|
|
201
|
-
if (plugin.source !== "github" || plugin.status === "inactive" || plugin.status === "blocked") continue;
|
|
202
|
-
const checkoutPath = this.resolvePluginSourcePath(plugin);
|
|
203
|
-
if (seen.has(checkoutPath) || !fs.existsSync(path.join(checkoutPath, ".git"))) continue;
|
|
204
|
-
seen.add(checkoutPath);
|
|
205
|
-
const before = await this.readGitHead(checkoutPath);
|
|
206
|
-
const checkoutChanged = await this.refreshGithubCheckout(plugin, checkoutPath, before);
|
|
207
|
-
const after = await this.readGitHead(checkoutPath);
|
|
208
|
-
if (checkoutChanged || before !== after) changedCheckouts.add(checkoutPath);
|
|
209
|
-
}
|
|
210
|
-
if (changedCheckouts.size === 0) return false;
|
|
211
|
-
await this.reload();
|
|
212
|
-
const updatedPluginNames = this.loadedPlugins.filter(
|
|
213
|
-
(plugin) => plugin.source === "github" && plugin.status !== "inactive" && plugin.status !== "blocked" && changedCheckouts.has(this.resolvePluginSourcePath(plugin))
|
|
214
|
-
).map((plugin) => plugin.name ?? plugin.id);
|
|
215
|
-
await this.notifyGithubUpdateListeners(updatedPluginNames);
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
async notifyGithubUpdateListeners(pluginNames) {
|
|
219
|
-
await Promise.all([...this.githubUpdateListeners].map((listener) => Promise.resolve(listener(pluginNames))));
|
|
220
|
-
}
|
|
221
|
-
async refreshGithubCheckout(plugin, checkoutPath, currentHead) {
|
|
222
|
-
await execa("git", ["fetch", "origin"], gitExecOptions(checkoutPath));
|
|
223
|
-
const upstream = await this.resolveGitUpstream(checkoutPath, plugin.ref);
|
|
224
|
-
if (!upstream) return false;
|
|
225
|
-
const [localOnly, remoteOnly] = await this.readGitAheadBehind(checkoutPath, upstream);
|
|
226
|
-
const hasLocalChanges = await this.hasGitWorkingTreeChanges(checkoutPath);
|
|
227
|
-
if (localOnly > 0 || hasLocalChanges) {
|
|
228
|
-
await this.backupGitCheckout(checkoutPath, currentHead, hasLocalChanges);
|
|
229
|
-
}
|
|
230
|
-
if (remoteOnly > 0 || localOnly > 0 || hasLocalChanges) {
|
|
231
|
-
await execa("git", ["reset", "--hard", upstream], gitExecOptions(checkoutPath));
|
|
232
|
-
try {
|
|
233
|
-
await installPluginDependenciesForEntry(checkoutPath, plugin.entry);
|
|
234
|
-
ensureMastraCodePackageLink(getEntryPackageRoot(checkoutPath, plugin.entry));
|
|
235
|
-
} catch (error) {
|
|
236
|
-
await execa("git", ["reset", "--hard", currentHead], gitExecOptions(checkoutPath));
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
async backupGitCheckout(checkoutPath, currentHead, includeWorkingTree) {
|
|
244
|
-
const backupBranch = this.createGitBackupBranchName(currentHead);
|
|
245
|
-
if (includeWorkingTree) {
|
|
246
|
-
const currentBranch = await this.readGitCurrentBranch(checkoutPath);
|
|
247
|
-
await execa("git", ["switch", "-c", backupBranch], gitExecOptions(checkoutPath));
|
|
248
|
-
await execa("git", ["add", "-A"], gitExecOptions(checkoutPath));
|
|
249
|
-
const hasStagedChanges = await this.hasGitStagedChanges(checkoutPath);
|
|
250
|
-
if (hasStagedChanges) {
|
|
251
|
-
await execa(
|
|
252
|
-
"git",
|
|
253
|
-
[
|
|
254
|
-
"-c",
|
|
255
|
-
"user.name=Mastra Code",
|
|
256
|
-
"-c",
|
|
257
|
-
"user.email=noreply@mastra.ai",
|
|
258
|
-
"commit",
|
|
259
|
-
"-m",
|
|
260
|
-
"chore: backup local plugin checkout changes"
|
|
261
|
-
],
|
|
262
|
-
gitExecOptions(checkoutPath)
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
await this.restoreGitCheckout(checkoutPath, currentBranch, currentHead);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
await execa("git", ["branch", backupBranch, "HEAD"], gitExecOptions(checkoutPath));
|
|
269
|
-
}
|
|
270
|
-
async resolveGitUpstream(cwd, installedRef) {
|
|
271
|
-
try {
|
|
272
|
-
const { stdout } = await execa(
|
|
273
|
-
"git",
|
|
274
|
-
["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
|
|
275
|
-
gitExecOptions(cwd)
|
|
276
|
-
);
|
|
277
|
-
return stdout.trim();
|
|
278
|
-
} catch {
|
|
279
|
-
return installedRef ? void 0 : "origin/main";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
async readGitAheadBehind(cwd, upstream) {
|
|
283
|
-
const { stdout } = await execa(
|
|
284
|
-
"git",
|
|
285
|
-
["rev-list", "--left-right", "--count", `HEAD...${upstream}`],
|
|
286
|
-
gitExecOptions(cwd)
|
|
287
|
-
);
|
|
288
|
-
const [ahead = "0", behind = "0"] = stdout.trim().split(/\s+/);
|
|
289
|
-
return [Number(ahead) || 0, Number(behind) || 0];
|
|
290
|
-
}
|
|
291
|
-
async hasGitWorkingTreeChanges(cwd) {
|
|
292
|
-
const { stdout } = await execa("git", ["status", "--porcelain"], gitExecOptions(cwd));
|
|
293
|
-
return stdout.trim().length > 0;
|
|
294
|
-
}
|
|
295
|
-
async hasGitStagedChanges(cwd) {
|
|
296
|
-
try {
|
|
297
|
-
await execa("git", ["diff", "--cached", "--quiet"], gitExecOptions(cwd));
|
|
298
|
-
return false;
|
|
299
|
-
} catch {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
async restoreGitCheckout(cwd, branch, fallbackHead) {
|
|
304
|
-
if (branch) {
|
|
305
|
-
await execa("git", ["switch", branch], gitExecOptions(cwd));
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
await execa("git", ["checkout", fallbackHead], gitExecOptions(cwd));
|
|
309
|
-
}
|
|
310
|
-
async readGitCurrentBranch(cwd) {
|
|
311
|
-
const { stdout } = await execa("git", ["branch", "--show-current"], gitExecOptions(cwd));
|
|
312
|
-
const branch = stdout.trim();
|
|
313
|
-
return branch.length > 0 ? branch : void 0;
|
|
314
|
-
}
|
|
315
|
-
createGitBackupBranchName(currentHead) {
|
|
316
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
317
|
-
return `mastracode/plugin-backup/${timestamp}-${currentHead.slice(0, 8)}`;
|
|
318
|
-
}
|
|
319
|
-
resolvePluginSourcePath(plugin) {
|
|
320
|
-
const paths = getPluginScopePaths(plugin.scope, this.options);
|
|
321
|
-
return path.isAbsolute(plugin.path) ? plugin.path : path.join(paths.root, plugin.path);
|
|
322
|
-
}
|
|
323
|
-
async readGitHead(cwd) {
|
|
324
|
-
const { stdout } = await execa("git", ["rev-parse", "HEAD"], gitExecOptions(cwd));
|
|
325
|
-
return stdout.trim();
|
|
326
|
-
}
|
|
327
|
-
discoverLocal(searchRoot = ".") {
|
|
328
|
-
return discoverLocalPlugins(searchRoot, this.options);
|
|
329
|
-
}
|
|
330
|
-
async installLocal(localPath, scope, options = {}) {
|
|
331
|
-
const id = await installLocalPlugin(localPath, scope, { ...this.options, ...options });
|
|
332
|
-
await this.reload();
|
|
333
|
-
return id;
|
|
334
|
-
}
|
|
335
|
-
async installGithub(url, scope, options = {}) {
|
|
336
|
-
const id = await installGithubPlugin(url, scope, { ...this.options, ...options });
|
|
337
|
-
await this.reload();
|
|
338
|
-
return id;
|
|
339
|
-
}
|
|
340
|
-
async setEnabled(pluginId, scope, enabled) {
|
|
341
|
-
const paths = getPluginScopePaths(scope, this.options);
|
|
342
|
-
const registry = loadPluginRegistry(paths.registryPath);
|
|
343
|
-
const record = registry.plugins[pluginId];
|
|
344
|
-
if (!record) {
|
|
345
|
-
throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
346
|
-
}
|
|
347
|
-
savePluginRegistry(paths.registryPath, setPluginRecord(registry, pluginId, { ...record, enabled }));
|
|
348
|
-
await this.reload();
|
|
349
|
-
}
|
|
350
|
-
async setConfigValue(pluginId, scope, key, value) {
|
|
351
|
-
const paths = getPluginScopePaths(scope, this.options);
|
|
352
|
-
const registry = loadPluginRegistry(paths.registryPath);
|
|
353
|
-
const record = registry.plugins[pluginId];
|
|
354
|
-
if (!record) {
|
|
355
|
-
throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
356
|
-
}
|
|
357
|
-
const config = { ...record.config ?? {} };
|
|
358
|
-
if (value === void 0 || value === "") {
|
|
359
|
-
delete config[key];
|
|
360
|
-
} else {
|
|
361
|
-
config[key] = value;
|
|
362
|
-
}
|
|
363
|
-
const nextRecord = { ...record, config: Object.keys(config).length > 0 ? config : void 0 };
|
|
364
|
-
savePluginRegistry(paths.registryPath, setPluginRecord(registry, pluginId, nextRecord));
|
|
365
|
-
await this.reload();
|
|
366
|
-
}
|
|
367
|
-
async uninstall(pluginId, scope) {
|
|
368
|
-
const paths = getPluginScopePaths(scope, this.options);
|
|
369
|
-
const registry = loadPluginRegistry(paths.registryPath);
|
|
370
|
-
const record = registry.plugins[pluginId];
|
|
371
|
-
if (!record) {
|
|
372
|
-
throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
373
|
-
}
|
|
374
|
-
savePluginRegistry(paths.registryPath, removePluginRecord(registry, pluginId));
|
|
375
|
-
if (record.source === "github") {
|
|
376
|
-
const checkoutPath = path.resolve(
|
|
377
|
-
path.isAbsolute(record.path) ? record.path : path.join(paths.root, record.path)
|
|
378
|
-
);
|
|
379
|
-
const githubSourcesPath = path.resolve(paths.sourcesPath, "github");
|
|
380
|
-
if (isInsideDirectory(checkoutPath, githubSourcesPath)) {
|
|
381
|
-
fs.rmSync(checkoutPath, { recursive: true, force: true });
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
await this.reload();
|
|
385
|
-
}
|
|
19
|
+
const stat = fs.statSync(entryPath, { bigint: true });
|
|
20
|
+
return `${stat.mtimeNs}:${stat.size}`;
|
|
386
21
|
}
|
|
387
|
-
|
|
388
|
-
|
|
22
|
+
var PluginManager = class {
|
|
23
|
+
options;
|
|
24
|
+
loadedPlugins = [];
|
|
25
|
+
pluginTools = {};
|
|
26
|
+
rawPluginTools = {};
|
|
27
|
+
toolRenderConfigs = /* @__PURE__ */ new Map();
|
|
28
|
+
watchedLocalEntries = /* @__PURE__ */ new Set();
|
|
29
|
+
localEntryVersions = /* @__PURE__ */ new Map();
|
|
30
|
+
githubPollTimer;
|
|
31
|
+
githubPollInFlight;
|
|
32
|
+
reloadInFlight;
|
|
33
|
+
reloadListeners = /* @__PURE__ */ new Set();
|
|
34
|
+
githubUpdateListeners = /* @__PURE__ */ new Set();
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.options = options;
|
|
37
|
+
}
|
|
38
|
+
onReload(listener) {
|
|
39
|
+
this.reloadListeners.add(listener);
|
|
40
|
+
return () => this.reloadListeners.delete(listener);
|
|
41
|
+
}
|
|
42
|
+
/** Notified with the display names of GitHub plugins that were updated by the background poll. */
|
|
43
|
+
onGithubPluginsUpdated(listener) {
|
|
44
|
+
this.githubUpdateListeners.add(listener);
|
|
45
|
+
return () => this.githubUpdateListeners.delete(listener);
|
|
46
|
+
}
|
|
47
|
+
async reload() {
|
|
48
|
+
if (this.reloadInFlight) return this.reloadInFlight;
|
|
49
|
+
this.reloadInFlight = (async () => {
|
|
50
|
+
this.loadedPlugins = await loadPlugins(this.options);
|
|
51
|
+
this.updateLocalEntryWatchers(this.loadedPlugins);
|
|
52
|
+
this.updateGithubPoller(this.loadedPlugins);
|
|
53
|
+
this.updatePluginRenderConfigs(this.loadedPlugins);
|
|
54
|
+
this.updatePluginTools(collectActivePluginTools(this.loadedPlugins));
|
|
55
|
+
await this.notifyReloadListeners(this.loadedPlugins);
|
|
56
|
+
return this.loadedPlugins;
|
|
57
|
+
})().finally(() => {
|
|
58
|
+
this.reloadInFlight = void 0;
|
|
59
|
+
});
|
|
60
|
+
return this.reloadInFlight;
|
|
61
|
+
}
|
|
62
|
+
async listPlugins() {
|
|
63
|
+
if (this.loadedPlugins.length === 0) await this.reload();
|
|
64
|
+
return this.loadedPlugins;
|
|
65
|
+
}
|
|
66
|
+
getLoadedPlugins() {
|
|
67
|
+
return this.loadedPlugins;
|
|
68
|
+
}
|
|
69
|
+
getPluginTools() {
|
|
70
|
+
return this.pluginTools;
|
|
71
|
+
}
|
|
72
|
+
getToolRenderConfig(toolName) {
|
|
73
|
+
return this.toolRenderConfigs.get(toolName);
|
|
74
|
+
}
|
|
75
|
+
getPluginSkillPaths() {
|
|
76
|
+
return this.loadedPlugins.flatMap((plugin) => plugin.status === "active" ? plugin.skillPaths ?? [] : []);
|
|
77
|
+
}
|
|
78
|
+
getPluginCommandPaths() {
|
|
79
|
+
return this.loadedPlugins.flatMap((plugin) => plugin.status === "active" ? plugin.commandPaths ?? [] : []);
|
|
80
|
+
}
|
|
81
|
+
getPluginInstructions() {
|
|
82
|
+
return this.loadedPlugins.flatMap((plugin) => plugin.status === "active" && plugin.instructions ? [plugin.instructions] : []);
|
|
83
|
+
}
|
|
84
|
+
async notifyReloadListeners(plugins) {
|
|
85
|
+
await Promise.all([...this.reloadListeners].map((listener) => Promise.resolve(listener(plugins))));
|
|
86
|
+
}
|
|
87
|
+
updatePluginRenderConfigs(plugins) {
|
|
88
|
+
this.toolRenderConfigs.clear();
|
|
89
|
+
for (const plugin of plugins) {
|
|
90
|
+
if (plugin.status !== "active") continue;
|
|
91
|
+
for (const [toolName, renderConfig] of Object.entries(plugin.renderConfigs ?? {})) if (!this.toolRenderConfigs.has(toolName)) this.toolRenderConfigs.set(toolName, renderConfig);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
updatePluginTools(nextTools) {
|
|
95
|
+
for (const name of Object.keys(this.rawPluginTools)) if (!(name in nextTools)) {
|
|
96
|
+
delete this.rawPluginTools[name];
|
|
97
|
+
delete this.pluginTools[name];
|
|
98
|
+
}
|
|
99
|
+
for (const [name, tool] of Object.entries(nextTools)) {
|
|
100
|
+
this.rawPluginTools[name] = tool;
|
|
101
|
+
if (!this.pluginTools[name]) this.pluginTools[name] = this.createLiveToolProxy(name);
|
|
102
|
+
this.syncLiveToolProxy(name, tool);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
createLiveToolProxy(toolName) {
|
|
106
|
+
return { execute: async (...args) => {
|
|
107
|
+
await this.reloadChangedLocalPlugins();
|
|
108
|
+
const latestTool = this.rawPluginTools[toolName];
|
|
109
|
+
if (!latestTool?.execute) throw new Error(`Plugin tool "${toolName}" is no longer available`);
|
|
110
|
+
return latestTool.execute(...args);
|
|
111
|
+
} };
|
|
112
|
+
}
|
|
113
|
+
syncLiveToolProxy(toolName, tool) {
|
|
114
|
+
const proxy = this.pluginTools[toolName];
|
|
115
|
+
if (!proxy) return;
|
|
116
|
+
const mutableProxy = proxy;
|
|
117
|
+
for (const key of Object.keys(mutableProxy)) delete mutableProxy[key];
|
|
118
|
+
Object.assign(proxy, tool);
|
|
119
|
+
proxy.execute = this.createLiveToolProxy(toolName).execute;
|
|
120
|
+
}
|
|
121
|
+
async reloadChangedLocalPlugins() {
|
|
122
|
+
for (const plugin of this.loadedPlugins) {
|
|
123
|
+
if (plugin.source !== "local" || plugin.status !== "active") continue;
|
|
124
|
+
const entryPath = resolvePluginEntryPath(plugin, this.options);
|
|
125
|
+
const currentVersion = getEntryVersion(entryPath);
|
|
126
|
+
if (this.localEntryVersions.get(entryPath) !== currentVersion) {
|
|
127
|
+
await this.reload();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
updateLocalEntryWatchers(plugins) {
|
|
133
|
+
const nextEntries = /* @__PURE__ */ new Set();
|
|
134
|
+
for (const plugin of plugins) {
|
|
135
|
+
if (plugin.source !== "local" || plugin.status !== "active") continue;
|
|
136
|
+
let entryPath;
|
|
137
|
+
let entryVersion;
|
|
138
|
+
try {
|
|
139
|
+
entryPath = resolvePluginEntryPath(plugin, this.options);
|
|
140
|
+
entryVersion = getEntryVersion(entryPath);
|
|
141
|
+
} catch {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
nextEntries.add(entryPath);
|
|
145
|
+
this.localEntryVersions.set(entryPath, entryVersion);
|
|
146
|
+
if (this.watchedLocalEntries.has(entryPath)) continue;
|
|
147
|
+
fs.watchFile(entryPath, { interval: 500 }, (current, previous) => {
|
|
148
|
+
if (current.mtimeMs === previous.mtimeMs) return;
|
|
149
|
+
this.reload().catch(() => void 0);
|
|
150
|
+
}).unref?.();
|
|
151
|
+
this.watchedLocalEntries.add(entryPath);
|
|
152
|
+
}
|
|
153
|
+
for (const entryPath of this.watchedLocalEntries) {
|
|
154
|
+
if (nextEntries.has(entryPath)) continue;
|
|
155
|
+
fs.unwatchFile(entryPath);
|
|
156
|
+
this.watchedLocalEntries.delete(entryPath);
|
|
157
|
+
this.localEntryVersions.delete(entryPath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
updateGithubPoller(plugins) {
|
|
161
|
+
const hasGithubPlugin = plugins.some((plugin) => plugin.source === "github" && plugin.status !== "inactive" && plugin.status !== "blocked");
|
|
162
|
+
if (hasGithubPlugin && !this.githubPollTimer) {
|
|
163
|
+
this.githubPollTimer = setInterval(() => {
|
|
164
|
+
this.pollGithubSourcesForUpdates().catch(() => void 0);
|
|
165
|
+
}, GITHUB_PLUGIN_POLL_INTERVAL_MS);
|
|
166
|
+
this.githubPollTimer.unref?.();
|
|
167
|
+
}
|
|
168
|
+
if (!hasGithubPlugin && this.githubPollTimer) {
|
|
169
|
+
clearInterval(this.githubPollTimer);
|
|
170
|
+
this.githubPollTimer = void 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async pollGithubSourcesForUpdates() {
|
|
174
|
+
if (this.githubPollInFlight) return this.githubPollInFlight;
|
|
175
|
+
this.githubPollInFlight = this.pollGithubSourcesForUpdatesOnce().finally(() => {
|
|
176
|
+
this.githubPollInFlight = void 0;
|
|
177
|
+
});
|
|
178
|
+
return this.githubPollInFlight;
|
|
179
|
+
}
|
|
180
|
+
async pollGithubSourcesForUpdatesOnce() {
|
|
181
|
+
const changedCheckouts = /* @__PURE__ */ new Set();
|
|
182
|
+
const seen = /* @__PURE__ */ new Set();
|
|
183
|
+
for (const plugin of this.loadedPlugins) {
|
|
184
|
+
if (plugin.source !== "github" || plugin.status === "inactive" || plugin.status === "blocked") continue;
|
|
185
|
+
const checkoutPath = this.resolvePluginSourcePath(plugin);
|
|
186
|
+
if (seen.has(checkoutPath) || !fs.existsSync(path.join(checkoutPath, ".git"))) continue;
|
|
187
|
+
seen.add(checkoutPath);
|
|
188
|
+
const before = await this.readGitHead(checkoutPath);
|
|
189
|
+
const checkoutChanged = await this.refreshGithubCheckout(plugin, checkoutPath, before);
|
|
190
|
+
const after = await this.readGitHead(checkoutPath);
|
|
191
|
+
if (checkoutChanged || before !== after) changedCheckouts.add(checkoutPath);
|
|
192
|
+
}
|
|
193
|
+
if (changedCheckouts.size === 0) return false;
|
|
194
|
+
await this.reload();
|
|
195
|
+
const updatedPluginNames = this.loadedPlugins.filter((plugin) => plugin.source === "github" && plugin.status !== "inactive" && plugin.status !== "blocked" && changedCheckouts.has(this.resolvePluginSourcePath(plugin))).map((plugin) => plugin.name ?? plugin.id);
|
|
196
|
+
await this.notifyGithubUpdateListeners(updatedPluginNames);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
async notifyGithubUpdateListeners(pluginNames) {
|
|
200
|
+
await Promise.all([...this.githubUpdateListeners].map((listener) => Promise.resolve(listener(pluginNames))));
|
|
201
|
+
}
|
|
202
|
+
async refreshGithubCheckout(plugin, checkoutPath, currentHead) {
|
|
203
|
+
await execa("git", ["fetch", "origin"], gitExecOptions(checkoutPath));
|
|
204
|
+
const upstream = await this.resolveGitUpstream(checkoutPath, plugin.ref);
|
|
205
|
+
if (!upstream) return false;
|
|
206
|
+
const [localOnly, remoteOnly] = await this.readGitAheadBehind(checkoutPath, upstream);
|
|
207
|
+
const hasLocalChanges = await this.hasGitWorkingTreeChanges(checkoutPath);
|
|
208
|
+
if (localOnly > 0 || hasLocalChanges) await this.backupGitCheckout(checkoutPath, currentHead, hasLocalChanges);
|
|
209
|
+
if (remoteOnly > 0 || localOnly > 0 || hasLocalChanges) {
|
|
210
|
+
await execa("git", [
|
|
211
|
+
"reset",
|
|
212
|
+
"--hard",
|
|
213
|
+
upstream
|
|
214
|
+
], gitExecOptions(checkoutPath));
|
|
215
|
+
try {
|
|
216
|
+
await installPluginDependenciesForEntry(checkoutPath, plugin.entry);
|
|
217
|
+
ensureMastraCodePackageLink(getEntryPackageRoot(checkoutPath, plugin.entry));
|
|
218
|
+
} catch (error) {
|
|
219
|
+
await execa("git", [
|
|
220
|
+
"reset",
|
|
221
|
+
"--hard",
|
|
222
|
+
currentHead
|
|
223
|
+
], gitExecOptions(checkoutPath));
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
async backupGitCheckout(checkoutPath, currentHead, includeWorkingTree) {
|
|
231
|
+
const backupBranch = this.createGitBackupBranchName(currentHead);
|
|
232
|
+
if (includeWorkingTree) {
|
|
233
|
+
const currentBranch = await this.readGitCurrentBranch(checkoutPath);
|
|
234
|
+
await execa("git", [
|
|
235
|
+
"switch",
|
|
236
|
+
"-c",
|
|
237
|
+
backupBranch
|
|
238
|
+
], gitExecOptions(checkoutPath));
|
|
239
|
+
await execa("git", ["add", "-A"], gitExecOptions(checkoutPath));
|
|
240
|
+
if (await this.hasGitStagedChanges(checkoutPath)) await execa("git", [
|
|
241
|
+
"-c",
|
|
242
|
+
"user.name=Mastra Code",
|
|
243
|
+
"-c",
|
|
244
|
+
"user.email=noreply@mastra.ai",
|
|
245
|
+
"commit",
|
|
246
|
+
"-m",
|
|
247
|
+
"chore: backup local plugin checkout changes"
|
|
248
|
+
], gitExecOptions(checkoutPath));
|
|
249
|
+
await this.restoreGitCheckout(checkoutPath, currentBranch, currentHead);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
await execa("git", [
|
|
253
|
+
"branch",
|
|
254
|
+
backupBranch,
|
|
255
|
+
"HEAD"
|
|
256
|
+
], gitExecOptions(checkoutPath));
|
|
257
|
+
}
|
|
258
|
+
async resolveGitUpstream(cwd, installedRef) {
|
|
259
|
+
try {
|
|
260
|
+
const { stdout } = await execa("git", [
|
|
261
|
+
"rev-parse",
|
|
262
|
+
"--abbrev-ref",
|
|
263
|
+
"--symbolic-full-name",
|
|
264
|
+
"@{u}"
|
|
265
|
+
], gitExecOptions(cwd));
|
|
266
|
+
return stdout.trim();
|
|
267
|
+
} catch {
|
|
268
|
+
return installedRef ? void 0 : "origin/main";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async readGitAheadBehind(cwd, upstream) {
|
|
272
|
+
const { stdout } = await execa("git", [
|
|
273
|
+
"rev-list",
|
|
274
|
+
"--left-right",
|
|
275
|
+
"--count",
|
|
276
|
+
`HEAD...${upstream}`
|
|
277
|
+
], gitExecOptions(cwd));
|
|
278
|
+
const [ahead = "0", behind = "0"] = stdout.trim().split(/\s+/);
|
|
279
|
+
return [Number(ahead) || 0, Number(behind) || 0];
|
|
280
|
+
}
|
|
281
|
+
async hasGitWorkingTreeChanges(cwd) {
|
|
282
|
+
const { stdout } = await execa("git", ["status", "--porcelain"], gitExecOptions(cwd));
|
|
283
|
+
return stdout.trim().length > 0;
|
|
284
|
+
}
|
|
285
|
+
async hasGitStagedChanges(cwd) {
|
|
286
|
+
try {
|
|
287
|
+
await execa("git", [
|
|
288
|
+
"diff",
|
|
289
|
+
"--cached",
|
|
290
|
+
"--quiet"
|
|
291
|
+
], gitExecOptions(cwd));
|
|
292
|
+
return false;
|
|
293
|
+
} catch {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async restoreGitCheckout(cwd, branch, fallbackHead) {
|
|
298
|
+
if (branch) {
|
|
299
|
+
await execa("git", ["switch", branch], gitExecOptions(cwd));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
await execa("git", ["checkout", fallbackHead], gitExecOptions(cwd));
|
|
303
|
+
}
|
|
304
|
+
async readGitCurrentBranch(cwd) {
|
|
305
|
+
const { stdout } = await execa("git", ["branch", "--show-current"], gitExecOptions(cwd));
|
|
306
|
+
const branch = stdout.trim();
|
|
307
|
+
return branch.length > 0 ? branch : void 0;
|
|
308
|
+
}
|
|
309
|
+
createGitBackupBranchName(currentHead) {
|
|
310
|
+
return `mastracode/plugin-backup/${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${currentHead.slice(0, 8)}`;
|
|
311
|
+
}
|
|
312
|
+
resolvePluginSourcePath(plugin) {
|
|
313
|
+
const paths = getPluginScopePaths(plugin.scope, this.options);
|
|
314
|
+
return path.isAbsolute(plugin.path) ? plugin.path : path.join(paths.root, plugin.path);
|
|
315
|
+
}
|
|
316
|
+
async readGitHead(cwd) {
|
|
317
|
+
const { stdout } = await execa("git", ["rev-parse", "HEAD"], gitExecOptions(cwd));
|
|
318
|
+
return stdout.trim();
|
|
319
|
+
}
|
|
320
|
+
discoverLocal(searchRoot = ".") {
|
|
321
|
+
return discoverLocalPlugins(searchRoot, this.options);
|
|
322
|
+
}
|
|
323
|
+
async installLocal(localPath, scope, options = {}) {
|
|
324
|
+
const id = await installLocalPlugin(localPath, scope, {
|
|
325
|
+
...this.options,
|
|
326
|
+
...options
|
|
327
|
+
});
|
|
328
|
+
await this.reload();
|
|
329
|
+
return id;
|
|
330
|
+
}
|
|
331
|
+
async installGithub(url, scope, options = {}) {
|
|
332
|
+
const id = await installGithubPlugin(url, scope, {
|
|
333
|
+
...this.options,
|
|
334
|
+
...options
|
|
335
|
+
});
|
|
336
|
+
await this.reload();
|
|
337
|
+
return id;
|
|
338
|
+
}
|
|
339
|
+
async setEnabled(pluginId, scope, enabled) {
|
|
340
|
+
const paths = getPluginScopePaths(scope, this.options);
|
|
341
|
+
const registry = loadPluginRegistry(paths.registryPath);
|
|
342
|
+
const record = registry.plugins[pluginId];
|
|
343
|
+
if (!record) throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
344
|
+
savePluginRegistry(paths.registryPath, setPluginRecord(registry, pluginId, {
|
|
345
|
+
...record,
|
|
346
|
+
enabled
|
|
347
|
+
}));
|
|
348
|
+
await this.reload();
|
|
349
|
+
}
|
|
350
|
+
async setConfigValue(pluginId, scope, key, value) {
|
|
351
|
+
const paths = getPluginScopePaths(scope, this.options);
|
|
352
|
+
const registry = loadPluginRegistry(paths.registryPath);
|
|
353
|
+
const record = registry.plugins[pluginId];
|
|
354
|
+
if (!record) throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
355
|
+
const config = { ...record.config ?? {} };
|
|
356
|
+
if (value === void 0 || value === "") delete config[key];
|
|
357
|
+
else config[key] = value;
|
|
358
|
+
const nextRecord = {
|
|
359
|
+
...record,
|
|
360
|
+
config: Object.keys(config).length > 0 ? config : void 0
|
|
361
|
+
};
|
|
362
|
+
savePluginRegistry(paths.registryPath, setPluginRecord(registry, pluginId, nextRecord));
|
|
363
|
+
await this.reload();
|
|
364
|
+
}
|
|
365
|
+
async uninstall(pluginId, scope) {
|
|
366
|
+
const paths = getPluginScopePaths(scope, this.options);
|
|
367
|
+
const registry = loadPluginRegistry(paths.registryPath);
|
|
368
|
+
const record = registry.plugins[pluginId];
|
|
369
|
+
if (!record) throw new Error(`Plugin "${pluginId}" is not installed in ${scope} scope`);
|
|
370
|
+
savePluginRegistry(paths.registryPath, removePluginRecord(registry, pluginId));
|
|
371
|
+
if (record.source === "github") {
|
|
372
|
+
const checkoutPath = path.resolve(path.isAbsolute(record.path) ? record.path : path.join(paths.root, record.path));
|
|
373
|
+
if (isInsideDirectory(checkoutPath, path.resolve(paths.sourcesPath, "github"))) fs.rmSync(checkoutPath, {
|
|
374
|
+
recursive: true,
|
|
375
|
+
force: true
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
await this.reload();
|
|
379
|
+
}
|
|
389
380
|
};
|
|
381
|
+
//#endregion
|
|
382
|
+
export { PluginManager };
|
|
383
|
+
|
|
390
384
|
//# sourceMappingURL=manager.js.map
|