@pragma-sh/sdk 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,1634 @@
1
+ var import_node_module = require("node:module");
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+
40
+ // src/index.ts
41
+ var exports_src = {};
42
+ __export(exports_src, {
43
+ runtimeAgentId: () => runtimeAgentId,
44
+ reportStopped: () => reportStopped,
45
+ reportStarted: () => reportStarted,
46
+ reportSessionName: () => reportSessionName,
47
+ reportMessage: () => reportMessage,
48
+ reportCleared: () => reportCleared,
49
+ reportAttention: () => reportAttention,
50
+ readEnv: () => readEnv,
51
+ hasPragmaEnvironment: () => hasPragmaEnvironment,
52
+ bytesToBase64: () => bytesToBase64,
53
+ base64ToBytes: () => base64ToBytes,
54
+ awaitAgentDecision: () => awaitAgentDecision,
55
+ awaitAgentAnswer: () => awaitAgentAnswer,
56
+ WorkspaceClient: () => WorkspaceClient,
57
+ WhiteboardsClient: () => WhiteboardsClient,
58
+ ThemeClient: () => ThemeClient,
59
+ ScratchpadsClient: () => ScratchpadsClient,
60
+ PushClient: () => PushClient,
61
+ PragmaTransportError: () => PragmaTransportError,
62
+ PragmaGatewayError: () => PragmaGatewayError,
63
+ PragmaClient: () => PragmaClient,
64
+ PRAGMA_ENV_KEYS: () => PRAGMA_ENV_KEYS,
65
+ HealthClient: () => HealthClient,
66
+ FanoutsClient: () => FanoutsClient,
67
+ AssetsClient: () => AssetsClient
68
+ });
69
+ module.exports = __toCommonJS(exports_src);
70
+
71
+ // src/env.ts
72
+ var PRAGMA_ENV_KEYS = {
73
+ gatewayUrl: "PRAGMA_GATEWAY_URL",
74
+ gatewayToken: "PRAGMA_GATEWAY_TOKEN",
75
+ tabId: "PRAGMA_TAB_ID",
76
+ worktreeId: "PRAGMA_WORKTREE_ID"
77
+ };
78
+ function readEnv(name, env) {
79
+ const globalProcess = globalThis.process;
80
+ return env?.[name] ?? globalProcess?.env?.[name];
81
+ }
82
+ function hasPragmaEnvironment(env) {
83
+ return Boolean(readEnv(PRAGMA_ENV_KEYS.gatewayUrl, env) && readEnv(PRAGMA_ENV_KEYS.gatewayToken, env) && readEnv(PRAGMA_ENV_KEYS.tabId, env) && readEnv(PRAGMA_ENV_KEYS.worktreeId, env));
84
+ }
85
+
86
+ // src/routes.ts
87
+ var routes = {
88
+ health: "/v1/health",
89
+ version: "/v1/version",
90
+ rpc: (method) => `/v1/rpc/${encodeURIComponent(method)}`,
91
+ sessions: "/v1/sessions",
92
+ sessionEvents: (id) => `/v1/sessions/${encodeURIComponent(id)}/events`,
93
+ sessionInput: (id) => `/v1/sessions/${encodeURIComponent(id)}/input`,
94
+ sessionResize: (id) => `/v1/sessions/${encodeURIComponent(id)}/resize`,
95
+ session: (id) => `/v1/sessions/${encodeURIComponent(id)}`,
96
+ agentReports: "/v1/agents/reports",
97
+ agentMessages: "/v1/agents/messages",
98
+ agentAnswers: "/v1/agents/answers",
99
+ agentDecisions: "/v1/agents/decisions",
100
+ agentInputs: "/v1/agents/inputs",
101
+ agentInterrupts: "/v1/agents/interrupts",
102
+ agentCatalog: "/v1/agents/catalog",
103
+ agentEvents: "/v1/agents/events",
104
+ asset: (hash) => `/v1/assets/${encodeURIComponent(hash)}`,
105
+ theme: "/v1/theme",
106
+ scratchpads: "/v1/scratchpads",
107
+ agentsSeen: (tabId) => `/v1/tabs/${encodeURIComponent(tabId)}/agents/seen`,
108
+ subscription: (event) => `/v1/subscriptions/${encodeURIComponent(event)}`,
109
+ control: (method) => `/v1/control/${encodeURIComponent(method)}`,
110
+ pushTokens: "/v1/push/tokens",
111
+ pushTest: "/v1/push/test",
112
+ pushPresence: "/v1/push/presence"
113
+ };
114
+
115
+ // src/errors.ts
116
+ class PragmaGatewayError extends Error {
117
+ code;
118
+ httpStatus;
119
+ details;
120
+ cause;
121
+ constructor(message, info) {
122
+ super(message, { cause: info.cause });
123
+ this.name = "PragmaGatewayError";
124
+ this.code = info.code;
125
+ this.httpStatus = info.httpStatus;
126
+ this.details = info.details;
127
+ this.cause = info.cause;
128
+ }
129
+ }
130
+
131
+ class PragmaTransportError extends Error {
132
+ cause;
133
+ constructor(message, cause) {
134
+ super(message, { cause });
135
+ this.name = "PragmaTransportError";
136
+ this.cause = cause;
137
+ }
138
+ }
139
+
140
+ // src/streaming.ts
141
+ async function* ndjsonStream(response, signal) {
142
+ if (!response.body) {
143
+ throw new PragmaTransportError("gateway response is not streamable");
144
+ }
145
+ const reader = response.body.getReader();
146
+ const decoder = new TextDecoder;
147
+ let buffered = "";
148
+ const onAbort = () => {
149
+ reader.cancel();
150
+ };
151
+ signal?.addEventListener("abort", onAbort, { once: true });
152
+ try {
153
+ while (true) {
154
+ if (signal?.aborted) {
155
+ return;
156
+ }
157
+ const { done, value } = await reader.read();
158
+ if (done) {
159
+ break;
160
+ }
161
+ buffered += decoder.decode(value, { stream: true });
162
+ let newline = buffered.indexOf(`
163
+ `);
164
+ while (newline >= 0) {
165
+ const line = buffered.slice(0, newline).trim();
166
+ buffered = buffered.slice(newline + 1);
167
+ if (line) {
168
+ yield JSON.parse(line);
169
+ }
170
+ newline = buffered.indexOf(`
171
+ `);
172
+ }
173
+ }
174
+ buffered += decoder.decode();
175
+ if (buffered.trim()) {
176
+ yield JSON.parse(buffered);
177
+ }
178
+ } finally {
179
+ signal?.removeEventListener("abort", onAbort);
180
+ await reader.cancel().catch(() => {
181
+ return;
182
+ });
183
+ }
184
+ }
185
+
186
+ // src/transport.ts
187
+ class Transport {
188
+ baseUrl;
189
+ token;
190
+ fetch;
191
+ headers;
192
+ constructor(config = {}) {
193
+ const { baseUrl, token } = requireGatewayConfig(config);
194
+ this.baseUrl = baseUrl;
195
+ this.token = token;
196
+ this.fetch = requireFetch(config.fetch);
197
+ this.headers = config.headers ?? {};
198
+ }
199
+ async request(path, options = {}) {
200
+ const response = await this.raw(path, options);
201
+ if (response.status === 204 || response.status === 202) {
202
+ return;
203
+ }
204
+ return parseJson(response);
205
+ }
206
+ async raw(path, options = {}) {
207
+ const headers = {
208
+ ...this.headers,
209
+ ...options.headers,
210
+ authorization: `Bearer ${this.token}`
211
+ };
212
+ let body;
213
+ if (options.rawBody) {
214
+ body = options.rawBody;
215
+ } else if (options.body !== undefined) {
216
+ headers["content-type"] = headers["content-type"] ?? "application/json";
217
+ body = JSON.stringify(options.body);
218
+ }
219
+ let response;
220
+ try {
221
+ response = await this.fetch(urlFor(this.baseUrl, path), {
222
+ method: options.method ?? (body ? "POST" : "GET"),
223
+ headers,
224
+ body,
225
+ signal: options.signal
226
+ });
227
+ } catch (error) {
228
+ throw new PragmaTransportError("failed to reach Pragma gateway", error);
229
+ }
230
+ if (!response.ok) {
231
+ throw await gatewayError(response);
232
+ }
233
+ return response;
234
+ }
235
+ }
236
+ function requireGatewayConfig(config) {
237
+ const baseUrl = stripTrailingSlash(config.baseUrl ?? readEnv(PRAGMA_ENV_KEYS.gatewayUrl) ?? "");
238
+ const token = config.token ?? readEnv(PRAGMA_ENV_KEYS.gatewayToken) ?? "";
239
+ if (!baseUrl || !token) {
240
+ throw new PragmaTransportError("Pragma gateway baseUrl and token are required");
241
+ }
242
+ return { baseUrl, token };
243
+ }
244
+ function requireFetch(fetch) {
245
+ const resolved = fetch ?? globalThis.fetch?.bind(globalThis);
246
+ if (!resolved) {
247
+ throw new PragmaTransportError("global fetch is not available");
248
+ }
249
+ return resolved;
250
+ }
251
+ function urlFor(baseUrl, path) {
252
+ return `${stripTrailingSlash(baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
253
+ }
254
+ async function parseJson(response) {
255
+ try {
256
+ return await response.json();
257
+ } catch (error) {
258
+ throw new PragmaTransportError("gateway returned non-JSON response", error);
259
+ }
260
+ }
261
+ async function gatewayError(response) {
262
+ let body = {};
263
+ try {
264
+ body = await response.json();
265
+ } catch (error) {
266
+ throw new PragmaTransportError("gateway returned non-JSON error response", error);
267
+ }
268
+ return new PragmaGatewayError(body.message ?? `gateway returned ${response.status}`, {
269
+ code: body.code ?? "internal",
270
+ httpStatus: response.status,
271
+ details: body.details
272
+ });
273
+ }
274
+ function stripTrailingSlash(value) {
275
+ return value.replace(/\/+$/, "");
276
+ }
277
+
278
+ // src/agents-client.ts
279
+ class AgentsClient {
280
+ transport;
281
+ constructor(transport) {
282
+ this.transport = transport;
283
+ }
284
+ report(payload, options = {}) {
285
+ return this.transport.request(routes.agentReports, {
286
+ method: "POST",
287
+ body: payload,
288
+ signal: options.signal
289
+ });
290
+ }
291
+ reportMessage(payload, options = {}) {
292
+ return this.transport.request(routes.agentMessages, {
293
+ method: "POST",
294
+ body: payload,
295
+ signal: options.signal
296
+ });
297
+ }
298
+ reportDecision(payload, options = {}) {
299
+ return this.transport.request(routes.agentDecisions, {
300
+ method: "POST",
301
+ body: payload,
302
+ signal: options.signal
303
+ });
304
+ }
305
+ reportAnswer(payload, options = {}) {
306
+ return this.transport.request(routes.agentAnswers, {
307
+ method: "POST",
308
+ body: payload,
309
+ signal: options.signal
310
+ });
311
+ }
312
+ reportInput(payload, options = {}) {
313
+ return this.transport.request(routes.agentInputs, {
314
+ method: "POST",
315
+ body: payload,
316
+ signal: options.signal
317
+ });
318
+ }
319
+ reportInterrupt(payload, options = {}) {
320
+ return this.transport.request(routes.agentInterrupts, {
321
+ method: "POST",
322
+ body: payload,
323
+ signal: options.signal
324
+ });
325
+ }
326
+ reportStarted(options) {
327
+ return reportWithClient(this, options, "running", null);
328
+ }
329
+ reportStopped(options) {
330
+ return reportWithClient(this, options, "done", null);
331
+ }
332
+ reportAttention(options) {
333
+ return reportWithClient(this, options, "attention", options.kind ?? "question");
334
+ }
335
+ reportCleared(options) {
336
+ return reportWithClient(this, options, "cleared", null);
337
+ }
338
+ reportSessionName(options) {
339
+ return reportWithClient(this, options, null, null, options.name);
340
+ }
341
+ catalog(options = {}) {
342
+ return this.transport.request(routes.agentCatalog, {
343
+ method: "GET",
344
+ signal: options.signal
345
+ });
346
+ }
347
+ launch(payload, options = {}) {
348
+ return this.transport.request(routes.control("agentSessionLaunch"), {
349
+ method: "POST",
350
+ body: payload,
351
+ signal: options.signal
352
+ });
353
+ }
354
+ markAgentsSeen(payload, options = {}) {
355
+ return this.transport.request(routes.agentsSeen(payload.tabId), {
356
+ method: "POST",
357
+ body: {},
358
+ signal: options.signal
359
+ });
360
+ }
361
+ async connect(options) {
362
+ const { agent, tabId, worktreeId } = options;
363
+ const controller = new AbortController;
364
+ const onAbort = () => controller.abort();
365
+ options.signal?.addEventListener("abort", onAbort);
366
+ const readStream = this.readStream.bind(this);
367
+ async function* iterate() {
368
+ try {
369
+ for await (const event of readStream({ signal: controller.signal })) {
370
+ if (matchesAgentTab(event, agent, tabId)) {
371
+ yield event;
372
+ }
373
+ }
374
+ } finally {
375
+ options.signal?.removeEventListener("abort", onAbort);
376
+ }
377
+ }
378
+ const connection = {
379
+ [Symbol.asyncIterator]: iterate,
380
+ send: (text, sendOptions = {}) => this.reportInput({
381
+ agent,
382
+ worktreeId,
383
+ tabId,
384
+ text,
385
+ ...sendOptions.requestId ? { requestId: sendOptions.requestId } : {}
386
+ }, { signal: controller.signal }),
387
+ answer: (requestId, reply) => this.reportAnswer({
388
+ agent,
389
+ worktreeId,
390
+ tabId,
391
+ requestId,
392
+ dismissed: reply === null,
393
+ ...reply !== null ? { answer: reply } : {}
394
+ }, { signal: controller.signal }),
395
+ decide: (requestId, approved) => this.reportDecision({ agent, worktreeId, tabId, requestId, approved }, { signal: controller.signal }),
396
+ interrupt: (requestId) => this.reportInterrupt({
397
+ agent,
398
+ worktreeId,
399
+ tabId,
400
+ ...requestId ? { requestId } : {}
401
+ }, { signal: controller.signal }),
402
+ close: () => controller.abort()
403
+ };
404
+ if (options.prompt !== undefined) {
405
+ await connection.send(options.prompt);
406
+ }
407
+ return connection;
408
+ }
409
+ async* readStream(options = {}) {
410
+ const response = await this.transport.raw(routes.agentEvents, { signal: options.signal });
411
+ yield* ndjsonStream(response, options.signal);
412
+ }
413
+ async awaitDecision(params) {
414
+ const controller = new AbortController;
415
+ const onAbort = () => controller.abort();
416
+ params.signal?.addEventListener("abort", onAbort);
417
+ const timer = params.timeoutMs && params.timeoutMs > 0 ? setTimeout(() => controller.abort(), params.timeoutMs) : undefined;
418
+ try {
419
+ for await (const event of this.readStream({ signal: controller.signal })) {
420
+ if (event.type === "agentDecision" && event.decision.requestId === params.requestId && event.decision.agent === params.agent) {
421
+ return event.decision.approved;
422
+ }
423
+ }
424
+ return null;
425
+ } catch {
426
+ return null;
427
+ } finally {
428
+ if (timer) {
429
+ clearTimeout(timer);
430
+ }
431
+ params.signal?.removeEventListener("abort", onAbort);
432
+ }
433
+ }
434
+ async awaitAnswer(params) {
435
+ const controller = new AbortController;
436
+ const onAbort = () => controller.abort();
437
+ params.signal?.addEventListener("abort", onAbort);
438
+ const timer = params.timeoutMs && params.timeoutMs > 0 ? setTimeout(() => controller.abort(), params.timeoutMs) : undefined;
439
+ try {
440
+ for await (const event of this.readStream({ signal: controller.signal })) {
441
+ if (event.type === "agentAnswer" && event.answer.requestId === params.requestId && event.answer.agent === params.agent) {
442
+ return event.answer.dismissed ? null : event.answer.answer ?? "";
443
+ }
444
+ }
445
+ return null;
446
+ } catch {
447
+ return null;
448
+ } finally {
449
+ if (timer) {
450
+ clearTimeout(timer);
451
+ }
452
+ params.signal?.removeEventListener("abort", onAbort);
453
+ }
454
+ }
455
+ }
456
+ function matchesAgentTab(event, agent, tabId) {
457
+ switch (event.type) {
458
+ case "agent":
459
+ return event.agent === agent && event.tabId === tabId;
460
+ case "agentMessage":
461
+ return event.message.agent === agent && event.message.tabId === tabId;
462
+ case "agentDecision":
463
+ return event.decision.agent === agent && event.decision.tabId === tabId;
464
+ case "agentAnswer":
465
+ return event.answer.agent === agent && event.answer.tabId === tabId;
466
+ case "agentInput":
467
+ return event.input.agent === agent && event.input.tabId === tabId;
468
+ case "agentInterrupt":
469
+ return event.interrupt.agent === agent && event.interrupt.tabId === tabId;
470
+ }
471
+ }
472
+ function reportStarted(options) {
473
+ return reportWithStatus(options, "running", null);
474
+ }
475
+ function reportStopped(options) {
476
+ return reportWithStatus(options, "done", null);
477
+ }
478
+ function reportAttention(options) {
479
+ return reportWithStatus(options, "attention", options.kind ?? "question");
480
+ }
481
+ function reportCleared(options) {
482
+ return reportWithStatus(options, "cleared", null);
483
+ }
484
+ function reportSessionName(options) {
485
+ return reportWithStatus(options, null, null, options.name);
486
+ }
487
+ async function reportMessage(options) {
488
+ if (!options.client && !hasPragmaEnvironment(options.env)) {
489
+ return;
490
+ }
491
+ const agents = options.client?.agents ?? new AgentsClient(new Transport({
492
+ baseUrl: readEnv(PRAGMA_ENV_KEYS.gatewayUrl, options.env),
493
+ token: readEnv(PRAGMA_ENV_KEYS.gatewayToken, options.env)
494
+ }));
495
+ await reportMessageWithClient(agents, options);
496
+ }
497
+ async function awaitAgentDecision(options) {
498
+ if (!options.client && !hasPragmaEnvironment(options.env)) {
499
+ return null;
500
+ }
501
+ const agents = options.client?.agents ?? new AgentsClient(new Transport({
502
+ baseUrl: readEnv(PRAGMA_ENV_KEYS.gatewayUrl, options.env),
503
+ token: readEnv(PRAGMA_ENV_KEYS.gatewayToken, options.env)
504
+ }));
505
+ return agents.awaitDecision({
506
+ agent: options.agent,
507
+ requestId: options.requestId,
508
+ timeoutMs: options.timeoutMs,
509
+ signal: options.signal
510
+ });
511
+ }
512
+ async function awaitAgentAnswer(options) {
513
+ if (!options.client && !hasPragmaEnvironment(options.env)) {
514
+ return null;
515
+ }
516
+ const agents = options.client?.agents ?? new AgentsClient(new Transport({
517
+ baseUrl: readEnv(PRAGMA_ENV_KEYS.gatewayUrl, options.env),
518
+ token: readEnv(PRAGMA_ENV_KEYS.gatewayToken, options.env)
519
+ }));
520
+ return agents.awaitAnswer({
521
+ agent: options.agent,
522
+ requestId: options.requestId,
523
+ timeoutMs: options.timeoutMs,
524
+ signal: options.signal
525
+ });
526
+ }
527
+ async function reportWithStatus(options, status, attentionKind, sessionName) {
528
+ if (!options.client && !hasPragmaEnvironment(options.env)) {
529
+ return;
530
+ }
531
+ const agents = options.client?.agents ?? new AgentsClient(new Transport({
532
+ baseUrl: readEnv(PRAGMA_ENV_KEYS.gatewayUrl, options.env),
533
+ token: readEnv(PRAGMA_ENV_KEYS.gatewayToken, options.env)
534
+ }));
535
+ await reportWithClient(agents, options, status, attentionKind, sessionName);
536
+ }
537
+ async function reportWithClient(agents, options, status, attentionKind, sessionName) {
538
+ const worktreeId = options.worktreeId ?? readEnv(PRAGMA_ENV_KEYS.worktreeId, options.env);
539
+ const tabId = readEnv(PRAGMA_ENV_KEYS.tabId, options.env);
540
+ if (!worktreeId || !tabId) {
541
+ return;
542
+ }
543
+ await agents.report(reportPayload(options, { worktreeId, tabId }, status, attentionKind, sessionName), { signal: options.signal });
544
+ }
545
+ function reportPayload(options, routing, status, attentionKind, sessionName) {
546
+ return {
547
+ agent: options.agent,
548
+ worktreeId: routing.worktreeId,
549
+ tabId: routing.tabId,
550
+ status,
551
+ attentionKind,
552
+ ...sessionName ? { sessionName } : {},
553
+ ...options.command ? { command: options.command } : {},
554
+ ...options.question ? { question: options.question } : {},
555
+ ...options.options && options.options.length > 0 ? { options: options.options } : {},
556
+ ...options.questions && options.questions.length > 0 ? { questions: options.questions } : {},
557
+ ...options.requestId ? { requestId: options.requestId } : {}
558
+ };
559
+ }
560
+ async function reportMessageWithClient(agents, options) {
561
+ const worktreeId = options.message.worktreeId ?? options.worktreeId ?? readEnv(PRAGMA_ENV_KEYS.worktreeId, options.env);
562
+ const tabId = options.message.tabId ?? readEnv(PRAGMA_ENV_KEYS.tabId, options.env);
563
+ if (!worktreeId || !tabId) {
564
+ return;
565
+ }
566
+ await agents.reportMessage({
567
+ ...options.message,
568
+ agent: options.message.agent ?? options.agent,
569
+ worktreeId,
570
+ tabId
571
+ }, { signal: options.signal });
572
+ }
573
+
574
+ // src/encoding.ts
575
+ var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
576
+ function bytesToBase64(bytes) {
577
+ let output = "";
578
+ for (let index = 0;index < bytes.length; index += 3) {
579
+ const first = bytes[index] ?? 0;
580
+ const second = bytes[index + 1] ?? 0;
581
+ const third = bytes[index + 2] ?? 0;
582
+ output += BASE64[first >> 2];
583
+ output += BASE64[(first & 3) << 4 | second >> 4];
584
+ output += index + 1 < bytes.length ? BASE64[(second & 15) << 2 | third >> 6] : "=";
585
+ output += index + 2 < bytes.length ? BASE64[third & 63] : "=";
586
+ }
587
+ return output;
588
+ }
589
+ function base64ToBytes(value) {
590
+ const clean = value.replace(/\s/g, "");
591
+ if (clean.length % 4 !== 0) {
592
+ throw new TypeError("invalid base64 length");
593
+ }
594
+ const bytes = [];
595
+ for (let index = 0;index < clean.length; index += 4) {
596
+ const chars = [clean[index], clean[index + 1], clean[index + 2], clean[index + 3]];
597
+ const values = chars.map((char) => char === "=" ? 0 : BASE64.indexOf(char ?? ""));
598
+ if (values.some((number) => number < 0)) {
599
+ throw new TypeError("invalid base64 character");
600
+ }
601
+ const first = values[0] ?? 0;
602
+ const second = values[1] ?? 0;
603
+ const third = values[2] ?? 0;
604
+ const fourth = values[3] ?? 0;
605
+ bytes.push(first << 2 | second >> 4);
606
+ if (chars[2] !== "=") {
607
+ bytes.push((second & 15) << 4 | third >> 2);
608
+ }
609
+ if (chars[3] !== "=") {
610
+ bytes.push((third & 3) << 6 | fourth);
611
+ }
612
+ }
613
+ return new Uint8Array(bytes);
614
+ }
615
+
616
+ // src/assets-client.ts
617
+ class AssetsClient {
618
+ transport;
619
+ constructor(transport) {
620
+ this.transport = transport;
621
+ }
622
+ async fetch(hash, options = {}) {
623
+ const response = await this.transport.raw(routes.asset(hash), { signal: options.signal });
624
+ const mime = response.headers.get("content-type") ?? "application/octet-stream";
625
+ const bytes = new Uint8Array(await response.arrayBuffer());
626
+ return { bytes, mime };
627
+ }
628
+ async toDataUri(hash, options = {}) {
629
+ const { bytes, mime } = await this.fetch(hash, options);
630
+ return `data:${mime};base64,${bytesToBase64(bytes)}`;
631
+ }
632
+ }
633
+
634
+ // src/events-client.ts
635
+ class EventsClient {
636
+ transport;
637
+ constructor(transport) {
638
+ this.transport = transport;
639
+ }
640
+ async* subscribe(event, options = {}) {
641
+ const query = new URLSearchParams;
642
+ if (options.cursor) {
643
+ query.set("cursor", options.cursor);
644
+ }
645
+ if (options.worktreeId) {
646
+ query.set("worktreeId", options.worktreeId);
647
+ }
648
+ if (options.cwd) {
649
+ query.set("cwd", options.cwd);
650
+ }
651
+ const suffix = query.toString() ? `?${query}` : "";
652
+ const response = await this.transport.raw(`${routes.subscription(event)}${suffix}`, {
653
+ signal: options.signal
654
+ });
655
+ for await (const line of ndjsonStream(response, options.signal)) {
656
+ if (line.type === "snapshot" || line.type === "delta") {
657
+ yield line;
658
+ }
659
+ }
660
+ }
661
+ }
662
+
663
+ // src/exec-client.ts
664
+ class ExecClient {
665
+ transport;
666
+ constructor(transport) {
667
+ this.transport = transport;
668
+ }
669
+ run(payload) {
670
+ return this.transport.request(routes.rpc("exec"), {
671
+ body: { ...payload, env: payload.env ?? [], maxConcurrent: payload.maxConcurrent ?? 1 }
672
+ });
673
+ }
674
+ }
675
+
676
+ // src/fanouts-client.ts
677
+ class FanoutsClient {
678
+ transport;
679
+ events;
680
+ constructor(transport, events) {
681
+ this.transport = transport;
682
+ this.events = events;
683
+ }
684
+ create(request, options = {}) {
685
+ return this.rpc("create", request, options);
686
+ }
687
+ get(reference, options = {}) {
688
+ return this.rpc("get", reference, options);
689
+ }
690
+ async read(request, options = {}) {
691
+ const result = await this.rpc("read", request, options);
692
+ return {
693
+ fanoutId: result.fanoutId,
694
+ targets: result.targets.map(({ data, ...target }) => ({
695
+ ...target,
696
+ raw: base64ToBytes(data)
697
+ }))
698
+ };
699
+ }
700
+ send(request, options = {}) {
701
+ return this.rpc("send", request, options);
702
+ }
703
+ retry(request, options = {}) {
704
+ return this.rpc("retry", request, options);
705
+ }
706
+ cancel(reference, options = {}) {
707
+ return this.rpc("cancel", reference, options);
708
+ }
709
+ pick(request, options = {}) {
710
+ return this.rpc("pick", request, options);
711
+ }
712
+ async* subscribe(options = {}) {
713
+ for await (const event of this.events.subscribe("fanouts", { signal: options.signal })) {
714
+ const narrowed = this.narrow(event);
715
+ if (!options.fanoutId) {
716
+ yield narrowed;
717
+ continue;
718
+ }
719
+ yield {
720
+ ...narrowed,
721
+ payload: {
722
+ fanouts: narrowed.payload.fanouts.filter((fanout) => fanout.id === options.fanoutId)
723
+ }
724
+ };
725
+ }
726
+ }
727
+ narrow(event) {
728
+ return {
729
+ type: event.type,
730
+ subscription: event.subscription,
731
+ payload: event.payload ?? { fanouts: [] }
732
+ };
733
+ }
734
+ rpc(action, payload, options) {
735
+ return this.transport.request(routes.rpc("fanouts"), {
736
+ method: "POST",
737
+ body: { action, ...payload },
738
+ signal: options.signal
739
+ });
740
+ }
741
+ }
742
+
743
+ // src/fs-client.ts
744
+ class FsClient {
745
+ transport;
746
+ constructor(transport) {
747
+ this.transport = transport;
748
+ }
749
+ listDir(payload) {
750
+ return this.rpc("listDir", payload);
751
+ }
752
+ createFile(payload) {
753
+ return this.rpc("createFile", payload);
754
+ }
755
+ createFolder(payload) {
756
+ return this.rpc("createFolder", payload);
757
+ }
758
+ pathExists(payload) {
759
+ return this.rpc("pathExists", payload);
760
+ }
761
+ readFile(payload) {
762
+ return this.rpc("readFile", payload);
763
+ }
764
+ writeFile(payload) {
765
+ return this.rpc("writeFile", payload);
766
+ }
767
+ rename(payload) {
768
+ return this.rpc("rename", payload);
769
+ }
770
+ delete(payload) {
771
+ return this.rpc("delete", payload);
772
+ }
773
+ rpc(op, payload) {
774
+ return this.transport.request(routes.rpc("filesystem"), { body: { op, ...payload } });
775
+ }
776
+ }
777
+
778
+ // src/git-client.ts
779
+ class GitClient {
780
+ transport;
781
+ constructor(transport) {
782
+ this.transport = transport;
783
+ }
784
+ worktreeChanges(payload) {
785
+ return this.rpc("worktreeChanges", payload);
786
+ }
787
+ mergedStatus(payload) {
788
+ return this.rpc("mergedStatus", payload);
789
+ }
790
+ fileDiff(payload) {
791
+ return this.rpc("fileDiff", payload);
792
+ }
793
+ discardUnstagedFile(payload) {
794
+ return this.rpc("discardUnstagedFile", payload);
795
+ }
796
+ discardAllUnstaged(payload) {
797
+ return this.rpc("discardAllUnstaged", payload);
798
+ }
799
+ stageFile(payload) {
800
+ return this.rpc("stageFile", payload);
801
+ }
802
+ stageAll(payload) {
803
+ return this.rpc("stageAll", payload);
804
+ }
805
+ unstageFile(payload) {
806
+ return this.rpc("unstageFile", payload);
807
+ }
808
+ unstageAll(payload) {
809
+ return this.rpc("unstageAll", payload);
810
+ }
811
+ commitStaged(payload) {
812
+ return this.rpc("commitStaged", payload);
813
+ }
814
+ mergeWorktreeToParent(payload) {
815
+ return this.rpc("mergeWorktreeToParent", payload);
816
+ }
817
+ prFileDiff(payload) {
818
+ return this.rpc("prFileDiff", payload);
819
+ }
820
+ githubRepoInfo(payload) {
821
+ return this.rpc("githubRepoInfo", payload);
822
+ }
823
+ githubDefaultPrTitle(payload) {
824
+ return this.rpc("githubDefaultPrTitle", payload);
825
+ }
826
+ githubFetchAndSync(payload) {
827
+ return this.rpc("githubFetchAndSync", payload);
828
+ }
829
+ githubPullBranch(payload) {
830
+ return this.rpc("githubPullBranch", payload);
831
+ }
832
+ githubSyncBranch(payload) {
833
+ return this.rpc("githubSyncBranch", payload);
834
+ }
835
+ githubPushBranch(payload) {
836
+ return this.rpc("githubPushBranch", payload);
837
+ }
838
+ githubDeleteRemoteBranch(payload) {
839
+ return this.rpc("githubDeleteRemoteBranch", payload);
840
+ }
841
+ ensurePragmaExcluded(payload) {
842
+ return this.rpc("ensurePragmaExcluded", payload);
843
+ }
844
+ createWorktree(payload) {
845
+ return this.rpc("createWorktree", payload);
846
+ }
847
+ removeWorktree(payload) {
848
+ return this.rpc("removeWorktree", payload);
849
+ }
850
+ deleteBranch(payload) {
851
+ return this.rpc("deleteBranch", payload);
852
+ }
853
+ isDirty(payload) {
854
+ return this.rpc("isDirty", payload);
855
+ }
856
+ rpc(op, payload) {
857
+ return this.transport.request(routes.rpc("git"), { body: { op, ...payload } });
858
+ }
859
+ }
860
+
861
+ // src/health-client.ts
862
+ class HealthClient {
863
+ transport;
864
+ constructor(transport) {
865
+ this.transport = transport;
866
+ }
867
+ check(options = {}) {
868
+ return this.transport.request(routes.health, { signal: options.signal });
869
+ }
870
+ }
871
+
872
+ // src/push-client.ts
873
+ class PushClient {
874
+ transport;
875
+ constructor(transport) {
876
+ this.transport = transport;
877
+ }
878
+ register(payload, options = {}) {
879
+ return this.transport.request(routes.pushTokens, {
880
+ method: "POST",
881
+ body: payload,
882
+ signal: options.signal
883
+ });
884
+ }
885
+ unregister(options = {}) {
886
+ return this.transport.request(routes.pushTokens, {
887
+ method: "DELETE",
888
+ body: {},
889
+ signal: options.signal
890
+ });
891
+ }
892
+ list(options = {}) {
893
+ return this.transport.request(routes.pushTokens, {
894
+ method: "GET",
895
+ signal: options.signal
896
+ });
897
+ }
898
+ test(options = {}) {
899
+ return this.transport.request(routes.pushTest, {
900
+ method: "POST",
901
+ body: {},
902
+ signal: options.signal
903
+ });
904
+ }
905
+ presence(payload, options = {}) {
906
+ return this.transport.request(routes.pushPresence, {
907
+ method: "POST",
908
+ body: payload,
909
+ signal: options.signal
910
+ });
911
+ }
912
+ }
913
+
914
+ // ../scratchpad-contract/src/guards.ts
915
+ function matchesShape(value, fields) {
916
+ if (typeof value !== "object" || value === null)
917
+ return false;
918
+ const record = value;
919
+ return Object.entries(fields).every(([field, guard]) => guard(record[field]));
920
+ }
921
+ var isString = (value) => typeof value === "string";
922
+ var isNumber = (value) => typeof value === "number";
923
+ function nullable(guard) {
924
+ return (value) => value === null || guard(value);
925
+ }
926
+
927
+ // ../scratchpad-contract/src/comments.ts
928
+ function scratchpadCommentsPath(filePath) {
929
+ return `${filePath}.comments.json`;
930
+ }
931
+ function parseScratchpadComments(source) {
932
+ const value = JSON.parse(source);
933
+ if (!Array.isArray(value))
934
+ throw new Error("Scratchpad comments must be an array.");
935
+ return value.filter(isScratchpadComment);
936
+ }
937
+ function serializeScratchpadComments(comments) {
938
+ return `${JSON.stringify(comments, null, 2)}
939
+ `;
940
+ }
941
+ function createScratchpadComment(block, text, now, id) {
942
+ return {
943
+ id,
944
+ from: 0,
945
+ to: 0,
946
+ quote: block.quote,
947
+ text: text.trim(),
948
+ createdAt: now,
949
+ resolvedAt: null,
950
+ blockIndex: block.index
951
+ };
952
+ }
953
+ function isScratchpadComment(value) {
954
+ return matchesShape(value, {
955
+ id: isString,
956
+ from: isNumber,
957
+ to: isNumber,
958
+ quote: isString,
959
+ text: isString,
960
+ createdAt: isNumber,
961
+ resolvedAt: nullable(isNumber)
962
+ });
963
+ }
964
+ // ../constants/values.json
965
+ var values_default = {
966
+ daemon: {
967
+ protocolVersion: "0.1.0",
968
+ socketFile: "daemon.sock",
969
+ lockFile: "server.lock",
970
+ logFile: "server.log"
971
+ },
972
+ gateway: {
973
+ apiVersion: "0.0.0",
974
+ discoveryFile: "gateway.json",
975
+ tokenFile: "gateway-token",
976
+ devicesFile: "gateway-devices.json",
977
+ tokenHeader: "authorization",
978
+ deviceHeaders: {
979
+ id: "x-pragma-device-id",
980
+ name: "x-pragma-device-name",
981
+ platform: "x-pragma-device-platform",
982
+ appVersion: "x-pragma-app-version"
983
+ },
984
+ push: {
985
+ sendUrl: "https://exp.host/--/api/v2/push/send",
986
+ batchSize: 100,
987
+ presenceTtlMs: 90000,
988
+ tokenPrefixes: [
989
+ "ExponentPushToken[",
990
+ "ExpoPushToken["
991
+ ]
992
+ },
993
+ web: {
994
+ enabled: false,
995
+ basePath: "/web",
996
+ manifestFile: "manifest.json",
997
+ resourceDir: "web"
998
+ }
999
+ },
1000
+ platform: {
1001
+ defaultBackend: "native",
1002
+ shells: {
1003
+ macos: "/bin/zsh",
1004
+ linux: "/bin/sh",
1005
+ windows: [
1006
+ "pwsh.exe",
1007
+ "powershell.exe"
1008
+ ]
1009
+ },
1010
+ wsl: {
1011
+ launcher: "wsl.exe",
1012
+ listArgs: [
1013
+ "--list",
1014
+ "--verbose"
1015
+ ],
1016
+ serverBinary: "pragma-server"
1017
+ }
1018
+ },
1019
+ tunnel: {
1020
+ defaultCommand: "ngrok http {port} --log stdout --log-format json",
1021
+ defaultUrlPattern: '(https://[^\\s"]+)'
1022
+ },
1023
+ plugins: {
1024
+ configFileName: ".pragma/config.json",
1025
+ storageScopes: [
1026
+ "global",
1027
+ "project"
1028
+ ]
1029
+ },
1030
+ theme: {
1031
+ fileName: ".pragma/theme.json",
1032
+ modes: [
1033
+ "light",
1034
+ "dark"
1035
+ ]
1036
+ },
1037
+ keybindings: {
1038
+ configFileName: ".pragma/keybindings.json"
1039
+ },
1040
+ bench: {
1041
+ hookGlobal: "__PRAGMA_BENCH__",
1042
+ runnerGlobal: "__PRAGMA_BENCH_RUNNER__",
1043
+ markerPrefix: "PRAGMABENCH",
1044
+ tabTitle: "pragma-bench"
1045
+ },
1046
+ agentStatus: {
1047
+ soundsDirName: ".pragma/assets/sounds",
1048
+ maxSoundSeconds: 5,
1049
+ maxSoundBytes: 5242880,
1050
+ soundExtensions: [
1051
+ "mp3",
1052
+ "wav",
1053
+ "ogg",
1054
+ "oga",
1055
+ "m4a",
1056
+ "aac",
1057
+ "flac",
1058
+ "webm"
1059
+ ],
1060
+ notificationsEnabled: true,
1061
+ notificationText: {
1062
+ doneTitle: "{agent} finished",
1063
+ attentionTitle: "{agent} needs attention",
1064
+ questionTitle: "{agent} is waiting for an answer",
1065
+ commandTitle: "{agent} wants to run a command",
1066
+ locationSeparator: " / ",
1067
+ tabSuffix: ' · tab "{tab}"',
1068
+ unknownLocation: "Open Pragma to continue."
1069
+ }
1070
+ },
1071
+ terminalDefaults: {
1072
+ hiddenDistros: [
1073
+ "docker-desktop",
1074
+ "docker-desktop-data"
1075
+ ],
1076
+ rememberLastShell: false,
1077
+ maxDroppedFileBytes: 10485760,
1078
+ droppedFilesDirName: "pragma-dropped-files",
1079
+ droppedFilesMaxAgeMs: 86400000
1080
+ },
1081
+ tabs: {
1082
+ defaultTitles: {
1083
+ fallback: "Shell",
1084
+ browser: "New tab",
1085
+ log: "Server Logs",
1086
+ scratchpad: "Scratchpad",
1087
+ whiteboard: "Whiteboard",
1088
+ prReview: "PR Review",
1089
+ pluginWebview: "Plugin"
1090
+ }
1091
+ },
1092
+ files: {
1093
+ chunkBytes: 4194304,
1094
+ maxBinaryBytes: 268435456
1095
+ },
1096
+ fanout: {
1097
+ stateFile: "fanouts.json",
1098
+ branchPrefix: "fanout",
1099
+ minMembers: 2,
1100
+ defaultJobs: 4,
1101
+ deliveryTimeoutMs: 15000,
1102
+ envFanoutId: "PRAGMA_FANOUT_ID",
1103
+ envMemberId: "PRAGMA_FANOUT_MEMBER_ID"
1104
+ },
1105
+ scratchpads: {
1106
+ directory: ".pragma/scratchpads",
1107
+ extension: "mdx",
1108
+ frontmatterKey: "pragmaScratchpad",
1109
+ version: 1
1110
+ },
1111
+ whiteboards: {
1112
+ databaseFile: "whiteboards.db",
1113
+ defaultTitle: "Whiteboard",
1114
+ maxTitleChars: 200,
1115
+ maxSceneBytes: 8388608,
1116
+ maxRenderDimension: 4096
1117
+ },
1118
+ protocol: {
1119
+ rpcMethods: [
1120
+ "git",
1121
+ "filesystem",
1122
+ "database",
1123
+ "kanban",
1124
+ "worktrees",
1125
+ "projects",
1126
+ "tabs",
1127
+ "settings",
1128
+ "github",
1129
+ "ai",
1130
+ "exec",
1131
+ "automations",
1132
+ "plugins",
1133
+ "tunnel",
1134
+ "scratchpads",
1135
+ "whiteboards",
1136
+ "wsl",
1137
+ "fanouts"
1138
+ ],
1139
+ events: [
1140
+ "agentStatus",
1141
+ "worktreeChanged",
1142
+ "kanbanChanged",
1143
+ "tabsChanged",
1144
+ "fileChanged",
1145
+ "echoMode",
1146
+ "automationPending",
1147
+ "automationsChanged",
1148
+ "workspace",
1149
+ "fanouts"
1150
+ ],
1151
+ errors: [
1152
+ "invalidPayload",
1153
+ "unsupportedMethod",
1154
+ "notFound",
1155
+ "staleWrite",
1156
+ "permissionDenied",
1157
+ "internal"
1158
+ ]
1159
+ },
1160
+ scripts: {
1161
+ maxConcurrentCommands: 4,
1162
+ configPath: ".pragma/scripts.json",
1163
+ migrationSources: [
1164
+ {
1165
+ id: "superset",
1166
+ label: "Superset",
1167
+ configPaths: [
1168
+ ".superset/config.json"
1169
+ ]
1170
+ },
1171
+ {
1172
+ id: "emdash",
1173
+ label: "Emdash",
1174
+ configPaths: [
1175
+ ".emdash.json"
1176
+ ]
1177
+ },
1178
+ {
1179
+ id: "orca",
1180
+ label: "Orca",
1181
+ configPaths: [
1182
+ "orca.yaml",
1183
+ "orca.yml",
1184
+ ".orca/config.json"
1185
+ ]
1186
+ }
1187
+ ],
1188
+ migrationCommitMessage: "chore(config): add pragma-app.sh config"
1189
+ },
1190
+ agents: {
1191
+ startDelayMs: 500,
1192
+ altScreenExtraWaitMs: 15000,
1193
+ altScreenSettleMs: 500
1194
+ },
1195
+ github: {
1196
+ oauthClientId: "Ov23li1WfY05SeQNp5tk",
1197
+ scopes: [
1198
+ "repo"
1199
+ ],
1200
+ deviceCodeUrl: "https://github.com/login/device/code",
1201
+ deviceVerifyUrl: "https://github.com/login/device",
1202
+ accessTokenUrl: "https://github.com/login/oauth/access_token",
1203
+ apiBaseUrl: "https://api.github.com",
1204
+ homepageUrl: "https://github.com/pragma-sh/pragma",
1205
+ prSignature: {
1206
+ enabled: true,
1207
+ startMarker: "<!-- pragma:pr-signature -->",
1208
+ endMarker: "<!-- /pragma:pr-signature -->",
1209
+ linkLabel: "Open worktree in Pragma",
1210
+ openUrl: "https://pragma-app.sh/open"
1211
+ }
1212
+ },
1213
+ app: {
1214
+ name: "Pragma",
1215
+ identifier: "com.pragma.app",
1216
+ version: "0.1.0"
1217
+ },
1218
+ onboarding: {
1219
+ mediaBaseUrl: "https://pragma-app.sh/media",
1220
+ skill: {
1221
+ id: "pragma",
1222
+ targets: [
1223
+ {
1224
+ id: "all-agents",
1225
+ directory: ".agents/skills",
1226
+ label: "All agents"
1227
+ },
1228
+ {
1229
+ id: "claude-code",
1230
+ directory: ".claude/skills",
1231
+ label: "Claude Code"
1232
+ }
1233
+ ]
1234
+ }
1235
+ },
1236
+ updates: {
1237
+ checkUrl: "https://pragma-app.sh/api/updates",
1238
+ devCheckUrl: "http://localhost:3000/api/updates",
1239
+ pollIntervalMs: 300000,
1240
+ autoDownload: true,
1241
+ manifestFile: "release.json",
1242
+ uiDirName: "ui",
1243
+ buttonLabel: "Install Update",
1244
+ changelogLabel: "View changelog",
1245
+ restartWarningTitle: "This update restarts the app server",
1246
+ restartWarningBody: "Open terminal sessions will stop. Save anything you need before installing.",
1247
+ applyModes: [
1248
+ "reload",
1249
+ "restart"
1250
+ ],
1251
+ platforms: [
1252
+ "darwin-aarch64",
1253
+ "darwin-x86_64",
1254
+ "linux-x86_64-deb",
1255
+ "linux-x86_64-rpm",
1256
+ "linux-x86_64-appimage",
1257
+ "linux-aarch64-deb",
1258
+ "linux-aarch64-rpm",
1259
+ "linux-aarch64-appimage",
1260
+ "windows-x86_64"
1261
+ ]
1262
+ },
1263
+ window: {
1264
+ defaultWidth: 1024,
1265
+ defaultHeight: 768,
1266
+ minWidth: 640,
1267
+ minHeight: 480,
1268
+ titlebarHeight: 38,
1269
+ trafficLightInset: 12
1270
+ },
1271
+ editorLaunchers: {
1272
+ defaultEditorId: "system",
1273
+ options: [
1274
+ {
1275
+ id: "system",
1276
+ name: "File Explorer",
1277
+ brandIcon: "lucide:folder-open",
1278
+ brandColor: "#38bdf8",
1279
+ cliCommand: null
1280
+ },
1281
+ {
1282
+ id: "vscode",
1283
+ name: "VS Code",
1284
+ brandIcon: "simple-icons:visualstudiocode",
1285
+ brandColor: "#007acc",
1286
+ cliCommand: "code"
1287
+ },
1288
+ {
1289
+ id: "cursor",
1290
+ name: "Cursor",
1291
+ brandIcon: "simple-icons:cursor",
1292
+ brandColor: "#ffffff",
1293
+ cliCommand: "cursor"
1294
+ },
1295
+ {
1296
+ id: "windsurf",
1297
+ name: "Windsurf",
1298
+ brandIcon: "simple-icons:windsurf",
1299
+ brandColor: "#00c7be",
1300
+ cliCommand: "windsurf"
1301
+ },
1302
+ {
1303
+ id: "zed",
1304
+ name: "Zed",
1305
+ brandIcon: "simple-icons:zedindustries",
1306
+ brandColor: "#084ccf",
1307
+ cliCommand: "zed"
1308
+ },
1309
+ {
1310
+ id: "sublime-text",
1311
+ name: "Sublime Text",
1312
+ brandIcon: "simple-icons:sublimetext",
1313
+ brandColor: "#ff9800",
1314
+ cliCommand: "subl"
1315
+ },
1316
+ {
1317
+ id: "intellij-idea",
1318
+ name: "IntelliJ IDEA",
1319
+ brandIcon: "simple-icons:intellijidea",
1320
+ brandColor: "#ff2d8f",
1321
+ cliCommand: "idea"
1322
+ }
1323
+ ]
1324
+ }
1325
+ };
1326
+
1327
+ // ../constants/src/index.ts
1328
+ var constants = values_default;
1329
+
1330
+ // ../scratchpad-contract/src/document.ts
1331
+ var FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n)?/;
1332
+ function parseScratchpadDocument(source) {
1333
+ const frontmatter = FRONTMATTER_PATTERN.exec(source);
1334
+ if (!frontmatter) {
1335
+ throw new Error("This MDX file is not a managed Pragma scratchpad.");
1336
+ }
1337
+ return {
1338
+ metadata: parseMetadata(metadataLine(frontmatter[1] ?? "")),
1339
+ body: source.slice(frontmatter[0].length),
1340
+ frontmatter: frontmatter[0]
1341
+ };
1342
+ }
1343
+ function metadataLine(frontmatter) {
1344
+ const key = constants.scratchpads.frontmatterKey;
1345
+ const line = frontmatter.split(/\r?\n/).find((entry) => entry.startsWith(`${key}: `));
1346
+ if (!line)
1347
+ throw new Error("Scratchpad frontmatter is missing managed metadata.");
1348
+ return line.slice(key.length + 2);
1349
+ }
1350
+ function parseMetadata(json) {
1351
+ const parsed = JSON.parse(json);
1352
+ if (!isScratchpadMetadata(parsed) || parsed.version !== constants.scratchpads.version) {
1353
+ throw new Error("Scratchpad metadata is invalid or uses an unsupported version.");
1354
+ }
1355
+ return parsed;
1356
+ }
1357
+ function attachScratchpadAgent(source, agent) {
1358
+ const document = parseScratchpadDocument(source);
1359
+ const metadata = {
1360
+ ...document.metadata,
1361
+ agentTabId: agent.tabId,
1362
+ agentId: agent.agentId
1363
+ };
1364
+ const key = constants.scratchpads.frontmatterKey;
1365
+ const nextLine = `${key}: ${JSON.stringify(metadata)}`;
1366
+ const lines = document.frontmatter.split(/\r?\n/);
1367
+ const index = lines.findIndex((line) => line.startsWith(`${key}: `));
1368
+ if (index < 0)
1369
+ throw new Error("Scratchpad frontmatter is missing managed metadata.");
1370
+ lines[index] = nextLine;
1371
+ return `${lines.join(`
1372
+ `)}${document.body}`;
1373
+ }
1374
+ function isScratchpadMetadata(value) {
1375
+ return matchesShape(value, {
1376
+ version: isNumber,
1377
+ id: isString,
1378
+ title: isString,
1379
+ agentTabId: nullable(isString),
1380
+ agentId: nullable(isString),
1381
+ createdAt: isNumber
1382
+ });
1383
+ }
1384
+ // src/scratchpads-client.ts
1385
+ class ScratchpadsClient {
1386
+ transport;
1387
+ fs;
1388
+ agents;
1389
+ constructor(transport, fs, agents) {
1390
+ this.transport = transport;
1391
+ this.fs = fs;
1392
+ this.agents = agents;
1393
+ }
1394
+ getScratchpads(options) {
1395
+ const query = `?root=${encodeURIComponent(options.root)}`;
1396
+ return this.transport.request(`${routes.scratchpads}${query}`, {
1397
+ signal: options.signal
1398
+ });
1399
+ }
1400
+ async getComments(options) {
1401
+ const path = scratchpadCommentsPath(options.filePath);
1402
+ const { root } = options;
1403
+ if (!await this.fs.pathExists({ root, path }))
1404
+ return [];
1405
+ const file = await this.fs.readFile({ root, path });
1406
+ if (file.binary || file.truncated)
1407
+ return [];
1408
+ return parseScratchpadComments(file.text);
1409
+ }
1410
+ async comment(options) {
1411
+ const { root, filePath, block, text } = options;
1412
+ const comment = createScratchpadComment(block, text, options.createdAt ?? Date.now(), options.id ?? randomCommentId());
1413
+ const existing = await this.getComments({ root, filePath });
1414
+ await this.writeComments({ root, filePath }, [...existing, comment]);
1415
+ return comment;
1416
+ }
1417
+ async setComments(options, comments) {
1418
+ await this.writeComments(options, comments);
1419
+ }
1420
+ async attachAgent(options) {
1421
+ const { root, filePath, tabId, agentId } = options;
1422
+ const contents = options.contents ?? await this.readScratchpad(root, filePath);
1423
+ await this.fs.writeFile({
1424
+ root,
1425
+ path: filePath,
1426
+ contents: attachScratchpadAgent(contents, { tabId, agentId })
1427
+ });
1428
+ }
1429
+ async sendAttached(options) {
1430
+ const { root, filePath, worktreeId, text } = options;
1431
+ const contents = options.contents ?? await this.readScratchpad(root, filePath);
1432
+ const { metadata } = parseScratchpadDocument(contents);
1433
+ if (!metadata.agentTabId || !metadata.agentId)
1434
+ return { delivered: false };
1435
+ const agent = runtimeAgentId(metadata.agentId);
1436
+ await this.agents.reportInput({ agent, worktreeId, tabId: metadata.agentTabId, text }, { signal: options.signal });
1437
+ return { delivered: true, agent, tabId: metadata.agentTabId };
1438
+ }
1439
+ async readScratchpad(root, filePath) {
1440
+ const file = await this.fs.readFile({ root, path: filePath });
1441
+ if (file.binary)
1442
+ throw new Error(`Scratchpad ${filePath} is not text.`);
1443
+ if (file.truncated)
1444
+ throw new Error(`Scratchpad ${filePath} was truncated by the host.`);
1445
+ return file.text;
1446
+ }
1447
+ writeComments(options, comments) {
1448
+ return this.fs.writeFile({
1449
+ root: options.root,
1450
+ path: scratchpadCommentsPath(options.filePath),
1451
+ contents: serializeScratchpadComments(comments)
1452
+ });
1453
+ }
1454
+ }
1455
+ function runtimeAgentId(agentId) {
1456
+ return agentId.split(".").at(-1) ?? agentId;
1457
+ }
1458
+ function randomCommentId() {
1459
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
1460
+ }
1461
+
1462
+ // src/sessions-client.ts
1463
+ class SessionsClient {
1464
+ transport;
1465
+ constructor(transport) {
1466
+ this.transport = transport;
1467
+ }
1468
+ spawn(payload, options = {}) {
1469
+ return this.transport.request(routes.sessions, {
1470
+ method: "POST",
1471
+ body: payload,
1472
+ signal: options.signal
1473
+ });
1474
+ }
1475
+ async* attach(sessionId, options = {}) {
1476
+ const response = await this.transport.raw(routes.sessionEvents(sessionId), {
1477
+ signal: options.signal
1478
+ });
1479
+ yield* ndjsonStream(response, options.signal);
1480
+ }
1481
+ write(sessionId, bytes, options = {}) {
1482
+ return this.transport.request(routes.sessionInput(sessionId), {
1483
+ method: "POST",
1484
+ rawBody: bytes,
1485
+ headers: { "content-type": "application/octet-stream" },
1486
+ signal: options.signal
1487
+ });
1488
+ }
1489
+ resize(sessionId, payload, options = {}) {
1490
+ return this.transport.request(routes.sessionResize(sessionId), {
1491
+ method: "POST",
1492
+ body: payload,
1493
+ signal: options.signal
1494
+ });
1495
+ }
1496
+ kill(sessionId, options = {}) {
1497
+ return this.transport.request(routes.session(sessionId), {
1498
+ method: "DELETE",
1499
+ signal: options.signal
1500
+ });
1501
+ }
1502
+ rename(sessionId, title) {
1503
+ return this.transport.request(routes.control("tabRename"), {
1504
+ method: "POST",
1505
+ body: { tabId: sessionId, title }
1506
+ });
1507
+ }
1508
+ killForCwd(cwd, options = {}) {
1509
+ return this.transport.request(`${routes.sessions}?cwd=${encodeURIComponent(cwd)}`, {
1510
+ method: "DELETE",
1511
+ signal: options.signal
1512
+ });
1513
+ }
1514
+ }
1515
+
1516
+ // src/theme-client.ts
1517
+ class ThemeClient {
1518
+ transport;
1519
+ constructor(transport) {
1520
+ this.transport = transport;
1521
+ }
1522
+ get(options = {}) {
1523
+ const query = options.root ? `?root=${encodeURIComponent(options.root)}` : "";
1524
+ return this.transport.request(`${routes.theme}${query}`, {
1525
+ signal: options.signal
1526
+ });
1527
+ }
1528
+ }
1529
+
1530
+ // src/whiteboards-client.ts
1531
+ class WhiteboardsClient {
1532
+ transport;
1533
+ constructor(transport) {
1534
+ this.transport = transport;
1535
+ }
1536
+ create(input) {
1537
+ return this.rpc("create", input);
1538
+ }
1539
+ get(input) {
1540
+ return this.rpc("get", input);
1541
+ }
1542
+ list(input) {
1543
+ return this.rpc("list", input);
1544
+ }
1545
+ search(input) {
1546
+ return this.rpc("list", input);
1547
+ }
1548
+ edit(input) {
1549
+ return this.rpc("edit", input);
1550
+ }
1551
+ async delete(input) {
1552
+ await this.rpc("delete", input);
1553
+ }
1554
+ async view(input) {
1555
+ const result = await this.rpc("view", input);
1556
+ return base64ToBytes(result.data);
1557
+ }
1558
+ rpc(action, input) {
1559
+ return this.transport.request(routes.rpc("whiteboards"), {
1560
+ method: "POST",
1561
+ body: { action, ...input }
1562
+ });
1563
+ }
1564
+ }
1565
+
1566
+ // src/workspace-client.ts
1567
+ class WorkspaceClient {
1568
+ events;
1569
+ constructor(events) {
1570
+ this.events = events;
1571
+ }
1572
+ async* subscribe(options = {}) {
1573
+ for await (const event of this.events.subscribe("workspace", options)) {
1574
+ yield this.narrow(event);
1575
+ }
1576
+ }
1577
+ narrow(event) {
1578
+ return {
1579
+ type: event.type,
1580
+ subscription: event.subscription,
1581
+ payload: event.payload ?? { projects: [], worktrees: [], tabs: [] }
1582
+ };
1583
+ }
1584
+ }
1585
+
1586
+ // src/client.ts
1587
+ class PragmaClient {
1588
+ fs;
1589
+ git;
1590
+ exec;
1591
+ sessions;
1592
+ agents;
1593
+ assets;
1594
+ events;
1595
+ workspace;
1596
+ fanouts;
1597
+ push;
1598
+ theme;
1599
+ health;
1600
+ scratchpads;
1601
+ whiteboards;
1602
+ transport;
1603
+ constructor(config = {}) {
1604
+ this.transport = new Transport(config);
1605
+ this.fs = new FsClient(this.transport);
1606
+ this.git = new GitClient(this.transport);
1607
+ this.exec = new ExecClient(this.transport);
1608
+ this.sessions = new SessionsClient(this.transport);
1609
+ this.agents = new AgentsClient(this.transport);
1610
+ this.assets = new AssetsClient(this.transport);
1611
+ this.events = new EventsClient(this.transport);
1612
+ this.workspace = new WorkspaceClient(this.events);
1613
+ this.fanouts = new FanoutsClient(this.transport, this.events);
1614
+ this.push = new PushClient(this.transport);
1615
+ this.theme = new ThemeClient(this.transport);
1616
+ this.health = new HealthClient(this.transport);
1617
+ this.scratchpads = new ScratchpadsClient(this.transport, this.fs, this.agents);
1618
+ this.whiteboards = new WhiteboardsClient(this.transport);
1619
+ }
1620
+ rpc(method, payload, options = {}) {
1621
+ return this.transport.request(routes.rpc(method), {
1622
+ method: "POST",
1623
+ body: payload,
1624
+ signal: options.signal
1625
+ });
1626
+ }
1627
+ createBoardDraft(payload, options = {}) {
1628
+ return this.transport.request(routes.control("boardDraftCreate"), {
1629
+ method: "POST",
1630
+ body: payload,
1631
+ signal: options.signal
1632
+ });
1633
+ }
1634
+ }