feat: use acorn AST parser (same as CodeMode) to extract tool calls
- acorn.parse() парсит JS в AST (тот же парсер, что в CodeMode) - acorn-walk.simple() обходит AST и находит CallExpression для tools.xxx.yyy - evaluateExpression() извлекает аргументы из AST (literals, identifiers, objects, arrays, template literals, binary, conditional, await, spread) - НЕ выполняет код — только парсит и анализирует AST
This commit is contained in:
parent
a829e0b114
commit
34f90534d2
237
index.ts
237
index.ts
|
|
@ -1,6 +1,8 @@
|
|||
import fs from "fs"
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
import * as acorn from "acorn"
|
||||
import * as acornWalk from "acorn-walk"
|
||||
import { MatrixBotClient, type MatrixEventContext } from "./matrix-client.js"
|
||||
import { SessionManager } from "./session-manager.js"
|
||||
import { loadConfig } from "./config-loader.js"
|
||||
|
|
@ -132,7 +134,7 @@ async function runPlugin(ctx: any, options: any, dataDir: string) {
|
|||
const sessionsBySessionID = new Map<string, SessionState>()
|
||||
|
||||
// =============================================================================
|
||||
// Parse tool calls from CodeMode JS script
|
||||
// Parse tool calls from CodeMode JS script using AST (same as OpenCode)
|
||||
// CodeMode creates nested tools: tools.<server>.<tool>(...)
|
||||
// e.g. tools.duckduckgo.search({ query: "..." })
|
||||
// =============================================================================
|
||||
|
|
@ -142,72 +144,209 @@ async function runPlugin(ctx: any, options: any, dataDir: string) {
|
|||
args: string
|
||||
}
|
||||
|
||||
// Minimal AST node type (acorn doesn't export AST namespace in usable way)
|
||||
type AstNode = { type: string; [key: string]: any }
|
||||
|
||||
/**
|
||||
* Parse the CodeMode JS script and extract all tool calls.
|
||||
* Uses acorn AST parser + walk — same approach as OpenCode's CodeMode interpreter.
|
||||
* Does NOT execute the code, only parses and analyzes the AST.
|
||||
*/
|
||||
function parseCodeModeTools(code: string): ChildToolInfo[] {
|
||||
const results: ChildToolInfo[] = []
|
||||
const seen = new Set<string>()
|
||||
const regex = /tools\.(\w+)\.(\w+)\s*\(/g
|
||||
let match
|
||||
|
||||
while ((match = regex.exec(code)) !== null) {
|
||||
const toolName = `${match[1]}_${match[2]}`
|
||||
if (seen.has(toolName)) continue
|
||||
seen.add(toolName)
|
||||
// Parse the code into an AST using acorn (same parser as CodeMode)
|
||||
const ast = acorn.parse(code, {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "script",
|
||||
allowReturnOutsideFunction: true,
|
||||
allowAwaitOutsideFunction: true,
|
||||
locations: true,
|
||||
}) as AstNode
|
||||
|
||||
// Extract the full argument block starting from the (
|
||||
const openParenPos = match.index + match[0].indexOf("(")
|
||||
const args = extractArgumentBlock(code, openParenPos)
|
||||
results.push({ name: toolName, args })
|
||||
}
|
||||
// Walk the AST and find all CallExpression nodes for tools.xxx.yyy(...)
|
||||
acornWalk.simple(ast as any, {
|
||||
CallExpression(node) {
|
||||
// Check if callee is tools.<server>.<tool>(...)
|
||||
const toolCall = extractToolCall(node)
|
||||
if (!toolCall) return
|
||||
|
||||
const { path, args } = toolCall
|
||||
const name = path.join("_")
|
||||
if (seen.has(name)) return
|
||||
seen.add(name)
|
||||
|
||||
// Evaluate the arguments to get their string representation
|
||||
const argsStr = evaluateArgs(args)
|
||||
results.push({ name, args: argsStr })
|
||||
},
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract argument block from code starting at the opening parenthesis.
|
||||
* Handles nested parentheses, brackets, and braces.
|
||||
* e.g. for tools.duckduckgo.fetch_content({ url: url, max_length: 5000 })
|
||||
* returns "{ url: url, max_length: 5000 }"
|
||||
* Check if a CallExpression is a tool call: tools.<server>.<tool>(...)
|
||||
* Returns { path, args } or null.
|
||||
*/
|
||||
function extractArgumentBlock(code: string, openParenPos: number): string {
|
||||
let depth = 0
|
||||
let start = openParenPos
|
||||
let end = openParenPos
|
||||
function extractToolCall(node: AstNode): { path: string[]; args: AstNode[] } | null {
|
||||
const callee = node.callee as AstNode
|
||||
|
||||
// Find the opening character: (, {, [, or "
|
||||
while (start < code.length) {
|
||||
const ch = code[start]
|
||||
if (ch === "(" || ch === "{" || ch === "[" || ch === '"') break
|
||||
start++
|
||||
// Must be a MemberExpression: tools.xxx.yyy
|
||||
if (callee.type !== "MemberExpression") return null
|
||||
if (callee.computed) return null
|
||||
|
||||
const prop = callee.property as AstNode
|
||||
if (prop.type !== "Identifier") return null
|
||||
const toolName = prop.name
|
||||
|
||||
// Object must be tools.xxx (nested MemberExpression) or just "tools"
|
||||
let obj: AstNode = callee.object
|
||||
const path: string[] = []
|
||||
|
||||
// Walk up the nested MemberExpression chain
|
||||
while (obj.type === "MemberExpression" && !obj.computed) {
|
||||
const innerProp = obj.property as AstNode
|
||||
if (innerProp.type !== "Identifier") return null
|
||||
path.unshift(innerProp.name)
|
||||
obj = obj.object
|
||||
}
|
||||
|
||||
if (start >= code.length) return "(variable)"
|
||||
// The base must be the identifier "tools"
|
||||
if (obj.type !== "Identifier" || obj.name !== "tools") return null
|
||||
|
||||
const openCh = code[start]
|
||||
const closeCh = openCh === "(" ? ")" : openCh === "{" ? "}" : openCh === "[" ? "]" : '"'
|
||||
path.unshift(toolName)
|
||||
return { path, args: (node.arguments ?? []) as AstNode[] }
|
||||
}
|
||||
|
||||
for (let i = start; i < code.length; i++) {
|
||||
const ch = code[i]
|
||||
if (ch === openCh) depth++
|
||||
else if (ch === closeCh) {
|
||||
depth--
|
||||
if (depth === 0) {
|
||||
end = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
// Skip string literals to avoid counting quotes inside strings
|
||||
if (ch === '"' || ch === "'") {
|
||||
let j = i + 1
|
||||
while (j < code.length && code[j] !== ch) {
|
||||
if (code[j] === "\\") j++
|
||||
j++
|
||||
}
|
||||
i = j
|
||||
}
|
||||
/**
|
||||
* Evaluate arguments to a string representation.
|
||||
* Handles literals, identifiers, objects, arrays, and template literals.
|
||||
* Matches how CodeMode's interpreter evaluates expression values.
|
||||
*/
|
||||
function evaluateArgs(args: AstNode[]): string {
|
||||
if (!args || args.length === 0) return ""
|
||||
|
||||
const values: string[] = []
|
||||
for (const arg of args) {
|
||||
values.push(evaluateExpression(arg))
|
||||
}
|
||||
|
||||
const result = code.slice(start, end).trim()
|
||||
return result || "(variable)"
|
||||
return values.join(", ")
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an AST expression node to its string value.
|
||||
* Handles the expression types used in CodeMode tool calls.
|
||||
*/
|
||||
function evaluateExpression(node: AstNode): string {
|
||||
switch (node.type) {
|
||||
// Literals
|
||||
case "Literal":
|
||||
return String(node.value)
|
||||
|
||||
// String/number/boolean/null/regexp literals
|
||||
case "Identifier": {
|
||||
const name = node.name
|
||||
// Known globals with known values
|
||||
if (name === "undefined") return "undefined"
|
||||
if (name === "true") return "true"
|
||||
if (name === "false") return "false"
|
||||
if (name === "null") return "null"
|
||||
// For variables, show the variable name
|
||||
return name
|
||||
}
|
||||
|
||||
// Object literals: { key: value, ... }
|
||||
case "ObjectExpression": {
|
||||
const parts: string[] = []
|
||||
for (const prop of node.properties ?? []) {
|
||||
if (prop.type === "Property") {
|
||||
const key = prop.key.type === "Identifier" ? prop.key.name : String((prop.key as AstNode).value)
|
||||
const value = evaluateExpression(prop.value)
|
||||
parts.push(`${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
return `{ ${parts.join(", ")} }`
|
||||
}
|
||||
|
||||
// Array literals: [value1, value2, ...]
|
||||
case "ArrayExpression": {
|
||||
const elements = (node.elements ?? []).map((el: AstNode | null) =>
|
||||
el ? evaluateExpression(el) : "",
|
||||
)
|
||||
return `[${elements.join(", ")}]`
|
||||
}
|
||||
|
||||
// Template literals: `text ${expr} text`
|
||||
case "TemplateLiteral": {
|
||||
let result = "`"
|
||||
const quasis = node.quasis ?? []
|
||||
const expressions = node.expressions ?? []
|
||||
for (let i = 0; i < quasis.length; i++) {
|
||||
result += quasis[i]?.value?.raw ?? ""
|
||||
if (expressions[i]) {
|
||||
result += "${" + evaluateExpression(expressions[i]) + "}"
|
||||
}
|
||||
}
|
||||
result += "`"
|
||||
return result
|
||||
}
|
||||
|
||||
// Binary expressions: a + b, a === b, etc.
|
||||
case "BinaryExpression": {
|
||||
const left = evaluateExpression(node.left)
|
||||
const right = evaluateExpression(node.right)
|
||||
return `${left} ${node.operator} ${right}`
|
||||
}
|
||||
|
||||
// Conditional: a ? b : c
|
||||
case "ConditionalExpression": {
|
||||
return `${evaluateExpression(node.test)} ? ${evaluateExpression(node.consequent)} : ${evaluateExpression(node.alternate)}`
|
||||
}
|
||||
|
||||
// Function expressions (arrow functions, etc.)
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression":
|
||||
return "function"
|
||||
|
||||
// Call expressions: other().method()
|
||||
case "CallExpression": {
|
||||
if (node.callee?.type === "MemberExpression") {
|
||||
const member = node.callee as AstNode
|
||||
if (member.object?.type === "Identifier") {
|
||||
return `${(member.object as AstNode).name}.${(member.property as AstNode)?.name}(${evaluateArgs(node.arguments ?? [])})`
|
||||
}
|
||||
}
|
||||
return "call"
|
||||
}
|
||||
|
||||
// Unary expressions: !a, -b, typeof c
|
||||
case "UnaryExpression": {
|
||||
const arg = evaluateExpression(node.argument)
|
||||
return `${node.operator}${arg}`
|
||||
}
|
||||
|
||||
// Logical expressions: a && b, a || b
|
||||
case "LogicalExpression": {
|
||||
return `${evaluateExpression(node.left)} ${node.operator} ${evaluateExpression(node.right)}`
|
||||
}
|
||||
|
||||
// Await expressions
|
||||
case "AwaitExpression": {
|
||||
return `await ${evaluateExpression(node.argument)}`
|
||||
}
|
||||
|
||||
// Spread: ...arr
|
||||
case "SpreadElement": {
|
||||
return `...${evaluateExpression(node.argument)}`
|
||||
}
|
||||
|
||||
// Default: show "(expr)"
|
||||
default:
|
||||
return `(expr:${node.type})`
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
{
|
||||
"name": "opencode-matrix-plugin",
|
||||
"name": "matrix-plugin",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opencode-matrix-plugin",
|
||||
"name": "matrix-plugin",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@matrix-org/matrix-sdk-crypto-nodejs": "^0.4.0",
|
||||
"@opencode-ai/codemode": "^1.18.4",
|
||||
"@opencode/plugin": "^2.0.16",
|
||||
"acorn": "^8.18.0",
|
||||
"acorn-walk": "^8.3.5",
|
||||
"effect": "^4.0.0-rc.112",
|
||||
"marked": "^15.0.0",
|
||||
"matrix-bot-sdk": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
},
|
||||
|
|
@ -936,6 +940,82 @@
|
|||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode": {
|
||||
"version": "1.18.4",
|
||||
"resolved": "https://registry.npmjs.org/@opencode-ai/codemode/-/codemode-1.18.4.tgz",
|
||||
"integrity": "sha512-Bbwj30vHydJ0znhHj9O+Pc3+FD+sPcDHN6QWHhmo8DjbzQYhGUKHWkq3lWlKw07c+RKhvdGeN8RiW2L5imzu1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "4.0.0-beta.101",
|
||||
"typescript": "5.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode/node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode/node_modules/effect": {
|
||||
"version": "4.0.0-beta.101",
|
||||
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.101.tgz",
|
||||
"integrity": "sha512-HjowumlIo+orthn4jMlEJPuzIYPBV+uq/XiciHWhiedLsXQpWHdNJHO5d59BVDP5s1LPuvERcktwFqRXnJqnhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"fast-check": "^4.9.0",
|
||||
"find-my-way-ts": "^0.1.6",
|
||||
"ini": "^7.0.0",
|
||||
"kubernetes-types": "^1.30.0",
|
||||
"msgpackr": "^2.0.4",
|
||||
"multipasta": "^0.2.8",
|
||||
"toml": "^4.1.2",
|
||||
"uuid": "^14.0.1",
|
||||
"yaml": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode/node_modules/ini": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz",
|
||||
"integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode/node_modules/typescript": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
||||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/codemode/node_modules/uuid": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz",
|
||||
"integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode/ai": {
|
||||
"version": "2.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@opencode/ai/-/ai-2.0.16.tgz",
|
||||
|
|
@ -1660,12 +1740,12 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.2.tgz",
|
||||
"integrity": "sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==",
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~8.9.0"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
|
|
@ -1762,6 +1842,30 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
|
||||
"integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
|
||||
"integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
|
|
@ -2762,6 +2866,12 @@
|
|||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-my-way-ts": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz",
|
||||
"integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
|
|
@ -3463,6 +3573,12 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/kubernetes-types": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz",
|
||||
"integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/launder": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz",
|
||||
|
|
@ -3934,6 +4050,12 @@
|
|||
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/multipasta": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.8.tgz",
|
||||
"integrity": "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.19",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
|
||||
|
|
@ -5414,6 +5536,15 @@
|
|||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/toml": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz",
|
||||
"integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
|
|
@ -5532,9 +5663,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
|
||||
"integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
|
|
@ -5775,6 +5906,21 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
|
||||
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -9,12 +9,16 @@
|
|||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/codemode": "^1.18.4",
|
||||
"@opencode/plugin": "^2.0.16",
|
||||
"matrix-bot-sdk": "^0.8.0",
|
||||
"marked": "^15.0.0"
|
||||
"acorn": "^8.18.0",
|
||||
"acorn-walk": "^8.3.5",
|
||||
"effect": "^4.0.0-rc.112",
|
||||
"marked": "^15.0.0",
|
||||
"matrix-bot-sdk": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.0",
|
||||
"@types/node": "^20.0.0"
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue