@agent-api/cli 0.0.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.
@@ -0,0 +1,154 @@
1
+ import type { LocalToolApprovalRequest, WorkdirAccessMode } from "../agent.js";
2
+ export type WorkbenchRole = "user" | "assistant" | "system";
3
+ export interface WorkbenchMessage {
4
+ id: string;
5
+ role: WorkbenchRole;
6
+ text: string;
7
+ }
8
+ export type ActivityLevel = "info" | "success" | "warning" | "error";
9
+ export interface WorkbenchActivity {
10
+ id: string;
11
+ level: ActivityLevel;
12
+ text: string;
13
+ timestamp: number;
14
+ }
15
+ export interface WorkbenchWorkdirStatus {
16
+ root: string;
17
+ name: string;
18
+ fileCount: number;
19
+ totalBytes: number;
20
+ scanTruncated: boolean;
21
+ }
22
+ export interface LocalToolApproval extends LocalToolApprovalRequest {
23
+ id: string;
24
+ createdAt: number;
25
+ }
26
+ export interface WorkbenchState {
27
+ messages: WorkbenchMessage[];
28
+ activities: WorkbenchActivity[];
29
+ busy: boolean;
30
+ contextEnabled: boolean;
31
+ workdir: WorkbenchWorkdirStatus | null;
32
+ activeAssistantMessageId: string | null;
33
+ pendingLocalTool: LocalToolApproval | null;
34
+ accessMode: WorkdirAccessMode;
35
+ currentConversation: string;
36
+ }
37
+ export type WorkbenchAction = {
38
+ type: "message.add";
39
+ role: WorkbenchRole;
40
+ text: string;
41
+ id?: string;
42
+ } | {
43
+ type: "message.append";
44
+ id: string;
45
+ delta: string;
46
+ } | {
47
+ type: "messages.clear";
48
+ } | {
49
+ type: "activity.add";
50
+ level?: ActivityLevel;
51
+ text: string;
52
+ } | {
53
+ type: "busy.set";
54
+ busy: boolean;
55
+ } | {
56
+ type: "context.toggle";
57
+ } | {
58
+ type: "context.set";
59
+ enabled: boolean;
60
+ } | {
61
+ type: "workdir.set";
62
+ workdir: WorkbenchWorkdirStatus;
63
+ } | {
64
+ type: "assistant.active";
65
+ id: string | null;
66
+ } | {
67
+ type: "local_tool.pending.set";
68
+ approval: LocalToolApprovalRequest;
69
+ } | {
70
+ type: "local_tool.pending.clear";
71
+ } | {
72
+ type: "access.set";
73
+ mode: WorkdirAccessMode;
74
+ } | {
75
+ type: "conversation.set";
76
+ name: string;
77
+ };
78
+ export type WorkbenchCommand = {
79
+ kind: "invalid";
80
+ command: string;
81
+ } | {
82
+ kind: "abort";
83
+ } | {
84
+ kind: "quit";
85
+ } | {
86
+ kind: "help";
87
+ } | {
88
+ kind: "clear";
89
+ } | {
90
+ kind: "login";
91
+ } | {
92
+ kind: "logout";
93
+ } | {
94
+ kind: "delete_profile";
95
+ } | {
96
+ kind: "switch_profile";
97
+ name?: string;
98
+ } | {
99
+ kind: "auth_status";
100
+ } | {
101
+ kind: "config";
102
+ field?: "preset";
103
+ value?: string;
104
+ } | {
105
+ kind: "context";
106
+ enabled?: boolean;
107
+ } | {
108
+ kind: "workdir";
109
+ enabled?: boolean;
110
+ } | {
111
+ kind: "access";
112
+ mode?: WorkdirAccessMode;
113
+ } | {
114
+ kind: "preset";
115
+ value?: string;
116
+ } | {
117
+ kind: "model";
118
+ value?: string;
119
+ } | {
120
+ kind: "summary";
121
+ } | {
122
+ kind: "search";
123
+ query: string;
124
+ } | {
125
+ kind: "new_conversation";
126
+ name?: string;
127
+ } | {
128
+ kind: "switch_conversation";
129
+ name: string;
130
+ } | {
131
+ kind: "list_conversations";
132
+ } | {
133
+ kind: "refresh_catalog";
134
+ } | {
135
+ kind: "preview";
136
+ } | {
137
+ kind: "apply";
138
+ } | {
139
+ kind: "apply_all";
140
+ } | {
141
+ kind: "reject";
142
+ };
143
+ export declare function createInitialWorkbenchState(options: {
144
+ contextEnabled: boolean;
145
+ accessMode?: WorkdirAccessMode;
146
+ conversation?: string;
147
+ }): WorkbenchState;
148
+ export declare function workbenchReducer(state: WorkbenchState, action: WorkbenchAction): WorkbenchState;
149
+ export declare function parseWorkbenchCommand(input: string): WorkbenchCommand | null;
150
+ export declare function parsePendingApprovalCommand(input: string): WorkbenchCommand | null;
151
+ export declare function helpText(): string;
152
+ export declare function workdirText(status: WorkbenchWorkdirStatus | null): string;
153
+ export declare function formatBytes(bytes: number): string;
154
+ export declare function activityColor(level: ActivityLevel): "green" | "yellow" | "red" | "gray";
@@ -0,0 +1,288 @@
1
+ export function createInitialWorkbenchState(options) {
2
+ const accessMode = options.accessMode ?? (options.contextEnabled ? "approval" : "off");
3
+ return {
4
+ messages: [
5
+ newMessage("system", "Agent API Workbench is ready. Type /help for commands."),
6
+ ],
7
+ activities: [
8
+ newActivity("info", "Workbench started"),
9
+ ],
10
+ busy: false,
11
+ contextEnabled: options.contextEnabled || accessMode === "approval" || accessMode === "full",
12
+ workdir: null,
13
+ activeAssistantMessageId: null,
14
+ pendingLocalTool: null,
15
+ accessMode,
16
+ currentConversation: options.conversation || "default",
17
+ };
18
+ }
19
+ export function workbenchReducer(state, action) {
20
+ switch (action.type) {
21
+ case "message.add":
22
+ return {
23
+ ...state,
24
+ messages: [...state.messages, newMessage(action.role, action.text, action.id)],
25
+ };
26
+ case "message.append":
27
+ return {
28
+ ...state,
29
+ messages: state.messages.map((message) => message.id === action.id ? { ...message, text: message.text + action.delta } : message),
30
+ };
31
+ case "messages.clear":
32
+ return {
33
+ ...state,
34
+ messages: [newMessage("system", "Cleared. Type /help for commands.")],
35
+ };
36
+ case "activity.add":
37
+ return {
38
+ ...state,
39
+ activities: [...state.activities, newActivity(action.level ?? "info", action.text)].slice(-20),
40
+ };
41
+ case "busy.set":
42
+ return { ...state, busy: action.busy };
43
+ case "context.toggle":
44
+ return setLocalAccess(state, state.contextEnabled ? "off" : "approval");
45
+ case "context.set":
46
+ return setLocalAccess(state, action.enabled ? "approval" : "off");
47
+ case "workdir.set":
48
+ return {
49
+ ...state,
50
+ workdir: action.workdir,
51
+ activities: [...state.activities, newActivity("success", `Workdir loaded: ${action.workdir.name}`)].slice(-20),
52
+ };
53
+ case "assistant.active":
54
+ return { ...state, activeAssistantMessageId: action.id };
55
+ case "local_tool.pending.set": {
56
+ const pending = {
57
+ ...action.approval,
58
+ id: `local-${Date.now()}`,
59
+ createdAt: Date.now(),
60
+ };
61
+ return {
62
+ ...state,
63
+ pendingLocalTool: pending,
64
+ activities: [
65
+ ...state.activities,
66
+ newActivity("warning", `Local approval ready: ${pending.name}${pending.action ? `.${pending.action}` : ""}`),
67
+ ].slice(-20),
68
+ };
69
+ }
70
+ case "local_tool.pending.clear":
71
+ return {
72
+ ...state,
73
+ pendingLocalTool: null,
74
+ };
75
+ case "access.set":
76
+ return setLocalAccess(state, action.mode);
77
+ case "conversation.set":
78
+ return {
79
+ ...state,
80
+ currentConversation: action.name,
81
+ activities: [...state.activities, newActivity("info", `Conversation: ${action.name}`)].slice(-20),
82
+ };
83
+ default:
84
+ return state;
85
+ }
86
+ }
87
+ function setLocalAccess(state, mode) {
88
+ return {
89
+ ...state,
90
+ accessMode: mode,
91
+ contextEnabled: mode !== "off",
92
+ pendingLocalTool: mode === "off" ? null : state.pendingLocalTool,
93
+ activities: [...state.activities, newActivity(mode === "off" ? "warning" : "success", `Local access: ${mode}`)].slice(-20),
94
+ };
95
+ }
96
+ export function parseWorkbenchCommand(input) {
97
+ const trimmed = input.trim();
98
+ if (!trimmed.startsWith("/"))
99
+ return null;
100
+ const [name = "", ...rest] = trimmed.slice(1).split(/\s+/);
101
+ switch (name) {
102
+ case "abort":
103
+ case "cancel":
104
+ return { kind: "abort" };
105
+ case "quit":
106
+ return { kind: "quit" };
107
+ case "exit":
108
+ return { kind: "quit" };
109
+ case "help":
110
+ return { kind: "help" };
111
+ case "clear":
112
+ return { kind: "clear" };
113
+ case "login":
114
+ case "signin":
115
+ return { kind: "login" };
116
+ case "logout":
117
+ case "signout":
118
+ return { kind: "logout" };
119
+ case "delete-profile":
120
+ case "delete_profile":
121
+ return { kind: "delete_profile" };
122
+ case "switch-profile":
123
+ case "switch_profile":
124
+ return { kind: "switch_profile", name: rest.join(" ").trim() || undefined };
125
+ case "auth":
126
+ return { kind: "auth_status" };
127
+ case "config":
128
+ case "settings": {
129
+ const [field, ...valueParts] = rest;
130
+ if (!field)
131
+ return { kind: "config" };
132
+ if (field === "preset") {
133
+ return { kind: "config", field, value: valueParts.join(" ").trim() || undefined };
134
+ }
135
+ return { kind: "invalid", command: `${name} ${field}` };
136
+ }
137
+ case "context":
138
+ return { kind: "context", enabled: parseOnOff(rest[0]) };
139
+ case "access": {
140
+ const mode = rest[0];
141
+ if (mode === "off" || mode === "approval" || mode === "full")
142
+ return { kind: "access", mode };
143
+ return { kind: "access" };
144
+ }
145
+ case "preset": {
146
+ const value = rest.join(" ").trim();
147
+ return { kind: "preset", value: value || undefined };
148
+ }
149
+ case "model": {
150
+ const value = rest.join(" ").trim();
151
+ return { kind: "model", value: value || undefined };
152
+ }
153
+ case "workdir":
154
+ case "local":
155
+ return { kind: "workdir", enabled: parseOnOff(rest[0]) };
156
+ case "summary":
157
+ return { kind: "summary" };
158
+ case "new":
159
+ case "thread":
160
+ return { kind: "new_conversation", name: rest.join(" ").trim() || undefined };
161
+ case "conversation":
162
+ case "switch":
163
+ case "use":
164
+ if (rest.length === 0)
165
+ return { kind: "list_conversations" };
166
+ return { kind: "switch_conversation", name: rest.join(" ").trim() };
167
+ case "conversations":
168
+ case "threads":
169
+ return { kind: "list_conversations" };
170
+ case "refresh":
171
+ case "reload":
172
+ case "refresh-catalog":
173
+ return { kind: "refresh_catalog" };
174
+ case "search":
175
+ case "grep":
176
+ return { kind: "search", query: rest.join(" ").trim() };
177
+ case "preview":
178
+ return { kind: "preview" };
179
+ case "apply":
180
+ return { kind: "apply" };
181
+ case "apply-all":
182
+ case "yes-all":
183
+ return { kind: "apply_all" };
184
+ case "reject":
185
+ return { kind: "reject" };
186
+ default:
187
+ return { kind: "invalid", command: name };
188
+ }
189
+ }
190
+ export function parsePendingApprovalCommand(input) {
191
+ const trimmed = input.trim().toLowerCase();
192
+ if (!trimmed.startsWith("/"))
193
+ return null;
194
+ const [name = ""] = trimmed.slice(1).split(/\s+/);
195
+ switch (name) {
196
+ case "apply":
197
+ case "yes":
198
+ return { kind: "apply" };
199
+ case "apply-all":
200
+ case "yes-all":
201
+ return { kind: "apply_all" };
202
+ case "reject":
203
+ case "no":
204
+ return { kind: "reject" };
205
+ default:
206
+ return null;
207
+ }
208
+ }
209
+ export function helpText() {
210
+ return [
211
+ "Commands:",
212
+ "/auth show current auth profile",
213
+ "/login return to auth gate without deleting profiles",
214
+ "/logout leave current session and return to auth gate",
215
+ "/switch-profile switch/sign in with a different profile",
216
+ "/delete-profile delete current saved profile and return to auth",
217
+ "/config show current run configuration and saved defaults",
218
+ "/config preset save default preset; use none/off for no preset, reset for built-in",
219
+ "/preset [name] show or set preset; use none/off to clear",
220
+ "/model [name] show or set explicit model; use auto/none/off to clear",
221
+ "/access [mode] show or set local tool access: off, approval, or full",
222
+ "/workdir show local workdir status",
223
+ "/workdir on shortcut for /access approval; /workdir off hides local tools",
224
+ "/new [name] start a fresh conversation in this workbench",
225
+ "/switch <name> switch to an existing/new conversation handle",
226
+ "/conversations list saved local conversation handles",
227
+ "/summary show local workdir summary previews",
228
+ "/search <query> search text in the local workdir",
229
+ "/preview show pending local action preview",
230
+ "/apply apply pending local action",
231
+ "/apply-all apply pending action and allow future local actions",
232
+ "/reject reject pending local action",
233
+ "/abort cancel the in-flight agent turn",
234
+ "/context toggle local context packaging for each agent turn",
235
+ "/clear clear the visible terminal transcript",
236
+ "/quit leave the workbench",
237
+ ].join("\n");
238
+ }
239
+ function parseOnOff(value) {
240
+ if (!value)
241
+ return undefined;
242
+ const normalized = value.toLowerCase();
243
+ if (["on", "enable", "enabled", "yes", "true"].includes(normalized))
244
+ return true;
245
+ if (["off", "disable", "disabled", "no", "false"].includes(normalized))
246
+ return false;
247
+ return undefined;
248
+ }
249
+ export function workdirText(status) {
250
+ if (!status)
251
+ return "Workdir summary is still loading.";
252
+ return [
253
+ `Workdir: ${status.root}`,
254
+ `Files: ${status.fileCount}`,
255
+ `Size: ${formatBytes(status.totalBytes)}`,
256
+ `Scan truncated: ${status.scanTruncated ? "yes" : "no"}`,
257
+ ].join("\n");
258
+ }
259
+ export function formatBytes(bytes) {
260
+ if (bytes < 1024)
261
+ return `${bytes} B`;
262
+ if (bytes < 1024 * 1024)
263
+ return `${(bytes / 1024).toFixed(1)} KB`;
264
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
265
+ }
266
+ export function activityColor(level) {
267
+ if (level === "success")
268
+ return "green";
269
+ if (level === "warning")
270
+ return "yellow";
271
+ if (level === "error")
272
+ return "red";
273
+ return "gray";
274
+ }
275
+ function newMessage(role, text, id = randomId()) {
276
+ return { id, role, text };
277
+ }
278
+ function newActivity(level, text) {
279
+ return {
280
+ id: randomId(),
281
+ level,
282
+ text,
283
+ timestamp: Date.now(),
284
+ };
285
+ }
286
+ function randomId() {
287
+ return Math.random().toString(36).slice(2);
288
+ }
@@ -0,0 +1,16 @@
1
+ export interface UpdateCheckResult {
2
+ current: string;
3
+ latest: string;
4
+ packageName: string;
5
+ updateAvailable: boolean;
6
+ }
7
+ export interface UpdateCheckOptions {
8
+ currentVersion?: string;
9
+ packageName?: string;
10
+ registryURL?: string;
11
+ signal?: AbortSignal;
12
+ timeoutMs?: number;
13
+ }
14
+ export declare function checkForUpdate(options?: UpdateCheckOptions): Promise<UpdateCheckResult | null>;
15
+ export declare function formatUpdateNotice(result: UpdateCheckResult): string;
16
+ export declare function compareVersions(a: string, b: string): number;
package/dist/update.js ADDED
@@ -0,0 +1,74 @@
1
+ import { cliVersion } from "./runtime/index.js";
2
+ const defaultPackageName = "@agent-api/cli";
3
+ const defaultRegistryURL = "https://registry.npmjs.org";
4
+ export async function checkForUpdate(options = {}) {
5
+ const packageName = options.packageName || process.env.AGENT_TUI_UPDATE_PACKAGE || defaultPackageName;
6
+ const current = options.currentVersion || cliVersion;
7
+ const registryURL = (options.registryURL || process.env.AGENT_TUI_NPM_REGISTRY || defaultRegistryURL).replace(/\/+$/, "");
8
+ const timeoutMs = options.timeoutMs ?? 1500;
9
+ const controller = new AbortController();
10
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
11
+ const signal = combineSignals(options.signal, controller.signal);
12
+ try {
13
+ const response = await fetch(`${registryURL}/${encodeURIComponent(packageName).replace(/^%40/, "@")}/latest`, {
14
+ headers: { Accept: "application/json" },
15
+ signal,
16
+ });
17
+ if (!response.ok)
18
+ return null;
19
+ const payload = await response.json().catch(() => undefined);
20
+ const latest = typeof payload?.version === "string" ? payload.version : "";
21
+ if (!latest)
22
+ return null;
23
+ return {
24
+ current,
25
+ latest,
26
+ packageName,
27
+ updateAvailable: compareVersions(latest, current) > 0,
28
+ };
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ finally {
34
+ clearTimeout(timer);
35
+ }
36
+ }
37
+ export function formatUpdateNotice(result) {
38
+ return `Update available: ${result.packageName} ${result.current} -> ${result.latest}. Run: npm install -g ${result.packageName}@latest`;
39
+ }
40
+ export function compareVersions(a, b) {
41
+ const left = parseVersion(a);
42
+ const right = parseVersion(b);
43
+ for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
44
+ const delta = (left[index] ?? 0) - (right[index] ?? 0);
45
+ if (delta !== 0)
46
+ return delta > 0 ? 1 : -1;
47
+ }
48
+ return 0;
49
+ }
50
+ function parseVersion(version) {
51
+ return version
52
+ .replace(/^[^\d]*/, "")
53
+ .split(/[.-]/)
54
+ .slice(0, 3)
55
+ .map((part) => {
56
+ const parsed = Number.parseInt(part, 10);
57
+ return Number.isFinite(parsed) ? parsed : 0;
58
+ });
59
+ }
60
+ function combineSignals(...signals) {
61
+ const active = signals.filter((signal) => Boolean(signal));
62
+ if (active.length === 1)
63
+ return active[0];
64
+ const controller = new AbortController();
65
+ const abort = () => controller.abort();
66
+ for (const signal of active) {
67
+ if (signal.aborted) {
68
+ controller.abort();
69
+ break;
70
+ }
71
+ signal.addEventListener("abort", abort, { once: true });
72
+ }
73
+ return controller.signal;
74
+ }
@@ -0,0 +1,22 @@
1
+ import { type LocalContextManifest, type LocalSummary, type LocalWorkdir, type LocalWorkdirSnapshot } from "@agent-api/sdk/local";
2
+ export interface WorkdirOptions {
3
+ path?: string;
4
+ name?: string;
5
+ }
6
+ export interface WorkdirContextOptions extends WorkdirOptions {
7
+ query?: string;
8
+ maxFiles?: number;
9
+ maxBytes?: number;
10
+ includeContent?: boolean;
11
+ }
12
+ export interface WorkdirService {
13
+ root: string;
14
+ name: string;
15
+ workdir: LocalWorkdir;
16
+ summarize(): Promise<LocalSummary>;
17
+ snapshot(): Promise<LocalWorkdirSnapshot>;
18
+ packageContext(options?: Omit<WorkdirContextOptions, "path" | "name">): Promise<LocalContextManifest>;
19
+ contextBlock(options?: Omit<WorkdirContextOptions, "path" | "name">): Promise<string>;
20
+ }
21
+ export declare function openWorkdir(options?: WorkdirOptions): Promise<WorkdirService>;
22
+ export declare function buildWorkdirContextBlock(options: WorkdirContextOptions): Promise<string>;
@@ -0,0 +1,46 @@
1
+ import { createLocalContextPackage, } from "@agent-api/sdk/local";
2
+ import { resolve } from "node:path";
3
+ import { runtime } from "../runtime/index.js";
4
+ export async function openWorkdir(options = {}) {
5
+ await runtime.ensure();
6
+ const root = resolve(options.path || process.cwd());
7
+ const name = options.name || root.split(/[\\/]/).filter(Boolean).at(-1) || "workdir";
8
+ const workdir = runtime.workdir(root, {
9
+ name,
10
+ trusted: true,
11
+ gitignore: true,
12
+ });
13
+ return {
14
+ root,
15
+ name,
16
+ workdir,
17
+ summarize: () => workdir.summarize(),
18
+ snapshot: () => workdir.snapshot({ hash: true }),
19
+ packageContext: (contextOptions = {}) => createLocalContextPackage(workdir, {
20
+ query: contextOptions.query,
21
+ includeSearch: Boolean(contextOptions.query),
22
+ maxFiles: contextOptions.maxFiles,
23
+ maxBytes: contextOptions.maxBytes,
24
+ includeContent: contextOptions.includeContent,
25
+ }),
26
+ contextBlock: async (contextOptions = {}) => {
27
+ const context = await createLocalContextPackage(workdir, {
28
+ query: contextOptions.query,
29
+ includeSearch: Boolean(contextOptions.query),
30
+ maxFiles: contextOptions.maxFiles,
31
+ maxBytes: contextOptions.maxBytes,
32
+ includeContent: contextOptions.includeContent,
33
+ });
34
+ return [
35
+ "Local workdir context follows. Use it as user-provided project context; do not assume files outside this manifest exist.",
36
+ "```json",
37
+ JSON.stringify(context, null, 2),
38
+ "```",
39
+ ].join("\n");
40
+ },
41
+ };
42
+ }
43
+ export async function buildWorkdirContextBlock(options) {
44
+ const service = await openWorkdir(options);
45
+ return service.contextBlock(options);
46
+ }
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@agent-api/cli",
3
+ "version": "0.0.1",
4
+ "description": "First-class command line interface for Agent API",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/scalebox-dev/agent-tui#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/scalebox-dev/agent-tui.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/scalebox-dev/agent-tui/issues"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "agent-api": "dist/index.js",
17
+ "agentsway": "dist/index.js",
18
+ "agent-tui": "dist/index.js"
19
+ },
20
+ "scripts": {
21
+ "sync-version": "node scripts/sync-version.mjs",
22
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
23
+ "build": "npm run clean && tsc -p tsconfig.json",
24
+ "dev": "npm run build && node dist/index.js",
25
+ "dev:link": "node scripts/dev-link.mjs",
26
+ "start": "node dist/index.js",
27
+ "smoke": "node dist/index.js --version && node dist/index.js --help",
28
+ "pack:local": "npm pack --pack-destination ./artifacts",
29
+ "release:local": "node scripts/release-local.mjs",
30
+ "prepack": "npm run sync-version && npm run build",
31
+ "test": "npm run sync-version && npm run build && node --test test/*.test.mjs"
32
+ },
33
+ "dependencies": {
34
+ "@agent-api/sdk": "^1.2.2",
35
+ "commander": "^14.0.3",
36
+ "ink": "^6.8.0",
37
+ "react": "^19.2.7",
38
+ "zod": "^4.4.3"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^24.0.0",
42
+ "@types/react": "^19.2.17",
43
+ "typescript": "^5.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=20"
47
+ },
48
+ "os": [
49
+ "darwin",
50
+ "linux",
51
+ "win32"
52
+ ],
53
+ "files": [
54
+ "dist",
55
+ "README.md",
56
+ "package.json"
57
+ ],
58
+ "publishConfig": {
59
+ "access": "public"
60
+ }
61
+ }