cerebras-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +27 -0
- package/Dockerfile +10 -0
- package/README.md +15 -0
- package/bin/opencode +84 -0
- package/bunfig.toml +4 -0
- package/package.json +128 -0
- package/parsers-config.ts +239 -0
- package/script/build.ts +151 -0
- package/script/postinstall.mjs +122 -0
- package/script/publish.ts +256 -0
- package/script/schema.ts +47 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +812 -0
- package/src/acp/session.ts +70 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +310 -0
- package/src/agent/generate.txt +75 -0
- package/src/auth/index.ts +70 -0
- package/src/bun/index.ts +152 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +142 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +88 -0
- package/src/cli/cmd/agent.ts +165 -0
- package/src/cli/cmd/auth.ts +369 -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 +41 -0
- package/src/cli/cmd/debug/lsp.ts +47 -0
- package/src/cli/cmd/debug/ripgrep.ts +83 -0
- package/src/cli/cmd/debug/scrap.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 +1200 -0
- package/src/cli/cmd/import.ts +98 -0
- package/src/cli/cmd/mcp.ts +400 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +342 -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 +732 -0
- package/src/cli/cmd/tui/attach.ts +25 -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-feedback.tsx +160 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +223 -0
- package/src/cli/cmd/tui/component/dialog-notification.tsx +78 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +222 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +97 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +114 -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/logo.tsx +37 -0
- package/src/cli/cmd/tui/component/notification-banner.tsx +58 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +530 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +107 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +931 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +12 -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 +111 -0
- package/src/cli/cmd/tui/context/kv.tsx +49 -0
- package/src/cli/cmd/tui/context/local.tsx +339 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +45 -0
- package/src/cli/cmd/tui/context/sdk.tsx +75 -0
- package/src/cli/cmd/tui/context/sync.tsx +374 -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-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/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/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/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 +1077 -0
- package/src/cli/cmd/tui/event.ts +39 -0
- package/src/cli/cmd/tui/routes/home.tsx +104 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +93 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +37 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +76 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +183 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +1703 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +586 -0
- package/src/cli/cmd/tui/spawn.ts +60 -0
- package/src/cli/cmd/tui/thread.ts +120 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +55 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +81 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +36 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +75 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +317 -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 +67 -0
- package/src/cli/cmd/web.ts +84 -0
- package/src/cli/error.ts +55 -0
- package/src/cli/ui.ts +84 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +79 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +73 -0
- package/src/config/config.ts +886 -0
- package/src/config/markdown.ts +41 -0
- package/src/env/index.ts +26 -0
- package/src/file/fzf.ts +124 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +326 -0
- package/src/file/ripgrep.ts +391 -0
- package/src/file/time.ts +38 -0
- package/src/file/watcher.ts +89 -0
- package/src/flag/flag.ts +28 -0
- package/src/format/formatter.ts +277 -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 +75 -0
- package/src/index.ts +158 -0
- package/src/installation/index.ts +194 -0
- package/src/lsp/client.ts +215 -0
- package/src/lsp/index.ts +370 -0
- package/src/lsp/language.ts +111 -0
- package/src/lsp/server.ts +1327 -0
- package/src/mcp/auth.ts +82 -0
- package/src/mcp/index.ts +576 -0
- package/src/mcp/oauth-callback.ts +203 -0
- package/src/mcp/oauth-provider.ts +132 -0
- package/src/notification/index.ts +101 -0
- package/src/patch/index.ts +622 -0
- package/src/permission/index.ts +198 -0
- package/src/plugin/index.ts +95 -0
- package/src/project/bootstrap.ts +31 -0
- package/src/project/instance.ts +68 -0
- package/src/project/project.ts +133 -0
- package/src/project/state.ts +65 -0
- package/src/project/vcs.ts +77 -0
- package/src/provider/auth.ts +143 -0
- package/src/provider/models-macro.ts +11 -0
- package/src/provider/models.ts +93 -0
- package/src/provider/provider.ts +996 -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 +27 -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 +406 -0
- package/src/pty/index.ts +226 -0
- package/src/ratelimit/index.ts +185 -0
- package/src/server/error.ts +36 -0
- package/src/server/project.ts +50 -0
- package/src/server/server.ts +2463 -0
- package/src/server/tui.ts +71 -0
- package/src/session/compaction.ts +257 -0
- package/src/session/index.ts +470 -0
- package/src/session/message-v2.ts +641 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +443 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +105 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +147 -0
- package/src/session/prompt/build-switch.txt +5 -0
- package/src/session/prompt/codex.txt +318 -0
- package/src/session/prompt/compaction.txt +12 -0
- package/src/session/prompt/copilot-gpt-5.txt +143 -0
- package/src/session/prompt/gemini.txt +155 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
- package/src/session/prompt/plan.txt +26 -0
- package/src/session/prompt/polaris.txt +107 -0
- package/src/session/prompt/qwen.txt +109 -0
- package/src/session/prompt/summarize.txt +4 -0
- package/src/session/prompt/title.txt +36 -0
- package/src/session/prompt.ts +1541 -0
- package/src/session/retry.ts +82 -0
- package/src/session/revert.ts +108 -0
- package/src/session/status.ts +75 -0
- package/src/session/summary.ts +203 -0
- package/src/session/system.ts +148 -0
- package/src/session/todo.ts +36 -0
- package/src/share/share-next.ts +195 -0
- package/src/share/share.ts +87 -0
- package/src/snapshot/index.ts +197 -0
- package/src/storage/storage.ts +226 -0
- package/src/telemetry/index.ts +232 -0
- package/src/tool/bash.ts +365 -0
- package/src/tool/bash.txt +128 -0
- package/src/tool/batch.ts +173 -0
- package/src/tool/batch.txt +28 -0
- package/src/tool/codesearch.ts +138 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +674 -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 +120 -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/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 +217 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +148 -0
- package/src/tool/task.ts +135 -0
- package/src/tool/task.txt +60 -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 +66 -0
- package/src/tool/webfetch.ts +187 -0
- package/src/tool/webfetch.txt +14 -0
- package/src/tool/websearch.ts +150 -0
- package/src/tool/websearch.txt +11 -0
- package/src/tool/write.ts +99 -0
- package/src/tool/write.txt +8 -0
- package/src/types/shims.d.ts +3 -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 +69 -0
- package/src/util/fn.ts +11 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +79 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +177 -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/bun.test.ts +53 -0
- package/test/config/agent-color.test.ts +66 -0
- package/test/config/config.test.ts +503 -0
- package/test/config/markdown.test.ts +89 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/fixture/fixture.ts +28 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/keybind.test.ts +317 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/preload.ts +38 -0
- package/test/project/project.test.ts +42 -0
- package/test/provider/provider.test.ts +1809 -0
- package/test/provider/transform.test.ts +305 -0
- package/test/session/retry.test.ts +61 -0
- package/test/session/session.test.ts +71 -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 +55 -0
- package/test/tool/patch.test.ts +259 -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 +17 -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,177 @@
|
|
|
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) => Bun.stderr.write(msg)
|
|
54
|
+
|
|
55
|
+
export async function init(options: Options) {
|
|
56
|
+
if (options.level) level = options.level
|
|
57
|
+
cleanup(Global.Path.log)
|
|
58
|
+
if (options.print) return
|
|
59
|
+
logpath = path.join(
|
|
60
|
+
Global.Path.log,
|
|
61
|
+
options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log",
|
|
62
|
+
)
|
|
63
|
+
const logfile = Bun.file(logpath)
|
|
64
|
+
await fs.truncate(logpath).catch(() => {})
|
|
65
|
+
const writer = logfile.writer()
|
|
66
|
+
write = async (msg: any) => {
|
|
67
|
+
const num = writer.write(msg)
|
|
68
|
+
writer.flush()
|
|
69
|
+
return num
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function cleanup(dir: string) {
|
|
74
|
+
const glob = new Bun.Glob("????-??-??T??????.log")
|
|
75
|
+
const files = await Array.fromAsync(
|
|
76
|
+
glob.scan({
|
|
77
|
+
cwd: dir,
|
|
78
|
+
absolute: true,
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
if (files.length <= 5) return
|
|
82
|
+
|
|
83
|
+
const filesToDelete = files.slice(0, -10)
|
|
84
|
+
await Promise.all(filesToDelete.map((file) => fs.unlink(file).catch(() => {})))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function formatError(error: Error, depth = 0): string {
|
|
88
|
+
const result = error.message
|
|
89
|
+
return error.cause instanceof Error && depth < 10
|
|
90
|
+
? result + " Caused by: " + formatError(error.cause, depth + 1)
|
|
91
|
+
: result
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let last = Date.now()
|
|
95
|
+
export function create(tags?: Record<string, any>) {
|
|
96
|
+
tags = tags || {}
|
|
97
|
+
|
|
98
|
+
const service = tags["service"]
|
|
99
|
+
if (service && typeof service === "string") {
|
|
100
|
+
const cached = loggers.get(service)
|
|
101
|
+
if (cached) {
|
|
102
|
+
return cached
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function build(message: any, extra?: Record<string, any>) {
|
|
107
|
+
const prefix = Object.entries({
|
|
108
|
+
...tags,
|
|
109
|
+
...extra,
|
|
110
|
+
})
|
|
111
|
+
.filter(([_, value]) => value !== undefined && value !== null)
|
|
112
|
+
.map(([key, value]) => {
|
|
113
|
+
const prefix = `${key}=`
|
|
114
|
+
if (value instanceof Error) return prefix + formatError(value)
|
|
115
|
+
if (typeof value === "object") return prefix + JSON.stringify(value)
|
|
116
|
+
return prefix + value
|
|
117
|
+
})
|
|
118
|
+
.join(" ")
|
|
119
|
+
const next = new Date()
|
|
120
|
+
const diff = next.getTime() - last
|
|
121
|
+
last = next.getTime()
|
|
122
|
+
return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
|
|
123
|
+
}
|
|
124
|
+
const result: Logger = {
|
|
125
|
+
debug(message?: any, extra?: Record<string, any>) {
|
|
126
|
+
if (shouldLog("DEBUG")) {
|
|
127
|
+
write("DEBUG " + build(message, extra))
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
info(message?: any, extra?: Record<string, any>) {
|
|
131
|
+
if (shouldLog("INFO")) {
|
|
132
|
+
write("INFO " + build(message, extra))
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
error(message?: any, extra?: Record<string, any>) {
|
|
136
|
+
if (shouldLog("ERROR")) {
|
|
137
|
+
write("ERROR " + build(message, extra))
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
warn(message?: any, extra?: Record<string, any>) {
|
|
141
|
+
if (shouldLog("WARN")) {
|
|
142
|
+
write("WARN " + build(message, extra))
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
tag(key: string, value: string) {
|
|
146
|
+
if (tags) tags[key] = value
|
|
147
|
+
return result
|
|
148
|
+
},
|
|
149
|
+
clone() {
|
|
150
|
+
return Log.create({ ...tags })
|
|
151
|
+
},
|
|
152
|
+
time(message: string, extra?: Record<string, any>) {
|
|
153
|
+
const now = Date.now()
|
|
154
|
+
result.info(message, { status: "started", ...extra })
|
|
155
|
+
function stop() {
|
|
156
|
+
result.info(message, {
|
|
157
|
+
status: "completed",
|
|
158
|
+
duration: Date.now() - now,
|
|
159
|
+
...extra,
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
stop,
|
|
164
|
+
[Symbol.dispose]() {
|
|
165
|
+
stop()
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (service && typeof service === "string") {
|
|
172
|
+
loggers.set(service, result)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return result
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -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
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
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { tmpdir } from "../fixture/fixture"
|
|
4
|
+
import { Instance } from "../../src/project/instance"
|
|
5
|
+
import { Config } from "../../src/config/config"
|
|
6
|
+
import { Agent as AgentSvc } from "../../src/agent/agent"
|
|
7
|
+
import { Color } from "../../src/util/color"
|
|
8
|
+
|
|
9
|
+
test("agent color parsed from project config", async () => {
|
|
10
|
+
await using tmp = await tmpdir({
|
|
11
|
+
init: async (dir) => {
|
|
12
|
+
await Bun.write(
|
|
13
|
+
path.join(dir, "opencode.json"),
|
|
14
|
+
JSON.stringify({
|
|
15
|
+
$schema: "https://opencode.ai/config.json",
|
|
16
|
+
agent: {
|
|
17
|
+
build: { color: "#FFA500" },
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
await Instance.provide({
|
|
24
|
+
directory: tmp.path,
|
|
25
|
+
fn: async () => {
|
|
26
|
+
const cfg = await Config.get()
|
|
27
|
+
expect(cfg.agent?.["build"]?.color).toBe("#FFA500")
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("Agent.get includes color from config", async () => {
|
|
33
|
+
await using tmp = await tmpdir({
|
|
34
|
+
init: async (dir) => {
|
|
35
|
+
await Bun.write(
|
|
36
|
+
path.join(dir, "opencode.json"),
|
|
37
|
+
JSON.stringify({
|
|
38
|
+
$schema: "https://opencode.ai/config.json",
|
|
39
|
+
agent: {
|
|
40
|
+
plan: { color: "#A855F7" },
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
await Instance.provide({
|
|
47
|
+
directory: tmp.path,
|
|
48
|
+
fn: async () => {
|
|
49
|
+
const plan = await AgentSvc.get("plan")
|
|
50
|
+
expect(plan?.color).toBe("#A855F7")
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("Color.hexToAnsiBold converts valid hex to ANSI", () => {
|
|
56
|
+
const result = Color.hexToAnsiBold("#FFA500")
|
|
57
|
+
expect(result).toBe("\x1b[38;2;255;165;0m\x1b[1m")
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("Color.hexToAnsiBold returns undefined for invalid hex", () => {
|
|
61
|
+
expect(Color.hexToAnsiBold(undefined)).toBeUndefined()
|
|
62
|
+
expect(Color.hexToAnsiBold("")).toBeUndefined()
|
|
63
|
+
expect(Color.hexToAnsiBold("#FFF")).toBeUndefined()
|
|
64
|
+
expect(Color.hexToAnsiBold("FFA500")).toBeUndefined()
|
|
65
|
+
expect(Color.hexToAnsiBold("#GGGGGG")).toBeUndefined()
|
|
66
|
+
})
|