@rivet-dev/pi 0.1.0 → 0.5.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/LICENSE +190 -0
- package/dist/actions.d.ts +188 -77
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +276 -103
- package/dist/actions.js.map +1 -1
- package/dist/actor-config.d.ts +32 -0
- package/dist/actor-config.d.ts.map +1 -0
- package/dist/actor-config.js +86 -0
- package/dist/actor-config.js.map +1 -0
- package/dist/actor-sqlite.d.ts +10 -0
- package/dist/actor-sqlite.d.ts.map +1 -0
- package/dist/actor-sqlite.js +32 -0
- package/dist/actor-sqlite.js.map +1 -0
- package/dist/actor-types.d.ts +34 -0
- package/dist/actor-types.d.ts.map +1 -0
- package/dist/actor-types.js +2 -0
- package/dist/actor-types.js.map +1 -0
- package/dist/actor.d.ts +27 -15
- package/dist/actor.d.ts.map +1 -1
- package/dist/actor.js +71 -78
- package/dist/actor.js.map +1 -1
- package/dist/credentials.d.ts +3 -4
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +11 -38
- package/dist/credentials.js.map +1 -1
- package/dist/drain.d.ts +12 -0
- package/dist/drain.d.ts.map +1 -0
- package/dist/drain.js +20 -0
- package/dist/drain.js.map +1 -0
- package/dist/durable.d.ts +24 -0
- package/dist/durable.d.ts.map +1 -0
- package/dist/durable.js +5 -0
- package/dist/durable.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +7 -33
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +4 -67
- package/dist/models.js.map +1 -1
- package/dist/runtime.d.ts +111 -43
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +444 -225
- package/dist/runtime.js.map +1 -1
- package/dist/storage.d.ts +27 -28
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +33 -55
- package/dist/storage.js.map +1 -1
- package/dist/tracing.d.ts +7 -4
- package/dist/tracing.d.ts.map +1 -1
- package/dist/tracing.js +11 -12
- package/dist/tracing.js.map +1 -1
- package/dist/watches.d.ts +77 -0
- package/dist/watches.d.ts.map +1 -0
- package/dist/watches.js +245 -0
- package/dist/watches.js.map +1 -0
- package/package.json +38 -15
- package/dist/sandbox.d.ts +0 -21
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/sandbox.js +0 -185
- package/dist/sandbox.js.map +0 -1
package/dist/watches.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
|
+
import { watchEvents, } from "@earendil-works/pi-durable";
|
|
3
|
+
import { deleteConnectionWatches, deleteWatch, loadWatches, saveWatch, } from "./storage.js";
|
|
4
|
+
export function createConnectionWatches() {
|
|
5
|
+
return { byConnection: new Map(), queue: Promise.resolve() };
|
|
6
|
+
}
|
|
7
|
+
function serially(watches, change) {
|
|
8
|
+
const run = watches.queue.then(change);
|
|
9
|
+
watches.queue = run.catch(() => { });
|
|
10
|
+
return run;
|
|
11
|
+
}
|
|
12
|
+
function watchKey(spec) {
|
|
13
|
+
switch (spec.kind) {
|
|
14
|
+
case "events":
|
|
15
|
+
case "view":
|
|
16
|
+
return `${spec.kind}:${spec.conversationId}`;
|
|
17
|
+
case "taskGraph":
|
|
18
|
+
return spec.kind;
|
|
19
|
+
case "doc":
|
|
20
|
+
return `doc:${spec.conversationId}:${spec.docKind}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Starts a watch for a connection, stored so it comes back after sleep, and
|
|
25
|
+
* returns the value it starts from. Watching the same thing again restarts
|
|
26
|
+
* it with a fresh value. `requireTarget` runs in the queue too, so a watch
|
|
27
|
+
* and an unwatch sent right after it take effect in the order they arrive.
|
|
28
|
+
*/
|
|
29
|
+
export async function watch(actor, watches, harness, documents, connId, spec, requireTarget) {
|
|
30
|
+
const key = watchKey(spec);
|
|
31
|
+
return serially(watches, async () => {
|
|
32
|
+
await requireTarget();
|
|
33
|
+
await saveWatch(actor.db, { connId, key, spec });
|
|
34
|
+
return attach(actor, watches, harness, documents, connId, key, spec, false);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export async function unwatch(actor, watches, connId, spec) {
|
|
38
|
+
const key = watchKey(spec);
|
|
39
|
+
await serially(watches, async () => {
|
|
40
|
+
const active = watches.byConnection.get(connId)?.get(key);
|
|
41
|
+
watches.byConnection.get(connId)?.delete(key);
|
|
42
|
+
await active?.stop();
|
|
43
|
+
await deleteWatch(actor.db, connId, key);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Ends every watch of a connection that left. */
|
|
47
|
+
export async function unwatchConnection(actor, watches, connId) {
|
|
48
|
+
await serially(watches, async () => {
|
|
49
|
+
const active = [...(watches.byConnection.get(connId)?.values() ?? [])];
|
|
50
|
+
watches.byConnection.delete(connId);
|
|
51
|
+
await Promise.all(active.map((watch) => watch.stop()));
|
|
52
|
+
await deleteConnectionWatches(actor.db, connId);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Ends every watch of this generation, without forgetting them. Called when the harness closes. */
|
|
56
|
+
export async function stopAllWatches(watches) {
|
|
57
|
+
await serially(watches, async () => {
|
|
58
|
+
const active = [...watches.byConnection.values()].flatMap((byKey) => [
|
|
59
|
+
...byKey.values(),
|
|
60
|
+
]);
|
|
61
|
+
watches.byConnection.clear();
|
|
62
|
+
await Promise.all(active.map((watch) => watch.stop()));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Restarts the stored watches of connections that are still open, after wake
|
|
67
|
+
* or a reopen. Each sends its whole value as a `seq` 0 frame. Watches of
|
|
68
|
+
* connections that are gone are forgotten.
|
|
69
|
+
*/
|
|
70
|
+
export async function reattachWatches(actor, watches, harness, documents) {
|
|
71
|
+
await serially(watches, async () => {
|
|
72
|
+
const open = new Set(actor.conns.keys());
|
|
73
|
+
for (const stored of await loadWatches(actor.db)) {
|
|
74
|
+
if (!open.has(stored.connId)) {
|
|
75
|
+
await deleteWatch(actor.db, stored.connId, stored.key);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await attach(actor, watches, harness, documents, stored.connId, stored.key, stored.spec, true);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
actor.log.warn({
|
|
83
|
+
msg: "pi durable could not reattach a watch after wake",
|
|
84
|
+
connId: stored.connId,
|
|
85
|
+
watch: stored.key,
|
|
86
|
+
error,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function attach(actor, watches, harness, documents, connId, key, spec, resend) {
|
|
93
|
+
const byKey = watches.byConnection.get(connId) ?? new Map();
|
|
94
|
+
watches.byConnection.set(connId, byKey);
|
|
95
|
+
await byKey.get(key)?.stop();
|
|
96
|
+
let seq = 0;
|
|
97
|
+
let active;
|
|
98
|
+
const send = (name, frame) => {
|
|
99
|
+
const conn = actor.conns.get(connId);
|
|
100
|
+
if (!conn) {
|
|
101
|
+
void active?.stop();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
conn.send(name, { ...frame, seq: seq++ });
|
|
105
|
+
};
|
|
106
|
+
const started = await start(actor.log, harness, documents, spec, send, resend);
|
|
107
|
+
active = started;
|
|
108
|
+
byKey.set(key, started);
|
|
109
|
+
if (!resend)
|
|
110
|
+
seq = 1;
|
|
111
|
+
return started.initial;
|
|
112
|
+
}
|
|
113
|
+
/** Starts the Pi watch. With `resend`, the initial value goes out as the first frame. */
|
|
114
|
+
async function start(log, harness, documents, spec, send, resend) {
|
|
115
|
+
const context = BACKGROUND_CONTEXT;
|
|
116
|
+
switch (spec.kind) {
|
|
117
|
+
case "events": {
|
|
118
|
+
const conversationId = spec.conversationId;
|
|
119
|
+
const stream = await watchEvents(harness, conversationId, context);
|
|
120
|
+
if (resend)
|
|
121
|
+
send("pi.events", {
|
|
122
|
+
conversationId,
|
|
123
|
+
events: [stream.snapshot],
|
|
124
|
+
});
|
|
125
|
+
stream.start(async (events) => send("pi.events", { conversationId, events }));
|
|
126
|
+
return {
|
|
127
|
+
initial: { snapshot: stream.snapshot },
|
|
128
|
+
stop: async () => void (await stream.stop()),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
case "view": {
|
|
132
|
+
const conversationId = spec.conversationId;
|
|
133
|
+
const conversation = await harness.conversation(conversationId, context);
|
|
134
|
+
if (!conversation)
|
|
135
|
+
throw new Error(`conversation ${conversationId} does not exist`);
|
|
136
|
+
const view = await conversation.watch(context);
|
|
137
|
+
if (resend)
|
|
138
|
+
send("pi.view", { conversationId, value: view.value });
|
|
139
|
+
view.start(async (_value, ops) => send("pi.view", { conversationId, ops }));
|
|
140
|
+
return {
|
|
141
|
+
initial: { value: view.value },
|
|
142
|
+
stop: async () => void (await view.stop()),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
case "taskGraph": {
|
|
146
|
+
const graph = await harness.watchTaskGraph(context);
|
|
147
|
+
if (resend)
|
|
148
|
+
send("pi.taskGraph", { value: graph.value });
|
|
149
|
+
graph.start(async (_value, ops) => send("pi.taskGraph", { ops }));
|
|
150
|
+
return {
|
|
151
|
+
initial: { value: graph.value },
|
|
152
|
+
stop: async () => void (await graph.stop()),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
case "doc":
|
|
156
|
+
return startDoc(log, harness, documents, spec, send, resend);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Pi's `watchDoc` never creates a document, and its watch ends when the
|
|
161
|
+
* document is retired. While the document does not exist, the watch attaches
|
|
162
|
+
* when a commit creates it, and its value is the next frame.
|
|
163
|
+
*/
|
|
164
|
+
async function startDoc(log, harness, documents, spec, send, resend) {
|
|
165
|
+
const token = documents.get(spec.docKind);
|
|
166
|
+
if (!token)
|
|
167
|
+
throw new Error(`document ${spec.docKind} is not listed in pi({ documents })`);
|
|
168
|
+
const { docKind: kind, conversationId } = spec;
|
|
169
|
+
const conversation = conversationId;
|
|
170
|
+
let docWatch;
|
|
171
|
+
let unsubscribe;
|
|
172
|
+
let stopped = false;
|
|
173
|
+
const follow = (handle, sendFirst) => {
|
|
174
|
+
docWatch = handle;
|
|
175
|
+
if (sendFirst)
|
|
176
|
+
send("pi.doc", { kind, conversationId, value: handle.value });
|
|
177
|
+
handle.start(async (value) => {
|
|
178
|
+
send("pi.doc", { kind, conversationId, value });
|
|
179
|
+
if (value !== null || stopped)
|
|
180
|
+
return;
|
|
181
|
+
// A retired document's watch ends, so wait for a commit that creates it again.
|
|
182
|
+
docWatch = undefined;
|
|
183
|
+
void handle.stop();
|
|
184
|
+
awaitCreation();
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
const awaitCreation = () => {
|
|
188
|
+
let attaching = false;
|
|
189
|
+
const attachCreated = async () => {
|
|
190
|
+
try {
|
|
191
|
+
const handle = await harness.watchDoc(token, conversation, BACKGROUND_CONTEXT);
|
|
192
|
+
if (stopped) {
|
|
193
|
+
await handle?.stop();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!handle) {
|
|
197
|
+
attaching = false;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
unsubscribe?.();
|
|
201
|
+
unsubscribe = undefined;
|
|
202
|
+
follow(handle, true);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
// The next write to the document tries again.
|
|
206
|
+
attaching = false;
|
|
207
|
+
log.warn({
|
|
208
|
+
msg: "pi durable could not attach a document watch",
|
|
209
|
+
kind,
|
|
210
|
+
conversationId,
|
|
211
|
+
error,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
unsubscribe = harness.subscribeCommits((publication) => {
|
|
216
|
+
const created = publication.changes.some((change) => change.type === "document" &&
|
|
217
|
+
change.record.kind === kind &&
|
|
218
|
+
change.conversationId === conversation &&
|
|
219
|
+
change.value !== null);
|
|
220
|
+
if (!created || attaching)
|
|
221
|
+
return;
|
|
222
|
+
attaching = true;
|
|
223
|
+
// Commit listeners must not call Pi Durable, so the watch attaches after the listener returns.
|
|
224
|
+
queueMicrotask(() => void attachCreated());
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
const existing = await harness.watchDoc(token, conversation, BACKGROUND_CONTEXT);
|
|
228
|
+
if (existing) {
|
|
229
|
+
follow(existing, resend);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
if (resend)
|
|
233
|
+
send("pi.doc", { kind, conversationId, value: null });
|
|
234
|
+
awaitCreation();
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
initial: { value: existing?.value },
|
|
238
|
+
stop: async () => {
|
|
239
|
+
stopped = true;
|
|
240
|
+
unsubscribe?.();
|
|
241
|
+
await docWatch?.stop();
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=watches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watches.js","sourceRoot":"","sources":["../src/watches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,EAUN,WAAW,GACX,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,uBAAuB,EACvB,WAAW,EACX,WAAW,EACX,SAAS,GAET,MAAM,cAAc,CAAC;AAuDtB,MAAM,UAAU,uBAAuB;IACtC,OAAO,EAAE,YAAY,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,QAAQ,CAChB,OAA0B,EAC1B,MAAwB;IAExB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC;AACZ,CAAC;AAID,SAAS,QAAQ,CAAC,IAAe;IAChC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC;QACd,KAAK,MAAM;YACV,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QAC9C,KAAK,WAAW;YACf,OAAO,IAAI,CAAC,IAAI,CAAC;QAClB,KAAK,KAAK;YACT,OAAO,OAAO,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAC1B,KAAgB,EAChB,OAA0B,EAC1B,OAAgB,EAChB,SAAoB,EACpB,MAAc,EACd,IAAe,EACf,aAAkC;IAElC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,aAAa,EAAE,CAAC;QACtB,MAAM,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,OAAO,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC5B,KAAgB,EAChB,OAA0B,EAC1B,MAAc,EACd,IAAe;IAEf,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC;QACrB,MAAM,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,KAAgB,EAChB,OAA0B,EAC1B,MAAc;IAEd,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,uBAAuB,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAA0B;IAE1B,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YACpE,GAAG,KAAK,CAAC,MAAM,EAAE;SACjB,CAAC,CAAC;QACH,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,KAAgB,EAChB,OAA0B,EAC1B,OAAgB,EAChB,SAAoB;IAEpB,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,MAAM,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,MAAM,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvD,SAAS;YACV,CAAC;YACD,IAAI,CAAC;gBACJ,MAAM,MAAM,CACX,KAAK,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,IAAI,EACX,IAAI,CACJ,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;oBACd,GAAG,EAAE,kDAAkD;oBACvD,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,MAAM,CAAC,GAAG;oBACjB,KAAK;iBACL,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,MAAM,CACpB,KAAgB,EAChB,OAA0B,EAC1B,OAAgB,EAChB,SAAoB,EACpB,MAAc,EACd,GAAW,EACX,IAAe,EACf,MAAe;IAEf,MAAM,KAAK,GACV,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAuB,CAAC;IACpE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IAE7B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,MAA+B,CAAC;IACpC,MAAM,IAAI,GAAG,CACZ,IAAO,EACP,KAAoC,EACnC,EAAE;QACH,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,KAAK,MAAM,EAAE,IAAI,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,KAAK,CAC1B,KAAK,CAAC,GAAG,EACT,OAAO,EACP,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,MAAM,CACN,CAAC;IACF,MAAM,GAAG,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM;QAAE,GAAG,GAAG,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC,OAAO,CAAC;AACxB,CAAC;AAOD,yFAAyF;AACzF,KAAK,UAAU,KAAK,CACnB,GAAqB,EACrB,OAAgB,EAChB,SAAoB,EACpB,IAAe,EACf,IAAU,EACV,MAAe;IAEf,MAAM,OAAO,GAAG,kBAAkB,CAAC;IACnC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,WAAW,CAC/B,OAAO,EACP,cAAgC,EAChC,OAAO,CACP,CAAC;YACF,IAAI,MAAM;gBACT,IAAI,CAAC,WAAW,EAAE;oBACjB,cAAc;oBACd,MAAM,EAAE,CAAC,MAAM,CAAC,QAAgC,CAAC;iBACjD,CAAC,CAAC;YACJ,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAC7B,IAAI,CAAC,WAAW,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAC7C,CAAC;YACF,OAAO;gBACN,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;gBACtC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;aAC5C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACb,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;YAC3C,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,YAAY,CAC9C,cAAgC,EAChC,OAAO,CACP,CAAC;YACF,IAAI,CAAC,YAAY;gBAChB,MAAM,IAAI,KAAK,CAAC,gBAAgB,cAAc,iBAAiB,CAAC,CAAC;YAClE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,MAAM;gBAAE,IAAI,CAAC,SAAS,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAChC,IAAI,CAAC,SAAS,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CACxC,CAAC;YACF,OAAO;gBACN,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;gBAC9B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1C,CAAC;QACH,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,MAAM;gBAAE,IAAI,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACzD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YAClE,OAAO;gBACN,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;gBAC/B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;aAC3C,CAAC;QACH,CAAC;QACD,KAAK,KAAK;YACT,OAAO,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,QAAQ,CACtB,GAAqB,EACrB,OAAgB,EAChB,SAAoB,EACpB,IAAyC,EACzC,IAAU,EACV,MAAe;IAEf,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QACT,MAAM,IAAI,KAAK,CACd,YAAY,IAAI,CAAC,OAAO,qCAAqC,CAC7D,CAAC;IACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAgC,CAAC;IACtD,IAAI,QAA8D,CAAC;IACnE,IAAI,WAAqC,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,MAAM,GAAG,CACd,MAAgD,EAChD,SAAkB,EACjB,EAAE;QACH,QAAQ,GAAG,MAAM,CAAC;QAClB,IAAI,SAAS;YACZ,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC5B,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;YAChD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO;gBAAE,OAAO;YACtC,+EAA+E;YAC/E,QAAQ,GAAG,SAAS,CAAC;YACrB,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;YACnB,aAAa,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,GAAG,EAAE;QAC1B,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;YAChC,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CACpC,KAAK,EACL,YAAY,EACZ,kBAAkB,CAClB,CAAC;gBACF,IAAI,OAAO,EAAE,CAAC;oBACb,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC;oBACrB,OAAO;gBACR,CAAC;gBACD,IAAI,CAAC,MAAM,EAAE,CAAC;oBACb,SAAS,GAAG,KAAK,CAAC;oBAClB,OAAO;gBACR,CAAC;gBACD,WAAW,EAAE,EAAE,CAAC;gBAChB,WAAW,GAAG,SAAS,CAAC;gBACxB,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACtB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,8CAA8C;gBAC9C,SAAS,GAAG,KAAK,CAAC;gBAClB,GAAG,CAAC,IAAI,CAAC;oBACR,GAAG,EAAE,8CAA8C;oBACnD,IAAI;oBACJ,cAAc;oBACd,KAAK;iBACL,CAAC,CAAC;YACJ,CAAC;QACF,CAAC,CAAC;QACF,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,EAAE;YACtD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CACvC,CAAC,MAAM,EAAE,EAAE,CACV,MAAM,CAAC,IAAI,KAAK,UAAU;gBAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI;gBAC3B,MAAM,CAAC,cAAc,KAAK,YAAY;gBACtC,MAAM,CAAC,KAAK,KAAK,IAAI,CACtB,CAAC;YACF,IAAI,CAAC,OAAO,IAAI,SAAS;gBAAE,OAAO;YAClC,SAAS,GAAG,IAAI,CAAC;YACjB,+FAA+F;YAC/F,cAAc,CAAC,GAAG,EAAE,CAAC,KAAK,aAAa,EAAE,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CACtC,KAAK,EACL,YAAY,EACZ,kBAAkB,CAClB,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC;SAAM,CAAC;QACP,IAAI,MAAM;YAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,aAAa,EAAE,CAAC;IACjB,CAAC;IACD,OAAO;QACN,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE;QACnC,IAAI,EAAE,KAAK,IAAI,EAAE;YAChB,OAAO,GAAG,IAAI,CAAC;YACf,WAAW,EAAE,EAAE,CAAC;YAChB,MAAM,QAAQ,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivet-dev/pi",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Run the Pi coding agent as a durable Rivet Actor",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/rivet-dev/agents.git",
|
|
9
|
+
"directory": "packages/pi"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://rivet.dev/agents/docs/pi",
|
|
12
|
+
"bugs": "https://github.com/rivet-dev/agents/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pi",
|
|
15
|
+
"coding-agent",
|
|
16
|
+
"agent",
|
|
17
|
+
"ai",
|
|
18
|
+
"rivet",
|
|
19
|
+
"rivetkit",
|
|
20
|
+
"actors",
|
|
21
|
+
"durable"
|
|
22
|
+
],
|
|
6
23
|
"publishConfig": {
|
|
7
24
|
"access": "public"
|
|
8
25
|
},
|
|
@@ -15,34 +32,40 @@
|
|
|
15
32
|
".": {
|
|
16
33
|
"types": "./dist/index.d.ts",
|
|
17
34
|
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./durable": {
|
|
37
|
+
"types": "./dist/durable.d.ts",
|
|
38
|
+
"default": "./dist/durable.js"
|
|
18
39
|
}
|
|
19
40
|
},
|
|
20
41
|
"main": "./dist/index.js",
|
|
21
42
|
"types": "./dist/index.d.ts",
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsc -p tsconfig.build.json",
|
|
24
|
-
"check-types": "tsc --noEmit",
|
|
25
|
-
"test": "vitest run"
|
|
26
|
-
},
|
|
27
43
|
"dependencies": {
|
|
28
|
-
"@earendil-works/pi-coding-agent": "1.0.
|
|
44
|
+
"@earendil-works/pi-coding-agent": "^1.0.4",
|
|
29
45
|
"@opentelemetry/api": "^1.1.0",
|
|
30
|
-
"@rivet-dev/sandbox-adapter": "0.1
|
|
31
|
-
|
|
46
|
+
"@rivet-dev/sandbox-adapter": "0.5.1"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@earendil-works/chord": "^1.0.0",
|
|
50
|
+
"@earendil-works/pi-durable": "^1.0.0",
|
|
51
|
+
"rivetkit": ">=2.3.23 <2.4.0"
|
|
32
52
|
},
|
|
33
53
|
"devDependencies": {
|
|
34
|
-
"@earendil-works/
|
|
54
|
+
"@earendil-works/chord": "^1.0.4",
|
|
55
|
+
"@earendil-works/pi-ai": "^1.0.4",
|
|
56
|
+
"@earendil-works/pi-durable": "^1.0.4",
|
|
35
57
|
"@opentelemetry/sdk-trace-node": "2.11.0",
|
|
36
58
|
"@types/node": "^24.0.0",
|
|
59
|
+
"rivetkit": "2.3.23",
|
|
37
60
|
"typescript": "^5.9.2",
|
|
38
61
|
"vitest": "^3.2.4"
|
|
39
62
|
},
|
|
40
63
|
"engines": {
|
|
41
64
|
"node": ">=22.19.0"
|
|
42
65
|
},
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc -p tsconfig.build.json",
|
|
68
|
+
"check-types": "tsc --noEmit",
|
|
69
|
+
"test": "vitest run"
|
|
47
70
|
}
|
|
48
|
-
}
|
|
71
|
+
}
|
package/dist/sandbox.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type BashOperations, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { Sandbox } from "@rivet-dev/sandbox-adapter";
|
|
3
|
-
/**
|
|
4
|
-
* Builds Pi's seven built-in coding tools with every file and shell operation
|
|
5
|
-
* routed into the sandbox. Passed as `customTools`, they replace the built-in
|
|
6
|
-
* tools of the same names.
|
|
7
|
-
*/
|
|
8
|
-
export declare function createSandboxTools(sandbox: Sandbox): ToolDefinition<any, any, any>[];
|
|
9
|
-
/**
|
|
10
|
-
* Command execution for Pi's bash tool and `executeBash`, inside the sandbox.
|
|
11
|
-
* Follows Pi's `BashOperations` contract: `timeout` is in seconds, and abort
|
|
12
|
-
* and timeout reject with `aborted` and `timeout:<seconds>`.
|
|
13
|
-
*
|
|
14
|
-
* Pi passes the actor host's environment as `options.env`. It is not
|
|
15
|
-
* forwarded, because it holds the host's provider keys; commands run with the
|
|
16
|
-
* sandbox's own environment.
|
|
17
|
-
*/
|
|
18
|
-
export declare function createSandboxBashOperations(sandbox: Sandbox): BashOperations;
|
|
19
|
-
/** Resolves `path` against `root` and rejects anything outside it. */
|
|
20
|
-
export declare function resolveSandboxPath(root: string, path: string): string;
|
|
21
|
-
//# sourceMappingURL=sandbox.d.ts.map
|
package/dist/sandbox.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,cAAc,EAUnB,KAAK,cAAc,EACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAO1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,OAAO,GACd,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAgHjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAgB5E;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrE"}
|
package/dist/sandbox.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { posix } from "node:path";
|
|
2
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
const MAX_FIND_RESULTS = 1_000;
|
|
4
|
-
const MAX_GREP_MATCHES = 1_000;
|
|
5
|
-
const MAX_GREP_OUTPUT_BYTES = 512 * 1024;
|
|
6
|
-
const IGNORED_DIRECTORIES = [".git", "node_modules"];
|
|
7
|
-
/**
|
|
8
|
-
* Builds Pi's seven built-in coding tools with every file and shell operation
|
|
9
|
-
* routed into the sandbox. Passed as `customTools`, they replace the built-in
|
|
10
|
-
* tools of the same names.
|
|
11
|
-
*/
|
|
12
|
-
export function createSandboxTools(sandbox) {
|
|
13
|
-
const root = normalizeRoot(sandbox.cwd);
|
|
14
|
-
const resolvePath = (path) => resolveSandboxPath(root, path);
|
|
15
|
-
const access = async (path) => {
|
|
16
|
-
const resolved = resolvePath(path);
|
|
17
|
-
if (!(await sandbox.exists(resolved))) {
|
|
18
|
-
throw new Error(`Path not found: ${resolved}`);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
const readFile = async (path) => Buffer.from(await sandbox.readFile(resolvePath(path)));
|
|
22
|
-
const writeFile = (path, content) => sandbox.writeFile(resolvePath(path), content);
|
|
23
|
-
const read = createReadToolDefinition(root, {
|
|
24
|
-
operations: {
|
|
25
|
-
access,
|
|
26
|
-
readFile,
|
|
27
|
-
detectImageMimeType: async (path) => imageMimeType(path),
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
const write = createWriteToolDefinition(root, {
|
|
31
|
-
operations: {
|
|
32
|
-
writeFile,
|
|
33
|
-
mkdir: (path) => sandbox.mkdir(resolvePath(path)),
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
const edit = createEditToolDefinition(root, {
|
|
37
|
-
operations: { access, readFile, writeFile },
|
|
38
|
-
});
|
|
39
|
-
const bash = createBashToolDefinition(root, {
|
|
40
|
-
operations: createSandboxBashOperations(sandbox),
|
|
41
|
-
});
|
|
42
|
-
const ls = createLsToolDefinition(root, {
|
|
43
|
-
operations: {
|
|
44
|
-
exists: (path) => sandbox.exists(resolvePath(path)),
|
|
45
|
-
readdir: (path) => sandbox.readdir(resolvePath(path)),
|
|
46
|
-
stat: async (path) => {
|
|
47
|
-
const stat = await sandbox.stat(resolvePath(path));
|
|
48
|
-
return { isDirectory: () => stat.isDirectory };
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
const find = createFindToolDefinition(root, {
|
|
53
|
-
operations: {
|
|
54
|
-
exists: (path) => sandbox.exists(resolvePath(path)),
|
|
55
|
-
glob: async (pattern, searchDirectory, options) => {
|
|
56
|
-
const searchRoot = resolvePath(searchDirectory);
|
|
57
|
-
const result = await sandbox.exec(`find . -type f ${IGNORED_DIRECTORIES.map((directory) => `-not -path ${shellQuote(`*/${directory}/*`)}`).join(" ")}`, { cwd: searchRoot });
|
|
58
|
-
if (result.exitCode !== 0) {
|
|
59
|
-
throw new Error(result.stderr.trim() || `find exited with ${result.exitCode}`);
|
|
60
|
-
}
|
|
61
|
-
return result.stdout
|
|
62
|
-
.split("\n")
|
|
63
|
-
.map(stripDotSlash)
|
|
64
|
-
.filter((path) => path.length > 0 && matchesToolGlob(path, pattern))
|
|
65
|
-
.slice(0, Math.min(options.limit, MAX_FIND_RESULTS));
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
const grepBase = createGrepToolDefinition(root);
|
|
70
|
-
const grep = {
|
|
71
|
-
...grepBase,
|
|
72
|
-
execute: async (_toolCallId, params, signal) => {
|
|
73
|
-
const searchPath = resolvePath(params.path ?? ".");
|
|
74
|
-
const limit = Math.min(Math.max(1, params.limit ?? 100), MAX_GREP_MATCHES);
|
|
75
|
-
const args = ["-r", "-n", "-I", "-H"];
|
|
76
|
-
if (params.ignoreCase)
|
|
77
|
-
args.push("-i");
|
|
78
|
-
if (params.literal)
|
|
79
|
-
args.push("-F");
|
|
80
|
-
if (params.glob)
|
|
81
|
-
args.push(`--include=${params.glob}`);
|
|
82
|
-
if (params.context && params.context > 0) {
|
|
83
|
-
args.push("-C", String(Math.floor(params.context)));
|
|
84
|
-
}
|
|
85
|
-
for (const directory of IGNORED_DIRECTORIES) {
|
|
86
|
-
args.push(`--exclude-dir=${directory}`);
|
|
87
|
-
}
|
|
88
|
-
args.push("-e", params.pattern, "--", posix.relative(root, searchPath) || ".");
|
|
89
|
-
const result = await sandbox.exec(`grep ${args.map(shellQuote).join(" ")}`, {
|
|
90
|
-
cwd: root,
|
|
91
|
-
signal,
|
|
92
|
-
});
|
|
93
|
-
if (result.exitCode !== 0 && result.exitCode !== 1) {
|
|
94
|
-
throw new Error(result.stderr.trim() || `grep exited with ${result.exitCode}`);
|
|
95
|
-
}
|
|
96
|
-
const lines = result.stdout.replace(/\n$/, "").split("\n");
|
|
97
|
-
const matched = lines[0] === "" ? [] : lines.map(stripDotSlash);
|
|
98
|
-
const selected = matched.slice(0, limit);
|
|
99
|
-
let output = selected.join("\n");
|
|
100
|
-
let linesTruncated = false;
|
|
101
|
-
if (Buffer.byteLength(output) > MAX_GREP_OUTPUT_BYTES) {
|
|
102
|
-
output = Buffer.from(output).subarray(0, MAX_GREP_OUTPUT_BYTES).toString();
|
|
103
|
-
linesTruncated = true;
|
|
104
|
-
}
|
|
105
|
-
const details = {};
|
|
106
|
-
if (matched.length > selected.length)
|
|
107
|
-
details.matchLimitReached = limit;
|
|
108
|
-
if (linesTruncated)
|
|
109
|
-
details.linesTruncated = true;
|
|
110
|
-
return {
|
|
111
|
-
content: [{ type: "text", text: output || "No matches found" }],
|
|
112
|
-
details: Object.keys(details).length > 0 ? details : undefined,
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
return [read, bash, edit, write, grep, find, ls];
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Command execution for Pi's bash tool and `executeBash`, inside the sandbox.
|
|
120
|
-
* Follows Pi's `BashOperations` contract: `timeout` is in seconds, and abort
|
|
121
|
-
* and timeout reject with `aborted` and `timeout:<seconds>`.
|
|
122
|
-
*
|
|
123
|
-
* Pi passes the actor host's environment as `options.env`. It is not
|
|
124
|
-
* forwarded, because it holds the host's provider keys; commands run with the
|
|
125
|
-
* sandbox's own environment.
|
|
126
|
-
*/
|
|
127
|
-
export function createSandboxBashOperations(sandbox) {
|
|
128
|
-
const root = normalizeRoot(sandbox.cwd);
|
|
129
|
-
return {
|
|
130
|
-
async exec(command, requestedCwd, options) {
|
|
131
|
-
const result = await sandbox.exec(command, {
|
|
132
|
-
cwd: resolveSandboxPath(root, requestedCwd),
|
|
133
|
-
timeoutMs: options.timeout === undefined ? undefined : options.timeout * 1000,
|
|
134
|
-
signal: options.signal,
|
|
135
|
-
onData: (chunk) => options.onData(Buffer.from(chunk)),
|
|
136
|
-
});
|
|
137
|
-
if (result.timedOut) {
|
|
138
|
-
throw new Error(`timeout:${options.timeout}`);
|
|
139
|
-
}
|
|
140
|
-
return { exitCode: result.exitCode };
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
/** Resolves `path` against `root` and rejects anything outside it. */
|
|
145
|
-
export function resolveSandboxPath(root, path) {
|
|
146
|
-
const normalizedRoot = normalizeRoot(root);
|
|
147
|
-
const resolved = posix.resolve(normalizedRoot, path);
|
|
148
|
-
const prefix = normalizedRoot === "/" ? "/" : `${normalizedRoot}/`;
|
|
149
|
-
if (resolved !== normalizedRoot && !resolved.startsWith(prefix)) {
|
|
150
|
-
throw new Error(`Path escapes the sandbox working directory: ${path}`);
|
|
151
|
-
}
|
|
152
|
-
return resolved;
|
|
153
|
-
}
|
|
154
|
-
function normalizeRoot(path) {
|
|
155
|
-
if (!posix.isAbsolute(path)) {
|
|
156
|
-
throw new Error(`sandbox cwd must be an absolute POSIX path, received ${path}`);
|
|
157
|
-
}
|
|
158
|
-
return posix.normalize(path);
|
|
159
|
-
}
|
|
160
|
-
/** Pi's find semantics: a pattern without a slash matches the file name at any depth. */
|
|
161
|
-
function matchesToolGlob(relativePath, pattern) {
|
|
162
|
-
if (pattern.includes("/")) {
|
|
163
|
-
return (posix.matchesGlob(relativePath, pattern) ||
|
|
164
|
-
posix.matchesGlob(relativePath, `**/${pattern}`));
|
|
165
|
-
}
|
|
166
|
-
return posix.matchesGlob(posix.basename(relativePath), pattern);
|
|
167
|
-
}
|
|
168
|
-
function stripDotSlash(path) {
|
|
169
|
-
return path.startsWith("./") ? path.slice(2) : path;
|
|
170
|
-
}
|
|
171
|
-
function imageMimeType(path) {
|
|
172
|
-
const extension = posix.extname(path).toLowerCase();
|
|
173
|
-
return {
|
|
174
|
-
".bmp": "image/bmp",
|
|
175
|
-
".gif": "image/gif",
|
|
176
|
-
".jpeg": "image/jpeg",
|
|
177
|
-
".jpg": "image/jpeg",
|
|
178
|
-
".png": "image/png",
|
|
179
|
-
".webp": "image/webp",
|
|
180
|
-
}[extension];
|
|
181
|
-
}
|
|
182
|
-
function shellQuote(value) {
|
|
183
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
184
|
-
}
|
|
185
|
-
//# sourceMappingURL=sandbox.js.map
|
package/dist/sandbox.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|