diff --git a/.gitignore b/.gitignore index c5acf0f..f0bdca7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ storage.cirru backups/ .DS_Store +lib diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4d9d5e1 --- /dev/null +++ b/.npmignore @@ -0,0 +1,23 @@ + + +.compact-inc.cirru +.calcit-error.cirru + +js-out/ +dist/ + +yarn-error.log + +storage.cirru + +backups/ + +.DS_Store + +*.cirru + +.github + +tsconfig.json + +index.html diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4a855c3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.exclude": { + "lib/**": true, + "js-out/**": true, + "node_modules/**": true, + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true, + "**/Thumbs.db": true + } +} diff --git a/README.md b/README.md index 1ff42a0..fc8549f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,26 @@ - -Macrophylla ----- +## Macrophylla > try smarter CLI tools, still experimenting... +Macrophylla 是一个命令行助手,它使用 Gemini API 与用户交互,并能够执行 Bash 命令和 Node.js 代码。 + +### 使用方式 + +1. **准备 Gemini API Key:** 确保你已经设置了 `GEMINI_API_KEY` 环境变量。 + +2. **运行工具:** 直接运行该工具。 + +3. **与助手交互:** 工具会提示你输入任务描述。 你可以使用自然语言描述你的需求。 + +4. **执行 Bash 命令和 Node.js 代码:** 工具会根据你的描述,自动判断是否需要执行 Bash 命令或 Node.js 代码来完成任务。 如果需要执行,会先向你确认,然后执行并将结果返回。 + +5. **示例:** + + - 用户: 读取当前目录下的 `README.md` 文件内容。 + - 助手: (判断需要执行 Bash 命令) Bash command to execute: `cat README.md` Execute this Bash command? (y/n): + - 用户: y + - 助手: (执行命令,并将结果返回) + ### License MIT diff --git a/bin.mjs b/bin.mjs new file mode 100755 index 0000000..fd05e55 --- /dev/null +++ b/bin.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import "./lib/main.mjs"; diff --git a/calcit.cirru b/calcit.cirru index 2c114ed..b0fa632 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -640,7 +640,7 @@ |comp-container $ %{} :CodeEntry (:doc |) :code $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} - |T $ %{} :Leaf (:at 1500541010211) (:by |root) (:text |defcomp) + |T $ %{} :Leaf (:at 1743920036200) (:by |B1y7Rc-Zz) (:text |defcomp) |j $ %{} :Leaf (:at 1500541010211) (:by |root) (:text |comp-container) |r $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} @@ -736,6 +736,7 @@ |v $ %{} :Leaf (:at 1657793910144) (:by |B1y7Rc-Zz) (:text |css/column) |r $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} + |D $ %{} :Leaf (:at 1743920400077) (:by |B1y7Rc-Zz) (:text |memof1-call) |T $ %{} :Leaf (:at 1523120265747) (:by |root) (:text |comp-navigation) |j $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} @@ -766,90 +767,14 @@ |n $ %{} :Expr (:at 1525106918943) (:by |root) :data $ {} |T $ %{} :Leaf (:at 1525106921967) (:by |root) (:text |:home) - |j $ %{} :Expr (:at 1615983410742) (:by |B1y7Rc-Zz) + |b $ %{} :Expr (:at 1743920233497) (:by |B1y7Rc-Zz) :data $ {} - |D $ %{} :Leaf (:at 1615983411413) (:by |B1y7Rc-Zz) (:text |div) - |L $ %{} :Expr (:at 1615983411569) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615983411979) (:by |B1y7Rc-Zz) (:text |{}) - |b $ %{} :Expr (:at 1657793997580) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1657794001542) (:by |B1y7Rc-Zz) (:text |:class-name) - |b $ %{} :Leaf (:at 1657794003581) (:by |B1y7Rc-Zz) (:text |css/expand) - |j $ %{} :Expr (:at 1615984833365) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615984834879) (:by |B1y7Rc-Zz) (:text |:style) - |j $ %{} :Expr (:at 1615984835144) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615984835584) (:by |B1y7Rc-Zz) (:text |{}) - |j $ %{} :Expr (:at 1615984836078) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615984838548) (:by |B1y7Rc-Zz) (:text |:padding) - |j $ %{} :Leaf (:at 1615984843071) (:by |B1y7Rc-Zz) (:text "|\"8px") - |P $ %{} :Expr (:at 1615983412854) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615983414003) (:by |B1y7Rc-Zz) (:text |input) - |j $ %{} :Expr (:at 1615983414320) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615983414651) (:by |B1y7Rc-Zz) (:text |{}) - |j $ %{} :Expr (:at 1615983414866) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1657793994071) (:by |B1y7Rc-Zz) (:text |:class-name) - |j $ %{} :Leaf (:at 1657793995830) (:by |B1y7Rc-Zz) (:text |css/input) - |r $ %{} :Expr (:at 1615985094926) (:by |B1y7Rc-Zz) - :data $ {} - |D $ %{} :Leaf (:at 1615985095890) (:by |B1y7Rc-Zz) (:text |:value) - |T $ %{} :Expr (:at 1615985092894) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615985093771) (:by |B1y7Rc-Zz) (:text |:demo) - |j $ %{} :Leaf (:at 1615985100223) (:by |B1y7Rc-Zz) (:text |state) - |R $ %{} :Expr (:at 1615984821815) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615984822633) (:by |B1y7Rc-Zz) (:text |=<) - |j $ %{} :Leaf (:at 1615984823004) (:by |B1y7Rc-Zz) (:text |8) - |r $ %{} :Leaf (:at 1615984823512) (:by |B1y7Rc-Zz) (:text |nil) - |T $ %{} :Expr (:at 1539195346168) (:by |root) + |T $ %{} :Leaf (:at 1743920239196) (:by |B1y7Rc-Zz) (:text |comp-main-ui) + |b $ %{} :Expr (:at 1743920280977) (:by |B1y7Rc-Zz) :data $ {} - |T $ %{} :Leaf (:at 1539195347113) (:by |root) (:text |<>) - |j $ %{} :Leaf (:at 1615984827345) (:by |B1y7Rc-Zz) (:text "|\"demo page") - |j $ %{} :Expr (:at 1615984262519) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1615984303661) (:by |B1y7Rc-Zz) (:text |pre) - |j $ %{} :Expr (:at 1615984304152) (:by |B1y7Rc-Zz) - :data $ {} - |D $ %{} :Leaf (:at 1615984304642) (:by |B1y7Rc-Zz) (:text |{}) - |L $ %{} :Expr (:at 1646150434536) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150435604) (:by |B1y7Rc-Zz) (:text |:style) - |b $ %{} :Expr (:at 1646150436256) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150436589) (:by |B1y7Rc-Zz) (:text |{}) - |b $ %{} :Expr (:at 1646150436881) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150442246) (:by |B1y7Rc-Zz) (:text |:line-height) - |b $ %{} :Leaf (:at 1646150449670) (:by |B1y7Rc-Zz) (:text |1.4) - |e $ %{} :Expr (:at 1646150479259) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150480685) (:by |B1y7Rc-Zz) (:text |:padding) - |b $ %{} :Leaf (:at 1646150482506) (:by |B1y7Rc-Zz) (:text |4) - |h $ %{} :Expr (:at 1646150454548) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150472578) (:by |B1y7Rc-Zz) (:text |:border) - |b $ %{} :Expr (:at 1646150459003) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1646150461073) (:by |B1y7Rc-Zz) (:text |str) - |b $ %{} :Leaf (:at 1646150477219) (:by |B1y7Rc-Zz) (:text "|\"1px solid #ddd") - |T $ %{} :Expr (:at 1615984305087) (:by |B1y7Rc-Zz) - :data $ {} - |D $ %{} :Leaf (:at 1615984307552) (:by |B1y7Rc-Zz) (:text |:inner-text) - |T $ %{} :Expr (:at 1615984265859) (:by |B1y7Rc-Zz) - :data $ {} - |L $ %{} :Leaf (:at 1615984302121) (:by |B1y7Rc-Zz) (:text |str) - |j $ %{} :Leaf (:at 1615984270059) (:by |B1y7Rc-Zz) (:text "|\"backend data") - |r $ %{} :Expr (:at 1615984270835) (:by |B1y7Rc-Zz) - :data $ {} - |T $ %{} :Leaf (:at 1623719461768) (:by |B1y7Rc-Zz) (:text |format-cirru-edn) - |j $ %{} :Leaf (:at 1615984279552) (:by |B1y7Rc-Zz) (:text |store) + |D $ %{} :Leaf (:at 1743920282012) (:by |B1y7Rc-Zz) (:text |>>) + |L $ %{} :Leaf (:at 1743920287384) (:by |B1y7Rc-Zz) (:text |states) + |T $ %{} :Leaf (:at 1743920258792) (:by |B1y7Rc-Zz) (:text |:home) |r $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} |T $ %{} :Leaf (:at 1500541010211) (:by |root) (:text |:profile) @@ -885,7 +810,7 @@ |L $ %{} :Leaf (:at 1521911495407) (:by |root) (:text |dev?) |T $ %{} :Expr (:at 1500541010211) (:by nil) :data $ {} - |T $ %{} :Leaf (:at 1500541010211) (:by |root) (:text |comp-inspect) + |T $ %{} :Leaf (:at 1743915836427) (:by |B1y7Rc-Zz) (:text |comp-inspect) |j $ %{} :Leaf (:at 1562176377826) (:by |B1y7Rc-Zz) (:text "|\"Store") |r $ %{} :Leaf (:at 1500541010211) (:by |root) (:text |store) |v $ %{} :Expr (:at 1500541010211) (:by nil) @@ -944,6 +869,88 @@ |r $ %{} :Expr (:at 1507828721052) (:by |root) :data $ {} |T $ %{} :Leaf (:at 1507828722268) (:by |root) (:text |{}) + |comp-main-ui $ %{} :CodeEntry (:doc |) + :code $ %{} :Expr (:at 1743920239640) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920241766) (:by |B1y7Rc-Zz) (:text |defn) + |b $ %{} :Leaf (:at 1743920239640) (:by |B1y7Rc-Zz) (:text |comp-main-ui) + |h $ %{} :Expr (:at 1743920239640) (:by |B1y7Rc-Zz) + :data $ {} + |b $ %{} :Leaf (:at 1743920254394) (:by |B1y7Rc-Zz) (:text |states) + |l $ %{} :Expr (:at 1743920293752) (:by |B1y7Rc-Zz) + :data $ {} + |D $ %{} :Leaf (:at 1743920294288) (:by |B1y7Rc-Zz) (:text |let) + |L $ %{} :Expr (:at 1743920294552) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Expr (:at 1743920295365) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920297445) (:by |B1y7Rc-Zz) (:text |cursor) + |b $ %{} :Expr (:at 1743920298123) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920298371) (:by |B1y7Rc-Zz) (:text |:cursor) + |b $ %{} :Leaf (:at 1743920301358) (:by |B1y7Rc-Zz) (:text |states) + |b $ %{} :Expr (:at 1743920302534) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920303935) (:by |B1y7Rc-Zz) (:text |state) + |b $ %{} :Expr (:at 1743920304375) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920304919) (:by |B1y7Rc-Zz) (:text |:data) + |b $ %{} :Leaf (:at 1743920306648) (:by |B1y7Rc-Zz) (:text |states) + |T $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |div) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |{}) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:class-name) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |str-spaced) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |css/expand) + |h $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |css/column) + |h $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:style) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |{}) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:padding) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text "|\"8px") + |h $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |div) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |{}) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:class-name) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |css/expand) + |l $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |textarea) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |{}) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:class-name) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |css/textarea) + |h $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:placeholder) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text "|\"prompt for task..") + |l $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:value) + |b $ %{} :Expr (:at 1743920242834) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |:demo) + |b $ %{} :Leaf (:at 1743920242834) (:by |B1y7Rc-Zz) (:text |state) |comp-offline $ %{} :CodeEntry (:doc |) :code $ %{} :Expr (:at 1519314599832) (:by |root) :data $ {} @@ -1225,6 +1232,7 @@ |y $ %{} :Leaf (:at 1507815955483) (:by |root) (:text |button) |yT $ %{} :Leaf (:at 1615983477070) (:by |B1y7Rc-Zz) (:text |input) |yj $ %{} :Leaf (:at 1615984295300) (:by |B1y7Rc-Zz) (:text |pre) + |z $ %{} :Leaf (:at 1743916051467) (:by |B1y7Rc-Zz) (:text |textarea) |xT $ %{} :Expr (:at 1657794056783) (:by |B1y7Rc-Zz) :data $ {} |T $ %{} :Leaf (:at 1657794057967) (:by |B1y7Rc-Zz) (:text |respo.css) @@ -1298,6 +1306,13 @@ |j $ %{} :Leaf (:at 1535564718729) (:by |B1y7Rc-Zz) (:text |app.config) |r $ %{} :Leaf (:at 1535564719687) (:by |B1y7Rc-Zz) (:text |:as) |v $ %{} :Leaf (:at 1535564721387) (:by |B1y7Rc-Zz) (:text |config) + |z $ %{} :Expr (:at 1743920404783) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920409331) (:by |B1y7Rc-Zz) (:text |memof.once) + |b $ %{} :Leaf (:at 1743920410058) (:by |B1y7Rc-Zz) (:text |:refer) + |h $ %{} :Expr (:at 1743920410261) (:by |B1y7Rc-Zz) + :data $ {} + |T $ %{} :Leaf (:at 1743920412686) (:by |B1y7Rc-Zz) (:text |memof1-call) |app.comp.login $ %{} :FileEntry :defs $ {} |comp-login $ %{} :CodeEntry (:doc |) diff --git a/compact.cirru b/compact.cirru index fd54368..8505aec 100644 --- a/compact.cirru +++ b/compact.cirru @@ -125,20 +125,10 @@ router-data $ :data router div {} $ :class-name (str-spaced css/preset css/global css/fullscreen css/column) - comp-navigation (:logged-in? store) (:count store) + memof1-call comp-navigation (:logged-in? store) (:count store) if (:logged-in? store) case-default (:name router) (<> router) - :home $ div - {} (:class-name css/expand) - :style $ {} (:padding "\"8px") - input $ {} (:class-name css/input) - :value $ :demo state - =< 8 nil - <> "\"demo page" - pre $ {} - :style $ {} (:line-height 1.4) (:padding 4) - :border $ str "\"1px solid #ddd" - :inner-text $ str "\"backend data" (format-cirru-edn store) + :home $ comp-main-ui (>> states :home) :profile $ comp-profile (:user store) (:data router) comp-login $ >> states :login comp-status-color $ :color store @@ -149,6 +139,19 @@ {} fn (info d!) (d! :session/remove-message info) when dev? $ comp-reel (:reel-length store) ({}) + |comp-main-ui $ %{} :CodeEntry (:doc |) + :code $ quote + defn comp-main-ui (states) + let + cursor $ :cursor states + state $ :data states + div + {} + :class-name $ str-spaced css/expand css/column + :style $ {} (:padding "\"8px") + div $ {} (:class-name css/expand) + textarea $ {} (:class-name css/textarea) (:placeholder "\"prompt for task..") + :value $ :demo state |comp-offline $ %{} :CodeEntry (:doc |) :code $ quote defcomp comp-offline (mark) @@ -192,7 +195,7 @@ respo.util.format :refer $ hsl respo-ui.core :as ui respo-ui.css :as css - respo.core :refer $ defcomp <> >> div span button input pre + respo.core :refer $ defcomp <> >> div span button input pre textarea respo.css :refer $ defstyle respo.comp.inspect :refer $ comp-inspect respo.comp.space :refer $ =< @@ -204,6 +207,7 @@ app.config :refer $ dev? app.schema :as schema app.config :as config + memof.once :refer $ memof1-call |app.comp.login $ %{} :FileEntry :defs $ {} |comp-login $ %{} :CodeEntry (:doc |) diff --git a/package.json b/package.json index 127d84e..b26b05f 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,30 @@ { + "name": "macrophylla", "dependencies": { - "@calcit/procs": "^0.9.9" + "@calcit/procs": "^0.9.9", + "@google/genai": "^0.8.0", + "@google/generative-ai": "^0.24.0", + "chalk": "^5.4.1", + "string-width": "^7.2.0" }, "scripts": { "compile-page": "cr --once js", "release-page": "vite build --base=./", - "watch-page": "cr js" + "watch-page": "cr js", + "tool": "tsc && node lib/main.mjs" + }, + "bin": { + "mcpl": "./bin.mjs" }, "devDependencies": { + "@types/node": "^22.14.0", "bottom-tip": "^0.1.5", + "typescript": "^5.8.3", "url-parse": "^1.5.10", "vite": "^6.2.5" }, - "version": "0.0.1" + "version": "0.0.1", + "cirruInfo": { + "calcitVersion": "0.9.9" + } } diff --git a/src/exec.mts b/src/exec.mts new file mode 100644 index 0000000..8b47d93 --- /dev/null +++ b/src/exec.mts @@ -0,0 +1,103 @@ +import { exec, execFile } from "child_process"; +import path from "path"; +import fs from "fs/promises"; + +// Define function to execute Node.js code +export const executeNodeJsCode = async ( + code: string, + tempDir: string +): Promise<{ + stdout: string; + stderr: string; +}> => { + // Create temp directory if it doesn't exist + try { + await fs.mkdir(tempDir, { recursive: true }); + } catch (err) { + // Directory might already exist + } + + // Create a temporary file with the code + const tempFilePath = path.join(tempDir, `exec_${Date.now()}.mjs`); + await fs.writeFile(tempFilePath, code); + // Execute the file as an ES module + const child = execFile("node", ["--experimental-modules", tempFilePath], { + encoding: "utf8", + maxBuffer: 10 * 1024 * 1024, // 10MB buffer to handle large outputs + }); + + let stdout = ""; + let stderr = ""; + + // Stream stdout in real-time while preserving color + child.stdout?.on("data", (data) => { + process.stdout.write(data); + stdout += data; + }); + + // Stream stderr in real-time while preserving color + child.stderr?.on("data", (data) => { + process.stderr.write(data); + stderr += data; + }); + + // Wait for process to complete + const result = await new Promise<{ stdout: string; stderr: string }>( + (resolve, reject) => { + child.on("close", (code) => { + // Clean up the temporary file, asynchronously + fs.unlink(tempFilePath).catch((err) => + console.error("Failed to delete temp file:", err) + ); + + if (code === 0 || code === null) { + resolve({ stdout, stderr }); + } else { + reject(new Error(`Process exited with code ${code}\n${stderr}`)); + } + }); + + child.on("error", reject); + } + ); + + return result; +}; + +export let execBash = async ( + command: string +): Promise<{ stdout: string; stderr: string }> => { + // Execute the command + const child = exec(command, { + encoding: "utf8", + maxBuffer: 10 * 1024 * 1024, // 10MB buffer to handle large outputs + }); + + let stdout = ""; + let stderr = ""; + + // Stream stdout in real-time while preserving colors + child.stdout?.on("data", (data) => { + process.stdout.write(data); + stdout += data; + }); + + // Stream stderr in real-time while preserving colors + child.stderr?.on("data", (data) => { + process.stderr.write(data); + stderr += data; + }); + + // Wait for process to complete + return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { + child.on("close", (code) => { + if (code === 0 || code === null) { + resolve({ stdout, stderr }); + } else { + reject(new Error(`Process exited with code ${code}\n${stderr}`)); + } + }); + + child.on("error", reject); + }); +}; diff --git a/src/main.mts b/src/main.mts new file mode 100644 index 0000000..fd24935 --- /dev/null +++ b/src/main.mts @@ -0,0 +1,420 @@ +import { + GoogleGenerativeAI, + FunctionCallingMode, + Tool, + SchemaType, + ToolConfig, +} from "@google/generative-ai"; +import os from "os"; +import * as readline from "readline"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { execSync } from "child_process"; +import chalk from "chalk"; +import { execBash, executeNodeJsCode } from "./exec.mjs"; +import { displayBoxedText } from "./util.mjs"; +import { GoogleGenAI } from "@google/genai"; + +// Initialize the Generative AI client +const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!); +const aiNew = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! }); + +const model = genAI.getGenerativeModel( + // { model: "gemini-1.5-flash" }, + { model: "gemini-2.0-flash-lite" }, + { baseUrl: "https://sf.chenyong.life" } +); + +// 添加一个函数来生成上下文提醒 +const getContextReminder = () => { + return ( + "提醒: 你是一个命令行助手。你可以:\n" + + "1. 使用 executeBashCommand 执行 bash 命令\n" + + "2. 使用 executeNodeCode 执行 Node.js 代码\n" + + "3. 使用 saveToFile 保存输出到文件\n" + + "4. 使用 readTextFile 读取文件内容\n" + + "5. 使用 groundSearch 搜索最新信息\n" + + "请在每次回答时都考虑使用这些工具来帮助用户。" + ); +}; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +const ask = (question: string, seperator: boolean = false) => { + if (seperator) { + console.log( + chalk.gray("\n-------------------------------------------------") + ); + } + return new Promise((resolve) => { + rl.question(chalk.cyan(question), (answer) => { + resolve(answer); + }); + }); +}; + +const sayingOk = (message: string) => { + return ( + message === "ok" || message === "yes" || message === "y" || message === "" + ); +}; + +const tools: Tool[] = [ + { + functionDeclarations: [ + { + name: "executeBashCommand", + description: + "provide bash command, run in system, and return the output. it can access file system. also called 'bash 脚本'", + parameters: { + type: SchemaType.OBJECT, + properties: { + command: { + type: SchemaType.STRING, + description: "The bash command to execute", + }, + }, + required: ["command"], + }, + }, + { + name: "executeNodeCode", + description: + "provide node.js script in ES Module syntax. Execute in system and return the output. it can access the file system. also called 'node.js 脚本'", + parameters: { + type: SchemaType.OBJECT, + properties: { + code: { + type: SchemaType.STRING, + description: + "The Node.js code to execute. Must use ES module syntax. Write self-contained code that doesn't depend on external files.", + }, + }, + required: ["code"], + }, + }, + { + name: "saveToFile", + description: + "save the output to a path based on the current working directory", + parameters: { + type: SchemaType.OBJECT, + properties: { + code: { + type: SchemaType.STRING, + description: "any code that you want to save to file", + }, + filepath: { + type: SchemaType.STRING, + description: + "the path to save the file, relative to the current working directory", + }, + }, + required: ["code", "filepath"], + }, + }, + { + name: "readTextFile", + description: + "read a text file and return the content(utf8). it can access the file system. also called '读取文本文件'", + parameters: { + type: SchemaType.OBJECT, + properties: { + filepath: { + type: SchemaType.STRING, + description: + "the path to read, relative to the current working directory", + }, + }, + required: ["filepath"], + }, + }, + { + name: "groundSearch", + description: + "search the web for the latest information, with gemini groundSearch", + parameters: { + type: SchemaType.OBJECT, + properties: { + query: { + type: SchemaType.STRING, + description: "The search query", + }, + }, + required: ["query"], + }, + }, + ], + }, +]; + +let toolsDict: Record< + string, + { + shortName: string; + toolFn: (args: any) => Promise; + previewFn: (args: any) => void; + } +> = { + executeBashCommand: { + shortName: "Bash", + toolFn: async (args: any) => { + const command = args.command; + return await execBash(command); + }, + previewFn: (args: any) => { + displayBoxedText(args.command); + }, + }, + executeNodeCode: { + toolFn: async (args: any) => { + const code = args.code; + const tempDir = path.join(process.cwd(), "./"); + return await executeNodeJsCode(code, tempDir); + }, + shortName: "Node.js", + previewFn: (args: any) => { + displayBoxedText(args.code); + }, + }, + saveToFile: { + shortName: "Saving", + previewFn: (args: any) => { + displayBoxedText( + `Saving to ${args.path}:\n-------------\n${args.code}...` + ); + }, + toolFn: async (args: any) => { + const code = args.code; + const filepath = args.filepath as string; + const filePath = filepath.startsWith("/") + ? filepath + : path.join("./", filepath); + await fs.writeFile(filePath, code); + return { + stdout: `File saved to ${filePath}`, + stderr: "", + success: true, + }; + }, + }, + readTextFile: { + shortName: "Read File", + previewFn: (args: any) => { + displayBoxedText(`Reading file ${args.filepath}`); + }, + toolFn: async (args: any) => { + const filepath = args.filepath as string; + const filePath = filepath.startsWith("/") + ? filepath + : path.join("./", filepath); + try { + console.log(`Reading file ${filePath}`); + const data = await fs.readFile(filePath, "utf8"); + console.log(data); + return { + stdout: data, + stderr: "", + success: true, + }; + } catch (error) { + return { + stdout: "", + stderr: `Error reading file ${filePath}: ${error.message}`, + success: false, + }; + } + }, + }, + groundSearch: { + shortName: "Ground Search", + previewFn: (args: any) => { + displayBoxedText(`Searching the web for ${args.query}`); + }, + toolFn: async (args: any) => { + const query = args.query as string; + console.log(`Searching the web for ${query}`); + + try { + const response = await aiNew.models.generateContent({ + model: "gemini-2.0-flash", + contents: [query], + config: { + tools: [{ googleSearch: {} }], + httpOptions: { + baseUrl: "https://sf.chenyong.life", + }, + }, + }); + let result = response.text; + console.log(chalk.gray(result)); + // To get grounding metadata as web content. + // response?.candidates?.[0].groundingMetadata?.searchEntryPoint + // ?.renderedContent; + if (result) { + return { + stdout: result, + stderr: "", + success: true, + }; + } else { + return { + stdout: "", + stderr: "No result found.", + success: false, + }; + } + } catch (err) { + console.error("Error:", err); + return { + stdout: "", + stderr: `Error searching the web: ${err.message}`, + success: false, + }; + } + }, + }, +}; + +const main = async () => { + let osInfo = `${process.platform}, 架构: ${process.arch}, CPU 核心数: ${ + os.cpus().length + }.`; + let nodeInfo = `${process.version}, 当前目录: ${process.cwd()}.`; + let bashInfo = execSync("bash --version | head -n 1"); + + try { + // Create a chat session + // Define a function declaration tool + + // Configure tool settings + const toolConfig: ToolConfig = { + functionCallingConfig: { + mode: FunctionCallingMode.AUTO, // Let the model decide when to call the function + }, + }; + + // Create a chat session with the defined tool + const chat = model.startChat({ + tools, + toolConfig, + history: [ + { + role: "user", + parts: [ + { + text: + "系统初始化配置:\n" + + "1. 你是一个专业的命令行助手,工作在当前进程目录下\n" + + "2. 你可以调用 executeBashCommand 执行 bash 命令\n" + + "3. 你可以调用 executeNodeCode 执行 Node.js 代码\n" + + "4. 每次回答都应该考虑使用这些工具收集信息\n" + + "5. 使用中文回复,但代码保持英文\n" + + "6. 失败时要理解上下文并优化重试\n" + + `当前系统信息: ${osInfo}\n` + + `Node.js 信息: ${nodeInfo}\n` + + `Bash 信息: ${bashInfo}`, + }, + ], + }, + ], + }); + + let nextQuestion: string = ""; + let messageCount = 0; + + outerWhile: while (true) { + let question = + nextQuestion || (await ask("\nWhat's the task: ", true)) || "继续"; + + if (question.toLowerCase() === "exit") { + console.log("\nBye!"); + break; + } + // 每隔 5 轮对话,插入上下文提醒 + messageCount++; + if (messageCount % 10 === 0) { + const reminder = getContextReminder(); + console.log(chalk.gray("\n\n" + reminder)); + question = `${reminder}\n\n${question}`; + } + + console.log(chalk.gray("\nResponding...\n")); + + // Use the chat API to send messages and get streaming responses + const response = await chat.sendMessageStream(question); + for await (const chunk of response.stream) { + process.stdout.write(chunk.text()); + } + + // clear the next question cache + nextQuestion = ""; + + // Handle function calls if any + const functionCalls = (await response.response).functionCalls() || []; + + for (const functionCall of functionCalls) { + let tool = toolsDict[functionCall.name]; + const args: any = functionCall.args; + if (tool) { + // Ask for user confirmation + console.log(`\n${tool.shortName} to execute:\n`); + tool.previewFn(args); + const confirmation = await ask( + `\nExecute this ${tool.shortName} script? (y/n): ` + ); + + if (sayingOk(confirmation)) { + console.log(chalk.gray(`\nExecuting ${tool.shortName} command...`)); + + try { + const { stdout, stderr } = await tool.toolFn(args); + const result = { stdout, stderr, success: true }; + if (result.success) { + console.log(chalk.green("运行成功.")); + } else { + console.log(chalk.red("运行失败\n" + result.stderr)); + } + + nextQuestion = JSON.stringify(result); + continue outerWhile; + } catch (error) { + const result = { + stdout: error.stdout || "", + stderr: error.stderr || error.message, + success: false, + }; + console.log("\n"); + console.log("Command failed:", result); + + nextQuestion = `命令执行过程当中失败: ${result.stderr}, 你能否改进一下方案?`; + continue outerWhile; + } + } else { + nextQuestion = `用户拒绝了这条命令: (${confirmation}), 尝试改进一下方案.`; + continue outerWhile; + } + } else { + console.log( + chalk.red( + `\n\nError: Unsupported function call ${functionCall.name}` + ) + ); + nextQuestion = `不支持的函数调用: ${functionCall.name}, 你能否改进一下方案?`; + continue outerWhile; + } + } + } + } catch (err) { + console.error("Error:", err); + rl.close(); + process.exit(1); + } finally { + rl.close(); + } +}; + +main(); diff --git a/src/util.mts b/src/util.mts new file mode 100644 index 0000000..702c7be --- /dev/null +++ b/src/util.mts @@ -0,0 +1,23 @@ +import stringWidth from "string-width"; +import chalk from "chalk"; + +export const displayBoxedText = (text: string) => { + const lines = text.split("\n"); + const maxLength = lines.reduce( + (max: number, line: string) => Math.max(max, stringWidth(line)), + 0 + ); + const horizontalLine = "┌" + "─".repeat(maxLength + 2) + "┐"; + const verticalLine = "│ "; + + console.log(chalk.gray(horizontalLine)); + lines.forEach((line: string) => { + const displayLength = stringWidth(line); + console.log( + chalk.gray( + verticalLine + line + " ".repeat(maxLength - displayLength) + " │" + ) + ); + }); + console.log(chalk.gray("└" + "─".repeat(maxLength + 2) + "┘")); +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..10a4bb9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "sourceMap": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "moduleResolution": "node", + "module": "ESNext", + "target": "es2020", + "jsx": "react-jsx", + "lib": ["es2022", "dom", "dom.iterable"], + "types": ["node"], + "baseUrl": "./src/", + "plugins": [], + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "./lib" + } +} diff --git a/yarn.lock b/yarn.lock index 13c594f..6f1e28f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -151,6 +151,19 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz#839f72c2decd378f86b8f525e1979a97b920c67d" integrity sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA== +"@google/genai@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@google/genai/-/genai-0.8.0.tgz#caf753288fe0123ab5cfca1f5f8613cc840e0029" + integrity sha512-Zs+OGyZKyMbFofGJTR9/jTQSv8kITh735N3tEuIZj4VlMQXTC0soCFahysJ9NaeenRlD7xGb6fyqmX+FwrpU6Q== + dependencies: + google-auth-library "^9.14.2" + ws "^8.18.0" + +"@google/generative-ai@^0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@google/generative-ai/-/generative-ai-0.24.0.tgz#4d27af7d944c924a27a593c17ad1336535d53846" + integrity sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q== + "@rollup/rollup-android-arm-eabi@4.39.0": version "4.39.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz#1d8cc5dd3d8ffe569d8f7f67a45c7909828a0f66" @@ -256,6 +269,33 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== +"@types/node@^22.14.0": + version "22.14.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.0.tgz#d3bfa3936fef0dbacd79ea3eb17d521c628bb47e" + integrity sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA== + dependencies: + undici-types "~6.21.0" + +agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +base64-js@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bignumber.js@^9.0.0: + version "9.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.2.1.tgz#3ad0854ad933560a25bbc7c93bc3b7ea6edcad85" + integrity sha512-+NzaKgOUvInq9TIUZ1+DRspzf/HApkCwD4btfuasFTdrfnOxqx853TgDpMolp+uv4RpRp7bPcEU2zKr9+fRmyw== + bottom-tip@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/bottom-tip/-/bottom-tip-0.1.5.tgz#ca81e738fba6ae956a5b4c55a78a127820c9b99e" @@ -269,16 +309,45 @@ browser-split@0.0.1: resolved "https://registry.yarnpkg.com/browser-split/-/browser-split-0.0.1.tgz#7b097574f8e3ead606fb4664e64adfdda2981a93" integrity sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow== +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + camelize@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== +chalk@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== + +debug@4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== + error@^4.3.0: version "4.4.0" resolved "https://registry.yarnpkg.com/error/-/error-4.4.0.tgz#bf69ff251fb4a279c19adccdaa6b61e90d9bf12a" @@ -326,11 +395,41 @@ ev-store@^7.0.0: dependencies: individual "^3.0.0" +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +gaxios@^6.0.0, gaxios@^6.1.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" + integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== + dependencies: + extend "^3.0.2" + https-proxy-agent "^7.0.1" + is-stream "^2.0.0" + node-fetch "^2.6.9" + uuid "^9.0.1" + +gcp-metadata@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.1.tgz#f65aa69f546bc56e116061d137d3f5f90bdec494" + integrity sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A== + dependencies: + gaxios "^6.1.1" + google-logging-utils "^0.0.2" + json-bigint "^1.0.0" + +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + global@^4.3.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" @@ -339,6 +438,39 @@ global@^4.3.0: min-document "^2.19.0" process "^0.11.10" +google-auth-library@^9.14.2: + version "9.15.1" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.1.tgz#0c5d84ed1890b2375f1cd74f03ac7b806b392928" + integrity sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng== + dependencies: + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" + jws "^4.0.0" + +google-logging-utils@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/google-logging-utils/-/google-logging-utils-0.0.2.tgz#5fd837e06fa334da450433b9e3e1870c1594466a" + integrity sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ== + +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== + dependencies: + gaxios "^6.0.0" + jws "^4.0.0" + +https-proxy-agent@^7.0.1: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + individual@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/individual/-/individual-3.0.0.tgz#e7ca4f85f8957b018734f285750dc22ec2f9862d" @@ -349,6 +481,35 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -356,6 +517,11 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.3.8: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" @@ -371,6 +537,13 @@ next-tick@^0.2.2: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" integrity sha512-f7h4svPtl+QidoBv4taKXUjJ70G2asaZ8G28nS0OkqaalX8dwwrtWtyxEDPK62AC00ur/+/E0pUwBwY5EPn15Q== +node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -429,6 +602,11 @@ rollup@^4.30.1: "@rollup/rollup-win32-x64-msvc" "4.39.0" fsevents "~2.3.2" +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -439,6 +617,37 @@ string-template@~0.2.0: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== +string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +typescript@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + url-parse@^1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" @@ -447,6 +656,11 @@ url-parse@^1.5.10: querystringify "^2.1.1" requires-port "^1.0.0" +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + virtual-dom@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/virtual-dom/-/virtual-dom-2.1.1.tgz#80eda2d481b9ede0c049118cefcb4a05f21d1375" @@ -472,6 +686,24 @@ vite@^6.2.5: optionalDependencies: fsevents "~2.3.3" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +ws@^8.18.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + x-is-array@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/x-is-array/-/x-is-array-0.1.0.tgz#de520171d47b3f416f5587d629b89d26b12dc29d"