indusagi-coding-agent 0.1.28 → 0.1.30

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE.md +22 -0
  3. package/README.md +2 -0
  4. package/dist/core/messages.d.ts +1 -76
  5. package/dist/core/messages.d.ts.map +1 -1
  6. package/dist/core/messages.js +1 -122
  7. package/dist/core/messages.js.map +1 -1
  8. package/dist/core/session-manager.d.ts +1 -447
  9. package/dist/core/session-manager.d.ts.map +1 -1
  10. package/dist/core/session-manager.js +1 -1203
  11. package/dist/core/session-manager.js.map +1 -1
  12. package/package.json +2 -2
  13. package/docs/COMPLETE-GUIDE.md +0 -300
  14. package/docs/COMPREHENSIVE-CLI-SUMMARY.md +0 -900
  15. package/docs/MODES-ARCHITECTURE.md +0 -565
  16. package/docs/PRINT-MODE-GUIDE.md +0 -456
  17. package/docs/README.md +0 -78
  18. package/docs/RPC-GUIDE.md +0 -705
  19. package/docs/UTILS-IMPLEMENTATION-SUMMARY.md +0 -647
  20. package/docs/UTILS-MODULE-OVERVIEW.md +0 -1480
  21. package/docs/UTILS-QA-CHECKLIST.md +0 -1061
  22. package/docs/UTILS-USAGE-GUIDE.md +0 -1419
  23. package/docs/compaction.md +0 -390
  24. package/docs/custom-provider.md +0 -538
  25. package/docs/development.md +0 -69
  26. package/docs/extensions.md +0 -1733
  27. package/docs/hooks.md +0 -378
  28. package/docs/images/doom-extension.png +0 -0
  29. package/docs/images/interactive-mode.png +0 -0
  30. package/docs/images/tree-view.png +0 -0
  31. package/docs/json.md +0 -79
  32. package/docs/keybindings.md +0 -162
  33. package/docs/models.md +0 -193
  34. package/docs/packages.md +0 -163
  35. package/docs/prompt-templates.md +0 -67
  36. package/docs/providers.md +0 -147
  37. package/docs/rpc.md +0 -1048
  38. package/docs/sdk.md +0 -969
  39. package/docs/session.md +0 -412
  40. package/docs/settings.md +0 -219
  41. package/docs/shell-aliases.md +0 -13
  42. package/docs/skills.md +0 -226
  43. package/docs/subagents.md +0 -225
  44. package/docs/terminal-setup.md +0 -65
  45. package/docs/themes.md +0 -295
  46. package/docs/tree.md +0 -219
  47. package/docs/tui.md +0 -887
  48. package/docs/web-tools.md +0 -304
  49. package/docs/windows.md +0 -17
  50. package/examples/README.md +0 -25
  51. package/examples/extensions/README.md +0 -192
  52. package/examples/extensions/antigravity-image-gen.ts +0 -414
  53. package/examples/extensions/auto-commit-on-exit.ts +0 -49
  54. package/examples/extensions/bookmark.ts +0 -50
  55. package/examples/extensions/claude-rules.ts +0 -86
  56. package/examples/extensions/confirm-destructive.ts +0 -59
  57. package/examples/extensions/custom-compaction.ts +0 -115
  58. package/examples/extensions/custom-footer.ts +0 -65
  59. package/examples/extensions/custom-header.ts +0 -73
  60. package/examples/extensions/custom-provider-anthropic/index.ts +0 -605
  61. package/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  62. package/examples/extensions/custom-provider-anthropic/package.json +0 -19
  63. package/examples/extensions/custom-provider-gitlab-duo/index.ts +0 -350
  64. package/examples/extensions/custom-provider-gitlab-duo/package.json +0 -16
  65. package/examples/extensions/custom-provider-gitlab-duo/test.ts +0 -83
  66. package/examples/extensions/dirty-repo-guard.ts +0 -56
  67. package/examples/extensions/doom-overlay/README.md +0 -46
  68. package/examples/extensions/doom-overlay/doom/build/doom.js +0 -21
  69. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  70. package/examples/extensions/doom-overlay/doom/build.sh +0 -152
  71. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +0 -72
  72. package/examples/extensions/doom-overlay/doom-component.ts +0 -133
  73. package/examples/extensions/doom-overlay/doom-engine.ts +0 -173
  74. package/examples/extensions/doom-overlay/doom-keys.ts +0 -105
  75. package/examples/extensions/doom-overlay/index.ts +0 -74
  76. package/examples/extensions/doom-overlay/wad-finder.ts +0 -51
  77. package/examples/extensions/event-bus.ts +0 -43
  78. package/examples/extensions/file-trigger.ts +0 -41
  79. package/examples/extensions/git-checkpoint.ts +0 -53
  80. package/examples/extensions/handoff.ts +0 -151
  81. package/examples/extensions/hello.ts +0 -25
  82. package/examples/extensions/inline-bash.ts +0 -94
  83. package/examples/extensions/input-transform.ts +0 -43
  84. package/examples/extensions/interactive-shell.ts +0 -196
  85. package/examples/extensions/mac-system-theme.ts +0 -47
  86. package/examples/extensions/message-renderer.ts +0 -60
  87. package/examples/extensions/modal-editor.ts +0 -86
  88. package/examples/extensions/model-status.ts +0 -31
  89. package/examples/extensions/notify.ts +0 -25
  90. package/examples/extensions/overlay-qa-tests.ts +0 -882
  91. package/examples/extensions/overlay-test.ts +0 -151
  92. package/examples/extensions/permission-gate.ts +0 -34
  93. package/examples/extensions/pirate.ts +0 -47
  94. package/examples/extensions/plan-mode/README.md +0 -65
  95. package/examples/extensions/plan-mode/index.ts +0 -341
  96. package/examples/extensions/plan-mode/utils.ts +0 -168
  97. package/examples/extensions/preset.ts +0 -399
  98. package/examples/extensions/protected-paths.ts +0 -30
  99. package/examples/extensions/qna.ts +0 -120
  100. package/examples/extensions/question.ts +0 -265
  101. package/examples/extensions/questionnaire.ts +0 -428
  102. package/examples/extensions/rainbow-editor.ts +0 -88
  103. package/examples/extensions/sandbox/index.ts +0 -318
  104. package/examples/extensions/sandbox/package-lock.json +0 -92
  105. package/examples/extensions/sandbox/package.json +0 -19
  106. package/examples/extensions/send-user-message.ts +0 -97
  107. package/examples/extensions/session-name.ts +0 -27
  108. package/examples/extensions/shutdown-command.ts +0 -63
  109. package/examples/extensions/snake.ts +0 -344
  110. package/examples/extensions/space-invaders.ts +0 -561
  111. package/examples/extensions/ssh.ts +0 -220
  112. package/examples/extensions/status-line.ts +0 -40
  113. package/examples/extensions/subagent/README.md +0 -172
  114. package/examples/extensions/subagent/agents/planner.md +0 -37
  115. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  116. package/examples/extensions/subagent/agents/scout.md +0 -50
  117. package/examples/extensions/subagent/agents/worker.md +0 -24
  118. package/examples/extensions/subagent/agents.ts +0 -127
  119. package/examples/extensions/subagent/index.ts +0 -964
  120. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  121. package/examples/extensions/subagent/prompts/implement.md +0 -10
  122. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
  123. package/examples/extensions/summarize.ts +0 -196
  124. package/examples/extensions/timed-confirm.ts +0 -70
  125. package/examples/extensions/todo.ts +0 -300
  126. package/examples/extensions/tool-override.ts +0 -144
  127. package/examples/extensions/tools.ts +0 -147
  128. package/examples/extensions/trigger-compact.ts +0 -40
  129. package/examples/extensions/truncated-tool.ts +0 -193
  130. package/examples/extensions/widget-placement.ts +0 -17
  131. package/examples/extensions/with-deps/index.ts +0 -36
  132. package/examples/extensions/with-deps/package-lock.json +0 -31
  133. package/examples/extensions/with-deps/package.json +0 -22
  134. package/examples/sdk/01-minimal.ts +0 -22
  135. package/examples/sdk/02-custom-model.ts +0 -50
  136. package/examples/sdk/03-custom-prompt.ts +0 -55
  137. package/examples/sdk/04-skills.ts +0 -46
  138. package/examples/sdk/05-tools.ts +0 -56
  139. package/examples/sdk/06-extensions.ts +0 -88
  140. package/examples/sdk/07-context-files.ts +0 -40
  141. package/examples/sdk/08-prompt-templates.ts +0 -47
  142. package/examples/sdk/09-api-keys-and-oauth.ts +0 -48
  143. package/examples/sdk/10-settings.ts +0 -38
  144. package/examples/sdk/11-sessions.ts +0 -48
  145. package/examples/sdk/12-full-control.ts +0 -82
  146. package/examples/sdk/13-codex-oauth.ts +0 -37
  147. package/examples/sdk/README.md +0 -144
package/docs/RPC-GUIDE.md DELETED
@@ -1,705 +0,0 @@
1
- # RPC Mode Complete Guide
2
-
3
- Comprehensive documentation for programmatic access via JSON-RPC 2.0 protocol.
4
-
5
- ---
6
-
7
- ## RPC Authentication & Security
8
-
9
- ### Authentication Methods
10
-
11
- **1. Token-Based Authentication**
12
- ```json
13
- {
14
- "id": "req-1",
15
- "method": "prompt",
16
- "params": {
17
- "message": "What is 2+2?",
18
- "token": "your_secret_token_here"
19
- }
20
- }
21
- ```
22
-
23
- **Token Generation:**
24
- ```bash
25
- # Generate secure random token
26
- openssl rand -hex 32
27
- # Output: a1f2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
28
-
29
- # Store in config
30
- indusagi config set rpc-token a1f2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
31
- ```
32
-
33
- **2. Environment Variable**
34
- ```bash
35
- export INDUSAGI_RPC_TOKEN="your_secret_token"
36
- indusagi --mode=rpc
37
- ```
38
-
39
- **3. File-Based Token**
40
- ```bash
41
- # Token stored in ~/.indusagi/rpc-token
42
- mkdir -p ~/.indusagi
43
- echo "your_secret_token" > ~/.indusagi/rpc-token
44
- chmod 600 ~/.indusagi/rpc-token
45
- ```
46
-
47
- **4. Header-Based (HTTP)**
48
- ```
49
- Authorization: Bearer your_secret_token
50
- ```
51
-
52
- ### Token Best Practices
53
-
54
- ✅ **Do:**
55
- - Use 32+ byte tokens (minimum entropy)
56
- - Rotate tokens every 90 days
57
- - Store securely in environment/files
58
- - Log token usage for audit trails
59
- - Revoke compromised tokens immediately
60
-
61
- ❌ **Don't:**
62
- - Commit tokens to version control
63
- - Use weak/predictable tokens
64
- - Share tokens via email/Slack
65
- - Log full tokens (log only hash)
66
- - Reuse same token across environments
67
-
68
- ### Security Considerations
69
-
70
- **1. Connection Security**
71
- ```javascript
72
- // Always use Unix sockets for local connections
73
- const socket = '/var/run/indusagi.sock';
74
-
75
- // Always use HTTPS for remote connections
76
- const url = 'https://agent.example.com:3000';
77
-
78
- // Never expose over HTTP publicly
79
- // ❌ DON'T: indusagi --rpc-port 3000 --rpc-host 0.0.0.0
80
- // ✅ DO: indusagi --rpc-socket /var/run/indusagi.sock
81
- ```
82
-
83
- **2. Rate Limiting**
84
- ```javascript
85
- // Implement rate limiting on client side
86
- class RateLimitedRpcClient {
87
- constructor(client, maxRequests = 10, windowMs = 1000) {
88
- this.client = client;
89
- this.maxRequests = maxRequests;
90
- this.windowMs = windowMs;
91
- this.requests = [];
92
- }
93
-
94
- async send(method, params) {
95
- const now = Date.now();
96
- this.requests = this.requests.filter(t => now - t < this.windowMs);
97
-
98
- if (this.requests.length >= this.maxRequests) {
99
- throw new Error('Rate limit exceeded');
100
- }
101
-
102
- this.requests.push(now);
103
- return this.client.send(method, params);
104
- }
105
- }
106
- ```
107
-
108
- **3. Input Validation**
109
- ```javascript
110
- // Always validate input before sending to RPC
111
- function validatePrompt(message) {
112
- if (!message || typeof message !== 'string') {
113
- throw new Error('Message must be a non-empty string');
114
- }
115
- if (message.length > 100000) {
116
- throw new Error('Message exceeds maximum length');
117
- }
118
- return message;
119
- }
120
-
121
- const message = validatePrompt(userInput);
122
- await rpcClient.prompt(message);
123
- ```
124
-
125
- ---
126
-
127
- ## RPC Connection Handling
128
-
129
- ### Connection Lifecycle
130
-
131
- ```
132
- ┌──────────────────────────────────────────────────┐
133
- │ RPC Connection Lifecycle │
134
- ├──────────────────────────────────────────────────┤
135
- │ │
136
- │ 1. CONNECTING │
137
- │ ↓ │
138
- │ 2. HANDSHAKE (authenticate, check version) │
139
- │ ↓ │
140
- │ 3. CONNECTED (ready to send commands) │
141
- │ ↓ │
142
- │ 4. REQUEST LOOP (send/receive) │
143
- │ ↓ │
144
- │ 5. DISCONNECTING │
145
- │ ↓ │
146
- │ 6. DISCONNECTED │
147
- │ │
148
- │ ERROR: Can reconnect and replay pending reqs │
149
- │ │
150
- └──────────────────────────────────────────────────┘
151
- ```
152
-
153
- ### Reconnection Strategy
154
-
155
- **Exponential Backoff:**
156
- ```javascript
157
- function calculateBackoff(attempt) {
158
- const baseDelay = 100; // ms
159
- const maxDelay = 30000; // 30 seconds
160
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
161
- const jitter = Math.random() * delay * 0.1; // ±10% jitter
162
- return delay + jitter;
163
- }
164
-
165
- // Attempt sequence:
166
- // 1st: ~100ms, 2nd: ~200ms, 3rd: ~400ms, 4th: ~800ms, ...
167
- // Max: 30000ms (30s)
168
- ```
169
-
170
- **Implementation:**
171
- ```javascript
172
- class RobustRpcClient {
173
- async connect() {
174
- let attempt = 0;
175
- const maxAttempts = 10;
176
-
177
- while (attempt < maxAttempts) {
178
- try {
179
- await this._doConnect();
180
- this.reconnectAttempt = 0; // Reset on success
181
- return;
182
- } catch (error) {
183
- if (attempt < maxAttempts - 1) {
184
- const delay = calculateBackoff(attempt);
185
- console.log(`Reconnecting in ${delay}ms...`);
186
- await new Promise(r => setTimeout(r, delay));
187
- attempt++;
188
- } else {
189
- throw new Error(`Failed to connect after ${maxAttempts} attempts`);
190
- }
191
- }
192
- }
193
- }
194
- }
195
- ```
196
-
197
- ### Event Subscription
198
-
199
- **Subscribing to Events:**
200
- ```json
201
- {
202
- "id": "sub-1",
203
- "method": "subscribe",
204
- "params": {
205
- "events": ["agent_start", "message_start", "message_end", "tool_*"]
206
- }
207
- }
208
- ```
209
-
210
- **Response:**
211
- ```json
212
- {
213
- "id": "sub-1",
214
- "result": {
215
- "subscription_id": "sub-abc-123",
216
- "active": true
217
- }
218
- }
219
- ```
220
-
221
- **Receiving Events:**
222
- ```json
223
- {
224
- "method": "event",
225
- "params": {
226
- "subscription_id": "sub-abc-123",
227
- "type": "agent_start",
228
- "timestamp": 1677000000000
229
- }
230
- }
231
- ```
232
-
233
- ### Handling Disconnections
234
-
235
- ```javascript
236
- class ReconnectingClient {
237
- constructor(options) {
238
- this.options = options;
239
- this.socket = null;
240
- this.pendingRequests = new Map();
241
- this.isConnected = false;
242
- }
243
-
244
- async send(method, params) {
245
- // Queue request if not connected
246
- if (!this.isConnected) {
247
- return new Promise((resolve, reject) => {
248
- const requestId = uuid();
249
- this.pendingRequests.set(requestId, {
250
- method,
251
- params,
252
- resolve,
253
- reject,
254
- timestamp: Date.now()
255
- });
256
-
257
- // Timeout after 30 seconds
258
- setTimeout(() => {
259
- this.pendingRequests.delete(requestId);
260
- reject(new Error('Request timeout'));
261
- }, 30000);
262
- });
263
- }
264
-
265
- return this._sendRequest(method, params);
266
- }
267
-
268
- async onReconnect() {
269
- // Replay pending requests
270
- const pending = Array.from(this.pendingRequests.values());
271
- this.pendingRequests.clear();
272
-
273
- for (const req of pending) {
274
- try {
275
- const result = await this._sendRequest(req.method, req.params);
276
- req.resolve(result);
277
- } catch (error) {
278
- req.reject(error);
279
- }
280
- }
281
- }
282
-
283
- onDisconnect() {
284
- this.isConnected = false;
285
- // Pending requests will be replayed on reconnect
286
- }
287
- }
288
- ```
289
-
290
- ---
291
-
292
- ## RPC Client Examples
293
-
294
- ### Node.js Client Implementation
295
-
296
- **Complete Example:**
297
- ```javascript
298
- const net = require('net');
299
- const { v4: uuid } = require('uuid');
300
-
301
- class IndusagiRpcClient {
302
- constructor(options = {}) {
303
- this.socket = options.socket || '/var/run/indusagi.sock';
304
- this.timeout = options.timeout || 30000;
305
- this.connection = null;
306
- this.requestMap = new Map();
307
- this.eventHandlers = new Map();
308
- }
309
-
310
- async connect() {
311
- return new Promise((resolve, reject) => {
312
- this.connection = net.createConnection(this.socket, () => {
313
- this.connection.on('data', (data) => this._handleData(data));
314
- this.connection.on('error', (err) => this._handleError(err));
315
- this.connection.on('close', () => this._handleClose());
316
- resolve();
317
- });
318
-
319
- this.connection.on('error', reject);
320
-
321
- setTimeout(() => {
322
- if (!this.connection.connecting) return;
323
- reject(new Error('Connection timeout'));
324
- }, this.timeout);
325
- });
326
- }
327
-
328
- async prompt(message, options = {}) {
329
- return this._send('prompt', { message, ...options });
330
- }
331
-
332
- async setModel(model) {
333
- return this._send('model.set', { model });
334
- }
335
-
336
- async compact() {
337
- return this._send('compact', {});
338
- }
339
-
340
- async subscribe(events) {
341
- return this._send('subscribe', { events });
342
- }
343
-
344
- on(eventType, handler) {
345
- if (!this.eventHandlers.has(eventType)) {
346
- this.eventHandlers.set(eventType, []);
347
- }
348
- this.eventHandlers.get(eventType).push(handler);
349
- }
350
-
351
- _send(method, params) {
352
- return new Promise((resolve, reject) => {
353
- const id = uuid();
354
- const request = { jsonrpc: '2.0', id, method, params };
355
-
356
- const timeoutId = setTimeout(() => {
357
- this.requestMap.delete(id);
358
- reject(new Error(`Request timeout: ${method}`));
359
- }, this.timeout);
360
-
361
- this.requestMap.set(id, { resolve, reject, timeoutId });
362
-
363
- try {
364
- this.connection.write(JSON.stringify(request) + '\n');
365
- } catch (error) {
366
- this.requestMap.delete(id);
367
- clearTimeout(timeoutId);
368
- reject(error);
369
- }
370
- });
371
- }
372
-
373
- _handleData(data) {
374
- const lines = data.toString().split('\n');
375
- for (const line of lines) {
376
- if (!line.trim()) continue;
377
- try {
378
- const msg = JSON.parse(line);
379
- if (msg.id && this.requestMap.has(msg.id)) {
380
- const { resolve, reject, timeoutId } = this.requestMap.get(msg.id);
381
- this.requestMap.delete(msg.id);
382
- clearTimeout(timeoutId);
383
-
384
- if (msg.error) {
385
- reject(new Error(msg.error.message));
386
- } else {
387
- resolve(msg.result);
388
- }
389
- } else if (msg.method === 'event') {
390
- this._emitEvent(msg.params);
391
- }
392
- } catch (error) {
393
- console.error('Failed to parse RPC response:', error);
394
- }
395
- }
396
- }
397
-
398
- _emitEvent(event) {
399
- const handlers = this.eventHandlers.get(event.type) || [];
400
- for (const handler of handlers) {
401
- try {
402
- handler(event);
403
- } catch (error) {
404
- console.error('Error in event handler:', error);
405
- }
406
- }
407
- }
408
-
409
- _handleError(error) {
410
- console.error('RPC connection error:', error);
411
- // Implement reconnection logic
412
- }
413
-
414
- _handleClose() {
415
- console.log('RPC connection closed');
416
- // Clean up pending requests
417
- for (const { reject, timeoutId } of this.requestMap.values()) {
418
- clearTimeout(timeoutId);
419
- reject(new Error('Connection closed'));
420
- }
421
- this.requestMap.clear();
422
- }
423
-
424
- disconnect() {
425
- return new Promise((resolve) => {
426
- if (this.connection) {
427
- this.connection.end(resolve);
428
- } else {
429
- resolve();
430
- }
431
- });
432
- }
433
- }
434
-
435
- module.exports = IndusagiRpcClient;
436
- ```
437
-
438
- ### Usage Pattern
439
-
440
- ```javascript
441
- const IndusagiRpcClient = require('./rpc-client');
442
-
443
- async function main() {
444
- const client = new IndusagiRpcClient({
445
- socket: '/var/run/indusagi.sock'
446
- });
447
-
448
- try {
449
- // Connect
450
- await client.connect();
451
- console.log('Connected to indusagi RPC server');
452
-
453
- // Subscribe to events
454
- await client.subscribe(['agent_start', 'message_*', 'tool_*']);
455
-
456
- // Listen for events
457
- client.on('agent_start', (event) => {
458
- console.log('Agent started:', event);
459
- });
460
-
461
- client.on('message_start', (event) => {
462
- console.log('Message:', event.content?.slice(0, 50));
463
- });
464
-
465
- // Send prompt
466
- const result = await client.prompt('What is 2+2?');
467
- console.log('Result:', result);
468
-
469
- } finally {
470
- await client.disconnect();
471
- }
472
- }
473
-
474
- main().catch(console.error);
475
- ```
476
-
477
- ### Python Client Example
478
-
479
- ```python
480
- import socket
481
- import json
482
- import uuid
483
- from typing import Dict, Any, Optional
484
-
485
- class IndusagiRpcClient:
486
- def __init__(self, socket_path: str = '/var/run/indusagi.sock'):
487
- self.socket_path = socket_path
488
- self.socket = None
489
- self.request_map = {}
490
- self.event_handlers = {}
491
-
492
- def connect(self):
493
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
494
- self.socket.connect(self.socket_path)
495
-
496
- def prompt(self, message: str, **options) -> Dict[str, Any]:
497
- return self._send('prompt', {'message': message, **options})
498
-
499
- def set_model(self, model: str) -> Dict[str, Any]:
500
- return self._send('model.set', {'model': model})
501
-
502
- def compact(self) -> Dict[str, Any]:
503
- return self._send('compact', {})
504
-
505
- def _send(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
506
- request_id = str(uuid.uuid4())
507
- request = {
508
- 'jsonrpc': '2.0',
509
- 'id': request_id,
510
- 'method': method,
511
- 'params': params
512
- }
513
-
514
- self.socket.sendall((json.dumps(request) + '\n').encode())
515
-
516
- # Wait for response
517
- response_data = b''
518
- while True:
519
- data = self.socket.recv(4096)
520
- if not data:
521
- break
522
- response_data += data
523
- if b'\n' in response_data:
524
- break
525
-
526
- response = json.loads(response_data.decode().strip())
527
-
528
- if 'error' in response:
529
- raise Exception(response['error']['message'])
530
-
531
- return response.get('result')
532
-
533
- def disconnect(self):
534
- if self.socket:
535
- self.socket.close()
536
-
537
- # Usage
538
- client = IndusagiRpcClient()
539
- client.connect()
540
- result = client.prompt('What is 2+2?')
541
- print(result)
542
- client.disconnect()
543
- ```
544
-
545
- ---
546
-
547
- ## RPC Error Handling
548
-
549
- ### Common Error Scenarios
550
-
551
- **1. Authentication Failure**
552
- ```json
553
- {
554
- "jsonrpc": "2.0",
555
- "error": {
556
- "code": -32005,
557
- "message": "Authentication required",
558
- "data": {
559
- "expected": "valid token",
560
- "received": "missing or invalid"
561
- }
562
- }
563
- }
564
- ```
565
-
566
- **Handling:**
567
- ```javascript
568
- try {
569
- await client.prompt('test');
570
- } catch (error) {
571
- if (error.code === -32005) {
572
- console.log('Auth failed, check RPC_TOKEN');
573
- // Re-authenticate or refresh token
574
- }
575
- }
576
- ```
577
-
578
- **2. Model Unavailable**
579
- ```json
580
- {
581
- "code": -32003,
582
- "message": "Model gpt-4 is unavailable",
583
- "data": {
584
- "retriable": true,
585
- "retry_after": 60
586
- }
587
- }
588
- ```
589
-
590
- **Handling:**
591
- ```javascript
592
- async function promptWithRetry(message, maxAttempts = 3) {
593
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
594
- try {
595
- return await client.prompt(message);
596
- } catch (error) {
597
- if (error.code === -32003 && error.data?.retriable) {
598
- const delay = error.data.retry_after || (attempt * 10) * 1000;
599
- console.log(`Retrying in ${delay}ms...`);
600
- await new Promise(r => setTimeout(r, delay));
601
- } else {
602
- throw error;
603
- }
604
- }
605
- }
606
- }
607
- ```
608
-
609
- **3. Timeout**
610
- ```javascript
611
- async function promptWithTimeout(message, timeoutMs = 30000) {
612
- return Promise.race([
613
- client.prompt(message),
614
- new Promise((_, reject) =>
615
- setTimeout(() => reject(new Error('Timeout')), timeoutMs)
616
- )
617
- ]);
618
- }
619
- ```
620
-
621
- ---
622
-
623
- ## Advanced Patterns
624
-
625
- ### Pattern: Batch Processing
626
-
627
- ```javascript
628
- async function batchProcess(items) {
629
- const client = new IndusagiRpcClient();
630
- await client.connect();
631
-
632
- const results = [];
633
- for (const item of items) {
634
- const result = await client.prompt(`Process: ${item}`);
635
- results.push(result);
636
- }
637
-
638
- await client.disconnect();
639
- return results;
640
- }
641
- ```
642
-
643
- ### Pattern: Event-Driven Processing
644
-
645
- ```javascript
646
- async function processWithEvents() {
647
- const client = new IndusagiRpcClient();
648
- await client.connect();
649
-
650
- const messages = [];
651
- client.on('message_start', (e) => {
652
- messages.push({ role: e.role, content: '' });
653
- });
654
-
655
- client.on('message_chunk', (e) => {
656
- if (messages.length > 0) {
657
- messages[messages.length - 1].content += e.chunk;
658
- }
659
- });
660
-
661
- client.on('message_end', (e) => {
662
- console.log('Message complete');
663
- });
664
-
665
- await client.prompt('Generate a poem');
666
- await client.disconnect();
667
-
668
- return messages;
669
- }
670
- ```
671
-
672
- ### Pattern: Session Management
673
-
674
- ```javascript
675
- async function persistentSession() {
676
- const client = new IndusagiRpcClient();
677
- await client.connect();
678
-
679
- // Create session
680
- const session = await client.send('session.create', {
681
- name: 'my-session',
682
- model: 'gpt-4'
683
- });
684
-
685
- // Continue in session
686
- await client.send('prompt', {
687
- message: 'First question',
688
- session_id: session.id
689
- });
690
-
691
- await client.send('prompt', {
692
- message: 'Follow-up',
693
- session_id: session.id
694
- });
695
-
696
- // Export session
697
- const html = await client.send('session.export', {
698
- session_id: session.id,
699
- format: 'html'
700
- });
701
-
702
- fs.writeFileSync('session.html', html);
703
- await client.disconnect();
704
- }
705
- ```