@kaoruisaac/pedelec 0.2.1 → 0.2.2
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 +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +33 -10
- package/dist/index.type-test.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Pedelec is a browser SDK and local bridge for applications that want to work wit
|
|
|
24
24
|
A web application can use Pedelec to:
|
|
25
25
|
|
|
26
26
|
- create an agent session on the user's machine;
|
|
27
|
+
- choose an application-managed workspace with the native directory picker;
|
|
27
28
|
- send user instructions and receive streamed assistant text;
|
|
28
29
|
- expose narrowly scoped browser-side tools to the agent;
|
|
29
30
|
- resume or end sessions; and
|
|
@@ -168,6 +169,21 @@ const session = await pedelec.createSession({
|
|
|
168
169
|
|
|
169
170
|
Pedelec creates missing `skills/`, `assets/`, `logs/`, and `tmp/` directories and preserves existing files. It never deletes an explicit workspace, and multiple active sessions may share it. Filesystem write conflicts are the application's responsibility. An explicit path must not overlap Pedelec's managed sandbox root.
|
|
170
171
|
|
|
172
|
+
## Selecting an Application Workspace
|
|
173
|
+
|
|
174
|
+
Use `directoryPicker()` when the application wants the user to choose a local workspace before creating a session:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
const path = await pedelec.directoryPicker();
|
|
178
|
+
if (path === null) return; // The user cancelled the native picker.
|
|
179
|
+
|
|
180
|
+
const session = await pedelec.createSession({
|
|
181
|
+
sandbox: { path },
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`directoryPicker()` returns `Promise<string | null>` and does not validate or initialize the selected directory. It requires the same origin approval as other sensitive Desktop APIs. The selected path remains application-managed; existing Core sandbox rules are applied later by `createSession()`, which may return `SANDBOX_PATH_INVALID` for an unacceptable path.
|
|
186
|
+
|
|
171
187
|
---
|
|
172
188
|
|
|
173
189
|
## Creating a Session
|
package/dist/index.d.ts
CHANGED
|
@@ -233,11 +233,13 @@ export declare class Pedelec {
|
|
|
233
233
|
constructor(options?: PedelecOptions);
|
|
234
234
|
createSession(): Promise<PedelecSession<string>>;
|
|
235
235
|
createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
236
|
+
directoryPicker(): Promise<string | null>;
|
|
236
237
|
listProviders(): Promise<ProviderInfo[]>;
|
|
237
238
|
getSettings(): Promise<PedelecSettings>;
|
|
238
239
|
getApprovalStatus(): Promise<ApprovalStatus>;
|
|
239
240
|
resumeSession(sessionId: string): Promise<PedelecSession<string>>;
|
|
240
241
|
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
242
|
+
private requestWithOptions;
|
|
241
243
|
unregisterSession(sessionId: string): void;
|
|
242
244
|
private resolveCreateSessionInput;
|
|
243
245
|
checkAvailability(): Promise<PedelecAvailability>;
|
package/dist/index.js
CHANGED
|
@@ -149,6 +149,10 @@ export class Pedelec {
|
|
|
149
149
|
}
|
|
150
150
|
return this.registerSession(result.sessionId, resolvedInput.provider, resolvedInput.effortLevel, resolvedInput.inlineToolHandlers);
|
|
151
151
|
}
|
|
152
|
+
async directoryPicker() {
|
|
153
|
+
const result = await this.requestWithOptions("pick_directory", {}, { timeoutMs: null });
|
|
154
|
+
return normalizeDirectoryPickerResponse(result);
|
|
155
|
+
}
|
|
152
156
|
async listProviders() {
|
|
153
157
|
return normalizeProviderInfoList(await this.request("list_providers"));
|
|
154
158
|
}
|
|
@@ -186,6 +190,9 @@ export class Pedelec {
|
|
|
186
190
|
return this.registerSession(result.sessionId, "", undefined);
|
|
187
191
|
}
|
|
188
192
|
request(type, payload = {}) {
|
|
193
|
+
return this.requestWithOptions(type, payload);
|
|
194
|
+
}
|
|
195
|
+
requestWithOptions(type, payload = {}, options = {}) {
|
|
189
196
|
let port;
|
|
190
197
|
try {
|
|
191
198
|
port = this.ensureExtensionPort();
|
|
@@ -196,13 +203,16 @@ export class Pedelec {
|
|
|
196
203
|
const requestId = `sdk_${Date.now()}_${this.nextRequestNumber++}`;
|
|
197
204
|
const message = { channelId: this.channelId, type, requestId, ...payload };
|
|
198
205
|
return new Promise((resolve, reject) => {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
const timeoutMs = options.timeoutMs === undefined ? this.bridgeTimeoutMs : options.timeoutMs;
|
|
207
|
+
const timeoutId = timeoutMs === null
|
|
208
|
+
? null
|
|
209
|
+
: setTimeout(() => {
|
|
210
|
+
this.pendingRequests.delete(requestId);
|
|
211
|
+
reject(makeError("SDK_BRIDGE_TIMEOUT", "Pedelec extension did not respond.", {
|
|
212
|
+
requestId,
|
|
213
|
+
type,
|
|
214
|
+
}));
|
|
215
|
+
}, timeoutMs);
|
|
206
216
|
this.pendingRequests.set(requestId, {
|
|
207
217
|
port,
|
|
208
218
|
resolve: (value) => resolve(value),
|
|
@@ -214,7 +224,8 @@ export class Pedelec {
|
|
|
214
224
|
}
|
|
215
225
|
catch (err) {
|
|
216
226
|
this.pendingRequests.delete(requestId);
|
|
217
|
-
|
|
227
|
+
if (timeoutId !== null)
|
|
228
|
+
clearTimeout(timeoutId);
|
|
218
229
|
const error = normalizeError(err, "EXTENSION_DISCONNECTED", "Pedelec extension disconnected.");
|
|
219
230
|
if (this.port === port) {
|
|
220
231
|
this.handleDisconnect(port, error);
|
|
@@ -398,7 +409,8 @@ export class Pedelec {
|
|
|
398
409
|
if (!pending)
|
|
399
410
|
return;
|
|
400
411
|
this.pendingRequests.delete(message.requestId);
|
|
401
|
-
|
|
412
|
+
if (pending.timeoutId !== null)
|
|
413
|
+
clearTimeout(pending.timeoutId);
|
|
402
414
|
if (message.ok) {
|
|
403
415
|
pending.resolve(message.result);
|
|
404
416
|
}
|
|
@@ -427,7 +439,8 @@ export class Pedelec {
|
|
|
427
439
|
if (pending.port !== port)
|
|
428
440
|
continue;
|
|
429
441
|
this.pendingRequests.delete(requestId);
|
|
430
|
-
|
|
442
|
+
if (pending.timeoutId !== null)
|
|
443
|
+
clearTimeout(pending.timeoutId);
|
|
431
444
|
pending.reject(error);
|
|
432
445
|
}
|
|
433
446
|
}
|
|
@@ -1013,6 +1026,16 @@ function normalizePedelecSettings(value) {
|
|
|
1013
1026
|
}
|
|
1014
1027
|
return { defaultProvider: value.defaultProvider };
|
|
1015
1028
|
}
|
|
1029
|
+
function normalizeDirectoryPickerResponse(value) {
|
|
1030
|
+
if (!isPlainObject(value) || !("path" in value)) {
|
|
1031
|
+
throw makeError("SDK_PROTOCOL_ERROR", "pick_directory response had an invalid shape");
|
|
1032
|
+
}
|
|
1033
|
+
const path = value.path;
|
|
1034
|
+
if (path !== null && (typeof path !== "string" || path.length === 0)) {
|
|
1035
|
+
throw makeError("SDK_PROTOCOL_ERROR", "pick_directory response had an invalid shape");
|
|
1036
|
+
}
|
|
1037
|
+
return path;
|
|
1038
|
+
}
|
|
1016
1039
|
function normalizeProviderInfoList(value) {
|
|
1017
1040
|
if (!Array.isArray(value))
|
|
1018
1041
|
throw makeError("SDK_PROTOCOL_ERROR", "list_providers response was not an array");
|
package/dist/index.type-test.js
CHANGED
|
@@ -136,6 +136,11 @@ async function availabilityHasPublicType() {
|
|
|
136
136
|
void availability;
|
|
137
137
|
void promise;
|
|
138
138
|
}
|
|
139
|
+
async function directoryPickerHasPublicType() {
|
|
140
|
+
const pedelec = new Pedelec();
|
|
141
|
+
const path = pedelec.directoryPicker();
|
|
142
|
+
void path;
|
|
143
|
+
}
|
|
139
144
|
function publicSecurityTypesAreRestricted() {
|
|
140
145
|
const status = {
|
|
141
146
|
installed: true,
|
|
@@ -154,6 +159,7 @@ void noSkillsFallsBackToString;
|
|
|
154
159
|
void effortLevelPublicTypeContract;
|
|
155
160
|
void listAssetsHasPublicTypes;
|
|
156
161
|
void availabilityHasPublicType;
|
|
162
|
+
void directoryPickerHasPublicType;
|
|
157
163
|
void publicSecurityTypesAreRestricted;
|
|
158
164
|
const baseContext = {
|
|
159
165
|
sessionId: "thread_1",
|