@factory/droid-sdk 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -47
- package/dist/index.cjs +1869 -1296
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6734 -4444
- package/dist/index.d.ts +6734 -4444
- package/dist/index.js +1815 -1298
- package/dist/index.js.map +1 -1
- package/docs/examples/init-metadata.md +79 -0
- package/docs/examples/list-sessions.md +64 -0
- package/docs/examples/multi-turn-session.md +77 -0
- package/docs/examples/one-shot-query.md +73 -0
- package/docs/examples/permission-handler.md +102 -0
- package/docs/examples/run.md +52 -0
- package/docs/examples/spec-mode.md +101 -0
- package/docs/examples/tool-controls.md +90 -0
- package/docs/typescript-sdk-reference.md +372 -0
- package/examples/.gitkeep +1 -0
- package/examples/abort-session-stream.ts +67 -0
- package/examples/droid-dev-structured-output.ts +44 -0
- package/examples/fork-session.ts +42 -0
- package/examples/init-metadata.ts +76 -0
- package/examples/interrupt-session.ts +67 -0
- package/examples/list-sessions.ts +65 -0
- package/examples/multi-turn-session.ts +67 -0
- package/examples/permission-handler.ts +98 -0
- package/examples/result-metadata.ts +62 -0
- package/examples/run.ts +38 -0
- package/examples/sdk-mcp-tool.ts +38 -0
- package/examples/simple-query.ts +53 -0
- package/examples/spec-mode-new-session.ts +121 -0
- package/examples/spec-mode-same-session.ts +133 -0
- package/examples/structured-output.ts +53 -0
- package/examples/test-compact.ts +60 -0
- package/examples/test-rewind.ts +155 -0
- package/examples/tool-controls.ts +69 -0
- package/package.json +26 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @factory/droid-sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for the [Factory](https://factory.ai) Droid CLI. Provides a high-level API for interacting with Droid as a subprocess, with streaming message support, multi-turn sessions, and tool permission handling.
|
|
3
|
+
TypeScript SDK for the [Factory](https://factory.ai) Droid CLI. Provides a high-level API for interacting with Droid as a subprocess, with streaming message support, multi-turn sessions, spec mode, tool controls, initialization metadata, session forking, session discovery, and tool permission handling.
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
@@ -18,19 +18,19 @@ npm install @factory/droid-sdk
|
|
|
18
18
|
Send a one-shot prompt and stream the response:
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
|
-
import { query } from
|
|
21
|
+
import { DroidMessageType, query } from '@factory/droid-sdk';
|
|
22
22
|
|
|
23
23
|
const stream = query({
|
|
24
|
-
prompt:
|
|
25
|
-
cwd:
|
|
24
|
+
prompt: 'What files are in the current directory?',
|
|
25
|
+
cwd: '/my/project',
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
for await (const msg of stream) {
|
|
29
|
-
if (msg.type ===
|
|
29
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
30
30
|
process.stdout.write(msg.text);
|
|
31
31
|
}
|
|
32
|
-
if (msg.type ===
|
|
33
|
-
console.log(
|
|
32
|
+
if (msg.type === DroidMessageType.TurnComplete) {
|
|
33
|
+
console.log('\nDone!');
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
```
|
|
@@ -40,19 +40,19 @@ for await (const msg of stream) {
|
|
|
40
40
|
Use `createSession()` for persistent conversations with multiple turns:
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
-
import { createSession } from
|
|
43
|
+
import { createSession, DroidMessageType } from '@factory/droid-sdk';
|
|
44
44
|
|
|
45
|
-
const session = await createSession({ cwd:
|
|
45
|
+
const session = await createSession({ cwd: '/my/project' });
|
|
46
46
|
|
|
47
47
|
// Streaming turn
|
|
48
|
-
for await (const msg of session.stream(
|
|
49
|
-
if (msg.type ===
|
|
48
|
+
for await (const msg of session.stream('List all TypeScript files')) {
|
|
49
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
50
50
|
process.stdout.write(msg.text);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Non-streaming turn
|
|
55
|
-
const result = await session.send(
|
|
55
|
+
const result = await session.send('Summarize the project');
|
|
56
56
|
console.log(result.text);
|
|
57
57
|
|
|
58
58
|
await session.close();
|
|
@@ -61,32 +61,186 @@ await session.close();
|
|
|
61
61
|
Resume an existing session by ID:
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
|
-
import { resumeSession } from
|
|
64
|
+
import { resumeSession } from '@factory/droid-sdk';
|
|
65
65
|
|
|
66
|
-
const session = await resumeSession(
|
|
67
|
-
const result = await session.send(
|
|
66
|
+
const session = await resumeSession('session-id-here');
|
|
67
|
+
const result = await session.send('Continue where we left off');
|
|
68
68
|
console.log(result.text);
|
|
69
69
|
await session.close();
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
The returned `DroidSession` also exposes `session.initResult`, which contains the raw `initialize_session` or `load_session` result returned by the JSON-RPC server.
|
|
73
|
+
|
|
74
|
+
## Initialization Metadata
|
|
75
|
+
|
|
76
|
+
Inspect the raw initialization metadata from `query()`, `createSession()`, and `resumeSession()`:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { createSession, query, resumeSession } from '@factory/droid-sdk';
|
|
80
|
+
|
|
81
|
+
const stream = query({
|
|
82
|
+
prompt: 'Reply with "ready" and nothing else.',
|
|
83
|
+
cwd: '/my/project',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log(stream.sessionId); // null before initialization
|
|
87
|
+
console.log(stream.initResult); // null before initialization
|
|
88
|
+
|
|
89
|
+
const initialized = await stream.initialized;
|
|
90
|
+
console.log(initialized.sessionId);
|
|
91
|
+
console.log(initialized.settings.modelId);
|
|
92
|
+
stream.abort();
|
|
93
|
+
|
|
94
|
+
const session = await createSession({ cwd: '/my/project' });
|
|
95
|
+
console.log(session.initResult.settings.modelId);
|
|
96
|
+
|
|
97
|
+
const resumed = await resumeSession(session.sessionId, { cwd: '/my/project' });
|
|
98
|
+
console.log(resumed.initResult.cwd);
|
|
99
|
+
|
|
100
|
+
await resumed.close();
|
|
101
|
+
await session.close();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Spec Mode
|
|
105
|
+
|
|
106
|
+
Start a session directly in spec mode, or enter spec mode later on an existing session:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import {
|
|
110
|
+
createSession,
|
|
111
|
+
DroidInteractionMode,
|
|
112
|
+
ReasoningEffort,
|
|
113
|
+
} from '@factory/droid-sdk';
|
|
114
|
+
|
|
115
|
+
const session = await createSession({
|
|
116
|
+
cwd: '/my/project',
|
|
117
|
+
interactionMode: DroidInteractionMode.Spec,
|
|
118
|
+
specModeReasoningEffort: ReasoningEffort.High,
|
|
119
|
+
specModeModelId: 'claude-sonnet-4-20250514',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const plan = await session.send('Draft a plan for adding integration tests');
|
|
123
|
+
console.log(plan.text);
|
|
124
|
+
|
|
125
|
+
await session.enterSpecMode({
|
|
126
|
+
specModeReasoningEffort: ReasoningEffort.High,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await session.close();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
When handling spec-mode approval, you can approve implementation in the same session with `ToolConfirmationOutcome.ProceedOnce`, or hand off to a fresh session with `ToolConfirmationOutcome.ProceedNewSessionHigh`.
|
|
133
|
+
|
|
134
|
+
## Tool Controls
|
|
135
|
+
|
|
136
|
+
Control which exec tools are available at session start, inspect the current tool catalog, and update tool overrides later:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { createSession } from '@factory/droid-sdk';
|
|
140
|
+
|
|
141
|
+
const session = await createSession({
|
|
142
|
+
cwd: '/my/project',
|
|
143
|
+
enabledToolIds: ['Read'],
|
|
144
|
+
disabledToolIds: ['Execute'],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const { tools } = await session.listTools();
|
|
148
|
+
console.log(
|
|
149
|
+
tools.map((tool) => ({
|
|
150
|
+
id: tool.llmId,
|
|
151
|
+
allowed: tool.currentlyAllowed,
|
|
152
|
+
}))
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
await session.updateSettings({
|
|
156
|
+
disabledToolIds: ['Read', 'Execute'],
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await session.close();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Forking Sessions
|
|
163
|
+
|
|
164
|
+
Fork the current server-side session and continue from the new session ID:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { createSession, resumeSession } from '@factory/droid-sdk';
|
|
168
|
+
|
|
169
|
+
const session = await createSession({ cwd: '/my/project' });
|
|
170
|
+
|
|
171
|
+
await session.send('Remember this phrase: mango sunrise');
|
|
172
|
+
|
|
173
|
+
const { newSessionId } = await session.forkSession();
|
|
174
|
+
const fork = await resumeSession(newSessionId, { cwd: '/my/project' });
|
|
175
|
+
|
|
176
|
+
const result = await fork.send('What phrase did I ask you to remember?');
|
|
177
|
+
console.log(result.text);
|
|
178
|
+
|
|
179
|
+
await fork.close();
|
|
180
|
+
await session.close();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Listing Sessions
|
|
184
|
+
|
|
185
|
+
Discover droid sessions saved on disk (mirrors the CLI's `/sessions` command). Reads `~/.factory/sessions/` directly — no droid process is spawned, so this works even when no session is running:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { listSessions } from '@factory/droid-sdk';
|
|
189
|
+
|
|
190
|
+
// Sessions for the current project (cwd defaults to process.cwd())
|
|
191
|
+
const current = await listSessions();
|
|
192
|
+
|
|
193
|
+
// 10 most recent sessions in the current project
|
|
194
|
+
const recent = await listSessions({ numSessions: 10 });
|
|
195
|
+
|
|
196
|
+
// Every session on disk, most recent first
|
|
197
|
+
const all = await listSessions({ fetchOutsideCWD: true });
|
|
198
|
+
|
|
199
|
+
// 10 most recent sessions across all projects
|
|
200
|
+
const recentAcrossProjects = await listSessions({
|
|
201
|
+
fetchOutsideCWD: true,
|
|
202
|
+
numSessions: 10,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Sessions for a specific other project
|
|
206
|
+
const other = await listSessions({ cwd: '/Users/me/other-repo' });
|
|
207
|
+
|
|
208
|
+
for (const s of current) {
|
|
209
|
+
console.log(`[${s.id}] ${s.title} (${s.messageCount} msgs)`);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Each `SessionMetadata` record includes `id`, `title`, `sessionTitle`, `owner`, `messageCount`, `modifiedTime`, `createdTime`, `isFavorite`, `cwd`, `decompSessionType`, and `decompMissionId`. Archived sessions (those with an `archivedAt` in their settings file) are excluded automatically. Results are sorted by `modifiedTime` descending.
|
|
214
|
+
|
|
215
|
+
`ListSessionsOptions`:
|
|
216
|
+
|
|
217
|
+
- **`cwd`** — working directory to scope the listing to (default `process.cwd()`). Ignored when `fetchOutsideCWD` is `true`.
|
|
218
|
+
- **`fetchOutsideCWD`** — return sessions from every working directory on disk (default `false`)
|
|
219
|
+
- **`numSessions`** — cap on total sessions returned
|
|
220
|
+
- **`sessionsDir`** — override the sessions root (default `~/.factory/sessions/`)
|
|
221
|
+
|
|
72
222
|
## Permission Handling
|
|
73
223
|
|
|
74
224
|
Handle tool confirmation requests with a custom permission handler:
|
|
75
225
|
|
|
76
226
|
```ts
|
|
77
|
-
import {
|
|
227
|
+
import {
|
|
228
|
+
DroidMessageType,
|
|
229
|
+
query,
|
|
230
|
+
ToolConfirmationOutcome,
|
|
231
|
+
} from '@factory/droid-sdk';
|
|
78
232
|
|
|
79
233
|
const stream = query({
|
|
80
|
-
prompt:
|
|
81
|
-
cwd:
|
|
234
|
+
prompt: 'Create a hello.txt file',
|
|
235
|
+
cwd: '/my/project',
|
|
82
236
|
permissionHandler(params) {
|
|
83
|
-
console.log(
|
|
237
|
+
console.log('Tool permission requested:', params);
|
|
84
238
|
return ToolConfirmationOutcome.ProceedOnce;
|
|
85
239
|
},
|
|
86
240
|
});
|
|
87
241
|
|
|
88
242
|
for await (const msg of stream) {
|
|
89
|
-
if (msg.type ===
|
|
243
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
90
244
|
process.stdout.write(msg.text);
|
|
91
245
|
}
|
|
92
246
|
}
|
|
@@ -96,11 +250,12 @@ for await (const msg of stream) {
|
|
|
96
250
|
|
|
97
251
|
### Top-Level Functions
|
|
98
252
|
|
|
99
|
-
| Function
|
|
100
|
-
|
|
101
|
-
| `query(options)`
|
|
102
|
-
| `createSession(options?)`
|
|
103
|
-
| `resumeSession(id, options?)` | Resume an existing session → `DroidSession`
|
|
253
|
+
| Function | Description |
|
|
254
|
+
| ----------------------------- | ---------------------------------------------------------------- |
|
|
255
|
+
| `query(options)` | One-shot prompt → async generator of `DroidMessage` events |
|
|
256
|
+
| `createSession(options?)` | Create a new multi-turn session → `DroidSession` |
|
|
257
|
+
| `resumeSession(id, options?)` | Resume an existing session → `DroidSession` |
|
|
258
|
+
| `listSessions(options?)` | List droid sessions saved on disk → `Promise<SessionMetadata[]>` |
|
|
104
259
|
|
|
105
260
|
### `query(options): DroidQuery`
|
|
106
261
|
|
|
@@ -109,6 +264,10 @@ Returns an async generator that yields `DroidMessage` events. The returned `Droi
|
|
|
109
264
|
- **`interrupt()`** — gracefully interrupt the agent's current turn
|
|
110
265
|
- **`abort()`** — forcefully kill the subprocess
|
|
111
266
|
- **`sessionId`** — the session ID (available after initialization)
|
|
267
|
+
- **`initResult`** — cached `initialize_session` result, or `null` before initialization
|
|
268
|
+
- **`initialized`** — promise that resolves with the `initialize_session` result
|
|
269
|
+
|
|
270
|
+
`query(options)` also accepts an `abortSignal` for external cancellation.
|
|
112
271
|
|
|
113
272
|
### `DroidSession`
|
|
114
273
|
|
|
@@ -119,8 +278,14 @@ Returned by `createSession()` and `resumeSession()`. Key methods:
|
|
|
119
278
|
- **`interrupt()`** — interrupt the current turn
|
|
120
279
|
- **`close()`** — close the session and release resources
|
|
121
280
|
- **`updateSettings(params)`** — update model, autonomy level, etc.
|
|
122
|
-
- **`
|
|
281
|
+
- **`enterSpecMode(params?)`** — switch the current session into spec mode
|
|
282
|
+
- **`forkSession()`** — create a forked server-side session and return its new session ID
|
|
283
|
+
- **`addMcpServer(params)`** / **`removeMcpServer(params)`** / **`toggleMcpServer(params)`** — manage MCP servers
|
|
284
|
+
- **`listMcpServers()`** / **`listMcpTools()`** / **`authenticateMcpServer(params)`** — inspect and authenticate MCP servers
|
|
285
|
+
- **`listTools(params?)`** — inspect the exec tool catalog and current allow/deny state
|
|
286
|
+
- **`renameSession(params)`** — rename the current session
|
|
123
287
|
- **`sessionId`** — the session ID
|
|
288
|
+
- **`initResult`** — cached `initialize_session` or `load_session` result
|
|
124
289
|
|
|
125
290
|
### `DroidResult`
|
|
126
291
|
|
|
@@ -134,18 +299,30 @@ Returned by `session.send()`:
|
|
|
134
299
|
|
|
135
300
|
All messages have a discriminated `type` field:
|
|
136
301
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
|
146
|
-
|
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
302
|
+
```ts
|
|
303
|
+
import { DroidMessageType } from '@factory/droid-sdk';
|
|
304
|
+
|
|
305
|
+
if (msg.type === DroidMessageType.AssistantTextDelta) {
|
|
306
|
+
process.stdout.write(msg.text);
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
| Type | Description |
|
|
311
|
+
| ----------------------- | --------------------------------------- |
|
|
312
|
+
| `assistant_text_delta` | Streaming text token from the assistant |
|
|
313
|
+
| `thinking_text_delta` | Streaming reasoning/thinking token |
|
|
314
|
+
| `tool_use` | Tool invocation by the assistant |
|
|
315
|
+
| `tool_result` | Result from a tool execution |
|
|
316
|
+
| `tool_progress` | Progress update during tool execution |
|
|
317
|
+
| `working_state_changed` | Agent working state transition |
|
|
318
|
+
| `token_usage_update` | Updated token usage counters |
|
|
319
|
+
| `create_message` | Full assistant message created |
|
|
320
|
+
| `turn_complete` | Sentinel: agent turn finished |
|
|
321
|
+
| `session_title_updated` | Session title changed |
|
|
322
|
+
| `mcp_status_changed` | MCP server status changed |
|
|
323
|
+
| `mcp_auth_required` | MCP authentication required |
|
|
324
|
+
| `mcp_auth_completed` | MCP authentication completed |
|
|
325
|
+
| `error` | Error event from the process |
|
|
149
326
|
|
|
150
327
|
### Options
|
|
151
328
|
|
|
@@ -155,26 +332,33 @@ All messages have a discriminated `type` field:
|
|
|
155
332
|
- **`cwd`** — working directory for the session
|
|
156
333
|
- **`modelId`** — LLM model identifier
|
|
157
334
|
- **`autonomyLevel`** — `AutonomyLevel` enum value
|
|
335
|
+
- **`interactionMode`** — `DroidInteractionMode` enum value
|
|
158
336
|
- **`reasoningEffort`** — `ReasoningEffort` enum value
|
|
337
|
+
- **`specModeModelId`** — override model used in spec mode
|
|
338
|
+
- **`specModeReasoningEffort`** — override reasoning level used in spec mode
|
|
339
|
+
- **`mcpServers`** — initial MCP server configurations, including SDK-backed MCP servers from `createSdkMcpServer()`
|
|
340
|
+
- **`enabledToolIds`** — explicit exec tool allowlist
|
|
341
|
+
- **`disabledToolIds`** — explicit exec tool denylist
|
|
159
342
|
- **`permissionHandler`** — callback for tool confirmations
|
|
160
343
|
- **`askUserHandler`** — callback for interactive questions
|
|
344
|
+
- **`abortSignal`** — standard `AbortSignal` for cancellation
|
|
161
345
|
- **`execPath`** — path to `droid` executable (default: `"droid"`)
|
|
162
346
|
- **`transport`** — provide a custom transport instead of spawning a process
|
|
163
347
|
|
|
164
348
|
### `DroidClient`
|
|
165
349
|
|
|
166
|
-
Low-level JSON-RPC client for advanced use. Provides typed methods for
|
|
350
|
+
Low-level JSON-RPC client for advanced use. Provides typed methods for the underlying protocol operations, including `listTools()` and `renameSession()`. Most users should prefer `query()` and `createSession()`.
|
|
167
351
|
|
|
168
352
|
### Error Types
|
|
169
353
|
|
|
170
|
-
| Error
|
|
171
|
-
|
|
172
|
-
| `ConnectionError`
|
|
173
|
-
| `ProtocolError`
|
|
174
|
-
| `SessionError`
|
|
175
|
-
| `SessionNotFoundError` | Session ID not found
|
|
176
|
-
| `TimeoutError`
|
|
177
|
-
| `ProcessExitError`
|
|
354
|
+
| Error | Description |
|
|
355
|
+
| ---------------------- | -------------------------------------- |
|
|
356
|
+
| `ConnectionError` | Failed to connect to the droid process |
|
|
357
|
+
| `ProtocolError` | JSON-RPC protocol error |
|
|
358
|
+
| `SessionError` | Base session error |
|
|
359
|
+
| `SessionNotFoundError` | Session ID not found |
|
|
360
|
+
| `TimeoutError` | Request timed out |
|
|
361
|
+
| `ProcessExitError` | Droid subprocess exited unexpectedly |
|
|
178
362
|
|
|
179
363
|
## Examples
|
|
180
364
|
|
|
@@ -182,7 +366,15 @@ See the [`examples/`](./examples) directory for runnable examples:
|
|
|
182
366
|
|
|
183
367
|
- **[`simple-query.ts`](./examples/simple-query.ts)** — one-shot query with streaming output
|
|
184
368
|
- **[`multi-turn-session.ts`](./examples/multi-turn-session.ts)** — multi-turn session lifecycle
|
|
369
|
+
- **[`abort-session-stream.ts`](./examples/abort-session-stream.ts)** — cancel an in-flight streaming session turn with `AbortSignal`
|
|
370
|
+
- **[`init-metadata.ts`](./examples/init-metadata.ts)** — read initialization and load metadata from query/session APIs
|
|
185
371
|
- **[`permission-handler.ts`](./examples/permission-handler.ts)** — custom permission handling
|
|
372
|
+
- **[`spec-mode-same-session.ts`](./examples/spec-mode-same-session.ts)** — approve a spec and continue in the same session
|
|
373
|
+
- **[`spec-mode-new-session.ts`](./examples/spec-mode-new-session.ts)** — approve a spec and hand off implementation to a new session
|
|
374
|
+
- **[`tool-controls.ts`](./examples/tool-controls.ts)** — configure allow/deny lists and inspect tool availability
|
|
375
|
+
- **[`sdk-mcp-tool.ts`](./examples/sdk-mcp-tool.ts)** — expose SDK-defined tools to Droid through MCP
|
|
376
|
+
- **[`fork-session.ts`](./examples/fork-session.ts)** — fork a session and continue from the new session ID
|
|
377
|
+
- **[`list-sessions.ts`](./examples/list-sessions.ts)** — discover droid sessions saved on disk
|
|
186
378
|
|
|
187
379
|
## License
|
|
188
380
|
|