rush-ai 0.2.0 → 0.3.1

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/README.md CHANGED
@@ -1,136 +1,230 @@
1
+ <div align="center">
2
+
1
3
  # rush-ai
2
4
 
3
- Rush CLI — Command-line interface for the Rush AI platform.
5
+ **Command-line interface for the Rush AI platform**
6
+
7
+ *Create tasks, manage agents, stream results, and integrate IDE plugins — all from the terminal*
8
+
9
+ [![npm version](https://img.shields.io/npm/v/rush-ai.svg)](https://www.npmjs.com/package/rush-ai)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+
12
+ </div>
13
+
14
+ ---
4
15
 
5
16
  ## Quick Start
6
17
 
7
18
  ```bash
8
- pnpm --filter rush-ai build
9
- pnpm --filter rush-ai test
19
+ npm install -g rush-ai
20
+
21
+ rush-ai auth login
22
+ rush-ai agent list
23
+ rush-ai task create -a <agent> -p "Build a landing page"
24
+ rush-ai task watch <task-id>
10
25
  ```
11
26
 
12
- ## Testing Strategy
27
+ ## Features
28
+
29
+ - **Task lifecycle** — Create, watch (SSE streaming), cancel, and download artifacts
30
+ - **Agent management** — Browse agents, inspect skills and MCP servers
31
+ - **MCP integration** — Discover MCP servers, list tools, run as MCP stdio server
32
+ - **IDE plugins** — Install, update, diagnose plugins for Claude Code and Cursor
33
+ - **Multi-profile** — Switch between environments (test, production, custom)
34
+ - **Shell completion** — Bash, Zsh, and Fish auto-completion
35
+ - **CI-friendly** — `--json` output, `--ci` mode, non-zero exit codes on failure
13
36
 
14
- rush-ai 采用三层测试策略,在不同阶段用最合适的方式验证代码正确性。
37
+ ## Installation
15
38
 
16
- ### L1: 单元测试(本地,秒级反馈)
39
+ **Requirements:** Node.js >= 18.0.0
17
40
 
18
- 验证 CLI 自身逻辑,完全不依赖外部服务。
41
+ ```bash
42
+ npm install -g rush-ai # Global install
43
+ npx rush-ai <command> # Or use npx directly
44
+ ```
19
45
 
20
- **覆盖范围:**
46
+ ## Commands
47
+
48
+ | Command | Alias | Description |
49
+ |---------|-------|-------------|
50
+ | `auth login` | - | Log in via browser (PKCE) or API key |
51
+ | `auth status` | - | Show current authentication status |
52
+ | `auth logout` | - | Log out and clear credentials |
53
+ | `agent list` | `agent ls` | List available agents |
54
+ | `agent info <name>` | - | Show agent details, skills, and MCP servers |
55
+ | `task create` | - | Create a task for an agent |
56
+ | `task send <id>` | - | Send a follow-up message to a task |
57
+ | `task status <id>` | - | Check task status |
58
+ | `task result <id>` | - | Get task result and file summary |
59
+ | `task files <id>` | - | List and download task artifacts |
60
+ | `task list` | `task ls` | List tasks |
61
+ | `task watch <id>` | - | Stream task execution in real-time (SSE) |
62
+ | `task cancel <id>` | - | Cancel a running task |
63
+ | `mcp serve` | - | Start MCP stdio server |
64
+ | `mcp list` | `mcp ls` | List available MCP servers |
65
+ | `mcp list-tools <id>` | - | List tools provided by an MCP server |
66
+ | `plugin install <name>` | - | Install an IDE plugin |
67
+ | `plugin update <name>` | - | Update an installed plugin |
68
+ | `plugin uninstall <name>` | - | Remove a plugin |
69
+ | `plugin list` | `plugin ls` | List available and installed plugins |
70
+ | `plugin status <name>` | - | Show plugin installation status |
71
+ | `plugin doctor [name]` | - | Diagnose plugin issues |
72
+ | `config show` | - | Show current configuration |
73
+ | `config set <key> <value>` | - | Set a config value (`api`, `collectMetrics`, `currentTeam`) |
74
+ | `config use <profile>` | - | Switch active profile |
75
+ | `config create <profile>` | - | Create a new profile |
76
+ | `config delete <profile>` | - | Delete a profile |
77
+ | `config list` | `config ls` | List all profiles |
78
+ | `completion install` | - | Auto-install shell completion |
79
+ | `completion bash\|zsh\|fish` | - | Print completion script |
80
+ | `doctor` | - | Run environment, auth, and connectivity diagnostics |
81
+
82
+ ### Global Options
83
+
84
+ | Option | Description |
85
+ |--------|-------------|
86
+ | `--json` | Output as JSON (machine-readable) |
87
+ | `--ci` | CI mode: JSON output, no interactive prompts, strict exit codes |
88
+ | `--verbose` | Show detailed output including HTTP requests |
89
+ | `--debug` | Debug-level output (implies `--verbose`) |
90
+ | `--no-color` | Disable color output |
91
+ | `-f, --format <type>` | Output format: `table`, `json`, `csv` |
21
92
 
22
- - 命令解析(commander 参数、flag、子命令路由)
23
- - 输出格式化(table / `--json` / `--stream`)
24
- - 配置管理(`~/.rush/` 读写、token 存取)
25
- - 错误处理(未认证提示、网络错误、API 错误码映射)
26
- - stdin 管道输入解析
93
+ ## Usage
27
94
 
28
- **技术方案:** vitest,mock HTTP 层(`lib/client.ts`),不启动任何服务。
95
+ ### Authentication
29
96
 
30
97
  ```bash
31
- pnpm --filter rush-ai test
98
+ # Browser login (PKCE flow — opens browser)
99
+ rush-ai auth login
100
+
101
+ # API key login
102
+ rush-ai auth login --api-key <key>
103
+
104
+ # Check status
105
+ rush-ai auth status --verify
32
106
  ```
33
107
 
34
- ### L2: 本地联调测试(本地,秒级反馈)
108
+ ### Working with Tasks
35
109
 
36
- CLI 对本地运行的 Agent 服务做真实调用,验证请求/响应链路。
110
+ ```bash
111
+ # Create a task
112
+ rush-ai task create -a rush -p "Add a contact form to the homepage"
37
113
 
38
- **前置条件:**
114
+ # Stream output in real-time
115
+ rush-ai task watch <task-id>
39
116
 
40
- - `pnpm dev:agent` 运行在 `localhost:4000`(需 `.env.local` 配好凭证)
41
- - `pnpm dev` 同时启动 web + agent
117
+ # Get result and download files
118
+ rush-ai task result <task-id>
119
+ rush-ai task files <task-id> --download-all -o ./output
42
120
 
43
- **覆盖范围:**
121
+ # Send follow-up message
122
+ rush-ai task send <task-id> -p "Change the button color to blue"
44
123
 
45
- - auth 认证流程
46
- - `agent list` / `agent info` 真实返回
47
- - `task run` 端到端执行(CLI → API → LLM → 结果)
48
- - `task create` + `task status` + `task result` 异步链路
49
- - SSE 流式输出(`task watch`)
124
+ # Pipe prompt from stdin
125
+ echo "Refactor the auth module" | rush-ai task create -a rush
50
126
 
51
- **技术方案:** vitest e2e 配置,测试前检查 `localhost` health,不可用则 skip。
127
+ # List recent tasks
128
+ rush-ai task list -l 10 -s completed
129
+ ```
130
+
131
+ ### Discovering Agents and MCP Servers
52
132
 
53
133
  ```bash
54
- # 需要先启动 agent
55
- pnpm dev:agent &
134
+ # Browse agents
135
+ rush-ai agent list
136
+ rush-ai agent info rush
56
137
 
57
- pnpm --filter rush-ai test:e2e
138
+ # Browse MCP servers and their tools
139
+ rush-ai mcp list --search "database"
140
+ rush-ai mcp list-tools <server-id>
58
141
  ```
59
142
 
60
- ### L3: 冒烟测试(CI / 手动,分钟级)
61
-
62
- 对真实环境做最终确认,验证完整 CLI 链路:`auth → agent list → task run → task result → task files → task list`。
143
+ ### IDE Plugin Management
63
144
 
64
- **触发时机:**
145
+ ```bash
146
+ # Install plugin for Claude Code
147
+ rush-ai plugin install claude-code
65
148
 
66
- - PR 合入后,CI 构建部署完成
67
- - 手动触发,指定目标环境
149
+ # Check plugin health
150
+ rush-ai plugin doctor claude-code
68
151
 
69
- **覆盖范围:**
152
+ # Update plugin
153
+ rush-ai plugin update claude-code --force
154
+ ```
70
155
 
71
- - 核心 happy path 全链路验证
72
- - 网络 / 鉴权 / 代理等环境相关的集成验证
73
- - production build 模式下的 API 行为
156
+ ### Multi-Profile Configuration
74
157
 
75
158
  ```bash
76
- RUSH_API_BASE_URL=https://rush-test.zhenguanyu.com \
77
- RUSH_AUTH_TOKEN=$CI_TEST_TOKEN \
78
- pnpm --filter rush-ai test:smoke
159
+ # Create profiles for different environments
160
+ rush-ai config create production --api https://rush.example.com
161
+ rush-ai config create staging --api https://rush-test.example.com
162
+
163
+ # Switch between profiles
164
+ rush-ai config use production
165
+ rush-ai config list
79
166
  ```
80
167
 
81
- ## Scripts
168
+ ### CI/CD Integration
82
169
 
83
- | Script | Description |
84
- |--------|-------------|
85
- | `test` | L1 单元测试 |
86
- | `test:e2e` | L2 本地联调测试 |
87
- | `test:smoke` | L3 冒烟测试 |
88
- | `test:all` | L1 + L2 |
89
- | `test:watch` | 单元测试 watch 模式 |
90
- | `typecheck` | TypeScript 类型检查 |
91
- | `build` | 构建 |
170
+ ```bash
171
+ # Non-interactive auth via API key
172
+ RUSH_API_KEY=$KEY rush-ai task create -a rush -p "Run tests" --ci
173
+
174
+ # Script-friendly JSON output
175
+ rush-ai task status <id> --json | jq '.status'
176
+
177
+ # Pipe results
178
+ rush-ai task result <id> --json | jq -r '.result'
179
+ ```
92
180
 
93
181
  ## Environment Variables
94
182
 
95
183
  | Variable | Description | Default |
96
184
  |----------|-------------|---------|
97
- | `RUSH_API_BASE_URL` | API 目标地址 | `http://localhost:3000` |
98
- | `RUSH_AUTH_TOKEN` | 认证 token(跳过 CAS 交互登录) | |
99
- | `RUSH_TEST_AGENT` | E2E 测试用的 Agent 名称 | `rush` |
185
+ | `RUSH_API_KEY` | API key for non-interactive auth (skips browser login) | - |
186
+ | `RUSH_API_URL` | API base URL override | `https://rush.zhenguanyu.com` |
187
+ | `RUSH_PROFILE` | Override active profile | - |
188
+ | `NO_COLOR` | Disable colored output | - |
189
+ | `DEBUG` | Enable debug logging | - |
100
190
 
101
- ## Development Verification Flow
191
+ ## Configuration
102
192
 
103
- 每完成一个命令实现:
193
+ Config files are stored in `~/.rush/`:
104
194
 
105
- ```bash
106
- pnpm --filter rush-ai typecheck # 类型检查
107
- pnpm --filter rush-ai test # L1 单元测试
108
- pnpm --filter rush-ai build # 确保能构建
195
+ ```
196
+ ~/.rush/
197
+ ├── auth.json # Credentials (token, refresh token, expiry)
198
+ ├── config.json # Global settings (profiles, metrics, team)
199
+ └── plugins/
200
+ └── installed.json # Plugin manifest
109
201
  ```
110
202
 
111
- 涉及 API 交互改动时(需先启动 dev:agent):
203
+ ## Development
112
204
 
113
205
  ```bash
114
- pnpm --filter rush-ai test:e2e # L2 本地联调
115
- ```
206
+ # Install dependencies
207
+ pnpm install
116
208
 
117
- PR 合入后(CI 自动或手动):
209
+ # Build
210
+ pnpm --filter rush-ai build
118
211
 
119
- ```bash
120
- pnpm --filter rush-ai test:smoke # L3 部署冒烟
121
- ```
212
+ # Type check
213
+ pnpm --filter rush-ai typecheck
122
214
 
123
- ## Project Structure
215
+ # Run unit tests
216
+ pnpm --filter rush-ai test
124
217
 
218
+ # Run E2E tests (requires web + agent: pnpm dev)
219
+ pnpm dev &
220
+ pnpm --filter rush-ai test:e2e
221
+
222
+ # Run smoke tests (against deployed environment)
223
+ RUSH_API_BASE_URL=https://rush-test.zhenguanyu.com \
224
+ RUSH_AUTH_TOKEN=$TOKEN \
225
+ pnpm --filter rush-ai test:smoke
125
226
  ```
126
- packages/rush-ai/
127
- ├── src/
128
- │ ├── commands/ # CLI commands (agent, auth, mcp, plugin, task)
129
- │ ├── e2e/ # E2E & smoke test files
130
- │ ├── output/ # Output formatting (table, json, stream)
131
- │ └── util/ # Shared utilities (client, config, auth, errors)
132
- ├── vitest.config.ts # L1 unit test config
133
- ├── vitest.e2e.config.ts # L2 e2e test config
134
- ├── vitest.smoke.config.ts # L3 smoke test config
135
- └── package.json
136
- ```
227
+
228
+ ## License
229
+
230
+ MIT
@@ -46,17 +46,6 @@ function createSSEParser(onEvent, onDone) {
46
46
  }
47
47
  };
48
48
  }
49
- async function consumeSSEStream(body, onEvent, onDone) {
50
- const reader = body.getReader();
51
- const decoder = new TextDecoder();
52
- const parser = createSSEParser(onEvent, onDone);
53
- for (; ; ) {
54
- const { done, value } = await reader.read();
55
- if (done) break;
56
- parser.feed(decoder.decode(value, { stream: true }));
57
- }
58
- parser.flush();
59
- }
60
49
  async function consumeSSEStreamWithReconnect(connectFn, onEvent, options = {}) {
61
50
  const {
62
51
  maxReconnects = 5,
@@ -142,7 +131,6 @@ async function consumeSSEStreamWithReconnect(connectFn, onEvent, options = {}) {
142
131
  }
143
132
 
144
133
  export {
145
- consumeSSEStream,
146
134
  consumeSSEStreamWithReconnect
147
135
  };
148
- //# sourceMappingURL=chunk-ISJJIX7U.js.map
136
+ //# sourceMappingURL=chunk-MG4HY2PD.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/util/sse.ts"],"sourcesContent":["/**\n * SSE (Server-Sent Events) line-buffered parser.\n * Handles events split across multiple chunks.\n */\nexport interface SSEEvent {\n type: string;\n data: string;\n}\n\nexport function createSSEParser(\n onEvent: (event: SSEEvent) => void,\n onDone?: () => void\n) {\n let buffer = '';\n\n return {\n feed(chunk: string): void {\n buffer += chunk;\n const lines = buffer.split('\\n');\n // Keep the last (possibly incomplete) line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') {\n onDone?.();\n return;\n }\n try {\n const parsed = JSON.parse(data);\n onEvent({\n type: parsed.type ?? 'unknown',\n data:\n typeof parsed.data === 'string'\n ? parsed.data\n : JSON.stringify(parsed),\n });\n } catch {\n // Non-JSON SSE data\n onEvent({ type: 'raw', data });\n }\n }\n }\n },\n flush(): void {\n // Process any remaining buffered data\n if (buffer.startsWith('data: ')) {\n const data = buffer.slice(6);\n if (data !== '[DONE]') {\n try {\n const parsed = JSON.parse(data);\n onEvent({\n type: parsed.type ?? 'unknown',\n data:\n typeof parsed.data === 'string'\n ? parsed.data\n : JSON.stringify(parsed),\n });\n } catch {\n onEvent({ type: 'raw', data });\n }\n }\n }\n buffer = '';\n },\n };\n}\n\n/**\n * Consume an SSE ReadableStream with proper cross-chunk buffering.\n */\nexport async function consumeSSEStream(\n body: ReadableStream<Uint8Array>,\n onEvent: (event: SSEEvent) => void,\n onDone?: () => void\n): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n const parser = createSSEParser(onEvent, onDone);\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n parser.flush();\n}\n\nexport interface SSEReconnectOptions {\n /** Maximum number of reconnection attempts (default: 5) */\n maxReconnects?: number;\n /** Initial reconnection delay in ms (default: 1000) */\n reconnectDelay?: number;\n /** AbortSignal to cancel the stream and reconnections */\n signal?: AbortSignal;\n /** Called before each reconnection attempt */\n onReconnect?: (attempt: number) => void;\n /**\n * Determines if an event represents a terminal state.\n * When this returns true, the stream stops and no reconnection occurs.\n */\n isTerminal?: (event: SSEEvent) => boolean;\n}\n\n/**\n * Consume an SSE stream with automatic reconnection and event deduplication.\n *\n * Key behaviors:\n * - [DONE] does NOT automatically stop the stream. Only `isTerminal(event)` stops it.\n * - If [DONE] is received without a terminal event, reconnection is triggered\n * (e.g., server 5-minute SSE connection lifetime timeout).\n * - On reconnection, the server replays all historical events from epoch.\n * Client-side deduplication skips already-seen events using an event counter.\n *\n * @param connectFn - Factory that creates a new SSE stream (called on each reconnect)\n * @param onEvent - Event handler (only called for new, non-duplicate events)\n * @param options - Reconnection options\n */\nexport async function consumeSSEStreamWithReconnect(\n connectFn: () => Promise<ReadableStream<Uint8Array>>,\n onEvent: (event: SSEEvent) => void,\n options: SSEReconnectOptions = {}\n): Promise<void> {\n const {\n maxReconnects = 5,\n reconnectDelay = 1000,\n signal,\n onReconnect,\n isTerminal,\n } = options;\n\n let emittedEventCount = 0;\n let reconnectAttempts = 0;\n let terminalReached = false;\n\n while (!terminalReached) {\n if (signal?.aborted) return;\n\n let receivedDone = false;\n let currentEventIndex = 0;\n\n try {\n const stream = await connectFn();\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n\n // Note: We do NOT reset reconnectAttempts here.\n // For [DONE]+running scenarios (server 5-min timeout), each reconnect\n // should count toward the limit. The counter only resets after the\n // function is called fresh.\n\n const parser = createSSEParser(\n (event) => {\n currentEventIndex++;\n\n // Dedup: skip events we've already emitted in previous connections\n if (currentEventIndex <= emittedEventCount) {\n // Still check for terminal even on dedup'd events\n if (isTerminal?.(event)) {\n terminalReached = true;\n }\n return;\n }\n\n emittedEventCount++;\n onEvent(event);\n\n if (isTerminal?.(event)) {\n terminalReached = true;\n }\n },\n () => {\n receivedDone = true;\n }\n );\n\n for (;;) {\n if (signal?.aborted || terminalReached) {\n reader.cancel().catch(() => {});\n return;\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n\n if (terminalReached) {\n reader.cancel().catch(() => {});\n return;\n }\n }\n parser.flush();\n } catch {\n // Stream error — will attempt reconnect below\n if (signal?.aborted || terminalReached) return;\n }\n\n // If terminal was reached, we're done\n if (terminalReached) return;\n\n // If [DONE] was received AND we already got a terminal event, stop\n // (This shouldn't happen because terminalReached would be true, but safety check)\n if (receivedDone && terminalReached) return;\n\n // Otherwise, reconnect: [DONE] without terminal = server timeout, or stream error\n reconnectAttempts++;\n if (reconnectAttempts > maxReconnects) {\n throw new Error(\n `SSE reconnection failed after ${maxReconnects} attempts`\n );\n }\n\n if (signal?.aborted) return;\n\n onReconnect?.(reconnectAttempts);\n\n // Exponential backoff for reconnection\n const delay = Math.min(\n reconnectDelay * 2 ** (reconnectAttempts - 1),\n 30_000\n );\n\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, delay);\n if (signal) {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/util/sse.ts"],"sourcesContent":["/**\n * SSE (Server-Sent Events) line-buffered parser.\n * Handles events split across multiple chunks.\n */\nexport interface SSEEvent {\n type: string;\n data: string;\n}\n\nexport function createSSEParser(\n onEvent: (event: SSEEvent) => void,\n onDone?: () => void\n) {\n let buffer = '';\n\n return {\n feed(chunk: string): void {\n buffer += chunk;\n const lines = buffer.split('\\n');\n // Keep the last (possibly incomplete) line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') {\n onDone?.();\n return;\n }\n try {\n const parsed = JSON.parse(data);\n onEvent({\n type: parsed.type ?? 'unknown',\n data:\n typeof parsed.data === 'string'\n ? parsed.data\n : JSON.stringify(parsed),\n });\n } catch {\n // Non-JSON SSE data\n onEvent({ type: 'raw', data });\n }\n }\n }\n },\n flush(): void {\n // Process any remaining buffered data\n if (buffer.startsWith('data: ')) {\n const data = buffer.slice(6);\n if (data !== '[DONE]') {\n try {\n const parsed = JSON.parse(data);\n onEvent({\n type: parsed.type ?? 'unknown',\n data:\n typeof parsed.data === 'string'\n ? parsed.data\n : JSON.stringify(parsed),\n });\n } catch {\n onEvent({ type: 'raw', data });\n }\n }\n }\n buffer = '';\n },\n };\n}\n\n/**\n * Consume an SSE ReadableStream with proper cross-chunk buffering.\n */\nexport async function consumeSSEStream(\n body: ReadableStream<Uint8Array>,\n onEvent: (event: SSEEvent) => void,\n onDone?: () => void\n): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n const parser = createSSEParser(onEvent, onDone);\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n parser.flush();\n}\n\nexport interface SSEReconnectOptions {\n /** Maximum number of reconnection attempts (default: 5) */\n maxReconnects?: number;\n /** Initial reconnection delay in ms (default: 1000) */\n reconnectDelay?: number;\n /** AbortSignal to cancel the stream and reconnections */\n signal?: AbortSignal;\n /** Called before each reconnection attempt */\n onReconnect?: (attempt: number) => void;\n /**\n * Determines if an event represents a terminal state.\n * When this returns true, the stream stops and no reconnection occurs.\n */\n isTerminal?: (event: SSEEvent) => boolean;\n}\n\n/**\n * Consume an SSE stream with automatic reconnection and event deduplication.\n *\n * Key behaviors:\n * - [DONE] does NOT automatically stop the stream. Only `isTerminal(event)` stops it.\n * - If [DONE] is received without a terminal event, reconnection is triggered\n * (e.g., server 5-minute SSE connection lifetime timeout).\n * - On reconnection, the server replays all historical events from epoch.\n * Client-side deduplication skips already-seen events using an event counter.\n *\n * @param connectFn - Factory that creates a new SSE stream (called on each reconnect)\n * @param onEvent - Event handler (only called for new, non-duplicate events)\n * @param options - Reconnection options\n */\nexport async function consumeSSEStreamWithReconnect(\n connectFn: () => Promise<ReadableStream<Uint8Array>>,\n onEvent: (event: SSEEvent) => void,\n options: SSEReconnectOptions = {}\n): Promise<void> {\n const {\n maxReconnects = 5,\n reconnectDelay = 1000,\n signal,\n onReconnect,\n isTerminal,\n } = options;\n\n let emittedEventCount = 0;\n let reconnectAttempts = 0;\n let terminalReached = false;\n\n while (!terminalReached) {\n if (signal?.aborted) return;\n\n let receivedDone = false;\n let currentEventIndex = 0;\n\n try {\n const stream = await connectFn();\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n\n // Note: We do NOT reset reconnectAttempts here.\n // For [DONE]+running scenarios (server 5-min timeout), each reconnect\n // should count toward the limit. The counter only resets after the\n // function is called fresh.\n\n const parser = createSSEParser(\n (event) => {\n currentEventIndex++;\n\n // Dedup: skip events we've already emitted in previous connections\n if (currentEventIndex <= emittedEventCount) {\n // Still check for terminal even on dedup'd events\n if (isTerminal?.(event)) {\n terminalReached = true;\n }\n return;\n }\n\n emittedEventCount++;\n onEvent(event);\n\n if (isTerminal?.(event)) {\n terminalReached = true;\n }\n },\n () => {\n receivedDone = true;\n }\n );\n\n for (;;) {\n if (signal?.aborted || terminalReached) {\n reader.cancel().catch(() => {});\n return;\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n\n if (terminalReached) {\n reader.cancel().catch(() => {});\n return;\n }\n }\n parser.flush();\n } catch {\n // Stream error — will attempt reconnect below\n if (signal?.aborted || terminalReached) return;\n }\n\n // If terminal was reached, we're done\n if (terminalReached) return;\n\n // If [DONE] was received AND we already got a terminal event, stop\n // (This shouldn't happen because terminalReached would be true, but safety check)\n if (receivedDone && terminalReached) return;\n\n // Otherwise, reconnect: [DONE] without terminal = server timeout, or stream error\n reconnectAttempts++;\n if (reconnectAttempts > maxReconnects) {\n throw new Error(\n `SSE reconnection failed after ${maxReconnects} attempts`\n );\n }\n\n if (signal?.aborted) return;\n\n onReconnect?.(reconnectAttempts);\n\n // Exponential backoff for reconnection\n const delay = Math.min(\n reconnectDelay * 2 ** (reconnectAttempts - 1),\n 30_000\n );\n\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, delay);\n if (signal) {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n }\n}\n"],"mappings":"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","names":[]}
@@ -867,4 +867,4 @@ export {
867
867
  RushClient,
868
868
  createClient
869
869
  };
870
- //# sourceMappingURL=chunk-7H2T3KCJ.js.map
870
+ //# sourceMappingURL=chunk-UJLVFMWM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/output/logger.ts","../src/version.ts","../src/util/config.ts","../src/util/auth.ts","../src/util/auth-session.ts","../src/util/errors.ts","../src/util/retry.ts","../src/util/verbosity.ts","../src/util/client.ts"],"sourcesContent":["import chalk from 'chalk';\n\n/**\n * Output utilities.\n *\n * Design principle: stdout is for **content** (data the user wants to pipe/redirect).\n * Status messages (success, warn, info, dim) go to **stderr** so they don't pollute pipes.\n *\n * rush-ai task create --agent rush --prompt \"generate HTML\" --json > task.json\n * # stdout → task.json (task data)\n * # stderr → \"Success! Task xxx created.\" (status info, visible in terminal)\n */\nexport const output = {\n /** Content output → stdout. Use for data the user may pipe/redirect. */\n log(message: string): void {\n console.log(message);\n },\n\n /** Status → stderr */\n success(message: string): void {\n console.error(chalk.green('Success!'), message);\n },\n\n /** Error → stderr */\n error(message: string): void {\n console.error(chalk.red('Error:'), message);\n },\n\n /** Warning → stderr */\n warn(message: string): void {\n console.error(chalk.yellow('Warning:'), message);\n },\n\n /** Info → stderr */\n info(message: string): void {\n console.error(chalk.cyan('Info:'), message);\n },\n\n /** Dim status → stderr */\n dim(message: string): void {\n console.error(chalk.dim(message));\n },\n\n table(data: Record<string, string>[]): void {\n console.table(data);\n },\n\n /** Newline → stderr (status separator) */\n newline(): void {\n console.error();\n },\n\n link(text: string, url: string): string {\n return chalk.cyan.underline(url);\n },\n\n bold(text: string): string {\n return chalk.bold(text);\n },\n};\n","import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Dynamically read version from package.json.\n *\n * Path resolution:\n * - tsup bundle: dist/index.js → __dirname = dist/ → ../package.json\n * - vitest source: src/version.ts → __dirname = src/ → ../package.json\n */\nexport function loadVersion(): string {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n\n const candidates = [\n resolve(__dirname, '..', 'package.json'),\n resolve(__dirname, '..', '..', 'package.json'),\n ];\n\n for (const candidate of candidates) {\n try {\n const pkg = JSON.parse(readFileSync(candidate, 'utf-8'));\n if (pkg.name === 'rush-ai' && pkg.version) {\n return pkg.version;\n }\n } catch {\n // try next candidate\n }\n }\n console.error('[rush-ai] Warning: Could not read version from package.json');\n return '0.0.0-unknown';\n}\n\nexport const VERSION: string = loadVersion();\n","import { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport Conf from 'conf';\n\nexport interface AuthConfig {\n token: string | null;\n expiresAt: number | null;\n refreshToken: string | null;\n /** How the token was obtained */\n method: 'cas' | 'api_key' | 'platform_token' | null;\n /** Platform Token jti (for remote revocation via DELETE /api/platform-tokens?id={tokenId}) */\n tokenId: string | null;\n}\n\nexport interface GlobalConfig {\n currentTeam: string | null;\n api: string;\n collectMetrics: boolean;\n}\n\nexport interface ProjectConfig {\n projectId: string | null;\n orgId: string | null;\n}\n\n// --- Profile store types ---\n\ninterface ProfiledGlobalStore {\n _version: number;\n activeProfile: string;\n profiles: Record<string, GlobalConfig>;\n}\n\ninterface ProfiledAuthStore {\n _version: number;\n [profileName: string]: AuthConfig | number; // profile entries + _version\n}\n\nconst AUTH_CONFIG_DIR = resolve(homedir(), '.rush');\nconst DEFAULT_API = 'https://rush.zhenguanyu.com';\n\nconst AUTH_DEFAULTS: AuthConfig = {\n token: null,\n expiresAt: null,\n refreshToken: null,\n method: null,\n tokenId: null,\n};\n\nconst GLOBAL_DEFAULTS: GlobalConfig = {\n currentTeam: null,\n api: DEFAULT_API,\n collectMetrics: true,\n};\n\n// --- Raw stores (untyped to support migration) ---\n\nconst globalStore = new Conf<Record<string, unknown>>({\n projectName: 'rush-ai',\n cwd: AUTH_CONFIG_DIR,\n configName: 'config',\n defaults: {},\n});\n\nconst authStore = new Conf<Record<string, unknown>>({\n projectName: 'rush-ai',\n cwd: AUTH_CONFIG_DIR,\n configName: 'auth',\n defaults: {},\n});\n\n// --- Migration ---\n\nfunction migrateGlobalStoreIfNeeded(): void {\n const version = globalStore.get('_version');\n if (version === 2) return; // Already migrated\n\n // Check if this is old format (has 'api' at top level but no 'profiles')\n const hasOldFormat = globalStore.has('api') && !globalStore.has('profiles');\n const isEmpty = globalStore.size === 0;\n\n if (hasOldFormat) {\n // Migrate old flat format → profile format\n const oldConfig: GlobalConfig = {\n currentTeam: (globalStore.get('currentTeam') as string | null) ?? null,\n api: (globalStore.get('api') as string) ?? DEFAULT_API,\n collectMetrics: (globalStore.get('collectMetrics') as boolean) ?? true,\n };\n\n globalStore.clear();\n globalStore.set('_version', 2);\n globalStore.set('activeProfile', 'default');\n globalStore.set('profiles', { default: oldConfig });\n } else if (isEmpty || !globalStore.has('profiles')) {\n // Fresh install or incomplete state — initialize\n globalStore.set('_version', 2);\n if (!globalStore.has('activeProfile')) {\n globalStore.set('activeProfile', 'default');\n }\n if (!globalStore.has('profiles')) {\n globalStore.set('profiles', { default: { ...GLOBAL_DEFAULTS } });\n }\n }\n}\n\nfunction migrateAuthStoreIfNeeded(): void {\n const version = authStore.get('_version');\n if (version === 2) return;\n\n // Check if this is old format (has 'token' at top level)\n const hasOldFormat = authStore.has('token') || authStore.has('method');\n const isEmpty = authStore.size === 0;\n\n if (hasOldFormat) {\n const oldAuth: AuthConfig = {\n token: (authStore.get('token') as string | null) ?? null,\n expiresAt: (authStore.get('expiresAt') as number | null) ?? null,\n refreshToken: (authStore.get('refreshToken') as string | null) ?? null,\n method: (authStore.get('method') as AuthConfig['method']) ?? null,\n tokenId: (authStore.get('tokenId') as string | null) ?? null,\n };\n\n authStore.clear();\n authStore.set('_version', 2);\n authStore.set('default', oldAuth);\n } else if (isEmpty) {\n authStore.set('_version', 2);\n }\n}\n\n// Run migrations on module load\nmigrateGlobalStoreIfNeeded();\nmigrateAuthStoreIfNeeded();\n\n// --- Profile helpers ---\n\n/**\n * Get the active profile name.\n * RUSH_PROFILE env var takes precedence over stored activeProfile.\n */\nexport function getActiveProfile(): string {\n return (\n process.env.RUSH_PROFILE ??\n (globalStore.get('activeProfile') as string) ??\n 'default'\n );\n}\n\nexport function setActiveProfile(name: string): void {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (!profiles[name]) {\n throw new Error(\n `Profile '${name}' does not exist. Available: ${Object.keys(profiles).join(', ')}`\n );\n }\n globalStore.set('activeProfile', name);\n}\n\nexport function listProfiles(): string[] {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n return Object.keys(profiles);\n}\n\nexport function createProfile(\n name: string,\n config?: Partial<GlobalConfig>\n): void {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (profiles[name]) {\n throw new Error(`Profile '${name}' already exists.`);\n }\n // Inherit from default profile\n const defaultConfig = profiles.default ?? GLOBAL_DEFAULTS;\n profiles[name] = { ...defaultConfig, ...config };\n globalStore.set('profiles', profiles);\n}\n\nexport function deleteProfile(name: string): void {\n if (name === 'default') {\n throw new Error(\"Cannot delete the 'default' profile.\");\n }\n const active = getActiveProfile();\n if (name === active) {\n throw new Error(\n `Cannot delete the active profile '${name}'. Switch to another profile first.`\n );\n }\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (!profiles[name]) {\n throw new Error(`Profile '${name}' does not exist.`);\n }\n delete profiles[name];\n globalStore.set('profiles', profiles);\n\n // Also clear auth for this profile\n authStore.delete(name);\n}\n\n// --- Auth config (profile-aware) ---\n\nexport function getAuthConfig(): AuthConfig {\n const profile = getActiveProfile();\n const profileAuth = authStore.get(profile) as AuthConfig | undefined;\n return {\n token: profileAuth?.token ?? null,\n expiresAt: profileAuth?.expiresAt ?? null,\n refreshToken: profileAuth?.refreshToken ?? null,\n method: profileAuth?.method ?? null,\n tokenId: profileAuth?.tokenId ?? null,\n };\n}\n\nexport function setAuthConfig(config: Partial<AuthConfig>): void {\n const profile = getActiveProfile();\n const current = (authStore.get(profile) as AuthConfig) ?? {\n ...AUTH_DEFAULTS,\n };\n authStore.set(profile, { ...current, ...config });\n}\n\n/** Clear auth for the current profile only */\nexport function clearAuthConfig(): void {\n const profile = getActiveProfile();\n authStore.set(profile, { ...AUTH_DEFAULTS });\n}\n\n/** Clear auth for ALL profiles */\nexport function clearAllAuthConfig(): void {\n const profiles = listProfiles();\n for (const profile of profiles) {\n authStore.set(profile, { ...AUTH_DEFAULTS });\n }\n}\n\n// --- Global config (profile-aware) ---\n\nexport function getGlobalConfig(): GlobalConfig {\n const profile = getActiveProfile();\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n const profileConfig = profiles[profile] ?? GLOBAL_DEFAULTS;\n return {\n currentTeam: profileConfig.currentTeam ?? null,\n api: profileConfig.api ?? DEFAULT_API,\n collectMetrics: profileConfig.collectMetrics ?? true,\n };\n}\n\nexport function setGlobalConfig(config: Partial<GlobalConfig>): void {\n const profile = getActiveProfile();\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n const current = profiles[profile] ?? { ...GLOBAL_DEFAULTS };\n profiles[profile] = { ...current, ...config };\n globalStore.set('profiles', profiles);\n}\n\nexport function getConfigDir(): string {\n return AUTH_CONFIG_DIR;\n}\n\nexport function isLoggedIn(): boolean {\n const auth = getAuthConfig();\n if (!auth.token) return false;\n if (auth.expiresAt && Date.now() > auth.expiresAt) {\n // CAS tokens with refresh_token can still be renewed lazily by the client.\n return auth.method === 'cas' && Boolean(auth.refreshToken);\n }\n return true;\n}\n\n/**\n * Get the config for a specific profile (for display purposes).\n */\nexport function getProfileConfig(name: string): GlobalConfig | null {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n return profiles[name] ?? null;\n}\n\n/**\n * Get auth config for a specific profile (for display purposes).\n */\nexport function getProfileAuth(name: string): AuthConfig {\n const profileAuth = authStore.get(name) as AuthConfig | undefined;\n return {\n token: profileAuth?.token ?? null,\n expiresAt: profileAuth?.expiresAt ?? null,\n refreshToken: profileAuth?.refreshToken ?? null,\n method: profileAuth?.method ?? null,\n tokenId: profileAuth?.tokenId ?? null,\n };\n}\n","import { getAuthConfig, getGlobalConfig } from './config.js';\n\nexport type AuthMethod = 'api_key' | 'cas' | 'platform_token' | null;\n\n/**\n * Determine the current authentication method.\n * Priority: RUSH_API_KEY env > stored CAS token > null\n */\nexport function getAuthMethod(): AuthMethod {\n if (process.env.RUSH_API_KEY) {\n return 'api_key';\n }\n const auth = getAuthConfig();\n if (auth.token) {\n // Use stored method if available; detect rush_sk_ prefix as fallback\n if (auth.method === 'api_key' || auth.token.startsWith('rush_sk_')) {\n return 'api_key';\n }\n if (auth.method === 'platform_token') {\n return 'platform_token';\n }\n return auth.method ?? 'cas';\n }\n return null;\n}\n\n/**\n * Get the active auth token.\n * Priority: RUSH_API_KEY env > stored token\n */\nexport function getAuthToken(): string | null {\n if (process.env.RUSH_API_KEY) {\n return process.env.RUSH_API_KEY;\n }\n const auth = getAuthConfig();\n return auth.token;\n}\n\nexport interface CasConfig {\n authorizeEndpoint: string;\n tokenEndpoint: string;\n revokeEndpoint: string;\n clientId: string;\n}\n\n/**\n * Get CAS PKCE configuration.\n * Supports env overrides for private deployments.\n */\nexport function getCasConfig(): CasConfig {\n const config = getGlobalConfig();\n const baseUrl = process.env.RUSH_API_URL ?? config.api;\n\n return {\n authorizeEndpoint:\n process.env.RUSH_CAS_AUTHORIZE_ENDPOINT ??\n `${baseUrl}/cas/oauth2/authorize`,\n tokenEndpoint:\n process.env.RUSH_CAS_TOKEN_ENDPOINT ?? `${baseUrl}/cas/oauth2/token`,\n revokeEndpoint:\n process.env.RUSH_CAS_REVOKE_ENDPOINT ?? `${baseUrl}/cas/oauth2/revoke`,\n clientId: process.env.RUSH_CAS_CLIENT_ID ?? 'rush-ai',\n };\n}\n\nexport interface Principal {\n provider: 'ldap' | 'service';\n subject: string;\n}\n\n/**\n * Build a Principal object from the current auth context.\n * M1: only ldap is supported.\n */\nexport function getPrincipal(subject: string): Principal {\n const method = getAuthMethod();\n return {\n provider: method === 'api_key' ? 'service' : 'ldap',\n subject,\n };\n}\n","import { getAuthToken, getCasConfig } from './auth.js';\nimport { getAuthConfig, getGlobalConfig, setAuthConfig } from './config.js';\n\nconst VERIFY_PATH = '/api/skill-auth/me';\nconst REFRESH_SKEW_MS = 60_000;\nconst AUTH_REQUEST_TIMEOUT_MS = 8_000;\n\ninterface RefreshTokenResponse {\n access_token: string;\n expires_in?: number;\n refresh_token?: string;\n}\n\nexport interface AuthVerificationResult {\n valid: boolean;\n status: number | null;\n message?: string;\n}\n\nexport interface RefreshOptions {\n force?: boolean;\n refreshWindowMs?: number;\n}\n\nfunction getApiBaseUrl(): string {\n return process.env.RUSH_API_URL ?? getGlobalConfig().api;\n}\n\nfunction buildBearerHeaders(token: string): Record<string, string> {\n return {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'rush-ai/0.2.0',\n };\n}\n\nfunction shouldRefreshToken(options?: RefreshOptions): boolean {\n const auth = getAuthConfig();\n // Platform tokens don't support refresh; only CAS tokens do\n if (!auth.token || auth.method !== 'cas' || !auth.refreshToken) {\n return false;\n }\n if (options?.force) {\n return true;\n }\n if (!auth.expiresAt) {\n return false;\n }\n const refreshWindowMs = options?.refreshWindowMs ?? REFRESH_SKEW_MS;\n return Date.now() >= auth.expiresAt - refreshWindowMs;\n}\n\nexport async function refreshCasAccessToken(\n options?: RefreshOptions\n): Promise<boolean> {\n if (process.env.RUSH_API_KEY || !shouldRefreshToken(options)) {\n return false;\n }\n\n const auth = getAuthConfig();\n if (!auth.refreshToken) {\n return false;\n }\n\n const cas = getCasConfig();\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: auth.refreshToken,\n client_id: cas.clientId,\n }).toString();\n\n try {\n const response = await fetch(cas.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body,\n signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n return false;\n }\n\n const tokenData = (await response.json()) as RefreshTokenResponse;\n if (!tokenData.access_token) {\n return false;\n }\n\n const expiresAt = tokenData.expires_in\n ? Date.now() + tokenData.expires_in * 1000\n : auth.expiresAt;\n\n setAuthConfig({\n token: tokenData.access_token,\n expiresAt,\n refreshToken: tokenData.refresh_token ?? auth.refreshToken,\n method: 'cas',\n });\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function verifyTokenOnline(\n token: string\n): Promise<AuthVerificationResult> {\n const url = `${getApiBaseUrl()}${VERIFY_PATH}`;\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: buildBearerHeaders(token),\n signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),\n });\n\n if (response.ok) {\n return { valid: true, status: response.status };\n }\n\n const message = await response.text().catch(() => undefined);\n return {\n valid: false,\n status: response.status,\n message: message || `Server rejected token (${response.status})`,\n };\n } catch (error) {\n return {\n valid: false,\n status: null,\n message: `Network error: ${error instanceof Error ? error.message : 'unknown'}`,\n };\n }\n}\n\nexport async function verifyCurrentAuthSession(): Promise<AuthVerificationResult> {\n const token = getAuthToken();\n if (!token) {\n return {\n valid: false,\n status: null,\n message: 'Not authenticated',\n };\n }\n\n await refreshCasAccessToken();\n\n let activeToken = getAuthToken();\n if (!activeToken) {\n return {\n valid: false,\n status: null,\n message: 'Not authenticated',\n };\n }\n\n const result = await verifyTokenOnline(activeToken);\n if (result.valid || result.status !== 401 || process.env.RUSH_API_KEY) {\n return result;\n }\n\n const refreshed = await refreshCasAccessToken({ force: true });\n if (!refreshed) {\n return result;\n }\n\n activeToken = getAuthToken();\n if (!activeToken) {\n return result;\n }\n return verifyTokenOnline(activeToken);\n}\n\nasync function revokeToken(\n revokeEndpoint: string,\n clientId: string,\n token: string,\n tokenTypeHint: 'access_token' | 'refresh_token'\n): Promise<boolean> {\n const body = new URLSearchParams({\n token,\n token_type_hint: tokenTypeHint,\n client_id: clientId,\n }).toString();\n\n try {\n const response = await fetch(revokeEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body,\n signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\nexport async function revokeCurrentSession(): Promise<boolean> {\n if (process.env.RUSH_API_KEY) {\n return false;\n }\n\n const auth = getAuthConfig();\n\n if (auth.method === 'platform_token') {\n return revokePlatformToken();\n }\n\n if (auth.method !== 'cas') {\n return false;\n }\n\n const cas = getCasConfig();\n const candidates: Array<{\n token: string;\n hint: 'access_token' | 'refresh_token';\n }> = [];\n if (auth.refreshToken) {\n candidates.push({ token: auth.refreshToken, hint: 'refresh_token' });\n }\n if (auth.token) {\n candidates.push({ token: auth.token, hint: 'access_token' });\n }\n\n if (candidates.length === 0) {\n return false;\n }\n\n let anyRevoked = false;\n for (const candidate of candidates) {\n const revoked = await revokeToken(\n cas.revokeEndpoint,\n cas.clientId,\n candidate.token,\n candidate.hint\n );\n if (revoked) {\n anyRevoked = true;\n }\n }\n\n return anyRevoked;\n}\n\n/** @deprecated Use revokeCurrentSession instead */\nexport const revokeCurrentCasSession = revokeCurrentSession;\n\nasync function revokePlatformToken(): Promise<boolean> {\n const auth = getAuthConfig();\n if (!auth.tokenId || !auth.token) {\n return false;\n }\n\n const baseUrl = getApiBaseUrl();\n const url = `${baseUrl}/api/platform-tokens?id=${encodeURIComponent(auth.tokenId)}`;\n\n try {\n const response = await fetch(url, {\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${auth.token}`,\n 'User-Agent': 'rush-ai/0.2.0',\n },\n signal: AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","export class RushError extends Error {\n public code: string;\n public meta: Record<string, unknown>;\n public exitCode: number;\n\n constructor(\n message: string,\n meta: Record<string, unknown> = {},\n code = 'RUSH_ERROR',\n exitCode = 2\n ) {\n super(message);\n this.name = 'RushError';\n this.code = code;\n this.meta = meta;\n this.exitCode = exitCode;\n }\n}\n\nexport class AuthError extends RushError {\n constructor(message = 'Not authenticated. Run `rush-ai auth login` first.') {\n super(message, {}, 'AUTH_ERROR', 3);\n this.name = 'AuthError';\n }\n}\n\nexport class TaskFailedError extends RushError {\n constructor(message: string, meta: Record<string, unknown> = {}) {\n super(message, meta, 'TASK_FAILED', 1);\n this.name = 'TaskFailedError';\n }\n}\n\n/**\n * Generate a user-friendly error message based on HTTP status.\n * Server-provided error messages take priority.\n */\nfunction friendlyMessage(status: number, serverMessage?: string): string {\n if (serverMessage) return serverMessage;\n\n switch (status) {\n case 401:\n return 'Authentication expired. Run `rush-ai auth login` to re-authenticate.';\n case 403:\n return 'Permission denied. You may not have access to this resource.';\n case 404:\n return 'Resource not found.';\n case 429:\n return 'Rate limited. The server is busy, please try again later.';\n case 502:\n case 503:\n case 504:\n return 'Service temporarily unavailable. Please try again in a moment.';\n default:\n return `Request failed with status ${status}.`;\n }\n}\n\nexport class ApiError extends RushError {\n public status: number;\n public retryAfter?: string;\n\n constructor(\n message: string,\n status: number,\n body?: string,\n retryAfter?: string\n ) {\n // Try to extract server error message from body\n let serverMessage: string | undefined;\n if (body) {\n try {\n const parsed = JSON.parse(body);\n if (parsed.error && typeof parsed.error === 'string') {\n serverMessage = parsed.error;\n }\n } catch {\n // Not JSON\n }\n }\n\n super(\n friendlyMessage(status, serverMessage),\n { status, body },\n 'API_ERROR'\n );\n this.name = 'ApiError';\n this.status = status;\n this.retryAfter = retryAfter;\n }\n}\n\nexport function isRushError(error: unknown): error is RushError {\n return error instanceof RushError;\n}\n","import { ApiError, RushError } from './errors.js';\n\nexport interface RetryOptions {\n /** Maximum number of retries (default: 3, total attempts = maxRetries + 1) */\n maxRetries?: number;\n /** Base delay in ms for exponential backoff (default: 1000) */\n baseDelay?: number;\n /** Maximum delay in ms (default: 30000) */\n maxDelay?: number;\n /** HTTP status codes eligible for retry (default: [429, 502, 503, 504]) */\n retryableStatuses?: number[];\n /** AbortSignal to cancel retries */\n signal?: AbortSignal;\n /** Called before each retry with attempt number (1-based) and delay in ms */\n onRetry?: (attempt: number, delayMs: number, status?: number) => void;\n}\n\n/**\n * Parse the Retry-After HTTP header value.\n * Supports both seconds (integer) and HTTP-date formats.\n * Returns delay in milliseconds, or null if unparseable.\n */\nexport function parseRetryAfter(value: string): number | null {\n // Try as integer seconds first\n const seconds = Number(value);\n if (!Number.isNaN(seconds) && seconds >= 0) {\n return seconds * 1000;\n }\n\n // Try as HTTP-date\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n const delayMs = date.getTime() - Date.now();\n return delayMs > 0 ? delayMs : 0;\n }\n\n return null;\n}\n\n/**\n * Calculate delay with exponential backoff and jitter.\n */\nfunction calculateDelay(\n attempt: number,\n baseDelay: number,\n maxDelay: number\n): number {\n const exponential = baseDelay * 2 ** attempt;\n const jitter = Math.random() * baseDelay * 0.5;\n return Math.min(exponential + jitter, maxDelay);\n}\n\nfunction isRetryableError(\n error: unknown,\n retryableStatuses: number[]\n): boolean {\n // Network errors (fetch failures) are retryable\n if (error instanceof RushError && error.code === 'NETWORK_ERROR') {\n return true;\n }\n\n // API errors with retryable status codes\n if (error instanceof ApiError && retryableStatuses.includes(error.status)) {\n return true;\n }\n\n return false;\n}\n\nfunction getRetryAfterFromError(error: unknown): number | null {\n if (error instanceof ApiError && error.status === 429) {\n // Use Retry-After HTTP header (propagated via ApiError.retryAfter)\n if (error.retryAfter) {\n return parseRetryAfter(error.retryAfter);\n }\n }\n return null;\n}\n\n/**\n * Wrap an async function with retry logic using exponential backoff.\n *\n * Important: This should only be used for idempotent operations.\n * The caller (RushClient) is responsible for only wrapping GET/HEAD/DELETE.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<T> {\n const {\n maxRetries = 3,\n baseDelay = 1000,\n maxDelay = 30_000,\n retryableStatuses = [429, 502, 503, 504],\n signal,\n onRetry,\n } = options;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n // Check abort before each attempt\n if (signal?.aborted) {\n throw lastError ?? new RushError('Request aborted', {}, 'ABORT_ERROR');\n }\n\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Don't retry if this is the last attempt\n if (attempt >= maxRetries) {\n throw error;\n }\n\n // Don't retry non-retryable errors\n if (!isRetryableError(error, retryableStatuses)) {\n throw error;\n }\n\n // Check abort before waiting\n if (signal?.aborted) {\n throw error;\n }\n\n // Calculate delay: use Retry-After if available (for 429), otherwise backoff\n let delayMs: number;\n const retryAfterMs = getRetryAfterFromError(error);\n if (retryAfterMs !== null) {\n // Cap Retry-After at 120 seconds\n delayMs = Math.min(retryAfterMs, 120_000);\n } else {\n delayMs = calculateDelay(attempt, baseDelay, maxDelay);\n }\n\n const status = error instanceof ApiError ? error.status : undefined;\n onRetry?.(attempt + 1, delayMs, status);\n\n // Wait with abort support\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, delayMs);\n if (signal) {\n const onAbort = () => {\n clearTimeout(timer);\n reject(lastError);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n // Clean up listener after timer fires\n const origResolve = resolve;\n setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n origResolve();\n }, delayMs);\n }\n });\n }\n }\n\n // Should never reach here, but TypeScript needs it\n throw lastError;\n}\n","let _verbose = false;\nlet _debug = false;\n\nexport function setVerbosity(verbose: boolean, debug: boolean): void {\n _debug = debug;\n _verbose = verbose || debug;\n}\n\nexport const isVerbose = (): boolean => _verbose;\nexport const isDebug = (): boolean => _debug;\n","import { output } from '../output/logger.js';\nimport { VERSION } from '../version.js';\nimport { getAuthToken } from './auth.js';\nimport { refreshCasAccessToken } from './auth-session.js';\nimport { getAuthConfig, getGlobalConfig } from './config.js';\nimport { ApiError, RushError } from './errors.js';\nimport { withRetry } from './retry.js';\nimport { isDebug, isVerbose } from './verbosity.js';\n\nconst SENSITIVE_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n];\n\nfunction maskSensitiveHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase())\n ? '[REDACTED]'\n : value;\n });\n return result;\n}\n\nexport interface FetchOptions extends RequestInit {\n json?: boolean;\n}\n\nexport interface ApiResponse<T = unknown> {\n ok: boolean;\n status: number;\n data: T;\n}\n\nexport interface ApiErrorBody {\n error: string;\n code: string;\n retryable: boolean;\n}\n\nexport class RushClient {\n private baseUrl: string;\n\n constructor() {\n const config = getGlobalConfig();\n const raw = process.env.RUSH_API_URL ?? config.api;\n this.baseUrl = raw.replace(/\\/+$/, '');\n }\n\n private async getHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'User-Agent': `rush-ai/${VERSION}`,\n };\n\n if (!process.env.RUSH_API_KEY) {\n await refreshCasAccessToken();\n }\n\n const token = getAuthToken();\n if (token) {\n // Both CAS token and service account API key (rush_sk_*) use Bearer.\n // Backend distinguishes by the rush_sk_ prefix.\n headers.Authorization = `Bearer ${token}`;\n }\n\n // Test mode: skip CAS auth in non-production environments\n if (process.env.RUSH_TEST_MODE === 'integration-test') {\n headers['X-Test-Mode'] = 'integration-test';\n }\n\n return headers;\n }\n\n private async performRequest(\n url: string,\n options: RequestInit,\n retryOnUnauthorized = true\n ): Promise<Response> {\n const requestHeaders = (options.headers as Record<string, string>) ?? {};\n const headers = await this.getHeaders();\n\n try {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...requestHeaders,\n },\n });\n\n if (\n response.status === 401 &&\n retryOnUnauthorized &&\n !process.env.RUSH_API_KEY\n ) {\n const auth = getAuthConfig();\n const canRefresh = auth.method === 'cas' && Boolean(auth.refreshToken);\n if (canRefresh && (await refreshCasAccessToken({ force: true }))) {\n const retryHeaders = await this.getHeaders();\n return await fetch(url, {\n ...options,\n headers: {\n ...retryHeaders,\n ...requestHeaders,\n },\n });\n }\n }\n\n return response;\n } catch (err) {\n throw new RushError(\n `Network error: ${err instanceof Error ? err.message : 'Failed to connect'}`,\n { url },\n 'NETWORK_ERROR'\n );\n }\n }\n\n /** Idempotent HTTP methods eligible for automatic retry */\n private static readonly RETRYABLE_METHODS = ['GET', 'HEAD', 'DELETE'];\n\n /**\n * Internal fetch with response parsing and error handling (no retry).\n */\n private async _doFetch<T = unknown>(\n path: string,\n options: FetchOptions = {}\n ): Promise<ApiResponse<T>> {\n const { json = true, ...fetchOptions } = options;\n const url = `${this.baseUrl}${path}`;\n const method = (fetchOptions.method ?? 'GET').toUpperCase();\n const startTime = Date.now();\n\n if (isVerbose()) {\n output.dim(`→ ${method} ${url}`);\n }\n\n const response = await this.performRequest(url, fetchOptions);\n\n if (isVerbose()) {\n output.dim(\n `← ${response.status} ${response.statusText} (${Date.now() - startTime}ms)`\n );\n }\n\n if (isDebug()) {\n output.dim(\n ` Response headers: ${JSON.stringify(maskSensitiveHeaders(response.headers))}`\n );\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => 'Unknown error');\n const retryAfter = response.headers.get('retry-after') ?? undefined;\n\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorBody,\n retryAfter\n );\n }\n\n const data = json\n ? ((await response.json()) as T)\n : ((await response.text()) as T);\n\n return {\n ok: true,\n status: response.status,\n data,\n };\n }\n\n /**\n * Fetch with response parsing, error handling, and automatic retry\n * for idempotent methods (GET, HEAD, DELETE).\n */\n async fetch<T = unknown>(\n path: string,\n options: FetchOptions = {}\n ): Promise<ApiResponse<T>> {\n const method = (options.method ?? 'GET').toUpperCase();\n const isRetryable = RushClient.RETRYABLE_METHODS.includes(method);\n\n if (!isRetryable) {\n return this._doFetch<T>(path, options);\n }\n\n return withRetry(() => this._doFetch<T>(path, options), {\n signal: options.signal ?? undefined,\n onRetry: (attempt, delayMs, status) => {\n const statusInfo = status ? ` (${status})` : '';\n console.error(\n `\\u27F3 Retrying ${method} ${path}${statusInfo} (attempt ${attempt}) in ${(delayMs / 1000).toFixed(1)}s...`\n );\n },\n });\n }\n\n /**\n * Raw fetch for streaming (SSE) responses.\n * Returns the raw Response object.\n */\n async fetchRaw(path: string, options: RequestInit = {}): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n\n const response = await this.performRequest(url, options);\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => 'Unknown error');\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status,\n errorBody\n );\n }\n\n return response;\n }\n\n async get<T = unknown>(path: string): Promise<ApiResponse<T>> {\n return this.fetch<T>(path, { method: 'GET' });\n }\n\n async post<T = unknown>(\n path: string,\n body?: unknown\n ): Promise<ApiResponse<T>> {\n return this.fetch<T>(path, {\n method: 'POST',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async put<T = unknown>(\n path: string,\n body?: unknown\n ): Promise<ApiResponse<T>> {\n return this.fetch<T>(path, {\n method: 'PUT',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async delete<T = unknown>(path: string): Promise<ApiResponse<T>> {\n return this.fetch<T>(path, { method: 'DELETE' });\n }\n}\n\nexport function createClient(): RushClient {\n return new RushClient();\n}\n"],"mappings":"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","names":["resolve","resolve"]}
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  RushClient,
4
4
  createClient
5
- } from "./chunk-7H2T3KCJ.js";
5
+ } from "./chunk-UJLVFMWM.js";
6
6
  export {
7
7
  RushClient,
8
8
  createClient
9
9
  };
10
- //# sourceMappingURL=client-2GSYT7IS.js.map
10
+ //# sourceMappingURL=client-NTECGN3O.js.map