@aliceshimada/mica 1.0.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/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +22 -0
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/README.zh-CN.md +308 -0
- package/SECURITY.md +22 -0
- package/dist/src/backend/agentRegistry.js +115 -0
- package/dist/src/backend/backendQueue.js +212 -0
- package/dist/src/backend/backendState.js +99 -0
- package/dist/src/backend/notebookRegistry.js +136 -0
- package/dist/src/backend/protocol.js +32 -0
- package/dist/src/bridge/httpBridge.js +366 -0
- package/dist/src/bridge/requestQueue.js +200 -0
- package/dist/src/bun/dashboard.js +387 -0
- package/dist/src/bun/httpServer.js +356 -0
- package/dist/src/bun/index.js +91 -0
- package/dist/src/cli/doctor.js +235 -0
- package/dist/src/cli/index.js +125 -0
- package/dist/src/index.js +54 -0
- package/dist/src/mcp/backendTools.js +216 -0
- package/dist/src/mcp/descriptions.js +6 -0
- package/dist/src/mcp/prompts.js +52 -0
- package/dist/src/mcp/toolResults.js +183 -0
- package/dist/src/mcp/toolSchemas.js +60 -0
- package/dist/src/mcp/tools.js +161 -0
- package/dist/src/runtime/config.js +76 -0
- package/dist/src/runtime/session.js +14 -0
- package/dist/src/runtimeOptions.js +3 -0
- package/dist/src/types.js +2 -0
- package/package.json +63 -0
- package/paclet/FrontEnd/Palettes/MMAAgentBridge.nb +22 -0
- package/paclet/Kernel/MMAAgentBridge.wl +1831 -0
- package/paclet/Kernel/init.wl +1 -0
- package/paclet/PacletInfo.wl +14 -0
- package/scripts/install.js +526 -0
- package/src/bun/index.ts +120 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const noArgsSchema = z.object({}).strict();
|
|
3
|
+
const notebookIdField = z.string().min(1);
|
|
4
|
+
const notebookSelectorFields = {
|
|
5
|
+
notebookId: notebookIdField.optional(),
|
|
6
|
+
displayName: z.string().min(1).optional()
|
|
7
|
+
};
|
|
8
|
+
const maxBytesField = z.number().int().positive().max(1024 * 1024);
|
|
9
|
+
export const selectNotebookSchema = z.object({
|
|
10
|
+
notebookId: notebookIdField.optional(),
|
|
11
|
+
displayName: z.string().min(1).optional()
|
|
12
|
+
}).strict();
|
|
13
|
+
export const readCellSchema = z.object({
|
|
14
|
+
...notebookSelectorFields,
|
|
15
|
+
cellId: z.string().min(1),
|
|
16
|
+
maxBytes: maxBytesField.optional()
|
|
17
|
+
}).strict();
|
|
18
|
+
export const insertCellSchema = z.object({
|
|
19
|
+
...notebookSelectorFields,
|
|
20
|
+
afterCellId: z.string().min(1).optional(),
|
|
21
|
+
style: z.string().min(1).default("Input"),
|
|
22
|
+
content: z.string()
|
|
23
|
+
}).strict();
|
|
24
|
+
export const modifyCellSchema = z.object({
|
|
25
|
+
...notebookSelectorFields,
|
|
26
|
+
cellId: z.string().min(1),
|
|
27
|
+
content: z.string()
|
|
28
|
+
}).strict();
|
|
29
|
+
export const deleteCellSchema = z.object({
|
|
30
|
+
...notebookSelectorFields,
|
|
31
|
+
cellId: z.string().min(1)
|
|
32
|
+
}).strict();
|
|
33
|
+
export const runCellSchema = z.object({
|
|
34
|
+
...notebookSelectorFields,
|
|
35
|
+
cellId: z.string().min(1),
|
|
36
|
+
timeoutSec: z.number().int().positive().max(3600).default(120)
|
|
37
|
+
}).strict();
|
|
38
|
+
export const abortEvaluationSchema = z.object({
|
|
39
|
+
...notebookSelectorFields
|
|
40
|
+
}).strict();
|
|
41
|
+
export const getCellOutputSchema = z.object({
|
|
42
|
+
...notebookSelectorFields,
|
|
43
|
+
cellId: z.string().min(1),
|
|
44
|
+
maxBytes: maxBytesField.optional()
|
|
45
|
+
}).strict();
|
|
46
|
+
export const readArtifactSchema = z.object({
|
|
47
|
+
...notebookSelectorFields,
|
|
48
|
+
artifactId: z.string().min(1),
|
|
49
|
+
offset: z.number().int().nonnegative().default(0),
|
|
50
|
+
limit: z.number().int().positive().max(1024 * 1024).default(65_536)
|
|
51
|
+
}).strict();
|
|
52
|
+
export const listCellsSchema = z.object({
|
|
53
|
+
...notebookSelectorFields
|
|
54
|
+
}).strict();
|
|
55
|
+
export const saveNotebookSchema = z.object({
|
|
56
|
+
...notebookSelectorFields
|
|
57
|
+
}).strict();
|
|
58
|
+
export const symbolLookupSchema = z.object({
|
|
59
|
+
query: z.string().min(1).describe("Symbol name or partial search term")
|
|
60
|
+
}).strict();
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { abortEvaluationSchema, deleteCellSchema, getCellOutputSchema, listCellsSchema, insertCellSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, selectNotebookSchema, saveNotebookSchema, runCellSchema, symbolLookupSchema } from "./toolSchemas.js";
|
|
2
|
+
import { INSERT_ANCHOR_GUIDANCE, notebookToolDescription } from "./descriptions.js";
|
|
3
|
+
import { toolSuccess, withToolErrors } from "./toolResults.js";
|
|
4
|
+
export function assertBridgeReadyForTool(status) {
|
|
5
|
+
if (!status.paletteConnected) {
|
|
6
|
+
throw new Error("Mathematica Palette is not connected. Open the MMA Agent Bridge palette and click 'Allow control of current Notebook'.");
|
|
7
|
+
}
|
|
8
|
+
if (!status.notebookAttached) {
|
|
9
|
+
throw new Error("No Mathematica notebook is attached. Click 'Allow control of current Notebook' in the MMA Agent Bridge palette.");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function resolveNotebookTarget(args, status) {
|
|
13
|
+
const explicit = args.notebookId;
|
|
14
|
+
if (typeof explicit === "string" && explicit.length > 0) {
|
|
15
|
+
const knownNotebook = status.notebooks?.some((notebook) => notebook.notebookId === explicit) ?? false;
|
|
16
|
+
if (!knownNotebook) {
|
|
17
|
+
throw new Error(`Unknown notebookId: ${explicit}`);
|
|
18
|
+
}
|
|
19
|
+
return explicit;
|
|
20
|
+
}
|
|
21
|
+
const displayName = args.displayName;
|
|
22
|
+
if (typeof displayName === "string" && displayName.trim().length > 0) {
|
|
23
|
+
throw new Error("Display-name notebook selection is not supported in the Node MCP tool path yet.");
|
|
24
|
+
}
|
|
25
|
+
if (typeof status.activeNotebookId === "string" && status.activeNotebookId.length > 0) {
|
|
26
|
+
return status.activeNotebookId;
|
|
27
|
+
}
|
|
28
|
+
throw new Error("No Mathematica notebook is selected");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Enqueue a tool call through the bridge queue and wire MCP-client
|
|
32
|
+
* cancellation via the AbortSignal provided by the SDK.
|
|
33
|
+
*
|
|
34
|
+
* When the MCP client cancels the tool call the SDK fires the signal's
|
|
35
|
+
* `abort` event. We call `queue.cancelFromMcp` so the request is rejected
|
|
36
|
+
* immediately (queued) or a one-shot cancellation notification is stored
|
|
37
|
+
* for the Palette (claimed). The listener is removed after the promise
|
|
38
|
+
* settles to avoid leaks.
|
|
39
|
+
*/
|
|
40
|
+
async function enqueueRequestWithCancellation(queue, tool, args, extra) {
|
|
41
|
+
const { requestId, promise } = queue.enqueueWithId(tool, args);
|
|
42
|
+
// If the signal was already aborted before we enqueued, cancel immediately.
|
|
43
|
+
if (extra?.signal?.aborted) {
|
|
44
|
+
queue.cancelFromMcp(requestId, "MCP client cancelled operation");
|
|
45
|
+
}
|
|
46
|
+
const onAbort = () => {
|
|
47
|
+
queue.cancelFromMcp(requestId, "MCP client cancelled operation");
|
|
48
|
+
};
|
|
49
|
+
// Only add the listener if the signal exists and isn't already aborted.
|
|
50
|
+
if (extra?.signal && !extra.signal.aborted) {
|
|
51
|
+
extra.signal.addEventListener("abort", onAbort, { once: true });
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const result = await promise;
|
|
55
|
+
return toolSuccess(result);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
extra?.signal?.removeEventListener("abort", onAbort);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function enqueueWithCancellation(queue, getStatus, tool, args, extra) {
|
|
62
|
+
const status = getStatus();
|
|
63
|
+
assertBridgeReadyForTool(status);
|
|
64
|
+
const notebookId = resolveNotebookTarget(args, status);
|
|
65
|
+
return enqueueRequestWithCancellation(queue, tool, { ...args, notebookId }, extra);
|
|
66
|
+
}
|
|
67
|
+
function registerLegacyQueuedTool(server, queue, getStatus, config) {
|
|
68
|
+
server.tool(config.name, notebookToolDescription(config.summary, config.extraGuidance), config.schema, async (args, extra) => {
|
|
69
|
+
const recordArgs = args;
|
|
70
|
+
return withToolErrors({ tool: config.name, args: recordArgs }, () => enqueueWithCancellation(queue, getStatus, config.name, recordArgs, extra));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function registerMmaTools(server, queue, getStatus) {
|
|
74
|
+
server.tool("mma_status", notebookToolDescription("Report Mathematica bridge, Palette, and notebook attachment status."), noArgsSchema.shape, async () => withToolErrors({ tool: "mma_status" }, () => toolSuccess(getStatus())));
|
|
75
|
+
server.tool("mma_list_notebooks", notebookToolDescription("List notebooks registered with the Mathematica bridge Palette."), noArgsSchema.shape, async () => {
|
|
76
|
+
return withToolErrors({ tool: "mma_list_notebooks" }, () => {
|
|
77
|
+
const status = getStatus();
|
|
78
|
+
return toolSuccess({ notebooks: status.notebooks ?? [], activeNotebookId: status.activeNotebookId });
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
server.tool("mma_select_notebook", notebookToolDescription("Select the active Mathematica notebook in the Palette registry."), selectNotebookSchema.shape, async (args, extra) => {
|
|
82
|
+
const recordArgs = args;
|
|
83
|
+
return withToolErrors({ tool: "mma_select_notebook", args: recordArgs }, () => {
|
|
84
|
+
const status = getStatus();
|
|
85
|
+
if (!status.paletteConnected) {
|
|
86
|
+
throw new Error("Mathematica Palette is not connected. Open the MMA Agent Bridge palette and click 'Allow control of current Notebook'.");
|
|
87
|
+
}
|
|
88
|
+
const notebookId = args.notebookId;
|
|
89
|
+
if (typeof args.displayName === "string" && args.displayName.trim().length > 0 && typeof notebookId !== "string") {
|
|
90
|
+
throw new Error("Display-name notebook selection is not supported in the Node MCP tool path yet.");
|
|
91
|
+
}
|
|
92
|
+
if (typeof notebookId !== "string" ||
|
|
93
|
+
notebookId.length === 0 ||
|
|
94
|
+
!(status.notebooks?.some((notebook) => notebook.notebookId === notebookId) ?? false)) {
|
|
95
|
+
throw new Error(`Unknown notebookId: ${notebookId}`);
|
|
96
|
+
}
|
|
97
|
+
return enqueueRequestWithCancellation(queue, "mma_select_notebook", recordArgs, extra);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
const queuedTools = [
|
|
101
|
+
{
|
|
102
|
+
name: "mma_list_cells",
|
|
103
|
+
summary: "List cells in the attached active Mathematica notebook.",
|
|
104
|
+
schema: listCellsSchema.shape,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "mma_read_cell",
|
|
108
|
+
summary: "Read one cell from the attached Mathematica notebook.",
|
|
109
|
+
schema: readCellSchema.shape,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "mma_insert_cell",
|
|
113
|
+
summary: "Insert a cell through the Mathematica FrontEnd bridge.",
|
|
114
|
+
schema: insertCellSchema.shape,
|
|
115
|
+
extraGuidance: INSERT_ANCHOR_GUIDANCE,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "mma_modify_cell",
|
|
119
|
+
summary: "Modify one existing cell through the Mathematica FrontEnd bridge.",
|
|
120
|
+
schema: modifyCellSchema.shape,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "mma_delete_cell",
|
|
124
|
+
summary: "Delete one cell through the Mathematica FrontEnd bridge.",
|
|
125
|
+
schema: deleteCellSchema.shape,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "mma_run_cell",
|
|
129
|
+
summary: "Run one cell in the attached Mathematica notebook.",
|
|
130
|
+
schema: runCellSchema.shape,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "mma_abort_evaluation",
|
|
134
|
+
summary: "Abort the running Wolfram evaluation in the attached notebook.",
|
|
135
|
+
schema: abortEvaluationSchema.shape,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "mma_get_cell_output",
|
|
139
|
+
summary: "Read output and messages for one Mathematica notebook cell, refreshing completed run status when observed.",
|
|
140
|
+
schema: getCellOutputSchema.shape,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "mma_read_artifact",
|
|
144
|
+
summary: "Read one large output or message artifact by byte page. Artifact ids are resolved against current notebook state and may become stale after notebook edits or reruns.",
|
|
145
|
+
schema: readArtifactSchema.shape,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "mma_save_notebook",
|
|
149
|
+
summary: "Save the attached Mathematica notebook through the FrontEnd.",
|
|
150
|
+
schema: saveNotebookSchema.shape,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "mma_symbol_lookup",
|
|
154
|
+
summary: "Look up Wolfram Language symbol documentation. Provide an exact symbol name (e.g. 'Plot') for full details including usage, options, attributes, and documentation URL, or a partial name (e.g. 'integrate') for a list of matching symbols.",
|
|
155
|
+
schema: symbolLookupSchema.shape,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
for (const config of queuedTools) {
|
|
159
|
+
registerLegacyQueuedTool(server, queue, getStatus, config);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
4
|
+
const DEFAULT_PORT = 19_791;
|
|
5
|
+
export function defaultSessionFile(env = process.env) {
|
|
6
|
+
const home = env.HOME || env.USERPROFILE || process.cwd();
|
|
7
|
+
return path.join(home, ".mica", "session.json");
|
|
8
|
+
}
|
|
9
|
+
export function generateAuthToken() {
|
|
10
|
+
return randomBytes(32).toString("base64url");
|
|
11
|
+
}
|
|
12
|
+
export function loadRuntimeConfig(options = {}) {
|
|
13
|
+
const env = options.env ?? process.env;
|
|
14
|
+
const argv = options.argv ?? process.argv.slice(2);
|
|
15
|
+
const cli = parseCliArgs(argv);
|
|
16
|
+
return {
|
|
17
|
+
host: cli.host ?? env.MICA_HOST ?? DEFAULT_HOST,
|
|
18
|
+
preferredPort: cli.port ?? parseOptionalPort(env.MICA_PORT, "MICA_PORT") ?? DEFAULT_PORT,
|
|
19
|
+
sessionFile: cli.sessionFile ?? env.MICA_SESSION_FILE ?? defaultSessionFile(env),
|
|
20
|
+
authToken: cli.token ?? nonEmptyString(env.MICA_TOKEN) ?? (options.randomToken ?? generateAuthToken)(),
|
|
21
|
+
bridgeOnly: cli.bridgeOnly,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function parseCliArgs(argv) {
|
|
25
|
+
const parsed = { bridgeOnly: false };
|
|
26
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
27
|
+
const arg = argv[index];
|
|
28
|
+
switch (arg) {
|
|
29
|
+
case "--host":
|
|
30
|
+
parsed.host = readFlagValue(argv, index, arg);
|
|
31
|
+
index += 1;
|
|
32
|
+
break;
|
|
33
|
+
case "--port":
|
|
34
|
+
parsed.port = parseRequiredPort(readFlagValue(argv, index, arg), arg);
|
|
35
|
+
index += 1;
|
|
36
|
+
break;
|
|
37
|
+
case "--session-file":
|
|
38
|
+
parsed.sessionFile = readFlagValue(argv, index, arg);
|
|
39
|
+
index += 1;
|
|
40
|
+
break;
|
|
41
|
+
case "--token":
|
|
42
|
+
parsed.token = readFlagValue(argv, index, arg);
|
|
43
|
+
index += 1;
|
|
44
|
+
break;
|
|
45
|
+
case "--bridge-only":
|
|
46
|
+
parsed.bridgeOnly = true;
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
function readFlagValue(argv, index, flag) {
|
|
55
|
+
const value = argv[index + 1];
|
|
56
|
+
if (!value || value.startsWith("--")) {
|
|
57
|
+
throw new Error(`${flag} requires a value`);
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
function parseOptionalPort(value, source) {
|
|
62
|
+
if (!value)
|
|
63
|
+
return undefined;
|
|
64
|
+
return parseRequiredPort(value, source);
|
|
65
|
+
}
|
|
66
|
+
function parseRequiredPort(value, source) {
|
|
67
|
+
const port = Number(value);
|
|
68
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
69
|
+
throw new Error(`${source} must be a positive integer port`);
|
|
70
|
+
}
|
|
71
|
+
return port;
|
|
72
|
+
}
|
|
73
|
+
function nonEmptyString(value) {
|
|
74
|
+
return value && value.length > 0 ? value : undefined;
|
|
75
|
+
}
|
|
76
|
+
export { DEFAULT_HOST, DEFAULT_PORT };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function writeSessionFile(sessionFile, input) {
|
|
4
|
+
const session = {
|
|
5
|
+
...input,
|
|
6
|
+
baseUrl: `http://${input.host}:${input.port}`,
|
|
7
|
+
updatedAt: input.updatedAt ?? new Date().toISOString(),
|
|
8
|
+
};
|
|
9
|
+
await mkdir(path.dirname(sessionFile), { recursive: true });
|
|
10
|
+
const tempFile = `${sessionFile}.${process.pid}.${Date.now()}.tmp`;
|
|
11
|
+
await writeFile(tempFile, `${JSON.stringify(session, null, 2)}\n`, "utf8");
|
|
12
|
+
await rename(tempFile, sessionFile);
|
|
13
|
+
return session;
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aliceshimada/mica",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Local MCP bridge for controlling live Wolfram Desktop / Mathematica notebooks.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Alice-Shimada/mica.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/Alice-Shimada/mica/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Alice-Shimada/mica#readme",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"wolfram",
|
|
17
|
+
"mathematica",
|
|
18
|
+
"notebook",
|
|
19
|
+
"agent"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"mica": "dist/src/cli/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/src",
|
|
26
|
+
"src/bun/index.ts",
|
|
27
|
+
"paclet",
|
|
28
|
+
"README.md",
|
|
29
|
+
"README.zh-CN.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"SECURITY.md",
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"CONTRIBUTING.md",
|
|
34
|
+
"scripts/install.js"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"start:mcp": "node dist/src/bun/index.js",
|
|
39
|
+
"dev:mcp": "tsx src/bun/index.ts",
|
|
40
|
+
"dev:bridge": "tsx src/bun/index.ts --bridge-only",
|
|
41
|
+
"dev:bun:mcp": "bun run src/bun/index.ts",
|
|
42
|
+
"dev:bun": "bun run src/bun/index.ts --bridge-only",
|
|
43
|
+
"dev:legacy": "tsx src/index.ts --bridge-only",
|
|
44
|
+
"dev:legacy:mcp": "tsx src/index.ts",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
51
|
+
"zod": "^3.25.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^22.15.0",
|
|
55
|
+
"tsx": "^4.19.0",
|
|
56
|
+
"typescript": "^5.8.0",
|
|
57
|
+
"vitest": "^3.1.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20"
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT"
|
|
63
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Notebook[
|
|
2
|
+
{
|
|
3
|
+
Cell[
|
|
4
|
+
BoxData[
|
|
5
|
+
ButtonBox[
|
|
6
|
+
"Open MMA Agent Bridge",
|
|
7
|
+
ButtonFunction :> (
|
|
8
|
+
Needs["MMAAgentBridge`"];
|
|
9
|
+
MMAAgentBridge`StartMMAAgentPalette[]
|
|
10
|
+
),
|
|
11
|
+
Evaluator -> Automatic,
|
|
12
|
+
Method -> "Queued"
|
|
13
|
+
]
|
|
14
|
+
],
|
|
15
|
+
"Input"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
WindowTitle -> "MMA Agent Bridge Launcher",
|
|
19
|
+
Saveable -> False,
|
|
20
|
+
FrontEndVersion -> "13.0 for Microsoft Windows (64-bit) (June 19, 2021)",
|
|
21
|
+
StyleDefinitions -> "Default.nb"
|
|
22
|
+
]
|