@omercnet/paseo-gas-city 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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/bun.lock +1217 -0
- package/client/city-operations.tsx +1505 -0
- package/client/contribute.tsx +96 -0
- package/client/dispatch-intent.ts +53 -0
- package/client/factory-panel.tsx +373 -0
- package/client/gas-city-surface.tsx +343 -0
- package/client/settings-screen.tsx +286 -0
- package/client/view-model.ts +318 -0
- package/docs/images/paseo-gas-city-compact-overview.webp +0 -0
- package/docs/images/paseo-gas-city-dispatch-confirmation.webp +0 -0
- package/docs/images/paseo-gas-city-wide-events.webp +0 -0
- package/docs/images/paseo-gas-city-wide-overview.webp +0 -0
- package/icon.svg +5 -0
- package/index.client.tsx +1 -0
- package/index.server.ts +42 -0
- package/package.json +73 -0
- package/paseo-plugin.json +4 -0
- package/server/gas-city-client.ts +668 -0
- package/server/handlers.ts +790 -0
- package/server/workspace-mapping.ts +170 -0
- package/shared/index.ts +4 -0
- package/shared/limits.ts +28 -0
- package/shared/rpc.ts +99 -0
- package/shared/schemas.ts +430 -0
- package/shared/settings.ts +89 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AttentionItem,
|
|
3
|
+
GasCityConvoy,
|
|
4
|
+
GasCityEvent,
|
|
5
|
+
GasCitySession,
|
|
6
|
+
GasCityWorkItem,
|
|
7
|
+
} from "../shared";
|
|
8
|
+
|
|
9
|
+
export type DashboardRow =
|
|
10
|
+
| { kind: "status"; tone: "loading" | "error" | "refreshing" | "stale"; message: string }
|
|
11
|
+
| { kind: "empty"; message: string }
|
|
12
|
+
| { kind: "attention"; item: AttentionItem }
|
|
13
|
+
| { kind: "session"; item: GasCitySession }
|
|
14
|
+
| { kind: "convoy"; item: GasCityConvoy }
|
|
15
|
+
| { kind: "work"; item: GasCityWorkItem }
|
|
16
|
+
| { kind: "event"; item: GasCityEvent };
|
|
17
|
+
|
|
18
|
+
export interface DashboardSection {
|
|
19
|
+
id: "attention" | "sessions" | "convoys" | "work" | "events";
|
|
20
|
+
title: string;
|
|
21
|
+
data: DashboardRow[];
|
|
22
|
+
truncated: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DashboardData {
|
|
26
|
+
attention:
|
|
27
|
+
| { scope: "city" | "city-and-rig"; items: readonly AttentionItem[]; truncated: boolean }
|
|
28
|
+
| undefined;
|
|
29
|
+
sessions:
|
|
30
|
+
| { scope: "city" | "rig"; items: readonly GasCitySession[]; truncated: boolean }
|
|
31
|
+
| undefined;
|
|
32
|
+
convoys:
|
|
33
|
+
| {
|
|
34
|
+
scope: "city" | "rig-and-unattributed";
|
|
35
|
+
items: readonly GasCityConvoy[];
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
}
|
|
38
|
+
| undefined;
|
|
39
|
+
work:
|
|
40
|
+
| { scope: "city" | "rig"; items: readonly GasCityWorkItem[]; truncated: boolean }
|
|
41
|
+
| undefined;
|
|
42
|
+
events:
|
|
43
|
+
| { scope: "supervisor-head" | "city"; items: readonly GasCityEvent[]; truncated: boolean }
|
|
44
|
+
| undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const severityOrder = { critical: 0, warning: 1, info: 2 } as const;
|
|
48
|
+
|
|
49
|
+
function timestamp(value: string | null): number {
|
|
50
|
+
if (value === null) return Number.NEGATIVE_INFINITY;
|
|
51
|
+
const parsed = Date.parse(value);
|
|
52
|
+
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function withEmptyRow<T>(items: readonly T[], message: string, wrap: (item: T) => DashboardRow) {
|
|
56
|
+
return items.length > 0 ? items.map(wrap) : [{ kind: "empty", message } satisfies DashboardRow];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function buildDashboardSections(data: DashboardData): DashboardSection[] {
|
|
60
|
+
const attention = [...(data.attention?.items ?? [])].sort((left, right) => {
|
|
61
|
+
const bySeverity = severityOrder[left.severity] - severityOrder[right.severity];
|
|
62
|
+
if (bySeverity !== 0) return bySeverity;
|
|
63
|
+
return timestamp(right.observedAt) - timestamp(left.observedAt);
|
|
64
|
+
});
|
|
65
|
+
const sessions = [...(data.sessions?.items ?? [])].sort((left, right) => {
|
|
66
|
+
if (left.running !== right.running) return left.running ? -1 : 1;
|
|
67
|
+
return (
|
|
68
|
+
timestamp(right.lastActiveAt ?? right.createdAt) -
|
|
69
|
+
timestamp(left.lastActiveAt ?? left.createdAt)
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
const convoys = [...(data.convoys?.items ?? [])].sort((left, right) => {
|
|
73
|
+
if (left.blocked !== right.blocked) return left.blocked ? -1 : 1;
|
|
74
|
+
const leftPriority = left.priority ?? Number.MAX_SAFE_INTEGER;
|
|
75
|
+
const rightPriority = right.priority ?? Number.MAX_SAFE_INTEGER;
|
|
76
|
+
if (leftPriority !== rightPriority) return leftPriority - rightPriority;
|
|
77
|
+
return (
|
|
78
|
+
timestamp(right.updatedAt ?? right.createdAt) - timestamp(left.updatedAt ?? left.createdAt)
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
const events = [...(data.events?.items ?? [])].sort(
|
|
82
|
+
(left, right) => right.sequence - left.sequence,
|
|
83
|
+
);
|
|
84
|
+
const work = [...(data.work?.items ?? [])].sort((left, right) => {
|
|
85
|
+
const leftPriority = left.priority ?? Number.MAX_SAFE_INTEGER;
|
|
86
|
+
const rightPriority = right.priority ?? Number.MAX_SAFE_INTEGER;
|
|
87
|
+
if (leftPriority !== rightPriority) return leftPriority - rightPriority;
|
|
88
|
+
return (
|
|
89
|
+
timestamp(right.updatedAt ?? right.createdAt) - timestamp(left.updatedAt ?? left.createdAt)
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
id: "attention",
|
|
96
|
+
title:
|
|
97
|
+
data.attention?.scope === "city-and-rig"
|
|
98
|
+
? "Attention · city + rig"
|
|
99
|
+
: "Attention · city-wide",
|
|
100
|
+
data: withEmptyRow(attention, "No resources need attention.", (item) => ({
|
|
101
|
+
kind: "attention",
|
|
102
|
+
item,
|
|
103
|
+
})),
|
|
104
|
+
truncated: data.attention?.truncated ?? false,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "sessions",
|
|
108
|
+
title: data.sessions?.scope === "rig" ? "Sessions · mapped rig" : "Sessions · city-wide",
|
|
109
|
+
data: withEmptyRow(sessions, "No sessions in this scope.", (item) => ({
|
|
110
|
+
kind: "session",
|
|
111
|
+
item,
|
|
112
|
+
})),
|
|
113
|
+
truncated: data.sessions?.truncated ?? false,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "convoys",
|
|
117
|
+
title:
|
|
118
|
+
data.convoys?.scope === "rig-and-unattributed"
|
|
119
|
+
? "Convoys · mapped rig + unattributed"
|
|
120
|
+
: "Convoys · city-wide",
|
|
121
|
+
data: withEmptyRow(convoys, "No convoys in this scope.", (item) => ({
|
|
122
|
+
kind: "convoy",
|
|
123
|
+
item,
|
|
124
|
+
})),
|
|
125
|
+
truncated: data.convoys?.truncated ?? false,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: "work",
|
|
129
|
+
title: data.work?.scope === "rig" ? "Work · mapped rig" : "Work · city-wide",
|
|
130
|
+
data: withEmptyRow(work, "No work in this scope.", (item) => ({ kind: "work", item })),
|
|
131
|
+
truncated: data.work?.truncated ?? false,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "events",
|
|
135
|
+
title:
|
|
136
|
+
data.events?.scope === "supervisor-head"
|
|
137
|
+
? "Supervisor events · head snapshot"
|
|
138
|
+
: "Recent events · city-wide",
|
|
139
|
+
data: withEmptyRow(events, "No recent events.", (item) => ({ kind: "event", item })),
|
|
140
|
+
truncated: data.events?.truncated ?? false,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type RefreshPresentation =
|
|
146
|
+
| { state: "loading"; label: string }
|
|
147
|
+
| { state: "error"; label: string }
|
|
148
|
+
| { state: "refreshing"; label: string }
|
|
149
|
+
| { state: "stale"; label: string }
|
|
150
|
+
| { state: "ready"; label: string };
|
|
151
|
+
|
|
152
|
+
export function presentSection(
|
|
153
|
+
section: DashboardSection,
|
|
154
|
+
query: { hasData: boolean; isPending: boolean; isFetching: boolean; error: unknown },
|
|
155
|
+
): DashboardSection {
|
|
156
|
+
if (!query.hasData && query.isPending) {
|
|
157
|
+
return {
|
|
158
|
+
...section,
|
|
159
|
+
data: [
|
|
160
|
+
{ kind: "status", tone: "loading", message: `Loading ${section.title.toLowerCase()}…` },
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (!query.hasData && query.error) {
|
|
165
|
+
return {
|
|
166
|
+
...section,
|
|
167
|
+
data: [
|
|
168
|
+
{
|
|
169
|
+
kind: "status",
|
|
170
|
+
tone: "error",
|
|
171
|
+
message: `Could not load ${section.title.toLowerCase()}.`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (query.hasData && query.error) {
|
|
177
|
+
return {
|
|
178
|
+
...section,
|
|
179
|
+
data: [
|
|
180
|
+
{ kind: "status", tone: "stale", message: "Refresh failed. Showing stale data." },
|
|
181
|
+
...section.data,
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (query.hasData && query.isFetching) {
|
|
186
|
+
return {
|
|
187
|
+
...section,
|
|
188
|
+
data: [
|
|
189
|
+
{ kind: "status", tone: "refreshing", message: "Refreshing stale data…" },
|
|
190
|
+
...section.data,
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return section;
|
|
195
|
+
}
|
|
196
|
+
export function refreshPresentation(input: {
|
|
197
|
+
hasData: boolean;
|
|
198
|
+
isPending: boolean;
|
|
199
|
+
isFetching: boolean;
|
|
200
|
+
error: unknown;
|
|
201
|
+
refreshedAt?: string | null;
|
|
202
|
+
}): RefreshPresentation {
|
|
203
|
+
if (!input.hasData && input.isPending) return { state: "loading", label: "Loading live data" };
|
|
204
|
+
if (!input.hasData && input.error) return { state: "error", label: "Load failed" };
|
|
205
|
+
if (input.hasData && input.error)
|
|
206
|
+
return { state: "stale", label: "Refresh failed · showing stale data" };
|
|
207
|
+
if (input.hasData && input.isFetching)
|
|
208
|
+
return { state: "refreshing", label: "Refreshing stale data" };
|
|
209
|
+
if (input.refreshedAt) {
|
|
210
|
+
const parsed = Date.parse(input.refreshedAt);
|
|
211
|
+
if (Number.isFinite(parsed)) {
|
|
212
|
+
return { state: "ready", label: `Updated ${new Date(parsed).toLocaleTimeString()}` };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { state: "ready", label: "Live data ready" };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export type SessionActionName =
|
|
219
|
+
| "wake"
|
|
220
|
+
| "message"
|
|
221
|
+
| "submit"
|
|
222
|
+
| "stop"
|
|
223
|
+
| "suspend"
|
|
224
|
+
| "close"
|
|
225
|
+
| "kill";
|
|
226
|
+
|
|
227
|
+
export function sessionActionsFor(
|
|
228
|
+
session: Pick<GasCitySession, "running" | "state" | "submissionKinds">,
|
|
229
|
+
): SessionActionName[] {
|
|
230
|
+
if (session.state.toLowerCase() === "closed") return [];
|
|
231
|
+
if (!session.running) return ["wake", "close"];
|
|
232
|
+
const actions: SessionActionName[] = [];
|
|
233
|
+
if (session.submissionKinds.includes("message")) actions.push("message");
|
|
234
|
+
if (session.submissionKinds.includes("submit")) actions.push("submit");
|
|
235
|
+
actions.push("stop", "suspend", "close", "kill");
|
|
236
|
+
return actions;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function selectAvailableCity(
|
|
240
|
+
preferred: string | null,
|
|
241
|
+
cities: readonly { name: string }[],
|
|
242
|
+
): string | null {
|
|
243
|
+
if (preferred && cities.some(({ name }) => name === preferred)) return preferred;
|
|
244
|
+
return cities[0]?.name ?? null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function cityQueryRoot(
|
|
248
|
+
hostId: string,
|
|
249
|
+
endpointUrl: string,
|
|
250
|
+
cityName: string,
|
|
251
|
+
rigName: string | null,
|
|
252
|
+
) {
|
|
253
|
+
return ["gas-city", hostId, endpointUrl, cityName, rigName ?? "all-rigs"] as const;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface SlingArguments {
|
|
257
|
+
beadId: string;
|
|
258
|
+
agent: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function tokenize(input: string): string[] | null {
|
|
262
|
+
const tokens: string[] = [];
|
|
263
|
+
let token = "";
|
|
264
|
+
let quote: '"' | "'" | null = null;
|
|
265
|
+
let escaped = false;
|
|
266
|
+
|
|
267
|
+
for (const character of input.trim()) {
|
|
268
|
+
if (escaped) {
|
|
269
|
+
token += character;
|
|
270
|
+
escaped = false;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (character === "\\") {
|
|
274
|
+
escaped = true;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (quote) {
|
|
278
|
+
if (character === quote) quote = null;
|
|
279
|
+
else token += character;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (character === '"' || character === "'") {
|
|
283
|
+
quote = character;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (/\s/.test(character)) {
|
|
287
|
+
if (token) {
|
|
288
|
+
tokens.push(token);
|
|
289
|
+
token = "";
|
|
290
|
+
}
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
token += character;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (escaped) token += "\\";
|
|
297
|
+
if (quote) return null;
|
|
298
|
+
if (token) tokens.push(token);
|
|
299
|
+
return tokens;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function parseSlingArguments(input: string): SlingArguments | null {
|
|
303
|
+
const tokens = tokenize(input);
|
|
304
|
+
if (!tokens || tokens.length > 2) return null;
|
|
305
|
+
return { beadId: tokens[0] ?? "", agent: tokens[1] ?? "" };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function sessionAccessibilityLabel(session: GasCitySession): string {
|
|
309
|
+
const state = session.running ? "running" : session.state;
|
|
310
|
+
const rig = session.rigName ? `Rig ${session.rigName}` : "City session";
|
|
311
|
+
const activity = session.activity ? `Activity ${session.activity}` : "No reported activity";
|
|
312
|
+
return `${session.title}. ${state}. ${rig}. Provider ${session.provider}. ${activity}.`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function convoyProgress(convoy: GasCityConvoy): string {
|
|
316
|
+
if (convoy.closedWork === null || convoy.totalWork === null) return "Progress unavailable";
|
|
317
|
+
return `${convoy.closedWork} of ${convoy.totalWork} work items closed`;
|
|
318
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/icon.svg
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title">
|
|
2
|
+
<title id="title">Gas City factory</title>
|
|
3
|
+
<path fill="#F59E0B" d="M8 54V29l15 8V27l15 8V16h8v23l10 5v10H8Z"/>
|
|
4
|
+
<path fill="#1F2937" d="M14 45h7v9h-7zm13 0h7v9h-7zm13 0h7v9h-7zM38 10h8v6h-8z"/>
|
|
5
|
+
</svg>
|
package/index.client.tsx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerGasCityClient as default } from "./client/contribute";
|
package/index.server.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
|
+
import {
|
|
3
|
+
handleDiscoverSupervisor,
|
|
4
|
+
handleDispatchWork,
|
|
5
|
+
handleGetCityRigSnapshot,
|
|
6
|
+
handleListAttention,
|
|
7
|
+
handleListConvoys,
|
|
8
|
+
handleListEvents,
|
|
9
|
+
handleListSessions,
|
|
10
|
+
handleListWork,
|
|
11
|
+
handlePerformSessionAction,
|
|
12
|
+
handleResolveWorkspaceRig,
|
|
13
|
+
} from "./server/handlers";
|
|
14
|
+
import {
|
|
15
|
+
discoverSupervisor,
|
|
16
|
+
dispatchWork,
|
|
17
|
+
gasCitySettings,
|
|
18
|
+
getCityRigSnapshot,
|
|
19
|
+
listAttention,
|
|
20
|
+
listConvoys,
|
|
21
|
+
listEvents,
|
|
22
|
+
listSessions,
|
|
23
|
+
listWork,
|
|
24
|
+
performSessionAction,
|
|
25
|
+
resolveWorkspaceRig,
|
|
26
|
+
} from "./shared";
|
|
27
|
+
|
|
28
|
+
export default function contribute(server: PluginServerContext) {
|
|
29
|
+
server.registerSettings(gasCitySettings);
|
|
30
|
+
server.handle(discoverSupervisor, handleDiscoverSupervisor);
|
|
31
|
+
server.handle(resolveWorkspaceRig, handleResolveWorkspaceRig);
|
|
32
|
+
server.handle(getCityRigSnapshot, handleGetCityRigSnapshot);
|
|
33
|
+
server.handle(listSessions, handleListSessions);
|
|
34
|
+
server.handle(listConvoys, handleListConvoys);
|
|
35
|
+
server.handle(listWork, handleListWork);
|
|
36
|
+
server.handle(listEvents, handleListEvents);
|
|
37
|
+
server.handle(listAttention, handleListAttention);
|
|
38
|
+
server.handle(dispatchWork, handleDispatchWork);
|
|
39
|
+
server.handle(performSessionAction, handlePerformSessionAction);
|
|
40
|
+
|
|
41
|
+
return () => {};
|
|
42
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omercnet/paseo-gas-city",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A Paseo control plane for observing and operating Gas City supervisors.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Omer Cohen",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/omercnet/paseo-plugins.git",
|
|
11
|
+
"directory": "paseo-gas-city"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/omercnet/paseo-plugins/tree/main/paseo-gas-city#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/omercnet/paseo-plugins/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"packageManager": "bun@1.4.0",
|
|
21
|
+
"engines": {
|
|
22
|
+
"bun": ">=1.4.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"paseo",
|
|
26
|
+
"paseo-plugin",
|
|
27
|
+
"gas-city",
|
|
28
|
+
"orchestration",
|
|
29
|
+
"gastown",
|
|
30
|
+
"beads",
|
|
31
|
+
"multi-agent",
|
|
32
|
+
"software-factory",
|
|
33
|
+
"coding-agents"
|
|
34
|
+
],
|
|
35
|
+
"files": [
|
|
36
|
+
"bun.lock",
|
|
37
|
+
"CHANGELOG.md",
|
|
38
|
+
"icon.svg",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"README.md",
|
|
41
|
+
"index.client.tsx",
|
|
42
|
+
"index.server.ts",
|
|
43
|
+
"client",
|
|
44
|
+
"docs",
|
|
45
|
+
"server",
|
|
46
|
+
"shared",
|
|
47
|
+
"paseo-plugin.json"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"check": "biome check .",
|
|
51
|
+
"check:write": "biome check --write .",
|
|
52
|
+
"test": "bun test",
|
|
53
|
+
"test:coverage": "bun test --coverage",
|
|
54
|
+
"typecheck": "tsc --noEmit -p tsconfig.client.json && tsc --noEmit -p tsconfig.server.json && tsc --noEmit -p tsconfig.scripts.json",
|
|
55
|
+
"package:release": "bun scripts/package-release.ts",
|
|
56
|
+
"verify:package": "bun scripts/verify-package.ts"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@biomejs/biome": "^2.5.10",
|
|
60
|
+
"@getpaseo/cli": "0.8.0",
|
|
61
|
+
"@getpaseo/client": "0.8.0",
|
|
62
|
+
"@getpaseo/plugin": "0.8.0",
|
|
63
|
+
"@getpaseo/protocol": "0.8.0",
|
|
64
|
+
"@tanstack/react-query": "^5.102.3",
|
|
65
|
+
"@types/bun": "^1.4.0",
|
|
66
|
+
"@types/react": "~19.2.0",
|
|
67
|
+
"fflate": "^0.8.3",
|
|
68
|
+
"react": "19.1.0",
|
|
69
|
+
"react-native": "0.81.5",
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"zod": "^4.4.3"
|
|
72
|
+
}
|
|
73
|
+
}
|