rird 1.0.200
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/AGENTS.md +27 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +336 -0
- package/bin/pty-wrapper.js +285 -0
- package/bunfig.toml +4 -0
- package/facebook_ads_library.png +0 -0
- package/nul`nif +0 -0
- package/package.json +111 -0
- package/parsers-config.ts +239 -0
- package/rird-1.0.199.tgz +0 -0
- package/script/build-windows.ts +54 -0
- package/script/build.ts +167 -0
- package/script/postinstall.mjs +544 -0
- package/script/publish-registries.ts +187 -0
- package/script/publish.ts +72 -0
- package/script/schema.ts +47 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1063 -0
- package/src/acp/session.ts +101 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +367 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +12 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +10 -0
- package/src/agent/prompt/title.txt +36 -0
- package/src/auth/index.ts +70 -0
- package/src/bun/index.ts +114 -0
- package/src/bus/bus-event.ts +43 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +105 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +88 -0
- package/src/cli/cmd/agent.ts +256 -0
- package/src/cli/cmd/auth.ts +391 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/debug/config.ts +15 -0
- package/src/cli/cmd/debug/file.ts +91 -0
- package/src/cli/cmd/debug/index.ts +43 -0
- package/src/cli/cmd/debug/lsp.ts +48 -0
- package/src/cli/cmd/debug/ripgrep.ts +83 -0
- package/src/cli/cmd/debug/scrap.ts +15 -0
- package/src/cli/cmd/debug/skill.ts +15 -0
- package/src/cli/cmd/debug/snapshot.ts +48 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1400 -0
- package/src/cli/cmd/import.ts +98 -0
- package/src/cli/cmd/mcp.ts +654 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +368 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +106 -0
- package/src/cli/cmd/stats.ts +298 -0
- package/src/cli/cmd/tui/app.tsx +696 -0
- package/src/cli/cmd/tui/attach.ts +30 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
- package/src/cli/cmd/tui/component/logo.tsx +35 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/tips.ts +27 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +23 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +101 -0
- package/src/cli/cmd/tui/context/kv.tsx +49 -0
- package/src/cli/cmd/tui/context/local.tsx +354 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +46 -0
- package/src/cli/cmd/tui/context/sdk.tsx +74 -0
- package/src/cli/cmd/tui/context/sync.tsx +372 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +245 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rird.json +245 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1109 -0
- package/src/cli/cmd/tui/event.ts +40 -0
- package/src/cli/cmd/tui/routes/home.tsx +138 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
- package/src/cli/cmd/tui/spawn.ts +60 -0
- package/src/cli/cmd/tui/thread.ts +142 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +127 -0
- package/src/cli/cmd/tui/util/editor.ts +32 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/worker.ts +63 -0
- package/src/cli/cmd/uninstall.ts +344 -0
- package/src/cli/cmd/upgrade.ts +100 -0
- package/src/cli/cmd/web.ts +84 -0
- package/src/cli/error.ts +56 -0
- package/src/cli/ui.ts +84 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +80 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +97 -0
- package/src/config/config.ts +995 -0
- package/src/config/markdown.ts +41 -0
- package/src/env/index.ts +26 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +328 -0
- package/src/file/ripgrep.ts +393 -0
- package/src/file/time.ts +64 -0
- package/src/file/watcher.ts +103 -0
- package/src/flag/flag.ts +46 -0
- package/src/format/formatter.ts +315 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +52 -0
- package/src/id/id.ts +73 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +240 -0
- package/src/installation/index.ts +239 -0
- package/src/lsp/client.ts +229 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +116 -0
- package/src/lsp/server.ts +1895 -0
- package/src/mcp/auth.ts +135 -0
- package/src/mcp/index.ts +690 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +154 -0
- package/src/patch/index.ts +622 -0
- package/src/permission/index.ts +199 -0
- package/src/plugin/index.ts +91 -0
- package/src/project/bootstrap.ts +31 -0
- package/src/project/instance.ts +78 -0
- package/src/project/project.ts +221 -0
- package/src/project/state.ts +65 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +143 -0
- package/src/provider/models-macro.ts +11 -0
- package/src/provider/models.ts +106 -0
- package/src/provider/provider.ts +1071 -0
- package/src/provider/sdk/openai-compatible/src/README.md +5 -0
- package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
- package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
- package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +455 -0
- package/src/pty/index.ts +231 -0
- package/src/security/guardrails.test.ts +341 -0
- package/src/security/guardrails.ts +558 -0
- package/src/security/index.ts +19 -0
- package/src/server/error.ts +36 -0
- package/src/server/project.ts +79 -0
- package/src/server/server.ts +2642 -0
- package/src/server/tui.ts +71 -0
- package/src/session/compaction.ts +223 -0
- package/src/session/index.ts +461 -0
- package/src/session/llm.ts +201 -0
- package/src/session/message-v2.ts +690 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +409 -0
- package/src/session/prompt/act-switch.txt +5 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +85 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +103 -0
- package/src/session/prompt/codex.txt +304 -0
- package/src/session/prompt/copilot-gpt-5.txt +138 -0
- package/src/session/prompt/gemini.txt +85 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
- package/src/session/prompt/plan.txt +24 -0
- package/src/session/prompt/polaris.txt +84 -0
- package/src/session/prompt/qwen.txt +106 -0
- package/src/session/prompt.ts +1509 -0
- package/src/session/retry.ts +86 -0
- package/src/session/revert.ts +108 -0
- package/src/session/sensitive-filter.test.ts +327 -0
- package/src/session/sensitive-filter.ts +466 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +194 -0
- package/src/session/system.ts +120 -0
- package/src/session/todo.ts +37 -0
- package/src/share/share-next.ts +194 -0
- package/src/share/share.ts +87 -0
- package/src/shell/shell.ts +67 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +83 -0
- package/src/snapshot/index.ts +197 -0
- package/src/storage/storage.ts +226 -0
- package/src/tests/agent.test.ts +308 -0
- package/src/tests/build-guards.test.ts +267 -0
- package/src/tests/config.test.ts +664 -0
- package/src/tests/tool-registry.test.ts +589 -0
- package/src/tool/bash.ts +317 -0
- package/src/tool/bash.txt +158 -0
- package/src/tool/batch.ts +175 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +168 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +675 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/glob.ts +65 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +121 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +110 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp-diagnostics.ts +26 -0
- package/src/tool/lsp-diagnostics.txt +1 -0
- package/src/tool/lsp-hover.ts +31 -0
- package/src/tool/lsp-hover.txt +1 -0
- package/src/tool/lsp.ts +87 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/patch.ts +233 -0
- package/src/tool/patch.txt +1 -0
- package/src/tool/read.ts +219 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +162 -0
- package/src/tool/skill.ts +100 -0
- package/src/tool/task.ts +136 -0
- package/src/tool/task.txt +51 -0
- package/src/tool/todo.ts +39 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +71 -0
- package/src/tool/webfetch.ts +198 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +180 -0
- package/src/tool/websearch.txt +11 -0
- package/src/tool/write.ts +110 -0
- package/src/tool/write.txt +8 -0
- package/src/util/archive.ts +16 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/defer.ts +12 -0
- package/src/util/eventloop.ts +20 -0
- package/src/util/filesystem.ts +83 -0
- package/src/util/fn.ts +11 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +102 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/license.ts +325 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +180 -0
- package/src/util/queue.ts +32 -0
- package/src/util/rpc.ts +42 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/wildcard.ts +54 -0
- package/sst-env.d.ts +9 -0
- package/test/agent/agent.test.ts +146 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/config/agent-color.test.ts +66 -0
- package/test/config/config.test.ts +535 -0
- package/test/config/markdown.test.ts +89 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/fixture/fixture.ts +36 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/keybind.test.ts +421 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/preload.ts +57 -0
- package/test/project/project.test.ts +72 -0
- package/test/provider/provider.test.ts +1809 -0
- package/test/provider/transform.test.ts +411 -0
- package/test/session/retry.test.ts +111 -0
- package/test/session/session.test.ts +71 -0
- package/test/skill/skill.test.ts +131 -0
- package/test/snapshot/snapshot.test.ts +939 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/bash.test.ts +434 -0
- package/test/tool/grep.test.ts +108 -0
- package/test/tool/patch.test.ts +259 -0
- package/test/tool/read.test.ts +42 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +55 -0
- package/tsconfig.json +16 -0
package/src/util/lock.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export namespace Lock {
|
|
2
|
+
const locks = new Map<
|
|
3
|
+
string,
|
|
4
|
+
{
|
|
5
|
+
readers: number
|
|
6
|
+
writer: boolean
|
|
7
|
+
waitingReaders: (() => void)[]
|
|
8
|
+
waitingWriters: (() => void)[]
|
|
9
|
+
}
|
|
10
|
+
>()
|
|
11
|
+
|
|
12
|
+
function get(key: string) {
|
|
13
|
+
if (!locks.has(key)) {
|
|
14
|
+
locks.set(key, {
|
|
15
|
+
readers: 0,
|
|
16
|
+
writer: false,
|
|
17
|
+
waitingReaders: [],
|
|
18
|
+
waitingWriters: [],
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
return locks.get(key)!
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function process(key: string) {
|
|
25
|
+
const lock = locks.get(key)
|
|
26
|
+
if (!lock || lock.writer || lock.readers > 0) return
|
|
27
|
+
|
|
28
|
+
// Prioritize writers to prevent starvation
|
|
29
|
+
if (lock.waitingWriters.length > 0) {
|
|
30
|
+
const nextWriter = lock.waitingWriters.shift()!
|
|
31
|
+
nextWriter()
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Wake up all waiting readers
|
|
36
|
+
while (lock.waitingReaders.length > 0) {
|
|
37
|
+
const nextReader = lock.waitingReaders.shift()!
|
|
38
|
+
nextReader()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Clean up empty locks
|
|
42
|
+
if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) {
|
|
43
|
+
locks.delete(key)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function read(key: string): Promise<Disposable> {
|
|
48
|
+
const lock = get(key)
|
|
49
|
+
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
if (!lock.writer && lock.waitingWriters.length === 0) {
|
|
52
|
+
lock.readers++
|
|
53
|
+
resolve({
|
|
54
|
+
[Symbol.dispose]: () => {
|
|
55
|
+
lock.readers--
|
|
56
|
+
process(key)
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
} else {
|
|
60
|
+
lock.waitingReaders.push(() => {
|
|
61
|
+
lock.readers++
|
|
62
|
+
resolve({
|
|
63
|
+
[Symbol.dispose]: () => {
|
|
64
|
+
lock.readers--
|
|
65
|
+
process(key)
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function write(key: string): Promise<Disposable> {
|
|
74
|
+
const lock = get(key)
|
|
75
|
+
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
if (!lock.writer && lock.readers === 0) {
|
|
78
|
+
lock.writer = true
|
|
79
|
+
resolve({
|
|
80
|
+
[Symbol.dispose]: () => {
|
|
81
|
+
lock.writer = false
|
|
82
|
+
process(key)
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
} else {
|
|
86
|
+
lock.waitingWriters.push(() => {
|
|
87
|
+
lock.writer = true
|
|
88
|
+
resolve({
|
|
89
|
+
[Symbol.dispose]: () => {
|
|
90
|
+
lock.writer = false
|
|
91
|
+
process(key)
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/util/log.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import fs from "fs/promises"
|
|
3
|
+
import { Global } from "../global"
|
|
4
|
+
import z from "zod"
|
|
5
|
+
|
|
6
|
+
export namespace Log {
|
|
7
|
+
export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" })
|
|
8
|
+
export type Level = z.infer<typeof Level>
|
|
9
|
+
|
|
10
|
+
const levelPriority: Record<Level, number> = {
|
|
11
|
+
DEBUG: 0,
|
|
12
|
+
INFO: 1,
|
|
13
|
+
WARN: 2,
|
|
14
|
+
ERROR: 3,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let level: Level = "INFO"
|
|
18
|
+
|
|
19
|
+
function shouldLog(input: Level): boolean {
|
|
20
|
+
return levelPriority[input] >= levelPriority[level]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type Logger = {
|
|
24
|
+
debug(message?: any, extra?: Record<string, any>): void
|
|
25
|
+
info(message?: any, extra?: Record<string, any>): void
|
|
26
|
+
error(message?: any, extra?: Record<string, any>): void
|
|
27
|
+
warn(message?: any, extra?: Record<string, any>): void
|
|
28
|
+
tag(key: string, value: string): Logger
|
|
29
|
+
clone(): Logger
|
|
30
|
+
time(
|
|
31
|
+
message: string,
|
|
32
|
+
extra?: Record<string, any>,
|
|
33
|
+
): {
|
|
34
|
+
stop(): void
|
|
35
|
+
[Symbol.dispose](): void
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const loggers = new Map<string, Logger>()
|
|
40
|
+
|
|
41
|
+
export const Default = create({ service: "default" })
|
|
42
|
+
|
|
43
|
+
export interface Options {
|
|
44
|
+
print: boolean
|
|
45
|
+
dev?: boolean
|
|
46
|
+
level?: Level
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let logpath = ""
|
|
50
|
+
export function file() {
|
|
51
|
+
return logpath
|
|
52
|
+
}
|
|
53
|
+
let write = (msg: any) => {
|
|
54
|
+
process.stderr.write(msg)
|
|
55
|
+
return msg.length
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function init(options: Options) {
|
|
59
|
+
if (options.level) level = options.level
|
|
60
|
+
cleanup(Global.Path.log)
|
|
61
|
+
if (options.print) return
|
|
62
|
+
logpath = path.join(
|
|
63
|
+
Global.Path.log,
|
|
64
|
+
options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log",
|
|
65
|
+
)
|
|
66
|
+
const logfile = Bun.file(logpath)
|
|
67
|
+
await fs.truncate(logpath).catch(() => {})
|
|
68
|
+
const writer = logfile.writer()
|
|
69
|
+
write = async (msg: any) => {
|
|
70
|
+
const num = writer.write(msg)
|
|
71
|
+
writer.flush()
|
|
72
|
+
return num
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function cleanup(dir: string) {
|
|
77
|
+
const glob = new Bun.Glob("????-??-??T??????.log")
|
|
78
|
+
const files = await Array.fromAsync(
|
|
79
|
+
glob.scan({
|
|
80
|
+
cwd: dir,
|
|
81
|
+
absolute: true,
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
if (files.length <= 5) return
|
|
85
|
+
|
|
86
|
+
const filesToDelete = files.slice(0, -10)
|
|
87
|
+
await Promise.all(filesToDelete.map((file) => fs.unlink(file).catch(() => {})))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatError(error: Error, depth = 0): string {
|
|
91
|
+
const result = error.message
|
|
92
|
+
return error.cause instanceof Error && depth < 10
|
|
93
|
+
? result + " Caused by: " + formatError(error.cause, depth + 1)
|
|
94
|
+
: result
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let last = Date.now()
|
|
98
|
+
export function create(tags?: Record<string, any>) {
|
|
99
|
+
tags = tags || {}
|
|
100
|
+
|
|
101
|
+
const service = tags["service"]
|
|
102
|
+
if (service && typeof service === "string") {
|
|
103
|
+
const cached = loggers.get(service)
|
|
104
|
+
if (cached) {
|
|
105
|
+
return cached
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function build(message: any, extra?: Record<string, any>) {
|
|
110
|
+
const prefix = Object.entries({
|
|
111
|
+
...tags,
|
|
112
|
+
...extra,
|
|
113
|
+
})
|
|
114
|
+
.filter(([_, value]) => value !== undefined && value !== null)
|
|
115
|
+
.map(([key, value]) => {
|
|
116
|
+
const prefix = `${key}=`
|
|
117
|
+
if (value instanceof Error) return prefix + formatError(value)
|
|
118
|
+
if (typeof value === "object") return prefix + JSON.stringify(value)
|
|
119
|
+
return prefix + value
|
|
120
|
+
})
|
|
121
|
+
.join(" ")
|
|
122
|
+
const next = new Date()
|
|
123
|
+
const diff = next.getTime() - last
|
|
124
|
+
last = next.getTime()
|
|
125
|
+
return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
|
|
126
|
+
}
|
|
127
|
+
const result: Logger = {
|
|
128
|
+
debug(message?: any, extra?: Record<string, any>) {
|
|
129
|
+
if (shouldLog("DEBUG")) {
|
|
130
|
+
write("DEBUG " + build(message, extra))
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
info(message?: any, extra?: Record<string, any>) {
|
|
134
|
+
if (shouldLog("INFO")) {
|
|
135
|
+
write("INFO " + build(message, extra))
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
error(message?: any, extra?: Record<string, any>) {
|
|
139
|
+
if (shouldLog("ERROR")) {
|
|
140
|
+
write("ERROR " + build(message, extra))
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
warn(message?: any, extra?: Record<string, any>) {
|
|
144
|
+
if (shouldLog("WARN")) {
|
|
145
|
+
write("WARN " + build(message, extra))
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
tag(key: string, value: string) {
|
|
149
|
+
if (tags) tags[key] = value
|
|
150
|
+
return result
|
|
151
|
+
},
|
|
152
|
+
clone() {
|
|
153
|
+
return Log.create({ ...tags })
|
|
154
|
+
},
|
|
155
|
+
time(message: string, extra?: Record<string, any>) {
|
|
156
|
+
const now = Date.now()
|
|
157
|
+
result.info(message, { status: "started", ...extra })
|
|
158
|
+
function stop() {
|
|
159
|
+
result.info(message, {
|
|
160
|
+
status: "completed",
|
|
161
|
+
duration: Date.now() - now,
|
|
162
|
+
...extra,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
stop,
|
|
167
|
+
[Symbol.dispose]() {
|
|
168
|
+
stop()
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (service && typeof service === "string") {
|
|
175
|
+
loggers.set(service, result)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class AsyncQueue<T> implements AsyncIterable<T> {
|
|
2
|
+
private queue: T[] = []
|
|
3
|
+
private resolvers: ((value: T) => void)[] = []
|
|
4
|
+
|
|
5
|
+
push(item: T) {
|
|
6
|
+
const resolve = this.resolvers.shift()
|
|
7
|
+
if (resolve) resolve(item)
|
|
8
|
+
else this.queue.push(item)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async next(): Promise<T> {
|
|
12
|
+
if (this.queue.length > 0) return this.queue.shift()!
|
|
13
|
+
return new Promise((resolve) => this.resolvers.push(resolve))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async *[Symbol.asyncIterator]() {
|
|
17
|
+
while (true) yield await this.next()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function work<T>(concurrency: number, items: T[], fn: (item: T) => Promise<void>) {
|
|
22
|
+
const pending = [...items]
|
|
23
|
+
await Promise.all(
|
|
24
|
+
Array.from({ length: concurrency }, async () => {
|
|
25
|
+
while (true) {
|
|
26
|
+
const item = pending.pop()
|
|
27
|
+
if (item === undefined) return
|
|
28
|
+
await fn(item)
|
|
29
|
+
}
|
|
30
|
+
}),
|
|
31
|
+
)
|
|
32
|
+
}
|
package/src/util/rpc.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export namespace Rpc {
|
|
2
|
+
type Definition = {
|
|
3
|
+
[method: string]: (input: any) => any
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function listen(rpc: Definition) {
|
|
7
|
+
onmessage = async (evt) => {
|
|
8
|
+
const parsed = JSON.parse(evt.data)
|
|
9
|
+
if (parsed.type === "rpc.request") {
|
|
10
|
+
const result = await rpc[parsed.method](parsed.input)
|
|
11
|
+
postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id }))
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function client<T extends Definition>(target: {
|
|
17
|
+
postMessage: (data: string) => void | null
|
|
18
|
+
onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null
|
|
19
|
+
}) {
|
|
20
|
+
const pending = new Map<number, (result: any) => void>()
|
|
21
|
+
let id = 0
|
|
22
|
+
target.onmessage = async (evt) => {
|
|
23
|
+
const parsed = JSON.parse(evt.data)
|
|
24
|
+
if (parsed.type === "rpc.result") {
|
|
25
|
+
const resolve = pending.get(parsed.id)
|
|
26
|
+
if (resolve) {
|
|
27
|
+
resolve(parsed.result)
|
|
28
|
+
pending.delete(parsed.id)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
call<Method extends keyof T>(method: Method, input: Parameters<T[Method]>[0]): Promise<ReturnType<T[Method]>> {
|
|
34
|
+
const requestId = id++
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
pending.set(requestId, resolve)
|
|
37
|
+
target.postMessage(JSON.stringify({ type: "rpc.request", method, input, id: requestId }))
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
|
2
|
+
let timeout: NodeJS.Timeout
|
|
3
|
+
return Promise.race([
|
|
4
|
+
promise.then((result) => {
|
|
5
|
+
clearTimeout(timeout)
|
|
6
|
+
return result
|
|
7
|
+
}),
|
|
8
|
+
new Promise<never>((_, reject) => {
|
|
9
|
+
timeout = setTimeout(() => {
|
|
10
|
+
reject(new Error(`Operation timed out after ${ms}ms`))
|
|
11
|
+
}, ms)
|
|
12
|
+
}),
|
|
13
|
+
])
|
|
14
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { sortBy, pipe } from "remeda"
|
|
2
|
+
|
|
3
|
+
export namespace Wildcard {
|
|
4
|
+
export function match(str: string, pattern: string) {
|
|
5
|
+
const regex = new RegExp(
|
|
6
|
+
"^" +
|
|
7
|
+
pattern
|
|
8
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&") // escape special regex chars
|
|
9
|
+
.replace(/\*/g, ".*") // * becomes .*
|
|
10
|
+
.replace(/\?/g, ".") + // ? becomes .
|
|
11
|
+
"$",
|
|
12
|
+
"s", // s flag enables multiline matching
|
|
13
|
+
)
|
|
14
|
+
return regex.test(str)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function all(input: string, patterns: Record<string, any>) {
|
|
18
|
+
const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
|
|
19
|
+
let result = undefined
|
|
20
|
+
for (const [pattern, value] of sorted) {
|
|
21
|
+
if (match(input, pattern)) {
|
|
22
|
+
result = value
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function allStructured(input: { head: string; tail: string[] }, patterns: Record<string, any>) {
|
|
30
|
+
const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
|
|
31
|
+
let result = undefined
|
|
32
|
+
for (const [pattern, value] of sorted) {
|
|
33
|
+
const parts = pattern.split(/\s+/)
|
|
34
|
+
if (!match(input.head, parts[0])) continue
|
|
35
|
+
if (parts.length === 1 || matchSequence(input.tail, parts.slice(1))) {
|
|
36
|
+
result = value
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function matchSequence(items: string[], patterns: string[]): boolean {
|
|
44
|
+
if (patterns.length === 0) return true
|
|
45
|
+
const [pattern, ...rest] = patterns
|
|
46
|
+
if (pattern === "*") return matchSequence(items, rest)
|
|
47
|
+
for (let i = 0; i < items.length; i++) {
|
|
48
|
+
if (match(items[i], pattern) && matchSequence(items.slice(i + 1), rest)) {
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
package/sst-env.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import fs from "fs/promises"
|
|
4
|
+
import { tmpdir } from "../fixture/fixture"
|
|
5
|
+
import { Instance } from "../../src/project/instance"
|
|
6
|
+
import { Agent } from "../../src/agent/agent"
|
|
7
|
+
|
|
8
|
+
test("loads built-in agents when no custom agents configured", async () => {
|
|
9
|
+
await using tmp = await tmpdir()
|
|
10
|
+
await Instance.provide({
|
|
11
|
+
directory: tmp.path,
|
|
12
|
+
fn: async () => {
|
|
13
|
+
const agents = await Agent.list()
|
|
14
|
+
const names = agents.map((a) => a.name)
|
|
15
|
+
expect(names).toContain("act")
|
|
16
|
+
expect(names).toContain("plan")
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("custom subagent works alongside built-in primary agents", async () => {
|
|
22
|
+
await using tmp = await tmpdir({
|
|
23
|
+
init: async (dir) => {
|
|
24
|
+
const opencodeDir = path.join(dir, ".opencode")
|
|
25
|
+
await fs.mkdir(opencodeDir, { recursive: true })
|
|
26
|
+
const agentDir = path.join(opencodeDir, "agent")
|
|
27
|
+
await fs.mkdir(agentDir, { recursive: true })
|
|
28
|
+
|
|
29
|
+
await Bun.write(
|
|
30
|
+
path.join(agentDir, "helper.md"),
|
|
31
|
+
`---
|
|
32
|
+
model: test/model
|
|
33
|
+
mode: subagent
|
|
34
|
+
---
|
|
35
|
+
Helper subagent prompt`,
|
|
36
|
+
)
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
await Instance.provide({
|
|
40
|
+
directory: tmp.path,
|
|
41
|
+
fn: async () => {
|
|
42
|
+
const agents = await Agent.list()
|
|
43
|
+
const helper = agents.find((a) => a.name === "helper")
|
|
44
|
+
expect(helper).toBeDefined()
|
|
45
|
+
expect(helper?.mode).toBe("subagent")
|
|
46
|
+
|
|
47
|
+
// Built-in primary agents should still exist
|
|
48
|
+
const act = agents.find((a) => a.name === "act")
|
|
49
|
+
expect(act).toBeDefined()
|
|
50
|
+
expect(act?.mode).toBe("primary")
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("throws error when all primary agents are disabled", async () => {
|
|
56
|
+
await using tmp = await tmpdir({
|
|
57
|
+
init: async (dir) => {
|
|
58
|
+
await Bun.write(
|
|
59
|
+
path.join(dir, "opencode.json"),
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
$schema: "https://rird.ai/config.json",
|
|
62
|
+
agent: {
|
|
63
|
+
act: { disable: true },
|
|
64
|
+
plan: { disable: true },
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
await Instance.provide({
|
|
71
|
+
directory: tmp.path,
|
|
72
|
+
fn: async () => {
|
|
73
|
+
try {
|
|
74
|
+
await Agent.list()
|
|
75
|
+
expect(true).toBe(false) // should not reach here
|
|
76
|
+
} catch (e: any) {
|
|
77
|
+
expect(e.data?.message).toContain("No primary agents are available")
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("does not throw when at least one primary agent remains", async () => {
|
|
84
|
+
await using tmp = await tmpdir({
|
|
85
|
+
init: async (dir) => {
|
|
86
|
+
await Bun.write(
|
|
87
|
+
path.join(dir, "opencode.json"),
|
|
88
|
+
JSON.stringify({
|
|
89
|
+
$schema: "https://rird.ai/config.json",
|
|
90
|
+
agent: {
|
|
91
|
+
act: { disable: true },
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
)
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
await Instance.provide({
|
|
98
|
+
directory: tmp.path,
|
|
99
|
+
fn: async () => {
|
|
100
|
+
const agents = await Agent.list()
|
|
101
|
+
const plan = agents.find((a) => a.name === "plan")
|
|
102
|
+
expect(plan).toBeDefined()
|
|
103
|
+
expect(plan?.mode).toBe("primary")
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test("custom primary agent satisfies requirement when built-ins disabled", async () => {
|
|
109
|
+
await using tmp = await tmpdir({
|
|
110
|
+
init: async (dir) => {
|
|
111
|
+
const opencodeDir = path.join(dir, ".opencode")
|
|
112
|
+
await fs.mkdir(opencodeDir, { recursive: true })
|
|
113
|
+
const agentDir = path.join(opencodeDir, "agent")
|
|
114
|
+
await fs.mkdir(agentDir, { recursive: true })
|
|
115
|
+
|
|
116
|
+
await Bun.write(
|
|
117
|
+
path.join(agentDir, "custom.md"),
|
|
118
|
+
`---
|
|
119
|
+
model: test/model
|
|
120
|
+
mode: primary
|
|
121
|
+
---
|
|
122
|
+
Custom primary agent`,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
await Bun.write(
|
|
126
|
+
path.join(dir, "opencode.json"),
|
|
127
|
+
JSON.stringify({
|
|
128
|
+
$schema: "https://rird.ai/config.json",
|
|
129
|
+
agent: {
|
|
130
|
+
act: { disable: true },
|
|
131
|
+
plan: { disable: true },
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
)
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
await Instance.provide({
|
|
138
|
+
directory: tmp.path,
|
|
139
|
+
fn: async () => {
|
|
140
|
+
const agents = await Agent.list()
|
|
141
|
+
const custom = agents.find((a) => a.name === "custom")
|
|
142
|
+
expect(custom).toBeDefined()
|
|
143
|
+
expect(custom?.mode).toBe("primary")
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
})
|
package/test/bun.test.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import fs from "fs/promises"
|
|
3
|
+
import path from "path"
|
|
4
|
+
|
|
5
|
+
describe("BunProc registry configuration", () => {
|
|
6
|
+
test("should not contain hardcoded registry parameters", async () => {
|
|
7
|
+
// Read the bun/index.ts file
|
|
8
|
+
const bunIndexPath = path.join(__dirname, "../src/bun/index.ts")
|
|
9
|
+
const content = await fs.readFile(bunIndexPath, "utf-8")
|
|
10
|
+
|
|
11
|
+
// Verify that no hardcoded registry is present
|
|
12
|
+
expect(content).not.toContain("--registry=")
|
|
13
|
+
expect(content).not.toContain("hasNpmRcConfig")
|
|
14
|
+
expect(content).not.toContain("NpmRc")
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test("should use Bun's default registry resolution", async () => {
|
|
18
|
+
// Read the bun/index.ts file
|
|
19
|
+
const bunIndexPath = path.join(__dirname, "../src/bun/index.ts")
|
|
20
|
+
const content = await fs.readFile(bunIndexPath, "utf-8")
|
|
21
|
+
|
|
22
|
+
// Verify that it uses Bun's default resolution
|
|
23
|
+
expect(content).toContain("Bun's default registry resolution")
|
|
24
|
+
expect(content).toContain("Bun will use them automatically")
|
|
25
|
+
expect(content).toContain("No need to pass --registry flag")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("should have correct command structure without registry", async () => {
|
|
29
|
+
// Read the bun/index.ts file
|
|
30
|
+
const bunIndexPath = path.join(__dirname, "../src/bun/index.ts")
|
|
31
|
+
const content = await fs.readFile(bunIndexPath, "utf-8")
|
|
32
|
+
|
|
33
|
+
// Extract the install function
|
|
34
|
+
const installFunctionMatch = content.match(/export async function install[\s\S]*?^ }/m)
|
|
35
|
+
expect(installFunctionMatch).toBeTruthy()
|
|
36
|
+
|
|
37
|
+
if (installFunctionMatch) {
|
|
38
|
+
const installFunction = installFunctionMatch[0]
|
|
39
|
+
|
|
40
|
+
// Verify expected arguments are present
|
|
41
|
+
expect(installFunction).toContain('"add"')
|
|
42
|
+
expect(installFunction).toContain('"--force"')
|
|
43
|
+
expect(installFunction).toContain('"--exact"')
|
|
44
|
+
expect(installFunction).toContain('"--cwd"')
|
|
45
|
+
expect(installFunction).toContain("Global.Path.cache")
|
|
46
|
+
expect(installFunction).toContain('pkg + "@" + version')
|
|
47
|
+
|
|
48
|
+
// Verify no registry argument is added
|
|
49
|
+
expect(installFunction).not.toContain('"--registry"')
|
|
50
|
+
expect(installFunction).not.toContain('args.push("--registry')
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
})
|