saeeol 1.0.5 → 1.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "name": "saeeol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
package/script/build.ts CHANGED
@@ -55,7 +55,99 @@ const baselineFlag = process.argv.includes("--baseline")
55
55
  const skipInstall = process.argv.includes("--skip-install")
56
56
  const sourcemapsFlag = process.argv.includes("--sourcemaps")
57
57
  const plugin = createSolidTransformPlugin()
58
- async function copyTreeSitterWasms(outputDir: string) {
58
+
59
+ // ══════════════════════════════════════════════════════════════════
60
+ // Tier-based tree-sitter WASM selection
61
+ //
62
+ // LIGHT = 5 langs (markdown, json, yaml, toml, tsx) ~8 MB
63
+ // CODE = 15 langs (+ python, rust, go, c, cpp...) ~18 MB
64
+ // MASTER = all 37 langs ~50 MB
65
+ // ══════════════════════════════════════════════════════════════════
66
+
67
+ // ══════════════════════════════════════════════════════════════════
68
+ // Tier-based external packages
69
+ // These packages are excluded from the bundle entirely
70
+ // ══════════════════════════════════════════════════════════════════
71
+
72
+ const allProviderPkgs = [
73
+ "@ai-sdk/amazon-bedrock",
74
+ "@ai-sdk/anthropic",
75
+ "@ai-sdk/azure",
76
+ "@ai-sdk/google",
77
+ "@ai-sdk/google-vertex",
78
+ "@ai-sdk/openai",
79
+ "@ai-sdk/openai-compatible",
80
+ "@openrouter/ai-sdk-provider",
81
+ "@ai-sdk/xai",
82
+ "@ai-sdk/mistral",
83
+ "@ai-sdk/groq",
84
+ "@ai-sdk/deepinfra",
85
+ "@ai-sdk/cerebras",
86
+ "@ai-sdk/cohere",
87
+ "@ai-sdk/gateway",
88
+ "@ai-sdk/togetherai",
89
+ "@ai-sdk/perplexity",
90
+ "@ai-sdk/vercel",
91
+ "@ai-sdk/alibaba",
92
+ "gitlab-ai-provider",
93
+ "venice-ai-sdk-provider",
94
+ ]
95
+
96
+ const lightRequired = new Set([
97
+ "@ai-sdk/anthropic",
98
+ "@ai-sdk/openai",
99
+ "@ai-sdk/openai-compatible",
100
+ "@ai-sdk/google",
101
+ "@saeeol/gateway",
102
+ ])
103
+
104
+ const codeRequired = new Set([
105
+ ...lightRequired,
106
+ "@ai-sdk/amazon-bedrock",
107
+ "@ai-sdk/azure",
108
+ "@ai-sdk/google-vertex",
109
+ "@openrouter/ai-sdk-provider",
110
+ "@ai-sdk/groq",
111
+ "@ai-sdk/deepinfra",
112
+ "@ai-sdk/gateway",
113
+ "@ai-sdk/alibaba",
114
+ "@ai-sdk/cerebras",
115
+ ])
116
+
117
+ function tierExternals(tier: string): string[] {
118
+ if (tier === "master") return []
119
+ const required = tier === "light" ? lightRequired : codeRequired
120
+ return allProviderPkgs.filter((p) => !required.has(p))
121
+ }
122
+
123
+ const tierArg = process.argv.find((a) => a.startsWith("--tier="))?.split("=")[1] ?? "master"
124
+
125
+ const treeSitterLanguages: Record<string, string[]> = {
126
+ light: [
127
+ "markdown", "markdown_inline",
128
+ "json",
129
+ "yaml",
130
+ "toml",
131
+ "tsx", "typescript",
132
+ ],
133
+ code: [
134
+ "markdown", "markdown_inline",
135
+ "json",
136
+ "yaml",
137
+ "toml",
138
+ "tsx", "typescript",
139
+ "python",
140
+ "rust",
141
+ "go",
142
+ "c", "cpp",
143
+ "java",
144
+ "javascript",
145
+ "bash",
146
+ ],
147
+ master: [], // empty = all
148
+ }
149
+
150
+ async function copyTreeSitterWasms(outputDir: string, tier: string) {
59
151
  const runtimeWasmPath = require.resolve("web-tree-sitter/tree-sitter.wasm")
60
152
  const languagePackagePath = require.resolve("tree-sitter-wasms/package.json")
61
153
  const languageWasmDir = path.join(path.dirname(languagePackagePath), "out")
@@ -64,13 +156,21 @@ async function copyTreeSitterWasms(outputDir: string) {
64
156
  await fs.promises.mkdir(targetDir, { recursive: true })
65
157
  await fs.promises.copyFile(runtimeWasmPath, path.join(targetDir, "tree-sitter.wasm"))
66
158
 
67
- const languageWasmFiles = (await fs.promises.readdir(languageWasmDir)).filter((file) => file.endsWith(".wasm"))
159
+ const allWasmFiles = (await fs.promises.readdir(languageWasmDir)).filter((file) => file.endsWith(".wasm"))
160
+ const allowed = treeSitterLanguages[tier]
161
+
162
+ const filesToCopy = allowed.length === 0
163
+ ? allWasmFiles
164
+ : allWasmFiles.filter((file) => {
165
+ const langName = file.replace("tree-sitter-", "").replace(".wasm", "")
166
+ return allowed.some((a) => langName === a || langName.startsWith(a + "-"))
167
+ })
68
168
 
69
169
  await Promise.all(
70
- languageWasmFiles.map((file) => fs.promises.copyFile(path.join(languageWasmDir, file), path.join(targetDir, file))),
170
+ filesToCopy.map((file) => fs.promises.copyFile(path.join(languageWasmDir, file), path.join(targetDir, file))),
71
171
  )
72
172
 
73
- console.log(`copied ${languageWasmFiles.length + 1} tree-sitter wasm files to ${targetDir}`)
173
+ console.log(`copied ${filesToCopy.length + 1} tree-sitter wasm files to ${targetDir} (tier=${tier})`)
74
174
  }
75
175
 
76
176
  const allTargets: {
@@ -192,7 +292,7 @@ for (const item of targets) {
192
292
  tsconfig: "./tsconfig.json",
193
293
  plugins: [plugin],
194
294
  sourcemap: Script.release ? "none" : "external",
195
- external: ["node-gyp", ...LanceDBRuntime.external],
295
+ external: ["node-gyp", ...LanceDBRuntime.external, ...tierExternals(tierArg)],
196
296
  format: "esm",
197
297
  minify: true,
198
298
  splitting: true,
@@ -216,10 +316,11 @@ for (const item of targets) {
216
316
  SAEEOL_CHANNEL: `'${Script.channel}'`,
217
317
  SAEEOL_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "",
218
318
  SAEEOL_BUILD_KIND: Script.release ? `'release'` : `'source'`,
319
+ SAEEOL_TIER: `'${tierArg}'`,
219
320
  },
220
321
  })
221
322
 
222
- await copyTreeSitterWasms(path.resolve(dir, `dist/${name}/bin`))
323
+ await copyTreeSitterWasms(path.resolve(dir, `dist/${name}/bin`), tierArg)
223
324
  if (item.os === "linux") {
224
325
  const interpreters: Record<string, string> = {
225
326
  x64: "/lib64/ld-linux-x86-64.so.2",
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── analytics: 사용량 통계 + 모델 테스트 + PR ────────────────────────
4
+ export const analytics: Addon = {
5
+ id: "analytics",
6
+ category: "analytics",
7
+ minTier: "master",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/stats").then((m) => ({ StatsCommand: m.StatsCommand })),
11
+ () => import("../cli/cmd/pr").then((m) => ({ PrCommand: m.PrCommand })),
12
+ () => import("../saeeol/cli/cmd/roll-call").then((m) => ({ RollCallCommand: m.RollCallCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,15 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── core: TUI 채팅 + 프롬프트 실행 ──────────────────────────────────
4
+ export const core: Addon = {
5
+ id: "core",
6
+ category: "core",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/tui/thread").then((m) => ({ TuiThreadCommand: m.TuiThreadCommand })),
11
+ () => import("../cli/cmd/run").then((m) => ({ RunCommand: m.RunCommand })),
12
+ () => import("../cli/cmd/generate").then((m) => ({ GenerateCommand: m.GenerateCommand })),
13
+ () => import("../cli/cmd/session").then((m) => ({ SessionCommand: m.SessionCommand })),
14
+ ],
15
+ }
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── data: 세션 내보내기/가져오기 + DB ───────────────────────────────
4
+ export const data: Addon = {
5
+ id: "data",
6
+ category: "data",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/export").then((m) => ({ ExportCommand: m.ExportCommand })),
11
+ () => import("../cli/cmd/import").then((m) => ({ ImportCommand: m.ImportCommand })),
12
+ () => import("../cli/cmd/db").then((m) => ({ DbCommand: m.DbCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── dev: 에이전트 관리 + 디버깅 ─────────────────────────────────────
4
+ export const dev: Addon = {
5
+ id: "dev",
6
+ category: "dev",
7
+ minTier: "code",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/agent").then((m) => ({ AgentCommand: m.AgentCommand })),
11
+ () => import("../cli/cmd/debug").then((m) => ({ DebugCommand: m.DebugCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── lifecycle: 설치/업그레이드/제거 ──────────────────────────────────
4
+ export const lifecycle: Addon = {
5
+ id: "lifecycle",
6
+ category: "lifecycle",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/upgrade").then((m) => ({ UpgradeCommand: m.UpgradeCommand })),
11
+ () => import("../cli/cmd/uninstall").then((m) => ({ UninstallCommand: m.UninstallCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── llm: LLM provider 설정 + 모델 관리 ──────────────────────────────
4
+ export const llm: Addon = {
5
+ id: "llm",
6
+ category: "llm",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/init").then((m) => ({ InitCommand: m.InitCommand })),
11
+ () => import("../cli/cmd/providers").then((m) => ({ ProvidersCommand: m.ProvidersCommand })),
12
+ () => import("../cli/cmd/models").then((m) => ({ ModelsCommand: m.ModelsCommand })),
13
+ () => import("../cli/cmd/config").then((m) => ({ ConfigCLICommand: m.ConfigCommand })),
14
+ ],
15
+ }
@@ -0,0 +1,12 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── remote: 실시간 세션 릴레이 ──────────────────────────────────────
4
+ export const remote: Addon = {
5
+ id: "remote",
6
+ category: "remote",
7
+ minTier: "master",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/remote").then((m) => ({ RemoteCommand: m.RemoteCommand })),
11
+ ],
12
+ }
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── server: headless 서버 + 원격 연결 + ACP ─────────────────────────
4
+ export const server: Addon = {
5
+ id: "server",
6
+ category: "server",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/serve").then((m) => ({ ServeCommand: m.ServeCommand })),
11
+ () => import("../cli/cmd/tui/attach").then((m) => ({ AttachCommand: m.AttachCommand })),
12
+ () => import("../cli/cmd/acp").then((m) => ({ AcpCommand: m.AcpCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── tools: MCP + 플러그인 시스템 ────────────────────────────────────
4
+ export const tools: Addon = {
5
+ id: "tools",
6
+ category: "tools",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/mcp").then((m) => ({ McpCommand: m.McpCommand })),
11
+ () => import("../cli/cmd/plug").then((m) => ({ PluginCommand: m.PluginCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── web: 브라우저 채팅 UI ───────────────────────────────────────────
4
+ export const web: Addon = {
5
+ id: "web",
6
+ category: "web",
7
+ minTier: "master",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/web").then((m) => ({ WebCommand: m.WebCommand })),
11
+ ],
12
+ }
@@ -0,0 +1,65 @@
1
+ import type { Addon, Tier } from "./types"
2
+ import { core } from "./addon-core"
3
+ import { llm } from "./addon-llm"
4
+ import { lifecycle } from "./addon-lifecycle"
5
+ import { server } from "./addon-server"
6
+ import { tools } from "./addon-tools"
7
+ import { data } from "./addon-data"
8
+ import { dev } from "./addon-dev"
9
+ import { web } from "./addon-web"
10
+ import { remote } from "./addon-remote"
11
+ import { analytics } from "./addon-analytics"
12
+
13
+ // ╔══════════════════════════════════════════════════════════════════╗
14
+ // ║ 애드온 레지스트리 ║
15
+ // ║ ║
16
+ // ║ LIGHT = core + llm + lifecycle (3 addons, 10 cmd) ║
17
+ // ║ CODE = LIGHT + server + tools + data + dev (8 addons, 20 cmd) ║
18
+ // ║ MASTER = CODE + web + remote + analytics (11 addons, 24 cmd)║
19
+ // ╚══════════════════════════════════════════════════════════════════╝
20
+
21
+ export const allAddons: Addon[] = [
22
+ // LIGHT
23
+ core, // TUI 채팅, run, generate, session
24
+ llm, // init, auth, models, config
25
+ lifecycle, // upgrade, uninstall
26
+
27
+ // CODE
28
+ server, // serve, attach, acp
29
+ tools, // mcp, plugin
30
+ data, // export, import, db
31
+ dev, // agent, debug
32
+
33
+ // MASTER
34
+ web, // web UI
35
+ remote, // real-time relay
36
+ analytics, // stats, pr, roll-call
37
+ ]
38
+
39
+ const tierPriority: Record<Tier, number> = { light: 0, code: 1, master: 2 }
40
+
41
+ export function addonsForTier(tier: Tier): Addon[] {
42
+ return allAddons.filter((a) => tierPriority[tier] >= tierPriority[a.minTier])
43
+ }
44
+
45
+ export async function commandsForTier(tier: Tier): Promise<any[]> {
46
+ const addons = addonsForTier(tier)
47
+ const commands: any[] = []
48
+ for (const addon of addons) {
49
+ for (const loader of addon.commands) {
50
+ const mod = await loader()
51
+ commands.push(...Object.values(mod))
52
+ }
53
+ }
54
+ return commands
55
+ }
56
+
57
+ export function manifest(tier: Tier) {
58
+ const addons = addonsForTier(tier)
59
+ return addons.map((a) => ({
60
+ id: a.id,
61
+ category: a.category,
62
+ commands: a.commands.length,
63
+ required: a.required,
64
+ }))
65
+ }
@@ -0,0 +1,31 @@
1
+ // ╔══════════════════════════════════════════════════════════════════╗
2
+ // ║ SAEEOL 애드온 시스템 ║
3
+ // ║ ║
4
+ // ║ 원자 단위 기능 → 애드온 → 티어별 자동 활성화 ║
5
+ // ║ ║
6
+ // ║ LIGHT = core + llm + lifecycle ║
7
+ // ║ CODE = LIGHT + server + tools + data + dev ║
8
+ // ║ MASTER = CODE + web + remote + analytics ║
9
+ // ╚══════════════════════════════════════════════════════════════════╝
10
+
11
+ export type Tier = "light" | "code" | "master"
12
+
13
+ export interface Addon {
14
+ /** 애드온 ID */
15
+ id: string
16
+ /** 분류 */
17
+ category: "core" | "llm" | "lifecycle" | "server" | "tools" | "data" | "web" | "remote" | "analytics" | "dev"
18
+ /** 포함 명령어 */
19
+ commands: (() => Promise<Record<string, any>>)[]
20
+ /** 필요 티어 (이 티어부터 활성화) */
21
+ minTier: Tier
22
+ /** 필수/선택 */
23
+ required: boolean
24
+ }
25
+
26
+ // 티어 우선순위
27
+ const tierPriority: Record<Tier, number> = { light: 0, code: 1, master: 2 }
28
+
29
+ export function tierActive(addon: Addon, tier: Tier): boolean {
30
+ return tierPriority[tier] >= tierPriority[addon.minTier]
31
+ }
package/src/index.ts CHANGED
@@ -1,68 +1,97 @@
1
1
  import yargs from "yargs"
2
2
  import { hideBin } from "yargs/helpers"
3
+
4
+ // ╔══════════════════════════════════════════════════════════════════╗
5
+ // ║ SAEEOL CLI — 애드온 아키텍처 ║
6
+ // ║ ║
7
+ // ║ 원자 단위 애드온 → 티어별 자동 활성화 ║
8
+ // ║ ║
9
+ // ║ LIGHT = core + llm + lifecycle ║
10
+ // ║ CODE = LIGHT + server + tools + data + dev ║
11
+ // ║ MASTER = CODE + web + remote + analytics ║
12
+ // ╚══════════════════════════════════════════════════════════════════╝
13
+
14
+ // ── LIGHT: core ────────────────────────────────────────────────────
15
+ import { TuiThreadCommand } from "./cli/cmd/tui/thread"
3
16
  import { RunCommand } from "./cli/cmd/run"
4
17
  import { GenerateCommand } from "./cli/cmd/generate"
5
- import * as Log from "@saeeol/core/util/log"
6
- // import { LoginCommand, LogoutCommand, SwitchCommand, OrgsCommand } from "./cli/cmd/account"
7
- // import { ConsoleCommand } from "./cli/cmd/account"
8
- import { ConsoleCommand } from "./cli/cmd/account"
18
+ import { SessionCommand } from "./cli/cmd/session"
19
+
20
+ // ── LIGHT: llm ─────────────────────────────────────────────────────
21
+ import { InitCommand } from "./cli/cmd/init"
9
22
  import { ProvidersCommand } from "./cli/cmd/providers"
10
- import { AgentCommand } from "./cli/cmd/agent"
23
+ import { ModelsCommand } from "./cli/cmd/models"
24
+ import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config"
25
+
26
+ // ── LIGHT: lifecycle ───────────────────────────────────────────────
11
27
  import { UpgradeCommand } from "./cli/cmd/upgrade"
12
28
  import { UninstallCommand } from "./cli/cmd/uninstall"
13
- import { ModelsCommand } from "./cli/cmd/models"
29
+
30
+ // ── CODE: server ───────────────────────────────────────────────────
31
+ import { ServeCommand } from "./cli/cmd/serve"
32
+ import { AttachCommand } from "./cli/cmd/tui/attach"
33
+ import { AcpCommand } from "./cli/cmd/acp"
34
+
35
+ // ── CODE: tools ────────────────────────────────────────────────────
36
+ import { McpCommand } from "./cli/cmd/mcp"
37
+ import { PluginCommand } from "./cli/cmd/plug"
38
+
39
+ // ── CODE: data ─────────────────────────────────────────────────────
40
+ import { ExportCommand } from "./cli/cmd/export"
41
+ import { ImportCommand } from "./cli/cmd/import"
42
+ import { DbCommand } from "./cli/cmd/db"
43
+
44
+ // ── CODE: dev (선택) ───────────────────────────────────────────────
45
+ import { AgentCommand } from "./cli/cmd/agent"
46
+ import { DebugCommand } from "./cli/cmd/debug"
47
+
48
+ // ── MASTER: web ────────────────────────────────────────────────────
49
+ import { WebCommand } from "./cli/cmd/web"
50
+
51
+ // ── MASTER: remote (선택) ──────────────────────────────────────────
52
+ import { RemoteCommand } from "./cli/cmd/remote"
53
+
54
+ // ── MASTER: analytics (선택) ───────────────────────────────────────
55
+ import { StatsCommand } from "./cli/cmd/stats"
56
+ import { PrCommand } from "./cli/cmd/pr"
57
+ import { RollCallCommand } from "./saeeol/cli/cmd/roll-call"
58
+
59
+ // ── DEV 전용 ───────────────────────────────────────────────────────
60
+ import { DevSetupCommand, DevAliasCommand } from "./saeeol/cli/dev-setup"
61
+
62
+ // ── 공통 ────────────────────────────────────────────────────────────
63
+ import * as Log from "@saeeol/core/util/log"
14
64
  import { UI } from "./cli/ui"
15
65
  import { Installation } from "./installation"
16
66
  import { InstallationBuildKind, InstallationVersion } from "@saeeol/core/installation/version"
17
67
  import { NamedError } from "@saeeol/core/util/error"
18
68
  import { FormatError } from "./cli/error"
19
- import { ServeCommand } from "./cli/cmd/serve"
20
69
  import { Filesystem } from "@/util/filesystem"
21
- import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config"
22
- import { DebugCommand } from "./cli/cmd/debug"
23
- import { StatsCommand } from "./cli/cmd/stats"
24
- import { McpCommand } from "./cli/cmd/mcp"
25
- // import { GithubCommand } from "./cli/cmd/github"
26
- import { ExportCommand } from "./cli/cmd/export"
27
- import { ImportCommand } from "./cli/cmd/import"
28
- import { AttachCommand } from "./cli/cmd/tui/attach"
29
- import { TuiThreadCommand } from "./cli/cmd/tui/thread"
30
- import { AcpCommand } from "./cli/cmd/acp"
31
70
  import { EOL } from "os"
32
- // import { WebCommand } from "./cli/cmd/web"
33
- import { PrCommand } from "./cli/cmd/pr"
34
- import { SessionCommand } from "./cli/cmd/session"
35
- import { RemoteCommand } from "./cli/cmd/remote"
36
- import { RollCallCommand } from "./saeeol/cli/cmd/roll-call"
37
- import { DevSetupCommand, DevAliasCommand } from "./saeeol/cli/dev-setup"
38
- import { WebCommand } from "./cli/cmd/web"
39
71
  import { Telemetry } from "@saeeol/telemetry"
40
72
  import { InstanceStore } from "./project/instance-store"
41
73
  import { migrateLegacySaeeolAuth, ENV_FEATURE, ENV_VERSION } from "@saeeol/gateway"
42
- // (extension, cloud) which set their own SAEEOL_FEATURE env var. Direct CLI use
43
- // (any command other than 'serve') is tagged as 'cli'. If 'serve' is spawned without
44
- // the env var, it gets 'unknown' so the misconfiguration is visible in data.
45
- if (!process.env[ENV_FEATURE]) {
46
- const isServe = process.argv.includes("serve")
47
- process.env[ENV_FEATURE] = isServe ? "unknown" : "cli"
48
- }
49
- if (!process.env[ENV_VERSION]) {
50
- process.env[ENV_VERSION] = InstallationVersion
51
- }
52
74
  import { Config } from "./config/config"
53
75
  import { Auth } from "./auth"
54
- import { DbCommand } from "./cli/cmd/db"
55
76
  import path from "path"
56
77
  import { Global } from "@saeeol/core/global"
57
78
  import { createHelpCommand } from "./saeeol/help-command"
58
79
  import { JsonMigration } from "@/storage/json-migration"
59
80
  import { Database } from "@/storage/db"
60
81
  import { errorMessage } from "./util/error"
61
- import { PluginCommand } from "./cli/cmd/plug"
62
- import { InitCommand } from "./cli/cmd/init"
63
82
  import { Heap } from "./cli/heap"
64
83
  import { drizzle } from "drizzle-orm/bun-sqlite"
65
84
  import { ensureProcessMetadata } from "@saeeol/core/util/saeeol-process"
85
+ import type { Tier } from "./addons/types"
86
+ import { addonsForTier, manifest } from "./addons/registry"
87
+
88
+ if (!process.env[ENV_FEATURE]) {
89
+ const isServe = process.argv.includes("serve")
90
+ process.env[ENV_FEATURE] = isServe ? "unknown" : "cli"
91
+ }
92
+ if (!process.env[ENV_VERSION]) {
93
+ process.env[ENV_VERSION] = InstallationVersion
94
+ }
66
95
 
67
96
  const processMetadata = ensureProcessMetadata("main")
68
97
 
@@ -205,38 +234,71 @@ let cli = yargs(args)
205
234
  })
206
235
  .usage("")
207
236
  .completion("completion", "generate shell completion script")
208
- .command(AcpCommand)
209
- .command(McpCommand)
210
- .command(TuiThreadCommand)
211
- .command(AttachCommand)
212
- .command(RunCommand)
213
- .command(GenerateCommand)
214
- .command(DebugCommand)
215
- // .command(LoginCommand)
216
- // .command(LogoutCommand)
217
- // .command(SwitchCommand)
218
- // .command(OrgsCommand)
219
- // .command(ConsoleCommand)
220
- .command(ProvidersCommand)
221
- .command(AgentCommand)
222
- .command(UpgradeCommand)
223
- .command(UninstallCommand)
224
- .command(ServeCommand)
225
- .command(WebCommand)
226
- // .command(WebCommand)
227
- .command(ModelsCommand)
228
- .command(RollCallCommand)
229
- .command(StatsCommand)
230
- .command(ExportCommand)
231
- .command(ImportCommand)
232
- // .command(GithubCommand)
233
- .command(PrCommand)
234
- .command(SessionCommand)
235
- .command(RemoteCommand)
236
- .command(ConfigCLICommand)
237
- .command(PluginCommand)
238
- .command(InitCommand)
239
- .command(DbCommand)
237
+
238
+ // ════════════════════════════════════════════════════════════════
239
+ // LIGHT: core (TUI 채팅 + 프롬프트 + 세션)
240
+ // ════════════════════════════════════════════════════════════════
241
+ .command(TuiThreadCommand) // [default] TUI 채팅
242
+ .command(RunCommand) // run 프롬프트 실행
243
+ .command(GenerateCommand) // generate 코드 생성
244
+ .command(SessionCommand) // session 세션 관리
245
+
246
+ // ════════════════════════════════════════════════════════════════
247
+ // LIGHT: llm (provider + 모델 + 설정)
248
+ // ════════════════════════════════════════════════════════════════
249
+ .command(InitCommand) // init 최초 설정
250
+ .command(ProvidersCommand) // auth provider 인증
251
+ .command(ModelsCommand) // models 모델 목록
252
+ .command(ConfigCLICommand) // config 설정 관리
253
+
254
+ // ════════════════════════════════════════════════════════════════
255
+ // LIGHT: lifecycle (설치/업그레이드/제거)
256
+ // ════════════════════════════════════════════════════════════════
257
+ .command(UpgradeCommand) // upgrade 버전 업그레이드
258
+ .command(UninstallCommand) // uninstall 제거
259
+
260
+ // ════════════════════════════════════════════════════════════════
261
+ // CODE: server (headless + 원격 + ACP)
262
+ // ════════════════════════════════════════════════════════════════
263
+ .command(ServeCommand) // serve headless 서버
264
+ .command(AttachCommand) // attach 원격 연결
265
+ .command(AcpCommand) // acp ACP 서버
266
+
267
+ // ════════════════════════════════════════════════════════════════
268
+ // CODE: tools (MCP + 플러그인)
269
+ // ════════════════════════════════════════════════════════════════
270
+ .command(McpCommand) // mcp MCP 서버 관리
271
+ .command(PluginCommand) // plugin 플러그인
272
+
273
+ // ════════════════════════════════════════════════════════════════
274
+ // CODE: data (export + import + db)
275
+ // ════════════════════════════════════════════════════════════════
276
+ .command(ExportCommand) // export 세션 내보내기
277
+ .command(ImportCommand) // import 세션 가져오기
278
+ .command(DbCommand) // db 데이터베이스
279
+
280
+ // ════════════════════════════════════════════════════════════════
281
+ // CODE: dev (선택)
282
+ // ════════════════════════════════════════════════════════════════
283
+ .command(AgentCommand) // agent 에이전트 관리
284
+ .command(DebugCommand) // debug 디버깅
285
+
286
+ // ════════════════════════════════════════════════════════════════
287
+ // MASTER: web (브라우저 채팅 UI)
288
+ // ════════════════════════════════════════════════════════════════
289
+ .command(WebCommand) // web 웹 UI + 브라우저
290
+
291
+ // ════════════════════════════════════════════════════════════════
292
+ // MASTER: remote (선택)
293
+ // ════════════════════════════════════════════════════════════════
294
+ .command(RemoteCommand) // remote 실시간 릴레이
295
+
296
+ // ════════════════════════════════════════════════════════════════
297
+ // MASTER: analytics (선택)
298
+ // ════════════════════════════════════════════════════════════════
299
+ .command(StatsCommand) // stats 사용량 통계
300
+ .command(PrCommand) // pr GitHub PR
301
+ .command(RollCallCommand) // roll-call 모델 연결 테스트
240
302
  if (InstallationBuildKind !== "release") {
241
303
  cli = cli.command(DevSetupCommand).command(DevAliasCommand)
242
304
  }
@@ -1,4 +1,3 @@
1
- import { SAEEOL_BUNDLED_PROVIDERS } from "@/saeeol/provider/provider"
2
1
  import type { BundledSDK } from "./provider-types"
3
2
 
4
3
  export function shouldUseCopilotResponsesApi(modelID: string): boolean {
@@ -11,30 +10,32 @@ export function useLanguageModel(sdk: any) {
11
10
  return sdk.responses === undefined && sdk.chat === undefined
12
11
  }
13
12
 
14
- export const BUNDLED_PROVIDERS: Record<string, () => Promise<(opts: any) => BundledSDK>> = {
15
- "@ai-sdk/amazon-bedrock": () => import("@ai-sdk/amazon-bedrock").then((m) => m.createAmazonBedrock),
16
- "@ai-sdk/anthropic": () => import("@ai-sdk/anthropic").then((m) => m.createAnthropic),
17
- "@ai-sdk/azure": () => import("@ai-sdk/azure").then((m) => m.createAzure),
18
- "@ai-sdk/google": () => import("@ai-sdk/google").then((m) => m.createGoogleGenerativeAI),
19
- "@ai-sdk/google-vertex": () => import("@ai-sdk/google-vertex").then((m) => m.createVertex),
20
- "@ai-sdk/google-vertex/anthropic": () =>
21
- import("@ai-sdk/google-vertex/anthropic").then((m) => m.createVertexAnthropic),
22
- "@ai-sdk/openai": () => import("@ai-sdk/openai").then((m) => m.createOpenAI),
23
- "@ai-sdk/openai-compatible": () => import("@ai-sdk/openai-compatible").then((m) => m.createOpenAICompatible),
24
- "@openrouter/ai-sdk-provider": () => import("@openrouter/ai-sdk-provider").then((m) => m.createOpenRouter),
25
- "@ai-sdk/xai": () => import("@ai-sdk/xai").then((m) => m.createXai),
26
- "@ai-sdk/mistral": () => import("@ai-sdk/mistral").then((m) => m.createMistral),
27
- "@ai-sdk/groq": () => import("@ai-sdk/groq").then((m) => m.createGroq),
28
- "@ai-sdk/deepinfra": () => import("@ai-sdk/deepinfra").then((m) => m.createDeepInfra),
29
- "@ai-sdk/cerebras": () => import("@ai-sdk/cerebras").then((m) => m.createCerebras),
30
- "@ai-sdk/cohere": () => import("@ai-sdk/cohere").then((m) => m.createCohere),
31
- "@ai-sdk/gateway": () => import("@ai-sdk/gateway").then((m) => m.createGateway),
32
- "@ai-sdk/togetherai": () => import("@ai-sdk/togetherai").then((m) => m.createTogetherAI),
33
- "@ai-sdk/perplexity": () => import("@ai-sdk/perplexity").then((m) => m.createPerplexity),
34
- "@ai-sdk/vercel": () => import("@ai-sdk/vercel").then((m) => m.createVercel),
35
- "@ai-sdk/alibaba": () => import("@ai-sdk/alibaba").then((m) => m.createAlibaba),
36
- "gitlab-ai-provider": () => import("gitlab-ai-provider").then((m) => m.createGitLab),
37
- "@ai-sdk/github-copilot": () => import("./sdk/copilot/copilot-provider").then((m) => m.createOpenaiCompatible),
38
- "venice-ai-sdk-provider": () => import("venice-ai-sdk-provider").then((m) => m.createVenice),
39
- ...SAEEOL_BUNDLED_PROVIDERS,
13
+ // ╔══════════════════════════════════════════════════════════════════╗
14
+ // ║ 티어별 provider 로딩 ║
15
+ // ║ ║
16
+ // ║ SAEEOL_TIER는 build.ts define으로 "light"|"code"|"master" 주입 ║
17
+ // ║ Bun은 dead branch를 제거하므로 다른 티어의 import() 제외됨 ║
18
+ // ╚══════════════════════════════════════════════════════════════════╝
19
+
20
+ declare const SAEEOL_TIER: string
21
+
22
+ type ProviderLoader = () => Promise<(opts: any) => BundledSDK>
23
+
24
+ // 티어별 파일에서 각각 서로 다른 provider 집합을 import
25
+ // Bun은 조건에 맞지 않는 branch의 import() dead code로 처리
26
+
27
+ function getProviders(): Record<string, ProviderLoader> {
28
+ if (SAEEOL_TIER === "light") {
29
+ // Bun replaces SAEEOL_TIER with literal, dead-eliminates else branches
30
+ const m = require("./tiers/light") as typeof import("./tiers/light")
31
+ return m.lightProviders
32
+ }
33
+ if (SAEEOL_TIER === "code") {
34
+ const m = require("./tiers/code") as typeof import("./tiers/code")
35
+ return m.codeProviders
36
+ }
37
+ const m = require("./tiers/master") as typeof import("./tiers/master")
38
+ return m.masterProviders
40
39
  }
40
+
41
+ export const BUNDLED_PROVIDERS: Record<string, ProviderLoader> = getProviders()
@@ -0,0 +1,21 @@
1
+ // CODE 티어 provider 맵 — LIGHT + 개발용 provider
2
+ import type { BundledSDK } from "../../provider/provider-types"
3
+ import { createSaeeol } from "@saeeol/gateway"
4
+ import { lightProviders } from "./light"
5
+
6
+ type L = () => Promise<(opts: any) => BundledSDK>
7
+
8
+ export const codeProviders: Record<string, L> = {
9
+ ...lightProviders,
10
+ "@ai-sdk/amazon-bedrock": () => import("@ai-sdk/amazon-bedrock").then((m) => m.createAmazonBedrock),
11
+ "@ai-sdk/azure": () => import("@ai-sdk/azure").then((m) => m.createAzure),
12
+ "@ai-sdk/google-vertex": () => import("@ai-sdk/google-vertex").then((m) => m.createVertex),
13
+ "@ai-sdk/google-vertex/anthropic": () =>
14
+ import("@ai-sdk/google-vertex/anthropic").then((m) => m.createVertexAnthropic),
15
+ "@openrouter/ai-sdk-provider": () => import("@openrouter/ai-sdk-provider").then((m) => m.createOpenRouter),
16
+ "@ai-sdk/groq": () => import("@ai-sdk/groq").then((m) => m.createGroq),
17
+ "@ai-sdk/deepinfra": () => import("@ai-sdk/deepinfra").then((m) => m.createDeepInfra),
18
+ "@ai-sdk/gateway": () => import("@ai-sdk/gateway").then((m) => m.createGateway),
19
+ "@ai-sdk/alibaba": () => import("@ai-sdk/alibaba").then((m) => m.createAlibaba),
20
+ "@ai-sdk/cerebras": () => import("@ai-sdk/cerebras").then((m) => m.createCerebras),
21
+ }
@@ -0,0 +1,14 @@
1
+ // LIGHT 티어 provider 맵 — 최소 provider만
2
+ import type { BundledSDK } from "../../provider/provider-types"
3
+ import { createSaeeol } from "@saeeol/gateway"
4
+
5
+ type L = () => Promise<(opts: any) => BundledSDK>
6
+
7
+ export const lightProviders: Record<string, L> = {
8
+ "@saeeol/gateway": async () => createSaeeol as any,
9
+ "@ai-sdk/anthropic": () => import("@ai-sdk/anthropic").then((m) => m.createAnthropic),
10
+ "@ai-sdk/openai": () => import("@ai-sdk/openai").then((m) => m.createOpenAI),
11
+ "@ai-sdk/openai-compatible": () => import("@ai-sdk/openai-compatible").then((m) => m.createOpenAICompatible),
12
+ "@ai-sdk/google": () => import("@ai-sdk/google").then((m) => m.createGoogleGenerativeAI),
13
+ "@ai-sdk/github-copilot": () => import("../sdk/copilot/copilot-provider").then((m) => m.createOpenaiCompatible),
14
+ }
@@ -0,0 +1,17 @@
1
+ // MASTER 티어 provider 맵 — 전체 provider
2
+ import type { BundledSDK } from "../../provider/provider-types"
3
+ import { codeProviders } from "./code"
4
+
5
+ type L = () => Promise<(opts: any) => BundledSDK>
6
+
7
+ export const masterProviders: Record<string, L> = {
8
+ ...codeProviders,
9
+ "@ai-sdk/xai": () => import("@ai-sdk/xai").then((m) => m.createXai),
10
+ "@ai-sdk/mistral": () => import("@ai-sdk/mistral").then((m) => m.createMistral),
11
+ "@ai-sdk/cohere": () => import("@ai-sdk/cohere").then((m) => m.createCohere),
12
+ "@ai-sdk/togetherai": () => import("@ai-sdk/togetherai").then((m) => m.createTogetherAI),
13
+ "@ai-sdk/perplexity": () => import("@ai-sdk/perplexity").then((m) => m.createPerplexity),
14
+ "@ai-sdk/vercel": () => import("@ai-sdk/vercel").then((m) => m.createVercel),
15
+ "gitlab-ai-provider": () => import("gitlab-ai-provider").then((m) => m.createGitLab),
16
+ "venice-ai-sdk-provider": () => import("venice-ai-sdk-provider").then((m) => m.createVenice),
17
+ }