naro-mcp 0.19.0 → 0.20.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.js +193 -38
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -15334,6 +15334,7 @@ var NaroClient = class {
|
|
|
15334
15334
|
db;
|
|
15335
15335
|
backend;
|
|
15336
15336
|
storage;
|
|
15337
|
+
realtime;
|
|
15337
15338
|
insights;
|
|
15338
15339
|
analytics;
|
|
15339
15340
|
protection;
|
|
@@ -15353,6 +15354,7 @@ var NaroClient = class {
|
|
|
15353
15354
|
this.db = new DbApi(this);
|
|
15354
15355
|
this.backend = new BackendApi(this);
|
|
15355
15356
|
this.storage = new StorageApi(this);
|
|
15357
|
+
this.realtime = new RealtimeApi(this);
|
|
15356
15358
|
this.insights = new InsightsApi(this);
|
|
15357
15359
|
this.analytics = new AnalyticsApi(this);
|
|
15358
15360
|
this.protection = new ProtectionApi(this);
|
|
@@ -16378,9 +16380,9 @@ function storageInfo(body) {
|
|
|
16378
16380
|
buckets: (body.buckets ?? []).map(storageBucket)
|
|
16379
16381
|
};
|
|
16380
16382
|
}
|
|
16381
|
-
function
|
|
16383
|
+
function provisionFailure(error51, feature) {
|
|
16382
16384
|
const details = isNaroError(error51) ? error51.details : void 0;
|
|
16383
|
-
if (isNaroError(error51) && error51.code === "BACKEND_PROVISION_FAILED" && isRecord(details?.
|
|
16385
|
+
if (isNaroError(error51) && error51.code === "BACKEND_PROVISION_FAILED" && isRecord(details?.[feature]) && isRecord(details.backend))
|
|
16384
16386
|
return new NaroError(error51.code, error51.message, {
|
|
16385
16387
|
...error51.status !== void 0 ? { status: error51.status } : {},
|
|
16386
16388
|
details: {
|
|
@@ -16818,7 +16820,7 @@ var StorageApi = class {
|
|
|
16818
16820
|
}
|
|
16819
16821
|
);
|
|
16820
16822
|
} catch (error51) {
|
|
16821
|
-
throw
|
|
16823
|
+
throw provisionFailure(error51, "storage");
|
|
16822
16824
|
}
|
|
16823
16825
|
return storageInfo(body.storage);
|
|
16824
16826
|
}
|
|
@@ -16856,7 +16858,7 @@ var StorageApi = class {
|
|
|
16856
16858
|
}
|
|
16857
16859
|
);
|
|
16858
16860
|
} catch (error51) {
|
|
16859
|
-
throw
|
|
16861
|
+
throw provisionFailure(error51, "storage");
|
|
16860
16862
|
}
|
|
16861
16863
|
return storageInfo(body.storage);
|
|
16862
16864
|
}
|
|
@@ -17015,6 +17017,114 @@ var StorageApi = class {
|
|
|
17015
17017
|
return body.objects;
|
|
17016
17018
|
}
|
|
17017
17019
|
};
|
|
17020
|
+
var RealtimeApi = class {
|
|
17021
|
+
constructor(client) {
|
|
17022
|
+
this.client = client;
|
|
17023
|
+
}
|
|
17024
|
+
path(projectId, suffix = "") {
|
|
17025
|
+
return `/api/projects/${encodeURIComponent(projectId)}/backend/realtime${suffix}`;
|
|
17026
|
+
}
|
|
17027
|
+
/**
|
|
17028
|
+
* Like `StorageApi.bucketPath`: URL parsing folds `.` and `..`, an empty
|
|
17029
|
+
* name is the collection, and `encodeURIComponent` throws on a lone
|
|
17030
|
+
* surrogate — so a name no URL can carry is refused here, before a
|
|
17031
|
+
* request. SQLite does make tables named `.` and `..`, and one with the
|
|
17032
|
+
* empty name (`CREATE TABLE "" (a)`); such a table cannot be published
|
|
17033
|
+
* or unpublished through this API.
|
|
17034
|
+
*/
|
|
17035
|
+
tablePath(projectId, table) {
|
|
17036
|
+
if (table === "" || table === "." || table === ".." || LONE_SURROGATE.test(table))
|
|
17037
|
+
throw new NaroError("INVALID_PATH", "The table name is not valid.");
|
|
17038
|
+
return this.path(projectId, `/tables/${encodeURIComponent(table)}`);
|
|
17039
|
+
}
|
|
17040
|
+
/**
|
|
17041
|
+
* Whether Realtime is on, the limits the hub enforces, the published
|
|
17042
|
+
* tables and the hub's own numbers. A project that never turned it on
|
|
17043
|
+
* answers `enabled: false` with no tables.
|
|
17044
|
+
*/
|
|
17045
|
+
async info(projectId) {
|
|
17046
|
+
const body = await this.client.request(
|
|
17047
|
+
this.path(projectId),
|
|
17048
|
+
{ context: "Read realtime" }
|
|
17049
|
+
);
|
|
17050
|
+
return body.realtime;
|
|
17051
|
+
}
|
|
17052
|
+
/**
|
|
17053
|
+
* Turns Realtime on: the request and the plan's limits are recorded, and
|
|
17054
|
+
* the Naro worker is uploaded again with its hub (a Durable Object). Safe
|
|
17055
|
+
* to repeat, and a repair — also after a plan change. The project must have
|
|
17056
|
+
* a backend, turned on, with its database. A backend in `error` or with a
|
|
17057
|
+
* run that stalled is not refused: its provisioning is resumed on the way.
|
|
17058
|
+
* Two minutes, like `backend.enable`.
|
|
17059
|
+
*
|
|
17060
|
+
* Refusals, in the order they are checked: `BACKEND_DISABLED` (the control
|
|
17061
|
+
* plane's backend switch), `FORBIDDEN` (`database.write`),
|
|
17062
|
+
* `REALTIME_DISABLED` (the control plane's Realtime switch),
|
|
17063
|
+
* `DATABASE_NOT_ENABLED` (the project's database is external),
|
|
17064
|
+
* `BACKEND_NOT_READY` (409, only when the project has no backend at all),
|
|
17065
|
+
* `BACKEND_OFF`, `BACKEND_BUSY`, `REALTIME_ENABLE_FAILED` (502: the request
|
|
17066
|
+
* could not be recorded; nothing changed), and `BACKEND_PROVISION_FAILED`
|
|
17067
|
+
* (502) when Realtime is recorded as on but the worker's upload stopped.
|
|
17068
|
+
* The last one's `details` hold a `RealtimeProvisionFailure` — `{ realtime:
|
|
17069
|
+
* { applied: false, enabled: true }, backend }`, the backend read like
|
|
17070
|
+
* `backend.info` — and `enable` again finishes.
|
|
17071
|
+
*/
|
|
17072
|
+
async enable(projectId) {
|
|
17073
|
+
let body;
|
|
17074
|
+
try {
|
|
17075
|
+
body = await this.client.request(
|
|
17076
|
+
this.path(projectId),
|
|
17077
|
+
{
|
|
17078
|
+
method: "POST",
|
|
17079
|
+
context: "Turn on Realtime",
|
|
17080
|
+
timeoutMs: PROVISION_TIMEOUT_MS
|
|
17081
|
+
}
|
|
17082
|
+
);
|
|
17083
|
+
} catch (error51) {
|
|
17084
|
+
throw provisionFailure(error51, "realtime");
|
|
17085
|
+
}
|
|
17086
|
+
return body.realtime;
|
|
17087
|
+
}
|
|
17088
|
+
/**
|
|
17089
|
+
* The tables whose Data API writes are published, in the order they were
|
|
17090
|
+
* published. `exists: false` is a published name whose table was dropped.
|
|
17091
|
+
* `REALTIME_NOT_ENABLED` (409) before `enable`; it answers while the
|
|
17092
|
+
* backend is off, since the list is in the project's database.
|
|
17093
|
+
*/
|
|
17094
|
+
async listTables(projectId) {
|
|
17095
|
+
const body = await this.client.request(
|
|
17096
|
+
this.path(projectId, "/tables"),
|
|
17097
|
+
{ context: "List realtime tables" }
|
|
17098
|
+
);
|
|
17099
|
+
return body.tables;
|
|
17100
|
+
}
|
|
17101
|
+
/**
|
|
17102
|
+
* Publishes a table's changes to the subscribers of its channel. Safe to
|
|
17103
|
+
* repeat. Only changes made through the Data API are published — writes
|
|
17104
|
+
* the app makes with `env.DB` are not (`warnings` says so).
|
|
17105
|
+
* `TABLE_NOT_FOUND` (404) for a table that does not exist;
|
|
17106
|
+
* `VALIDATION_FAILED` (400) for a Naro system table, a virtual table, a
|
|
17107
|
+
* name over 256 characters, or the 100-table cap; `REALTIME_NOT_ENABLED`
|
|
17108
|
+
* (409) before `enable`.
|
|
17109
|
+
*/
|
|
17110
|
+
async publishTable(projectId, table) {
|
|
17111
|
+
return this.client.request(
|
|
17112
|
+
this.tablePath(projectId, table),
|
|
17113
|
+
{ method: "PUT", context: "Publish a realtime table" }
|
|
17114
|
+
);
|
|
17115
|
+
}
|
|
17116
|
+
/**
|
|
17117
|
+
* Stops publishing a table's changes; a table dropped since can still be
|
|
17118
|
+
* unpublished. `TABLE_NOT_PUBLISHED` (404) for a table that does not
|
|
17119
|
+
* publish, `REALTIME_NOT_ENABLED` (409) before `enable`.
|
|
17120
|
+
*/
|
|
17121
|
+
async unpublishTable(projectId, table) {
|
|
17122
|
+
return this.client.request(
|
|
17123
|
+
this.tablePath(projectId, table),
|
|
17124
|
+
{ method: "DELETE", context: "Stop publishing a realtime table" }
|
|
17125
|
+
);
|
|
17126
|
+
}
|
|
17127
|
+
};
|
|
17018
17128
|
|
|
17019
17129
|
// ../../packages/sdk/src/database.ts
|
|
17020
17130
|
var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
|
|
@@ -17041,6 +17151,39 @@ var MAX_FILE_BYTES2 = 1024 * 1024;
|
|
|
17041
17151
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
17042
17152
|
import { join as join4 } from "path";
|
|
17043
17153
|
|
|
17154
|
+
// ../../packages/data-core/src/system-tables.ts
|
|
17155
|
+
var SYSTEM_TABLE_PREFIXES = [
|
|
17156
|
+
"_naro_",
|
|
17157
|
+
"_cf_",
|
|
17158
|
+
"sqlite_"
|
|
17159
|
+
];
|
|
17160
|
+
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
17161
|
+
function asciiLower(text) {
|
|
17162
|
+
return text.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
17163
|
+
}
|
|
17164
|
+
function isSystemTable(name) {
|
|
17165
|
+
const lower = asciiLower(name);
|
|
17166
|
+
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
17167
|
+
}
|
|
17168
|
+
function likeLiteral(text) {
|
|
17169
|
+
return text.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
17170
|
+
}
|
|
17171
|
+
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
17172
|
+
function systemTablePredicate(column = "name") {
|
|
17173
|
+
if (!COLUMN_REFERENCE.test(column))
|
|
17174
|
+
throw new Error(`Not a column reference: ${column}`);
|
|
17175
|
+
return [
|
|
17176
|
+
...SYSTEM_TABLE_PREFIXES.map(
|
|
17177
|
+
(prefix) => `${column} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
17178
|
+
),
|
|
17179
|
+
...SYSTEM_TABLE_NAMES.map(
|
|
17180
|
+
(name) => `${column} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
17181
|
+
),
|
|
17182
|
+
`${column} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
17183
|
+
].join(" AND ");
|
|
17184
|
+
}
|
|
17185
|
+
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
17186
|
+
|
|
17044
17187
|
// ../../packages/data-core/src/storage-path.ts
|
|
17045
17188
|
var STORAGE_RESERVED_WORDS = [
|
|
17046
17189
|
"authenticated",
|
|
@@ -17118,39 +17261,6 @@ var STORAGE_BUCKET_DELETING_COLUMN = {
|
|
|
17118
17261
|
definition: "TEXT"
|
|
17119
17262
|
};
|
|
17120
17263
|
|
|
17121
|
-
// ../../packages/data-core/src/system-tables.ts
|
|
17122
|
-
var SYSTEM_TABLE_PREFIXES = [
|
|
17123
|
-
"_naro_",
|
|
17124
|
-
"_cf_",
|
|
17125
|
-
"sqlite_"
|
|
17126
|
-
];
|
|
17127
|
-
var SYSTEM_TABLE_NAMES = ["d1_migrations"];
|
|
17128
|
-
function asciiLower(text) {
|
|
17129
|
-
return text.replace(/[A-Z]+/g, (run) => run.toLowerCase());
|
|
17130
|
-
}
|
|
17131
|
-
function isSystemTable(name) {
|
|
17132
|
-
const lower = asciiLower(name);
|
|
17133
|
-
return SYSTEM_TABLE_PREFIXES.some((prefix) => lower.startsWith(prefix)) || SYSTEM_TABLE_NAMES.includes(lower);
|
|
17134
|
-
}
|
|
17135
|
-
function likeLiteral(text) {
|
|
17136
|
-
return text.replace(/[\\%_]/g, (char) => `\\${char}`).replace(/'/g, "''");
|
|
17137
|
-
}
|
|
17138
|
-
var COLUMN_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
17139
|
-
function systemTablePredicate(column = "name") {
|
|
17140
|
-
if (!COLUMN_REFERENCE.test(column))
|
|
17141
|
-
throw new Error(`Not a column reference: ${column}`);
|
|
17142
|
-
return [
|
|
17143
|
-
...SYSTEM_TABLE_PREFIXES.map(
|
|
17144
|
-
(prefix) => `${column} NOT LIKE '${likeLiteral(prefix)}%' ESCAPE '\\'`
|
|
17145
|
-
),
|
|
17146
|
-
...SYSTEM_TABLE_NAMES.map(
|
|
17147
|
-
(name) => `${column} NOT LIKE '${likeLiteral(name)}' ESCAPE '\\'`
|
|
17148
|
-
),
|
|
17149
|
-
`${column} NOT IN (SELECT name FROM pragma_table_list WHERE schema = 'main' AND type = 'shadow')`
|
|
17150
|
-
].join(" AND ");
|
|
17151
|
-
}
|
|
17152
|
-
var SYSTEM_TABLE_SQL_PREDICATE = systemTablePredicate("name");
|
|
17153
|
-
|
|
17154
17264
|
// ../../packages/data-core/src/errors.ts
|
|
17155
17265
|
var STORAGE_MIGRATION_PENDING = new RegExp(
|
|
17156
17266
|
`^no such column: (?:b\\.|${STORAGE_BUCKET_DELETING_COLUMN.table}\\.)?${STORAGE_BUCKET_DELETING_COLUMN.column}\\b`
|
|
@@ -19396,8 +19506,34 @@ var MAX_STATEMENT_BYTES = 1e5;
|
|
|
19396
19506
|
// ../../packages/data-core/src/policy-index.ts
|
|
19397
19507
|
var INDEX_SQL = `SELECT t.name AS table_name, il.name AS index_name, il."partial" AS partial, ix.name AS column_name, ix.coll AS collation FROM pragma_table_list AS t JOIN pragma_index_list(t.name, t.schema) AS il JOIN pragma_index_xinfo(il.name, t.schema) AS ix WHERE t.schema = 'main' AND t.type = 'table' AND ${systemTablePredicate("t.name")} AND ix.seqno = 0`;
|
|
19398
19508
|
|
|
19509
|
+
// ../../packages/data-core/src/realtime-filter.ts
|
|
19510
|
+
var FILTER_REASONS = {
|
|
19511
|
+
bytes: "must be a string of up to 1024 bytes in UTF-8",
|
|
19512
|
+
control: "must have no control character",
|
|
19513
|
+
syntax: "supports eq, neq, lt, lte, gt, gte and in, one condition",
|
|
19514
|
+
list: "needs in.(a,b) with 1 to 100 values"
|
|
19515
|
+
};
|
|
19516
|
+
|
|
19399
19517
|
// ../../packages/data-core/src/realtime-protocol.ts
|
|
19400
19518
|
var REALTIME_PROTOCOL_VERSION = "1.0.0";
|
|
19519
|
+
var MAX_CHANGE_CHANNELS_PER_TABLE = 4;
|
|
19520
|
+
var JOIN_CONFIG_REASONS = {
|
|
19521
|
+
object: "must be an object",
|
|
19522
|
+
unsupported: "is not supported",
|
|
19523
|
+
boolean: "must be true or false",
|
|
19524
|
+
presenceKey: "must be 1 to 128 characters",
|
|
19525
|
+
changes: "must be an array of up to 16 subscriptions",
|
|
19526
|
+
changeEvent: "must be *, INSERT, UPDATE or DELETE",
|
|
19527
|
+
schema: "must be public",
|
|
19528
|
+
table: "must be a table name of 1 to 128 characters, or *",
|
|
19529
|
+
filter: FILTER_REASONS.bytes,
|
|
19530
|
+
filterControl: FILTER_REASONS.control,
|
|
19531
|
+
filterSyntax: FILTER_REASONS.syntax,
|
|
19532
|
+
filterList: FILTER_REASONS.list,
|
|
19533
|
+
filterTable: "needs a table: a filter cannot go with *",
|
|
19534
|
+
/** The hub's, after `config.postgres_changes[<i>].table` (3-2c-1): `MAX_CHANGE_CHANNELS_PER_TABLE` against the connection's joins. */
|
|
19535
|
+
changeChannels: `would put more than ${String(MAX_CHANGE_CHANNELS_PER_TABLE)} channels of this connection on one table's changes (* counts for every table): add the subscription to a channel it already has`
|
|
19536
|
+
};
|
|
19401
19537
|
var REALTIME_CLOSE = {
|
|
19402
19538
|
UNSUPPORTED_PROTOCOL: {
|
|
19403
19539
|
code: 4e3,
|
|
@@ -19486,6 +19622,12 @@ var LIVE_BUCKET = `${STORAGE_BUCKET_DELETING_COLUMN.column} IS NULL`;
|
|
|
19486
19622
|
var STORAGE_PROBE_NONCE = "f".repeat(STORAGE_NONCE_MAX_DIGITS);
|
|
19487
19623
|
var REALTIME_PROBE_NONCE = "f".repeat(64);
|
|
19488
19624
|
|
|
19625
|
+
// ../../packages/data-core/src/realtime-publication.ts
|
|
19626
|
+
var REALTIME_TABLES = "_naro_realtime_tables";
|
|
19627
|
+
var REALTIME_TABLES_SQL = `CREATE TABLE IF NOT EXISTS ${REALTIME_TABLES} (table_name TEXT NOT NULL PRIMARY KEY COLLATE NOCASE, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`;
|
|
19628
|
+
var MAX_PUBLISHED_TABLES = 100;
|
|
19629
|
+
var PUBLISHED_TABLES_SQL = `SELECT table_name FROM ${REALTIME_TABLES} ORDER BY rowid LIMIT ${String(MAX_PUBLISHED_TABLES)}`;
|
|
19630
|
+
|
|
19489
19631
|
// ../../packages/data-core/src/system-sql.ts
|
|
19490
19632
|
async function convertLegacyPolicies(db, found) {
|
|
19491
19633
|
const rows = rowsOf(await db.prepare(POLICY_SQL.list).all());
|
|
@@ -19519,6 +19661,7 @@ var MIGRATIONS_TABLE = "CREATE TABLE IF NOT EXISTS _naro_migrations (version INT
|
|
|
19519
19661
|
var STORAGE_SUBQUERIES_VERSION = 7;
|
|
19520
19662
|
var STORAGE_SUBQUERIES_NAME = "storage_subquery_policies";
|
|
19521
19663
|
var STORAGE_SUBQUERIES_SQL = `SELECT version FROM _naro_migrations WHERE version = ${String(STORAGE_SUBQUERIES_VERSION)} AND name = '${STORAGE_SUBQUERIES_NAME}'`;
|
|
19664
|
+
var REALTIME_TABLES_VERSION = 8;
|
|
19522
19665
|
var SYSTEM_MIGRATIONS = [
|
|
19523
19666
|
{
|
|
19524
19667
|
version: 1,
|
|
@@ -19607,6 +19750,14 @@ var SYSTEM_MIGRATIONS = [
|
|
|
19607
19750
|
version: STORAGE_SUBQUERIES_VERSION,
|
|
19608
19751
|
name: STORAGE_SUBQUERIES_NAME,
|
|
19609
19752
|
statements: []
|
|
19753
|
+
},
|
|
19754
|
+
{
|
|
19755
|
+
// Realtime 3-2b (original spec §9.1, design §7.1): the tables whose
|
|
19756
|
+
// Data API writes reach the hub. Empty until the publish API (3-2c)
|
|
19757
|
+
// writes it. A worker from before it never reads it.
|
|
19758
|
+
version: REALTIME_TABLES_VERSION,
|
|
19759
|
+
name: "realtime",
|
|
19760
|
+
statements: [REALTIME_TABLES_SQL]
|
|
19610
19761
|
}
|
|
19611
19762
|
];
|
|
19612
19763
|
|
|
@@ -19648,6 +19799,10 @@ var ENTERPRISE_REALTIME_LIMITS = Object.freeze({
|
|
|
19648
19799
|
restRequestsPerSecond: 100
|
|
19649
19800
|
});
|
|
19650
19801
|
|
|
19802
|
+
// ../../packages/data-core/src/realtime-changes.ts
|
|
19803
|
+
var CHANGE_TYPES = Object.freeze({ insert: "INSERT", update: "UPDATE", delete: "DELETE" });
|
|
19804
|
+
var CHANGE_EVENT_CODES = Object.freeze({ "*": 0, INSERT: 1, UPDATE: 2, DELETE: 3 });
|
|
19805
|
+
|
|
19651
19806
|
// ../../packages/data-core/src/storage-multipart.ts
|
|
19652
19807
|
var MiB = 1024 * 1024;
|
|
19653
19808
|
var MULTIPART_PART_SIZES = [16 * MiB, 32 * MiB, 64 * MiB];
|
|
@@ -23010,7 +23165,7 @@ async function openClaims(client, projectId) {
|
|
|
23010
23165
|
}
|
|
23011
23166
|
|
|
23012
23167
|
// src/server.ts
|
|
23013
|
-
var SERVER_VERSION = "0.
|
|
23168
|
+
var SERVER_VERSION = "0.20.0";
|
|
23014
23169
|
function visibleTools(options2) {
|
|
23015
23170
|
const { features, readOnly } = options2;
|
|
23016
23171
|
return TOOLS.flatMap((tool) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naro-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"naro-mcp": "./dist/index.js"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
20
20
|
"@supabase/supabase-js": "2.112.3",
|
|
21
|
-
"naro-cli": "0.
|
|
21
|
+
"naro-cli": "0.21.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"tsup": "8.5.0",
|