From 1652569cae4ca1d51e33b4a4f7838dfc8671873b Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Mon, 8 Jun 2026 14:53:37 -0700 Subject: [PATCH 1/7] feat: add local CLI MVP --- .gitignore | 2 + backend/src/env.ts | 11 + backend/src/index.ts | 247 +++++++ bigset-cli-mvp.excalidraw | 65 ++ cli/bin/run.js | 2 + cli/package-lock.json | 1094 ++++++++++++++++++++++++++++++++ cli/package.json | 23 + cli/src/client.ts | 169 +++++ cli/src/commands/create.ts | 161 +++++ cli/src/commands/export.ts | 47 ++ cli/src/commands/list.ts | 29 + cli/src/commands/populate.ts | 54 ++ cli/src/commands/rows.ts | 42 ++ cli/src/commands/status.ts | 33 + cli/src/commands/stop.ts | 29 + cli/src/config.ts | 16 + cli/src/csv.ts | 20 + cli/src/index.ts | 57 ++ cli/tsconfig.json | 14 + frontend/convex/datasetRows.ts | 16 + frontend/convex/datasets.ts | 56 ++ makefiles/Makefile | 1 + 22 files changed, 2188 insertions(+) create mode 100644 bigset-cli-mvp.excalidraw create mode 100755 cli/bin/run.js create mode 100644 cli/package-lock.json create mode 100644 cli/package.json create mode 100644 cli/src/client.ts create mode 100644 cli/src/commands/create.ts create mode 100644 cli/src/commands/export.ts create mode 100644 cli/src/commands/list.ts create mode 100644 cli/src/commands/populate.ts create mode 100644 cli/src/commands/rows.ts create mode 100644 cli/src/commands/status.ts create mode 100644 cli/src/commands/stop.ts create mode 100644 cli/src/config.ts create mode 100644 cli/src/csv.ts create mode 100644 cli/src/index.ts create mode 100644 cli/tsconfig.json diff --git a/.gitignore b/.gitignore index 65cd7ef..0a04472 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ yarn-debug.log* dist/ tmp/ temp/ +cli/dist/ +cli/*.csv .mastra diff --git a/backend/src/env.ts b/backend/src/env.ts index 97c410f..97380ec 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -18,6 +18,12 @@ function numberFromEnv(name: string, fallback: number): number { return Number.isFinite(parsed) ? parsed : fallback; } +function booleanFromEnv(name: string, fallback = false): boolean { + const raw = process.env[name]; + if (!raw) return fallback; + return raw === "true" || raw === "1"; +} + export const env = { PROD: process.env.PROD, IS_PROD: process.env.PROD === "1", @@ -85,4 +91,9 @@ export const env = { "REFRESH_SCHEDULER_STALE_AFTER_MS", 6 * 60 * 60 * 1000, ), + + // Local trusted CLI MVP. These endpoints intentionally use backend system + // access, so keep them disabled unless explicitly enabled in local dev. + CLI_MVP_ENABLED: booleanFromEnv("BIGSET_CLI_MVP_ENABLED"), + CLI_MVP_OWNER_ID: process.env.BIGSET_CLI_MVP_OWNER_ID, }; diff --git a/backend/src/index.ts b/backend/src/index.ts index cb57cd1..d37cc12 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,6 +1,7 @@ import Fastify, { type FastifyBaseLogger, type FastifyReply } from "fastify"; import fastifyCors from "@fastify/cors"; import type { ClerkClient } from "@clerk/backend"; +import { z } from "zod"; import { env } from "./env.js"; import clerkAuthPlugin, { requireAuth, getUserEmail } from "./clerk-auth.js"; @@ -47,6 +48,49 @@ type DatasetUpdateBeginOutcome = | "already_updating"; type UpdateWorkflowRun = Awaited>; +const refreshCadenceSchema = z.enum(["manual", "30m", "6h", "12h", "daily", "weekly"]); +const cliCreateDatasetSchema = z.object({ + prompt: z.string().trim().min(1), + maxRowCount: z.number().int().min(1).max(2500).default(100), + refreshCadence: refreshCadenceSchema.default("manual"), +}); +const cliDatasetIdParamsSchema = z.object({ + datasetId: z.string().min(1), +}); + +function cliDatasetNameFromSchemaName(name: string): string { + return name + .split("_") + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); +} + +function mapSchemaTypeToDatasetType( + type: "string" | "url" | "date" | "number" | "boolean" | "enum", +): "text" | "number" | "boolean" | "url" | "date" { + if (type === "url" || type === "date" || type === "number" || type === "boolean") { + return type; + } + return "text"; +} + +function requireCliMvp(reply: FastifyReply): string | null { + if (!env.CLI_MVP_ENABLED) { + void reply.code(404).send({ error: "Not found" }); + return null; + } + if (!env.CLI_MVP_OWNER_ID) { + void reply.code(500).send({ error: "BIGSET_CLI_MVP_OWNER_ID is required" }); + return null; + } + if (!env.CONVEX_ADMIN_KEY) { + void reply.code(500).send({ error: "CONVEX_SELF_HOSTED_ADMIN_KEY is required" }); + return null; + } + return env.CLI_MVP_OWNER_ID; +} + function statusErrorMessage(err: unknown): string { const message = err instanceof Error ? err.message : String(err); return message.slice(0, 500); @@ -782,6 +826,209 @@ fastify.get("/openrouter/models", async (req, reply) => { } }); +// ──────────────────────────────────────────────────────────────────────── +// Local trusted CLI MVP routes +// ──────────────────────────────────────────────────────────────────────── + +fastify.get("/cli/datasets", async (_req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + try { + const datasets = await convex.query(internal.datasets.listByOwnerInternal, { + ownerId, + }); + return { datasets }; + } catch (err) { + fastify.log.error(err, "CLI dataset list failed"); + return reply.code(502).send({ error: "Failed to list datasets" }); + } +}); + +fastify.post("/cli/datasets", async (req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + const parsed = cliCreateDatasetSchema.safeParse(req.body); + if (!parsed.success) { + return reply.code(400).send({ + error: "Invalid request", + details: parsed.error.flatten().fieldErrors, + }); + } + + try { + const schema = await inferSchema(parsed.data.prompt); + const columns = schema.columns.map((column) => ({ + name: column.name, + type: mapSchemaTypeToDatasetType(column.type), + description: column.retrieval_hint, + isPrimaryKey: column.is_primary_key || undefined, + })); + + const datasetId = await convex.mutation( + internal.datasets.createForOwnerInternal, + { + ownerId, + name: cliDatasetNameFromSchemaName(schema.dataset_name), + description: parsed.data.prompt, + refreshCadence: parsed.data.refreshCadence, + maxRowCount: parsed.data.maxRowCount, + columns, + retrievalStrategy: schema.retrieval_strategy, + sourceHint: schema.source_hint, + }, + ); + + const dataset = await convex.query(internal.datasets.getOwnedInternal, { + id: datasetId, + ownerId, + }); + + return reply.code(201).send({ dataset, schema }); + } catch (err) { + req.log.error(err, "CLI dataset create failed"); + return reply.code(502).send({ error: "Failed to create dataset" }); + } +}); + +fastify.get("/cli/datasets/:datasetId", async (req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + const params = cliDatasetIdParamsSchema.safeParse(req.params); + if (!params.success) { + return reply.code(400).send({ error: "datasetId is required" }); + } + + try { + const dataset = await convex.query(internal.datasets.getOwnedInternal, { + id: params.data.datasetId, + ownerId, + }); + if (!dataset) return reply.code(404).send({ error: "Dataset not found" }); + return { dataset }; + } catch (err) { + req.log.error(err, "CLI dataset get failed"); + return reply.code(400).send({ error: "Invalid datasetId" }); + } +}); + +fastify.get("/cli/datasets/:datasetId/rows", async (req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + const params = cliDatasetIdParamsSchema.safeParse(req.params); + if (!params.success) { + return reply.code(400).send({ error: "datasetId is required" }); + } + + try { + const rows = await convex.query( + internal.datasetRows.listByOwnedDatasetInternal, + { + datasetId: params.data.datasetId, + ownerId, + }, + ); + if (!rows) return reply.code(404).send({ error: "Dataset not found" }); + return { rows }; + } catch (err) { + req.log.error(err, "CLI rows get failed"); + return reply.code(400).send({ error: "Invalid datasetId" }); + } +}); + +fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + const params = cliDatasetIdParamsSchema.safeParse(req.params); + if (!params.success) { + return reply.code(400).send({ error: "datasetId is required" }); + } + + try { + const dataset = await convex.query(internal.datasets.getOwnedInternal, { + id: params.data.datasetId, + ownerId, + }); + if (!dataset) return reply.code(404).send({ error: "Dataset not found" }); + + const populateOutcome = await beginDatasetPopulate(dataset._id, ownerId); + if (populateOutcome === "already_building") { + return reply.code(409).send({ error: "Dataset is already being populated" }); + } + if (populateOutcome === "already_updating") { + return reply.code(409).send({ error: "Dataset is already being updated" }); + } + if (populateOutcome !== "started") { + return reply.code(409).send({ error: `Cannot populate dataset: ${populateOutcome}` }); + } + + const { getModelConfig } = await import("./config/models.js"); + const modelConfig = await getModelConfig(ownerId); + const run = await populateWorkflow.createRun(); + const controller = registerDataset(dataset._id); + + void runPopulateWorkflowInBackground({ + input: { + datasetId: dataset._id, + datasetName: dataset.name, + description: dataset.description, + maxRowCount: dataset.maxRowCount ?? 100, + columns: dataset.columns, + }, + run, + controller, + authorizedUserId: ownerId, + logger: req.log, + clerk: req.server.clerk, + modelConfig, + }); + + return reply.code(202).send({ success: true, runId: run.runId }); + } catch (err) { + req.log.error(err, "CLI populate failed"); + return reply.code(502).send({ error: "Failed to populate dataset" }); + } +}); + +fastify.post("/cli/datasets/:datasetId/stop", async (req, reply) => { + const ownerId = requireCliMvp(reply); + if (!ownerId) return; + + const params = cliDatasetIdParamsSchema.safeParse(req.params); + if (!params.success) { + return reply.code(400).send({ error: "datasetId is required" }); + } + + try { + const dataset = await convex.query(internal.datasets.getOwnedInternal, { + id: params.data.datasetId, + ownerId, + }); + if (!dataset) return reply.code(404).send({ error: "Dataset not found" }); + if (dataset.status !== "building" && dataset.status !== "updating") { + return reply.code(409).send({ error: "Dataset is not currently running" }); + } + + const aborted = abortDataset(dataset._id); + if (!aborted) { + await setDatasetPopulateStatus( + dataset._id, + "failed", + "Run interrupted: no active local CLI run registered", + ); + return reply.code(200).send({ success: true }); + } + return reply.code(202).send({ success: true }); + } catch (err) { + req.log.error(err, "CLI stop failed"); + return reply.code(502).send({ error: "Failed to stop dataset run" }); + } +}); + // ──────────────────────────────────────────────────────────────────────── // Protected routes — gated by Clerk JWT verification // ──────────────────────────────────────────────────────────────────────── diff --git a/bigset-cli-mvp.excalidraw b/bigset-cli-mvp.excalidraw new file mode 100644 index 0000000..d9e4ca6 --- /dev/null +++ b/bigset-cli-mvp.excalidraw @@ -0,0 +1,65 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + {"id":"title","type":"text","x":330,"y":-30,"width":430,"height":34,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":101,"version":1,"versionNonce":10101,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"BigSet CLI MVP - Request Routing","fontSize":28,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"BigSet CLI MVP - Request Routing","lineHeight":1.25,"baseline":26}, + + {"id":"zone_cli","type":"rectangle","x":20,"y":40,"width":300,"height":610,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"#dbe4ff","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":102,"version":1,"versionNonce":10202,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"zone_cli_t","type":"text","x":40,"y":55,"width":95,"height":25,"angle":0,"strokeColor":"#2563eb","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":103,"version":1,"versionNonce":10303,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Local CLI","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Local CLI","lineHeight":1.25,"baseline":19}, + {"id":"term","type":"rectangle","x":60,"y":110,"width":220,"height":80,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":104,"version":1,"versionNonce":10404,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"term_t","type":"text","x":92,"y":126,"width":155,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":105,"version":1,"versionNonce":10505,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"User / Agent\nbigset create ...","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"User / Agent\nbigset create ...","lineHeight":1.25,"baseline":41}, + {"id":"a1","type":"arrow","x":170,"y":190,"width":0,"height":55,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":106,"version":1,"versionNonce":10606,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"oclif","type":"rectangle","x":60,"y":245,"width":220,"height":80,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":107,"version":1,"versionNonce":10707,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"oclif_t","type":"text","x":98,"y":261,"width":145,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":108,"version":1,"versionNonce":10808,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"oclif commands\ncreate/list/rows","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"oclif commands\ncreate/list/rows","lineHeight":1.25,"baseline":41}, + {"id":"a2","type":"arrow","x":170,"y":325,"width":0,"height":55,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":109,"version":1,"versionNonce":10909,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"client","type":"rectangle","x":60,"y":380,"width":220,"height":80,"angle":0,"strokeColor":"#06b6d4","backgroundColor":"#c3fae8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":110,"version":1,"versionNonce":11010,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"client_t","type":"text","x":102,"y":396,"width":137,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":111,"version":1,"versionNonce":11111,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"CLI client\nfetch + zod","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"CLI client\nfetch + zod","lineHeight":1.25,"baseline":41}, + {"id":"env","type":"rectangle","x":45,"y":520,"width":250,"height":95,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":112,"version":1,"versionNonce":11212,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"env_t","type":"text","x":58,"y":536,"width":224,"height":60,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":113,"version":1,"versionNonce":11313,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Reads local config\nBIGSET_CLI_MVP_ENABLED\nBIGSET_CLI_MVP_OWNER_ID","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Reads local config\nBIGSET_CLI_MVP_ENABLED\nBIGSET_CLI_MVP_OWNER_ID","lineHeight":1.25,"baseline":56}, + + {"id":"zone_backend","type":"rectangle","x":390,"y":40,"width":340,"height":610,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#e5dbff","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":114,"version":1,"versionNonce":11414,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"zone_backend_t","type":"text","x":410,"y":55,"width":145,"height":25,"angle":0,"strokeColor":"#6d3fd9","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":115,"version":1,"versionNonce":11515,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Fastify Backend","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Fastify Backend","lineHeight":1.25,"baseline":19}, + {"id":"a3","type":"arrow","x":280,"y":420,"width":140,"height":0,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":116,"version":1,"versionNonce":11616,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,0]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a3_t","type":"text","x":318,"y":392,"width":88,"height":20,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":117,"version":1,"versionNonce":11717,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"HTTP /cli/*","fontSize":16,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"HTTP /cli/*","lineHeight":1.25,"baseline":15}, + {"id":"routes","type":"rectangle","x":430,"y":115,"width":260,"height":120,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":118,"version":1,"versionNonce":11818,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"routes_t","type":"text","x":448,"y":131,"width":226,"height":80,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":119,"version":1,"versionNonce":11919,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Dev-only /cli routes\nPOST /cli/datasets\nPOST /cli/datasets/:id/populate\nGET /cli/datasets/:id/rows","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Dev-only /cli routes\nPOST /cli/datasets\nPOST /cli/datasets/:id/populate\nGET /cli/datasets/:id/rows","lineHeight":1.25,"baseline":76}, + {"id":"a4","type":"arrow","x":560,"y":235,"width":0,"height":60,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":120,"version":1,"versionNonce":12020,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,60]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"guard","type":"diamond","x":455,"y":295,"width":210,"height":110,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":121,"version":1,"versionNonce":12121,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"guard_t","type":"text","x":494,"y":325,"width":132,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":122,"version":1,"versionNonce":12222,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Local guard\nenabled + owner id?","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Local guard\nenabled + owner id?","lineHeight":1.25,"baseline":36}, + {"id":"a5","type":"arrow","x":560,"y":405,"width":0,"height":55,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":123,"version":1,"versionNonce":12323,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"admin","type":"rectangle","x":430,"y":460,"width":260,"height":85,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#ffd8a8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":124,"version":1,"versionNonce":12424,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"admin_t","type":"text","x":453,"y":481,"width":214,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":125,"version":1,"versionNonce":12525,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Backend uses Convex admin key\nbut pins ownerId","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Backend uses Convex admin key\nbut pins ownerId","lineHeight":1.25,"baseline":36}, + {"id":"a6","type":"arrow","x":560,"y":545,"width":0,"height":55,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":126,"version":1,"versionNonce":12626,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"workflow","type":"rectangle","x":430,"y":600,"width":260,"height":70,"angle":0,"strokeColor":"#ec4899","backgroundColor":"#eebefa","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":127,"version":1,"versionNonce":12727,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"workflow_t","type":"text","x":457,"y":621,"width":206,"height":21,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":128,"version":1,"versionNonce":12828,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Existing populate workflow","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Existing populate workflow","lineHeight":1.25,"baseline":20}, + + {"id":"zone_data","type":"rectangle","x":790,"y":40,"width":340,"height":610,"angle":0,"strokeColor":"#22c55e","backgroundColor":"#d3f9d8","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":129,"version":1,"versionNonce":12929,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"zone_data_t","type":"text","x":810,"y":55,"width":220,"height":25,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":130,"version":1,"versionNonce":13030,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Data + External Services","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Data + External Services","lineHeight":1.25,"baseline":19}, + {"id":"convex","type":"rectangle","x":830,"y":115,"width":240,"height":105,"angle":0,"strokeColor":"#06b6d4","backgroundColor":"#c3fae8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":131,"version":1,"versionNonce":13131,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"convex_t","type":"text","x":852,"y":133,"width":196,"height":60,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":132,"version":1,"versionNonce":13232,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Convex\ndatasets + rows\nownerId = configured dev user","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Convex\ndatasets + rows\nownerId = configured dev user","lineHeight":1.25,"baseline":56}, + {"id":"models","type":"rectangle","x":830,"y":285,"width":240,"height":80,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":133,"version":1,"versionNonce":13333,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"models_t","type":"text","x":875,"y":304,"width":150,"height":43,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":134,"version":1,"versionNonce":13434,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"OpenRouter\nschema + agents","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"OpenRouter\nschema + agents","lineHeight":1.25,"baseline":39}, + {"id":"web","type":"rectangle","x":830,"y":430,"width":240,"height":80,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#ffd8a8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":135,"version":1,"versionNonce":13535,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"web_t","type":"text","x":888,"y":449,"width":124,"height":43,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":136,"version":1,"versionNonce":13636,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"TinyFish\nsearch + fetch","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"TinyFish\nsearch + fetch","lineHeight":1.25,"baseline":39}, + {"id":"a7","type":"arrow","x":690,"y":500,"width":120,"height":0,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":137,"version":1,"versionNonce":13737,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[120,0]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a7_t","type":"text","x":710,"y":472,"width":88,"height":19,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":138,"version":1,"versionNonce":13838,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Convex calls","fontSize":15,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Convex calls","lineHeight":1.25,"baseline":14}, + {"id":"a9","type":"arrow","x":690,"y":635,"width":140,"height":-295,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":139,"version":1,"versionNonce":13939,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,-295]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a10","type":"arrow","x":690,"y":635,"width":140,"height":-165,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":140,"version":1,"versionNonce":14040,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,-165]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a8","type":"arrow","x":690,"y":635,"width":120,"height":-25,"angle":0,"strokeColor":"#ec4899","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":141,"version":1,"versionNonce":14141,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[120,-25]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a11","type":"arrow","x":830,"y":195,"width":-550,"height":360,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"dashed","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":142,"version":1,"versionNonce":14242,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[-210,220],[-550,360]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a11_t","type":"text","x":490,"y":360,"width":165,"height":20,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":143,"version":1,"versionNonce":14343,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"poll status / read rows","fontSize":16,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"poll status / read rows","lineHeight":1.25,"baseline":15}, + + {"id":"csv","type":"rectangle","x":60,"y":700,"width":220,"height":70,"angle":0,"strokeColor":"#22c55e","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":144,"version":1,"versionNonce":14444,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"csv_t","type":"text","x":82,"y":714,"width":176,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":145,"version":1,"versionNonce":14545,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Optional CSV export\ndemo.csv","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Optional CSV export\ndemo.csv","lineHeight":1.25,"baseline":41}, + {"id":"a12","type":"arrow","x":170,"y":460,"width":0,"height":240,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"dashed","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":146,"version":1,"versionNonce":14646,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,240]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, + {"id":"a12_t","type":"text","x":182,"y":570,"width":86,"height":19,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":147,"version":1,"versionNonce":14747,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"--wait --csv","fontSize":15,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"--wait --csv","lineHeight":1.25,"baseline":14}, + {"id":"note","type":"rectangle","x":360,"y":720,"width":430,"height":70,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":148,"version":1,"versionNonce":14848,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, + {"id":"note_t","type":"text","x":383,"y":734,"width":386,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":149,"version":1,"versionNonce":14949,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"MVP boundary: trusted local tooling. Public CLI should replace this with real user/API-token auth.","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"MVP boundary: trusted local tooling. Public CLI should replace this with real user/API-token auth.","lineHeight":1.25,"baseline":36} + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} diff --git a/cli/bin/run.js b/cli/bin/run.js new file mode 100755 index 0000000..6cf1a59 --- /dev/null +++ b/cli/bin/run.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "../dist/index.js"; diff --git a/cli/package-lock.json b/cli/package-lock.json new file mode 100644 index 0000000..9f0f325 --- /dev/null +++ b/cli/package-lock.json @@ -0,0 +1,1094 @@ +{ + "name": "@bigset/cli", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@bigset/cli", + "version": "0.1.0", + "dependencies": { + "@oclif/core": "^4.2.10", + "dotenv": "^16.4.7", + "zod": "^4.1.13" + }, + "bin": { + "bigset": "bin/run.js" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@oclif/core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.4.tgz", + "integrity": "sha512-URwiQ5ALx/sJ2iH4vzXEd+H4K6NAI7LRs6Jag3hrgKEpGmaE6alfRC8qjO4GIgb6A3ACaJumqP9twi/M9ywdHQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.8.1", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.16", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.19.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.20.tgz", + "integrity": "sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tsx": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", + "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..6ebef59 --- /dev/null +++ b/cli/package.json @@ -0,0 +1,23 @@ +{ + "name": "@bigset/cli", + "version": "0.1.0", + "private": true, + "type": "module", + "bin": { + "bigset": "./bin/run.js" + }, + "scripts": { + "build": "tsc", + "dev": "tsx src/index.ts" + }, + "dependencies": { + "@oclif/core": "^4.2.10", + "dotenv": "^16.4.7", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2" + } +} diff --git a/cli/src/client.ts b/cli/src/client.ts new file mode 100644 index 0000000..75a7465 --- /dev/null +++ b/cli/src/client.ts @@ -0,0 +1,169 @@ +import { z } from "zod"; +import { backendUrl } from "./config.js"; + +const datasetColumnSchema = z.object({ + name: z.string(), + type: z.string(), + description: z.string().optional(), + isPrimaryKey: z.boolean().optional(), +}); + +export const datasetSchema = z.object({ + _id: z.string(), + _creationTime: z.number(), + name: z.string(), + description: z.string(), + ownerId: z.string(), + status: z.enum(["live", "paused", "building", "updating", "failed"]), + lastStatusError: z.string().optional(), + rowCount: z.number().optional(), + maxRowCount: z.number().optional(), + refreshCadence: z.string().optional(), + columns: z.array(datasetColumnSchema), +}); +export type Dataset = z.infer; + +const rowSchema = z.object({ + _id: z.string(), + data: z.record(z.string(), z.unknown()), + sources: z.array(z.string()).optional(), + rowSummary: z.string().optional(), + howFound: z.string().optional(), +}); +export type DatasetRow = z.infer; + +const inferredColumnSchema = z.object({ + name: z.string(), + display_name: z.string(), + type: z.string(), + is_primary_key: z.boolean(), + is_enumerable: z.boolean(), + retrieval_hint: z.string(), + nullable: z.boolean(), +}); + +const inferredSchemaSchema = z.object({ + dataset_name: z.string(), + description: z.string(), + columns: z.array(inferredColumnSchema), + primary_key: z.union([z.string(), z.array(z.string())]), + retrieval_strategy: z.string(), + source_hint: z.string(), +}); +export type InferredSchema = z.infer; + +const createDatasetResponseSchema = z.object({ + dataset: datasetSchema, + schema: inferredSchemaSchema, +}); + +const listDatasetsResponseSchema = z.object({ + datasets: z.array(datasetSchema), +}); + +const getDatasetResponseSchema = z.object({ + dataset: datasetSchema, +}); + +const getRowsResponseSchema = z.object({ + rows: z.array(rowSchema), +}); + +const startRunResponseSchema = z.object({ + success: z.boolean(), + runId: z.string(), +}); + +async function requestJson( + path: string, + schema: z.ZodType, + init?: RequestInit, +): Promise { + const res = await fetch(`${backendUrl()}${path}`, { + ...init, + headers: { + ...(init?.body ? { "Content-Type": "application/json" } : {}), + ...(init?.headers ?? {}), + }, + }); + + const body = await res.json().catch(() => null); + if (!res.ok) { + const message = + body && typeof body === "object" && "error" in body + ? String(body.error) + : `BigSet backend error (${res.status})`; + throw new Error(message); + } + + return schema.parse(body); +} + +export async function createDataset(input: { + prompt: string; + maxRowCount: number; + refreshCadence: string; +}) { + return await requestJson("/cli/datasets", createDatasetResponseSchema, { + method: "POST", + body: JSON.stringify(input), + }); +} + +export async function listDatasets() { + return await requestJson("/cli/datasets", listDatasetsResponseSchema); +} + +export async function getDataset(datasetId: string) { + const result = await requestJson( + `/cli/datasets/${encodeURIComponent(datasetId)}`, + getDatasetResponseSchema, + ); + return result.dataset; +} + +export async function getRows(datasetId: string) { + const result = await requestJson( + `/cli/datasets/${encodeURIComponent(datasetId)}/rows`, + getRowsResponseSchema, + ); + return result.rows; +} + +export async function populateDataset(datasetId: string) { + return await requestJson( + `/cli/datasets/${encodeURIComponent(datasetId)}/populate`, + startRunResponseSchema, + { method: "POST" }, + ); +} + +export async function stopDataset(datasetId: string) { + return await requestJson( + `/cli/datasets/${encodeURIComponent(datasetId)}/stop`, + z.object({ success: z.boolean() }), + { method: "POST" }, + ); +} + +export async function waitForDataset( + datasetId: string, + options: { + intervalMs: number; + timeoutMs: number; + onPoll?: (dataset: Dataset) => void; + }, +): Promise { + const startedAt = Date.now(); + for (;;) { + const dataset = await getDataset(datasetId); + options.onPoll?.(dataset); + if (dataset.status === "live" || dataset.status === "failed") { + return dataset; + } + if (Date.now() - startedAt > options.timeoutMs) { + throw new Error(`Timed out waiting for dataset ${datasetId}`); + } + await new Promise((resolve) => setTimeout(resolve, options.intervalMs)); + } +} diff --git a/cli/src/commands/create.ts b/cli/src/commands/create.ts new file mode 100644 index 0000000..23e8727 --- /dev/null +++ b/cli/src/commands/create.ts @@ -0,0 +1,161 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { writeFile } from "node:fs/promises"; +import { + createDataset, + getRows, + type InferredSchema, + populateDataset, + waitForDataset, +} from "../client.js"; +import { buildCsv } from "../csv.js"; + +function formatPrimaryKey(primaryKey: InferredSchema["primary_key"]): string { + return Array.isArray(primaryKey) ? primaryKey.join(", ") : primaryKey; +} + +function labelValue(label: string, value: string | number): string { + return ` ${label.padEnd(12)} ${value}\n`; +} + +function truncate(text: string, max = 110): string { + if (text.length <= max) return text; + return `${text.slice(0, max - 3)}...`; +} + +function printSchema(schema: InferredSchema): void { + const nameWidth = Math.max(18, ...schema.columns.map((column) => column.name.length)); + process.stdout.write("\n[schema]\n"); + process.stdout.write(labelValue("name", schema.dataset_name)); + process.stdout.write(labelValue("primary key", formatPrimaryKey(schema.primary_key))); + process.stdout.write(labelValue("retrieval", schema.retrieval_strategy)); + process.stdout.write(labelValue("source", truncate(schema.source_hint))); + process.stdout.write("\n[columns]\n"); + for (const column of schema.columns) { + const tags = [ + column.type, + column.is_primary_key ? "pk" : null, + column.nullable ? "nullable" : null, + ].filter(Boolean).join(", "); + process.stdout.write( + ` ${column.name.padEnd(nameWidth)} ${tags.padEnd(20)} ${truncate(column.retrieval_hint)}\n`, + ); + } + process.stdout.write("\n"); +} + +export default class CreateCommand extends Command { + static description = "Infer a schema, create a dataset, and optionally populate it."; + + static args = { + prompt: Args.string({ required: true }), + }; + + static flags = { + rows: Flags.integer({ + char: "r", + default: 100, + description: "maximum rows to collect", + }), + cadence: Flags.string({ + default: "manual", + description: "refresh cadence", + options: ["manual", "30m", "6h", "12h", "daily", "weekly"], + }), + wait: Flags.boolean({ + default: false, + description: "wait until the populate run finishes", + }), + csv: Flags.string({ + description: "write final rows to a CSV file; implies --wait", + }), + "skip-populate": Flags.boolean({ + default: false, + description: "create the dataset but do not start population", + }), + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(CreateCommand); + const shouldWait = flags.wait || Boolean(flags.csv); + + const created = await createDataset({ + prompt: args.prompt, + maxRowCount: flags.rows, + refreshCadence: flags.cadence, + }); + + let runId: string | undefined; + let dataset = created.dataset; + + if (!flags.json) { + printSchema(created.schema); + process.stdout.write("[dataset]\n"); + process.stdout.write(labelValue("id", dataset._id)); + process.stdout.write(labelValue("name", dataset.name)); + process.stdout.write(labelValue("rows", dataset.maxRowCount ?? flags.rows)); + process.stdout.write(labelValue("cadence", flags.cadence)); + } + + if (!flags["skip-populate"]) { + const run = await populateDataset(dataset._id); + runId = run.runId; + if (!flags.json) { + process.stdout.write("\n[run]\n"); + process.stdout.write(labelValue("id", run.runId)); + process.stdout.write(labelValue("status", "started")); + } + } + + if (shouldWait && !flags["skip-populate"]) { + dataset = await waitForDataset(dataset._id, { + intervalMs: 5000, + timeoutMs: 20 * 60 * 1000, + onPoll: flags.json + ? undefined + : (current) => { + process.stdout.write( + ` ${new Date().toLocaleTimeString()} ${current.status.padEnd(9)} rows=${current.rowCount ?? 0}\n`, + ); + }, + }); + + if (dataset.status === "failed") { + throw new Error(dataset.lastStatusError ?? "Dataset population failed"); + } + } + + let csvPath: string | undefined; + if (flags.csv) { + const rows = await getRows(dataset._id); + await writeFile(flags.csv, buildCsv(dataset, rows), "utf8"); + csvPath = flags.csv; + if (!flags.json) { + process.stdout.write("\n[export]\n"); + process.stdout.write(labelValue("file", flags.csv)); + process.stdout.write(labelValue("rows", rows.length)); + } + } + + const output = { + datasetId: dataset._id, + name: dataset.name, + status: dataset.status, + rowCount: dataset.rowCount ?? 0, + runId, + csvPath, + }; + + if (flags.json) { + this.log(JSON.stringify(output, null, 2)); + return; + } + + this.log(`\n[done]\n${labelValue("dataset", output.datasetId).trimEnd()}`); + this.log(labelValue("status", output.status).trimEnd()); + this.log(labelValue("rows", output.rowCount).trimEnd()); + } +} diff --git a/cli/src/commands/export.ts b/cli/src/commands/export.ts new file mode 100644 index 0000000..c877208 --- /dev/null +++ b/cli/src/commands/export.ts @@ -0,0 +1,47 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { writeFile } from "node:fs/promises"; +import { getDataset, getRows } from "../client.js"; +import { buildCsv } from "../csv.js"; + +export default class ExportCommand extends Command { + static description = "Export dataset rows to a file."; + + static args = { + datasetId: Args.string({ required: true }), + }; + + static flags = { + csv: Flags.string({ + char: "o", + required: true, + description: "CSV output path", + }), + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ExportCommand); + const [dataset, rows] = await Promise.all([ + getDataset(args.datasetId), + getRows(args.datasetId), + ]); + + await writeFile(flags.csv, buildCsv(dataset, rows), "utf8"); + + if (flags.json) { + this.log( + JSON.stringify( + { datasetId: dataset._id, rowCount: rows.length, csvPath: flags.csv }, + null, + 2, + ), + ); + return; + } + + this.log(`Wrote ${rows.length} rows to ${flags.csv}`); + } +} diff --git a/cli/src/commands/list.ts b/cli/src/commands/list.ts new file mode 100644 index 0000000..5e6c098 --- /dev/null +++ b/cli/src/commands/list.ts @@ -0,0 +1,29 @@ +import { Command, Flags } from "@oclif/core"; +import { listDatasets } from "../client.js"; + +export default class ListCommand extends Command { + static description = "List local CLI-scoped datasets."; + + static flags = { + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { flags } = await this.parse(ListCommand); + const { datasets } = await listDatasets(); + + if (flags.json) { + this.log(JSON.stringify({ datasets }, null, 2)); + return; + } + + for (const dataset of datasets) { + this.log( + `${dataset._id}\t${dataset.status}\t${dataset.rowCount ?? 0}\t${dataset.name}`, + ); + } + } +} diff --git a/cli/src/commands/populate.ts b/cli/src/commands/populate.ts new file mode 100644 index 0000000..e17b970 --- /dev/null +++ b/cli/src/commands/populate.ts @@ -0,0 +1,54 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { populateDataset, waitForDataset } from "../client.js"; + +export default class PopulateCommand extends Command { + static description = "Start population for an existing dataset."; + + static args = { + datasetId: Args.string({ required: true }), + }; + + static flags = { + wait: Flags.boolean({ + default: false, + description: "wait until the populate run finishes", + }), + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(PopulateCommand); + const run = await populateDataset(args.datasetId); + let status: string | undefined; + let rowCount: number | undefined; + + if (flags.wait) { + const dataset = await waitForDataset(args.datasetId, { + intervalMs: 5000, + timeoutMs: 20 * 60 * 1000, + onPoll: flags.json + ? undefined + : (current) => { + process.stderr.write( + `Status: ${current.status}, rows: ${current.rowCount ?? 0}\n`, + ); + }, + }); + status = dataset.status; + rowCount = dataset.rowCount ?? 0; + if (dataset.status === "failed") { + throw new Error(dataset.lastStatusError ?? "Dataset population failed"); + } + } + + if (flags.json) { + this.log(JSON.stringify({ runId: run.runId, status, rowCount }, null, 2)); + return; + } + + this.log(`Started populate run ${run.runId}`); + } +} diff --git a/cli/src/commands/rows.ts b/cli/src/commands/rows.ts new file mode 100644 index 0000000..c731757 --- /dev/null +++ b/cli/src/commands/rows.ts @@ -0,0 +1,42 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { getDataset, getRows } from "../client.js"; +import { buildCsv } from "../csv.js"; + +export default class RowsCommand extends Command { + static description = "Print dataset rows."; + + static args = { + datasetId: Args.string({ required: true }), + }; + + static flags = { + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + csv: Flags.boolean({ + default: false, + description: "print CSV", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(RowsCommand); + const [dataset, rows] = await Promise.all([ + getDataset(args.datasetId), + getRows(args.datasetId), + ]); + + if (flags.csv) { + this.log(buildCsv(dataset, rows)); + return; + } + + if (flags.json) { + this.log(JSON.stringify({ rows }, null, 2)); + return; + } + + this.log(buildCsv(dataset, rows)); + } +} diff --git a/cli/src/commands/status.ts b/cli/src/commands/status.ts new file mode 100644 index 0000000..7ba2946 --- /dev/null +++ b/cli/src/commands/status.ts @@ -0,0 +1,33 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { getDataset } from "../client.js"; + +export default class StatusCommand extends Command { + static description = "Show one dataset's status."; + + static args = { + datasetId: Args.string({ required: true }), + }; + + static flags = { + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(StatusCommand); + const dataset = await getDataset(args.datasetId); + + if (flags.json) { + this.log(JSON.stringify({ dataset }, null, 2)); + return; + } + + this.log(`id: ${dataset._id}`); + this.log(`name: ${dataset.name}`); + this.log(`status: ${dataset.status}`); + this.log(`rows: ${dataset.rowCount ?? 0}`); + if (dataset.lastStatusError) this.log(`error: ${dataset.lastStatusError}`); + } +} diff --git a/cli/src/commands/stop.ts b/cli/src/commands/stop.ts new file mode 100644 index 0000000..3c23e5c --- /dev/null +++ b/cli/src/commands/stop.ts @@ -0,0 +1,29 @@ +import { Args, Command, Flags } from "@oclif/core"; +import { stopDataset } from "../client.js"; + +export default class StopCommand extends Command { + static description = "Stop a running dataset population."; + + static args = { + datasetId: Args.string({ required: true }), + }; + + static flags = { + json: Flags.boolean({ + default: false, + description: "print machine-readable JSON", + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(StopCommand); + const result = await stopDataset(args.datasetId); + + if (flags.json) { + this.log(JSON.stringify(result, null, 2)); + return; + } + + this.log("Stop requested"); + } +} diff --git a/cli/src/config.ts b/cli/src/config.ts new file mode 100644 index 0000000..7df55e6 --- /dev/null +++ b/cli/src/config.ts @@ -0,0 +1,16 @@ +import { config as loadDotenv } from "dotenv"; +import { existsSync } from "node:fs"; +import { resolve } from "node:path"; + +const cwdEnv = resolve(process.cwd(), ".env"); +const parentEnv = resolve(process.cwd(), "..", ".env"); + +loadDotenv({ path: existsSync(cwdEnv) ? cwdEnv : parentEnv }); + +export function backendUrl(): string { + return ( + process.env.BIGSET_BACKEND_URL || + process.env.NEXT_PUBLIC_BACKEND_URL || + `http://localhost:${process.env.PORT || "3501"}` + ).replace(/\/$/, ""); +} diff --git a/cli/src/csv.ts b/cli/src/csv.ts new file mode 100644 index 0000000..21c07f1 --- /dev/null +++ b/cli/src/csv.ts @@ -0,0 +1,20 @@ +import type { Dataset, DatasetRow } from "./client.js"; + +function csvEscape(value: unknown): string { + if (value === null || value === undefined) return ""; + const text = String(value); + if (/[",\n\r]/.test(text)) { + return `"${text.replace(/"/g, '""')}"`; + } + return text; +} + +export function buildCsv(dataset: Dataset, rows: DatasetRow[]): string { + const header = dataset.columns.map((column) => csvEscape(column.name)).join(","); + const body = rows.map((row) => + dataset.columns + .map((column) => csvEscape(row.data[column.name])) + .join(","), + ); + return [header, ...body].join("\n"); +} diff --git a/cli/src/index.ts b/cli/src/index.ts new file mode 100644 index 0000000..4f15876 --- /dev/null +++ b/cli/src/index.ts @@ -0,0 +1,57 @@ +import CreateCommand from "./commands/create.js"; +import ExportCommand from "./commands/export.js"; +import ListCommand from "./commands/list.js"; +import PopulateCommand from "./commands/populate.js"; +import RowsCommand from "./commands/rows.js"; +import StatusCommand from "./commands/status.js"; +import StopCommand from "./commands/stop.js"; + +const commands = { + create: CreateCommand, + export: ExportCommand, + list: ListCommand, + populate: PopulateCommand, + rows: RowsCommand, + status: StatusCommand, + stop: StopCommand, +}; + +function printHelp(): void { + console.log(`BigSet CLI MVP + +Usage: + bigset [args] [flags] + +Commands: + create Infer schema, create a dataset, and start populate + list List local CLI-scoped datasets + status Show dataset status + rows Print dataset rows + export Export dataset rows + populate Start population for an existing dataset + stop Stop a running populate/update + +Run "bigset --help" for command flags.`); +} + +const [commandName, ...argv] = process.argv.slice(2); + +if (!commandName || commandName === "help" || commandName === "--help" || commandName === "-h") { + printHelp(); + process.exit(0); +} + +const command = commands[commandName as keyof typeof commands]; +if (!command) { + console.error(`Unknown command: ${commandName}`); + printHelp(); + process.exit(1); +} + +try { + await command.run(argv); +} catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(message); + process.exit(1); +} diff --git a/cli/tsconfig.json b/cli/tsconfig.json new file mode 100644 index 0000000..2d7992a --- /dev/null +++ b/cli/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist", + "declaration": true, + "resolveJsonModule": true + }, + "include": ["src"] +} diff --git a/frontend/convex/datasetRows.ts b/frontend/convex/datasetRows.ts index eac39ea..a4a877c 100644 --- a/frontend/convex/datasetRows.ts +++ b/frontend/convex/datasetRows.ts @@ -44,6 +44,22 @@ export const listByDataset = query({ }, }); +export const listByOwnedDatasetInternal = internalQuery({ + args: { + datasetId: v.id("datasets"), + ownerId: v.string(), + }, + handler: async (ctx, args) => { + const dataset = await ctx.db.get(args.datasetId); + if (!dataset || dataset.ownerId !== args.ownerId) return null; + + return await ctx.db + .query("datasetRows") + .withIndex("by_dataset", (q) => q.eq("datasetId", args.datasetId)) + .collect(); + }, +}); + /** * Row writes are SYSTEM-LEVEL operations performed by the agent runner, * never by end users directly. They are exposed as `internalMutation` so diff --git a/frontend/convex/datasets.ts b/frontend/convex/datasets.ts index d295327..791671b 100644 --- a/frontend/convex/datasets.ts +++ b/frontend/convex/datasets.ts @@ -406,6 +406,62 @@ export const create = mutation({ }, }); +export const createForOwnerInternal = internalMutation({ + args: { + ownerId: v.string(), + name: v.string(), + description: v.string(), + refreshCadence: refreshCadenceValidator, + maxRowCount: v.number(), + columns: v.array(columnValidator), + retrievalStrategy: v.optional( + v.union( + v.literal("search_fetch"), + v.literal("browser"), + v.literal("hybrid") + ) + ), + sourceHint: v.optional(v.string()), + }, + handler: async (ctx, args) => { + assertNotReservedOwner(args.ownerId); + validateMaxRowCount(args.maxRowCount); + await requireQuotaRemaining(ctx, args.ownerId, args.maxRowCount); + + return await ctx.db.insert("datasets", { + ...args, + status: "paused", + visibility: "private", + rowCount: 0, + refreshEnabled: args.refreshCadence !== "manual", + nextRefreshAt: nextRefreshAtFor(args.refreshCadence, Date.now()), + }); + }, +}); + +export const listByOwnerInternal = internalQuery({ + args: { ownerId: v.string() }, + handler: async (ctx, args) => { + return await ctx.db + .query("datasets") + .withIndex("by_owner", (q) => q.eq("ownerId", args.ownerId)) + .order("desc") + .collect(); + }, +}); + +export const getOwnedInternal = internalQuery({ + args: { + id: v.id("datasets"), + ownerId: v.string(), + }, + handler: async (ctx, args) => { + const dataset = await ctx.db.get(args.id); + if (!dataset || dataset.ownerId !== args.ownerId) return null; + return dataset; + }, +}); + export const updateRefreshSettings = mutation({ args: { id: v.id("datasets"), diff --git a/makefiles/Makefile b/makefiles/Makefile index cd7ef5e..6fb05bf 100644 --- a/makefiles/Makefile +++ b/makefiles/Makefile @@ -10,6 +10,7 @@ all: dev install-deps: @cd frontend && npm install --silent @cd backend && npm install --silent + @cd cli && npm install --silent build-release: install-deps node scripts/build-release.mjs From b85004db37f9553ad0b8810b76e7452ffc73eb11 Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Mon, 8 Jun 2026 16:01:31 -0700 Subject: [PATCH 2/7] refactor cli routes for local mode --- backend/src/env.ts | 10 ---------- backend/src/index.ts | 27 +++++++++++++-------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/backend/src/env.ts b/backend/src/env.ts index 97380ec..adcec52 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -18,12 +18,6 @@ function numberFromEnv(name: string, fallback: number): number { return Number.isFinite(parsed) ? parsed : fallback; } -function booleanFromEnv(name: string, fallback = false): boolean { - const raw = process.env[name]; - if (!raw) return fallback; - return raw === "true" || raw === "1"; -} - export const env = { PROD: process.env.PROD, IS_PROD: process.env.PROD === "1", @@ -92,8 +86,4 @@ export const env = { 6 * 60 * 60 * 1000, ), - // Local trusted CLI MVP. These endpoints intentionally use backend system - // access, so keep them disabled unless explicitly enabled in local dev. - CLI_MVP_ENABLED: booleanFromEnv("BIGSET_CLI_MVP_ENABLED"), - CLI_MVP_OWNER_ID: process.env.BIGSET_CLI_MVP_OWNER_ID, }; diff --git a/backend/src/index.ts b/backend/src/index.ts index d37cc12..5f486bc 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -16,6 +16,7 @@ import { capture, shutdown as shutdownAnalytics } from "./analytics/posthog.js"; import { EVENTS } from "./analytics/events.js"; import { registerDataset, deregisterDataset, abortDataset } from "./abort-registry.js"; import { + LOCAL_USER_ID, clearLegacyPlaintextLocalCredentials, exchangeOpenRouterOAuthCode, getLocalSetupStatus, @@ -75,20 +76,16 @@ function mapSchemaTypeToDatasetType( return "text"; } -function requireCliMvp(reply: FastifyReply): string | null { - if (!env.CLI_MVP_ENABLED) { +function requireLocalCli(reply: FastifyReply): string | null { + if (!env.IS_LOCAL_MODE) { void reply.code(404).send({ error: "Not found" }); return null; } - if (!env.CLI_MVP_OWNER_ID) { - void reply.code(500).send({ error: "BIGSET_CLI_MVP_OWNER_ID is required" }); - return null; - } if (!env.CONVEX_ADMIN_KEY) { void reply.code(500).send({ error: "CONVEX_SELF_HOSTED_ADMIN_KEY is required" }); return null; } - return env.CLI_MVP_OWNER_ID; + return LOCAL_USER_ID; } function statusErrorMessage(err: unknown): string { @@ -827,11 +824,11 @@ fastify.get("/openrouter/models", async (req, reply) => { }); // ──────────────────────────────────────────────────────────────────────── -// Local trusted CLI MVP routes +// Local trusted CLI routes // ──────────────────────────────────────────────────────────────────────── fastify.get("/cli/datasets", async (_req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; try { @@ -846,8 +843,9 @@ fastify.get("/cli/datasets", async (_req, reply) => { }); fastify.post("/cli/datasets", async (req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; + if (!(await ensureLocalSetupReady(reply))) return; const parsed = cliCreateDatasetSchema.safeParse(req.body); if (!parsed.success) { @@ -893,7 +891,7 @@ fastify.post("/cli/datasets", async (req, reply) => { }); fastify.get("/cli/datasets/:datasetId", async (req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; const params = cliDatasetIdParamsSchema.safeParse(req.params); @@ -915,7 +913,7 @@ fastify.get("/cli/datasets/:datasetId", async (req, reply) => { }); fastify.get("/cli/datasets/:datasetId/rows", async (req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; const params = cliDatasetIdParamsSchema.safeParse(req.params); @@ -940,8 +938,9 @@ fastify.get("/cli/datasets/:datasetId/rows", async (req, reply) => { }); fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; + if (!(await ensureLocalSetupReady(reply))) return; const params = cliDatasetIdParamsSchema.safeParse(req.params); if (!params.success) { @@ -995,7 +994,7 @@ fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { }); fastify.post("/cli/datasets/:datasetId/stop", async (req, reply) => { - const ownerId = requireCliMvp(reply); + const ownerId = requireLocalCli(reply); if (!ownerId) return; const params = cliDatasetIdParamsSchema.safeParse(req.params); From a64ca6802e5f57c2772d65543ac68ada26cc30bd Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Mon, 8 Jun 2026 16:34:30 -0700 Subject: [PATCH 3/7] include frontend package metadata in release --- scripts/build-release.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/build-release.mjs b/scripts/build-release.mjs index 45e3ea3..77aa686 100644 --- a/scripts/build-release.mjs +++ b/scripts/build-release.mjs @@ -208,6 +208,10 @@ async function bundleBackend() { } async function copyConvexRuntime() { + await cp( + join(frontendDir, "package.json"), + join(packageRoot, "frontend", "package.json"), + ); await cp(join(frontendDir, "convex"), join(packageRoot, "frontend", "convex"), { recursive: true, }); From ceb70eed07b03a6596f4cce776b6088e917d219c Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Mon, 8 Jun 2026 16:36:51 -0700 Subject: [PATCH 4/7] fix release frontend standalone path --- scripts/build-release.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/build-release.mjs b/scripts/build-release.mjs index 77aa686..9405e92 100644 --- a/scripts/build-release.mjs +++ b/scripts/build-release.mjs @@ -138,7 +138,7 @@ start( start( "frontend", process.execPath, - [fromRoot("./frontend/server.js")], + [fromRoot("./frontend/frontend/server.js")], { ...process.env, PORT: frontendPort, @@ -148,7 +148,7 @@ start( NEXT_PUBLIC_PROD: process.env.NEXT_PUBLIC_PROD || "", PROD: process.env.PROD || "", }, - fromRoot("./frontend"), + fromRoot("./frontend/frontend"), ); console.log(""); @@ -254,12 +254,12 @@ async function main() { console.log("Assembling release directory..."); await cp(standaloneDir, join(packageRoot, "frontend"), { recursive: true }); - await cp(join(frontendDir, "public"), join(packageRoot, "frontend", "public"), { + await cp(join(frontendDir, "public"), join(packageRoot, "frontend", "frontend", "public"), { recursive: true, }); await cp( join(frontendDir, ".next", "static"), - join(packageRoot, "frontend", ".next", "static"), + join(packageRoot, "frontend", "frontend", ".next", "static"), { recursive: true }, ); await copyConvexRuntime(); From 5f1669a66afbf103b59966595016b45d53355bc3 Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Tue, 9 Jun 2026 13:01:26 -0700 Subject: [PATCH 5/7] remove standalone cli package --- .gitignore | 2 - bigset-cli-mvp.excalidraw | 65 -- cli/bin/run.js | 2 - cli/package-lock.json | 1094 ---------------------------------- cli/package.json | 23 - cli/src/client.ts | 169 ------ cli/src/commands/create.ts | 161 ----- cli/src/commands/export.ts | 47 -- cli/src/commands/list.ts | 29 - cli/src/commands/populate.ts | 54 -- cli/src/commands/rows.ts | 42 -- cli/src/commands/status.ts | 33 - cli/src/commands/stop.ts | 29 - cli/src/config.ts | 16 - cli/src/csv.ts | 20 - cli/src/index.ts | 57 -- cli/tsconfig.json | 14 - makefiles/Makefile | 1 - 18 files changed, 1858 deletions(-) delete mode 100644 bigset-cli-mvp.excalidraw delete mode 100755 cli/bin/run.js delete mode 100644 cli/package-lock.json delete mode 100644 cli/package.json delete mode 100644 cli/src/client.ts delete mode 100644 cli/src/commands/create.ts delete mode 100644 cli/src/commands/export.ts delete mode 100644 cli/src/commands/list.ts delete mode 100644 cli/src/commands/populate.ts delete mode 100644 cli/src/commands/rows.ts delete mode 100644 cli/src/commands/status.ts delete mode 100644 cli/src/commands/stop.ts delete mode 100644 cli/src/config.ts delete mode 100644 cli/src/csv.ts delete mode 100644 cli/src/index.ts delete mode 100644 cli/tsconfig.json diff --git a/.gitignore b/.gitignore index 0a04472..65cd7ef 100644 --- a/.gitignore +++ b/.gitignore @@ -22,8 +22,6 @@ yarn-debug.log* dist/ tmp/ temp/ -cli/dist/ -cli/*.csv .mastra diff --git a/bigset-cli-mvp.excalidraw b/bigset-cli-mvp.excalidraw deleted file mode 100644 index d9e4ca6..0000000 --- a/bigset-cli-mvp.excalidraw +++ /dev/null @@ -1,65 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - {"id":"title","type":"text","x":330,"y":-30,"width":430,"height":34,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":101,"version":1,"versionNonce":10101,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"BigSet CLI MVP - Request Routing","fontSize":28,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"BigSet CLI MVP - Request Routing","lineHeight":1.25,"baseline":26}, - - {"id":"zone_cli","type":"rectangle","x":20,"y":40,"width":300,"height":610,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"#dbe4ff","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":102,"version":1,"versionNonce":10202,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"zone_cli_t","type":"text","x":40,"y":55,"width":95,"height":25,"angle":0,"strokeColor":"#2563eb","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":103,"version":1,"versionNonce":10303,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Local CLI","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Local CLI","lineHeight":1.25,"baseline":19}, - {"id":"term","type":"rectangle","x":60,"y":110,"width":220,"height":80,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":104,"version":1,"versionNonce":10404,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"term_t","type":"text","x":92,"y":126,"width":155,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":105,"version":1,"versionNonce":10505,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"User / Agent\nbigset create ...","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"User / Agent\nbigset create ...","lineHeight":1.25,"baseline":41}, - {"id":"a1","type":"arrow","x":170,"y":190,"width":0,"height":55,"angle":0,"strokeColor":"#4a9eed","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":106,"version":1,"versionNonce":10606,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"oclif","type":"rectangle","x":60,"y":245,"width":220,"height":80,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":107,"version":1,"versionNonce":10707,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"oclif_t","type":"text","x":98,"y":261,"width":145,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":108,"version":1,"versionNonce":10808,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"oclif commands\ncreate/list/rows","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"oclif commands\ncreate/list/rows","lineHeight":1.25,"baseline":41}, - {"id":"a2","type":"arrow","x":170,"y":325,"width":0,"height":55,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":109,"version":1,"versionNonce":10909,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"client","type":"rectangle","x":60,"y":380,"width":220,"height":80,"angle":0,"strokeColor":"#06b6d4","backgroundColor":"#c3fae8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":110,"version":1,"versionNonce":11010,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"client_t","type":"text","x":102,"y":396,"width":137,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":111,"version":1,"versionNonce":11111,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"CLI client\nfetch + zod","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"CLI client\nfetch + zod","lineHeight":1.25,"baseline":41}, - {"id":"env","type":"rectangle","x":45,"y":520,"width":250,"height":95,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":112,"version":1,"versionNonce":11212,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"env_t","type":"text","x":58,"y":536,"width":224,"height":60,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":113,"version":1,"versionNonce":11313,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Reads local config\nBIGSET_CLI_MVP_ENABLED\nBIGSET_CLI_MVP_OWNER_ID","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Reads local config\nBIGSET_CLI_MVP_ENABLED\nBIGSET_CLI_MVP_OWNER_ID","lineHeight":1.25,"baseline":56}, - - {"id":"zone_backend","type":"rectangle","x":390,"y":40,"width":340,"height":610,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#e5dbff","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":114,"version":1,"versionNonce":11414,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"zone_backend_t","type":"text","x":410,"y":55,"width":145,"height":25,"angle":0,"strokeColor":"#6d3fd9","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":115,"version":1,"versionNonce":11515,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Fastify Backend","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Fastify Backend","lineHeight":1.25,"baseline":19}, - {"id":"a3","type":"arrow","x":280,"y":420,"width":140,"height":0,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":116,"version":1,"versionNonce":11616,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,0]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a3_t","type":"text","x":318,"y":392,"width":88,"height":20,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":117,"version":1,"versionNonce":11717,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"HTTP /cli/*","fontSize":16,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"HTTP /cli/*","lineHeight":1.25,"baseline":15}, - {"id":"routes","type":"rectangle","x":430,"y":115,"width":260,"height":120,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":118,"version":1,"versionNonce":11818,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"routes_t","type":"text","x":448,"y":131,"width":226,"height":80,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":119,"version":1,"versionNonce":11919,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Dev-only /cli routes\nPOST /cli/datasets\nPOST /cli/datasets/:id/populate\nGET /cli/datasets/:id/rows","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Dev-only /cli routes\nPOST /cli/datasets\nPOST /cli/datasets/:id/populate\nGET /cli/datasets/:id/rows","lineHeight":1.25,"baseline":76}, - {"id":"a4","type":"arrow","x":560,"y":235,"width":0,"height":60,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":120,"version":1,"versionNonce":12020,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,60]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"guard","type":"diamond","x":455,"y":295,"width":210,"height":110,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":121,"version":1,"versionNonce":12121,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"guard_t","type":"text","x":494,"y":325,"width":132,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":122,"version":1,"versionNonce":12222,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Local guard\nenabled + owner id?","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Local guard\nenabled + owner id?","lineHeight":1.25,"baseline":36}, - {"id":"a5","type":"arrow","x":560,"y":405,"width":0,"height":55,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":123,"version":1,"versionNonce":12323,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"admin","type":"rectangle","x":430,"y":460,"width":260,"height":85,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#ffd8a8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":124,"version":1,"versionNonce":12424,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"admin_t","type":"text","x":453,"y":481,"width":214,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":125,"version":1,"versionNonce":12525,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Backend uses Convex admin key\nbut pins ownerId","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Backend uses Convex admin key\nbut pins ownerId","lineHeight":1.25,"baseline":36}, - {"id":"a6","type":"arrow","x":560,"y":545,"width":0,"height":55,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":126,"version":1,"versionNonce":12626,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,55]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"workflow","type":"rectangle","x":430,"y":600,"width":260,"height":70,"angle":0,"strokeColor":"#ec4899","backgroundColor":"#eebefa","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":127,"version":1,"versionNonce":12727,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"workflow_t","type":"text","x":457,"y":621,"width":206,"height":21,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":128,"version":1,"versionNonce":12828,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Existing populate workflow","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Existing populate workflow","lineHeight":1.25,"baseline":20}, - - {"id":"zone_data","type":"rectangle","x":790,"y":40,"width":340,"height":610,"angle":0,"strokeColor":"#22c55e","backgroundColor":"#d3f9d8","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":35,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":129,"version":1,"versionNonce":12929,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"zone_data_t","type":"text","x":810,"y":55,"width":220,"height":25,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":130,"version":1,"versionNonce":13030,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Data + External Services","fontSize":20,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Data + External Services","lineHeight":1.25,"baseline":19}, - {"id":"convex","type":"rectangle","x":830,"y":115,"width":240,"height":105,"angle":0,"strokeColor":"#06b6d4","backgroundColor":"#c3fae8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":131,"version":1,"versionNonce":13131,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"convex_t","type":"text","x":852,"y":133,"width":196,"height":60,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":132,"version":1,"versionNonce":13232,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Convex\ndatasets + rows\nownerId = configured dev user","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Convex\ndatasets + rows\nownerId = configured dev user","lineHeight":1.25,"baseline":56}, - {"id":"models","type":"rectangle","x":830,"y":285,"width":240,"height":80,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"#d0bfff","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":133,"version":1,"versionNonce":13333,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"models_t","type":"text","x":875,"y":304,"width":150,"height":43,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":134,"version":1,"versionNonce":13434,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"OpenRouter\nschema + agents","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"OpenRouter\nschema + agents","lineHeight":1.25,"baseline":39}, - {"id":"web","type":"rectangle","x":830,"y":430,"width":240,"height":80,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#ffd8a8","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":135,"version":1,"versionNonce":13535,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"web_t","type":"text","x":888,"y":449,"width":124,"height":43,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":136,"version":1,"versionNonce":13636,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"TinyFish\nsearch + fetch","fontSize":17,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"TinyFish\nsearch + fetch","lineHeight":1.25,"baseline":39}, - {"id":"a7","type":"arrow","x":690,"y":500,"width":120,"height":0,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":137,"version":1,"versionNonce":13737,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[120,0]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a7_t","type":"text","x":710,"y":472,"width":88,"height":19,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":138,"version":1,"versionNonce":13838,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Convex calls","fontSize":15,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Convex calls","lineHeight":1.25,"baseline":14}, - {"id":"a9","type":"arrow","x":690,"y":635,"width":140,"height":-295,"angle":0,"strokeColor":"#8b5cf6","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":139,"version":1,"versionNonce":13939,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,-295]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a10","type":"arrow","x":690,"y":635,"width":140,"height":-165,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":140,"version":1,"versionNonce":14040,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[140,-165]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a8","type":"arrow","x":690,"y":635,"width":120,"height":-25,"angle":0,"strokeColor":"#ec4899","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":141,"version":1,"versionNonce":14141,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[120,-25]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a11","type":"arrow","x":830,"y":195,"width":-550,"height":360,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"dashed","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":142,"version":1,"versionNonce":14242,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[-210,220],[-550,360]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a11_t","type":"text","x":490,"y":360,"width":165,"height":20,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":143,"version":1,"versionNonce":14343,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"poll status / read rows","fontSize":16,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"poll status / read rows","lineHeight":1.25,"baseline":15}, - - {"id":"csv","type":"rectangle","x":60,"y":700,"width":220,"height":70,"angle":0,"strokeColor":"#22c55e","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":144,"version":1,"versionNonce":14444,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"csv_t","type":"text","x":82,"y":714,"width":176,"height":45,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":145,"version":1,"versionNonce":14545,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"Optional CSV export\ndemo.csv","fontSize":18,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"Optional CSV export\ndemo.csv","lineHeight":1.25,"baseline":41}, - {"id":"a12","type":"arrow","x":170,"y":460,"width":0,"height":240,"angle":0,"strokeColor":"#22c55e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"dashed","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":146,"version":1,"versionNonce":14646,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"points":[[0,0],[0,240]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}, - {"id":"a12_t","type":"text","x":182,"y":570,"width":86,"height":19,"angle":0,"strokeColor":"#15803d","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":147,"version":1,"versionNonce":14747,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"--wait --csv","fontSize":15,"fontFamily":1,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"--wait --csv","lineHeight":1.25,"baseline":14}, - {"id":"note","type":"rectangle","x":360,"y":720,"width":430,"height":70,"angle":0,"strokeColor":"#f59e0b","backgroundColor":"#fff3bf","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":148,"version":1,"versionNonce":14848,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false}, - {"id":"note_t","type":"text","x":383,"y":734,"width":386,"height":40,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":null,"seed":149,"version":1,"versionNonce":14949,"isDeleted":false,"boundElements":null,"updated":1,"link":null,"locked":false,"text":"MVP boundary: trusted local tooling. Public CLI should replace this with real user/API-token auth.","fontSize":16,"fontFamily":1,"textAlign":"center","verticalAlign":"top","containerId":null,"originalText":"MVP boundary: trusted local tooling. Public CLI should replace this with real user/API-token auth.","lineHeight":1.25,"baseline":36} - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} diff --git a/cli/bin/run.js b/cli/bin/run.js deleted file mode 100755 index 6cf1a59..0000000 --- a/cli/bin/run.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -import "../dist/index.js"; diff --git a/cli/package-lock.json b/cli/package-lock.json deleted file mode 100644 index 9f0f325..0000000 --- a/cli/package-lock.json +++ /dev/null @@ -1,1094 +0,0 @@ -{ - "name": "@bigset/cli", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@bigset/cli", - "version": "0.1.0", - "dependencies": { - "@oclif/core": "^4.2.10", - "dotenv": "^16.4.7", - "zod": "^4.1.13" - }, - "bin": { - "bigset": "bin/run.js" - }, - "devDependencies": { - "@types/node": "^22.10.2", - "tsx": "^4.19.2", - "typescript": "^5.7.2" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", - "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@oclif/core": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.4.tgz", - "integrity": "sha512-URwiQ5ALx/sJ2iH4vzXEd+H4K6NAI7LRs6Jag3hrgKEpGmaE6alfRC8qjO4GIgb6A3ACaJumqP9twi/M9ywdHQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.2", - "ansis": "^3.17.0", - "clean-stack": "^3.0.1", - "cli-spinners": "^2.9.2", - "debug": "^4.4.3", - "ejs": "^3.1.10", - "get-package-type": "^0.1.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "lilconfig": "^3.1.3", - "minimatch": "^10.2.5", - "semver": "^7.8.1", - "string-width": "^4.2.3", - "supports-color": "^8", - "tinyglobby": "^0.2.16", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@types/node": { - "version": "22.19.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.20.tgz", - "integrity": "sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", - "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/filelist": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", - "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.4", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", - "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.6", - "filelist": "^1.0.4", - "picocolors": "^1.1.1" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/cli/package.json b/cli/package.json deleted file mode 100644 index 6ebef59..0000000 --- a/cli/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "@bigset/cli", - "version": "0.1.0", - "private": true, - "type": "module", - "bin": { - "bigset": "./bin/run.js" - }, - "scripts": { - "build": "tsc", - "dev": "tsx src/index.ts" - }, - "dependencies": { - "@oclif/core": "^4.2.10", - "dotenv": "^16.4.7", - "zod": "^4.1.13" - }, - "devDependencies": { - "@types/node": "^22.10.2", - "tsx": "^4.19.2", - "typescript": "^5.7.2" - } -} diff --git a/cli/src/client.ts b/cli/src/client.ts deleted file mode 100644 index 75a7465..0000000 --- a/cli/src/client.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { z } from "zod"; -import { backendUrl } from "./config.js"; - -const datasetColumnSchema = z.object({ - name: z.string(), - type: z.string(), - description: z.string().optional(), - isPrimaryKey: z.boolean().optional(), -}); - -export const datasetSchema = z.object({ - _id: z.string(), - _creationTime: z.number(), - name: z.string(), - description: z.string(), - ownerId: z.string(), - status: z.enum(["live", "paused", "building", "updating", "failed"]), - lastStatusError: z.string().optional(), - rowCount: z.number().optional(), - maxRowCount: z.number().optional(), - refreshCadence: z.string().optional(), - columns: z.array(datasetColumnSchema), -}); -export type Dataset = z.infer; - -const rowSchema = z.object({ - _id: z.string(), - data: z.record(z.string(), z.unknown()), - sources: z.array(z.string()).optional(), - rowSummary: z.string().optional(), - howFound: z.string().optional(), -}); -export type DatasetRow = z.infer; - -const inferredColumnSchema = z.object({ - name: z.string(), - display_name: z.string(), - type: z.string(), - is_primary_key: z.boolean(), - is_enumerable: z.boolean(), - retrieval_hint: z.string(), - nullable: z.boolean(), -}); - -const inferredSchemaSchema = z.object({ - dataset_name: z.string(), - description: z.string(), - columns: z.array(inferredColumnSchema), - primary_key: z.union([z.string(), z.array(z.string())]), - retrieval_strategy: z.string(), - source_hint: z.string(), -}); -export type InferredSchema = z.infer; - -const createDatasetResponseSchema = z.object({ - dataset: datasetSchema, - schema: inferredSchemaSchema, -}); - -const listDatasetsResponseSchema = z.object({ - datasets: z.array(datasetSchema), -}); - -const getDatasetResponseSchema = z.object({ - dataset: datasetSchema, -}); - -const getRowsResponseSchema = z.object({ - rows: z.array(rowSchema), -}); - -const startRunResponseSchema = z.object({ - success: z.boolean(), - runId: z.string(), -}); - -async function requestJson( - path: string, - schema: z.ZodType, - init?: RequestInit, -): Promise { - const res = await fetch(`${backendUrl()}${path}`, { - ...init, - headers: { - ...(init?.body ? { "Content-Type": "application/json" } : {}), - ...(init?.headers ?? {}), - }, - }); - - const body = await res.json().catch(() => null); - if (!res.ok) { - const message = - body && typeof body === "object" && "error" in body - ? String(body.error) - : `BigSet backend error (${res.status})`; - throw new Error(message); - } - - return schema.parse(body); -} - -export async function createDataset(input: { - prompt: string; - maxRowCount: number; - refreshCadence: string; -}) { - return await requestJson("/cli/datasets", createDatasetResponseSchema, { - method: "POST", - body: JSON.stringify(input), - }); -} - -export async function listDatasets() { - return await requestJson("/cli/datasets", listDatasetsResponseSchema); -} - -export async function getDataset(datasetId: string) { - const result = await requestJson( - `/cli/datasets/${encodeURIComponent(datasetId)}`, - getDatasetResponseSchema, - ); - return result.dataset; -} - -export async function getRows(datasetId: string) { - const result = await requestJson( - `/cli/datasets/${encodeURIComponent(datasetId)}/rows`, - getRowsResponseSchema, - ); - return result.rows; -} - -export async function populateDataset(datasetId: string) { - return await requestJson( - `/cli/datasets/${encodeURIComponent(datasetId)}/populate`, - startRunResponseSchema, - { method: "POST" }, - ); -} - -export async function stopDataset(datasetId: string) { - return await requestJson( - `/cli/datasets/${encodeURIComponent(datasetId)}/stop`, - z.object({ success: z.boolean() }), - { method: "POST" }, - ); -} - -export async function waitForDataset( - datasetId: string, - options: { - intervalMs: number; - timeoutMs: number; - onPoll?: (dataset: Dataset) => void; - }, -): Promise { - const startedAt = Date.now(); - for (;;) { - const dataset = await getDataset(datasetId); - options.onPoll?.(dataset); - if (dataset.status === "live" || dataset.status === "failed") { - return dataset; - } - if (Date.now() - startedAt > options.timeoutMs) { - throw new Error(`Timed out waiting for dataset ${datasetId}`); - } - await new Promise((resolve) => setTimeout(resolve, options.intervalMs)); - } -} diff --git a/cli/src/commands/create.ts b/cli/src/commands/create.ts deleted file mode 100644 index 23e8727..0000000 --- a/cli/src/commands/create.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { writeFile } from "node:fs/promises"; -import { - createDataset, - getRows, - type InferredSchema, - populateDataset, - waitForDataset, -} from "../client.js"; -import { buildCsv } from "../csv.js"; - -function formatPrimaryKey(primaryKey: InferredSchema["primary_key"]): string { - return Array.isArray(primaryKey) ? primaryKey.join(", ") : primaryKey; -} - -function labelValue(label: string, value: string | number): string { - return ` ${label.padEnd(12)} ${value}\n`; -} - -function truncate(text: string, max = 110): string { - if (text.length <= max) return text; - return `${text.slice(0, max - 3)}...`; -} - -function printSchema(schema: InferredSchema): void { - const nameWidth = Math.max(18, ...schema.columns.map((column) => column.name.length)); - process.stdout.write("\n[schema]\n"); - process.stdout.write(labelValue("name", schema.dataset_name)); - process.stdout.write(labelValue("primary key", formatPrimaryKey(schema.primary_key))); - process.stdout.write(labelValue("retrieval", schema.retrieval_strategy)); - process.stdout.write(labelValue("source", truncate(schema.source_hint))); - process.stdout.write("\n[columns]\n"); - for (const column of schema.columns) { - const tags = [ - column.type, - column.is_primary_key ? "pk" : null, - column.nullable ? "nullable" : null, - ].filter(Boolean).join(", "); - process.stdout.write( - ` ${column.name.padEnd(nameWidth)} ${tags.padEnd(20)} ${truncate(column.retrieval_hint)}\n`, - ); - } - process.stdout.write("\n"); -} - -export default class CreateCommand extends Command { - static description = "Infer a schema, create a dataset, and optionally populate it."; - - static args = { - prompt: Args.string({ required: true }), - }; - - static flags = { - rows: Flags.integer({ - char: "r", - default: 100, - description: "maximum rows to collect", - }), - cadence: Flags.string({ - default: "manual", - description: "refresh cadence", - options: ["manual", "30m", "6h", "12h", "daily", "weekly"], - }), - wait: Flags.boolean({ - default: false, - description: "wait until the populate run finishes", - }), - csv: Flags.string({ - description: "write final rows to a CSV file; implies --wait", - }), - "skip-populate": Flags.boolean({ - default: false, - description: "create the dataset but do not start population", - }), - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(CreateCommand); - const shouldWait = flags.wait || Boolean(flags.csv); - - const created = await createDataset({ - prompt: args.prompt, - maxRowCount: flags.rows, - refreshCadence: flags.cadence, - }); - - let runId: string | undefined; - let dataset = created.dataset; - - if (!flags.json) { - printSchema(created.schema); - process.stdout.write("[dataset]\n"); - process.stdout.write(labelValue("id", dataset._id)); - process.stdout.write(labelValue("name", dataset.name)); - process.stdout.write(labelValue("rows", dataset.maxRowCount ?? flags.rows)); - process.stdout.write(labelValue("cadence", flags.cadence)); - } - - if (!flags["skip-populate"]) { - const run = await populateDataset(dataset._id); - runId = run.runId; - if (!flags.json) { - process.stdout.write("\n[run]\n"); - process.stdout.write(labelValue("id", run.runId)); - process.stdout.write(labelValue("status", "started")); - } - } - - if (shouldWait && !flags["skip-populate"]) { - dataset = await waitForDataset(dataset._id, { - intervalMs: 5000, - timeoutMs: 20 * 60 * 1000, - onPoll: flags.json - ? undefined - : (current) => { - process.stdout.write( - ` ${new Date().toLocaleTimeString()} ${current.status.padEnd(9)} rows=${current.rowCount ?? 0}\n`, - ); - }, - }); - - if (dataset.status === "failed") { - throw new Error(dataset.lastStatusError ?? "Dataset population failed"); - } - } - - let csvPath: string | undefined; - if (flags.csv) { - const rows = await getRows(dataset._id); - await writeFile(flags.csv, buildCsv(dataset, rows), "utf8"); - csvPath = flags.csv; - if (!flags.json) { - process.stdout.write("\n[export]\n"); - process.stdout.write(labelValue("file", flags.csv)); - process.stdout.write(labelValue("rows", rows.length)); - } - } - - const output = { - datasetId: dataset._id, - name: dataset.name, - status: dataset.status, - rowCount: dataset.rowCount ?? 0, - runId, - csvPath, - }; - - if (flags.json) { - this.log(JSON.stringify(output, null, 2)); - return; - } - - this.log(`\n[done]\n${labelValue("dataset", output.datasetId).trimEnd()}`); - this.log(labelValue("status", output.status).trimEnd()); - this.log(labelValue("rows", output.rowCount).trimEnd()); - } -} diff --git a/cli/src/commands/export.ts b/cli/src/commands/export.ts deleted file mode 100644 index c877208..0000000 --- a/cli/src/commands/export.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { writeFile } from "node:fs/promises"; -import { getDataset, getRows } from "../client.js"; -import { buildCsv } from "../csv.js"; - -export default class ExportCommand extends Command { - static description = "Export dataset rows to a file."; - - static args = { - datasetId: Args.string({ required: true }), - }; - - static flags = { - csv: Flags.string({ - char: "o", - required: true, - description: "CSV output path", - }), - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(ExportCommand); - const [dataset, rows] = await Promise.all([ - getDataset(args.datasetId), - getRows(args.datasetId), - ]); - - await writeFile(flags.csv, buildCsv(dataset, rows), "utf8"); - - if (flags.json) { - this.log( - JSON.stringify( - { datasetId: dataset._id, rowCount: rows.length, csvPath: flags.csv }, - null, - 2, - ), - ); - return; - } - - this.log(`Wrote ${rows.length} rows to ${flags.csv}`); - } -} diff --git a/cli/src/commands/list.ts b/cli/src/commands/list.ts deleted file mode 100644 index 5e6c098..0000000 --- a/cli/src/commands/list.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Command, Flags } from "@oclif/core"; -import { listDatasets } from "../client.js"; - -export default class ListCommand extends Command { - static description = "List local CLI-scoped datasets."; - - static flags = { - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { flags } = await this.parse(ListCommand); - const { datasets } = await listDatasets(); - - if (flags.json) { - this.log(JSON.stringify({ datasets }, null, 2)); - return; - } - - for (const dataset of datasets) { - this.log( - `${dataset._id}\t${dataset.status}\t${dataset.rowCount ?? 0}\t${dataset.name}`, - ); - } - } -} diff --git a/cli/src/commands/populate.ts b/cli/src/commands/populate.ts deleted file mode 100644 index e17b970..0000000 --- a/cli/src/commands/populate.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { populateDataset, waitForDataset } from "../client.js"; - -export default class PopulateCommand extends Command { - static description = "Start population for an existing dataset."; - - static args = { - datasetId: Args.string({ required: true }), - }; - - static flags = { - wait: Flags.boolean({ - default: false, - description: "wait until the populate run finishes", - }), - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(PopulateCommand); - const run = await populateDataset(args.datasetId); - let status: string | undefined; - let rowCount: number | undefined; - - if (flags.wait) { - const dataset = await waitForDataset(args.datasetId, { - intervalMs: 5000, - timeoutMs: 20 * 60 * 1000, - onPoll: flags.json - ? undefined - : (current) => { - process.stderr.write( - `Status: ${current.status}, rows: ${current.rowCount ?? 0}\n`, - ); - }, - }); - status = dataset.status; - rowCount = dataset.rowCount ?? 0; - if (dataset.status === "failed") { - throw new Error(dataset.lastStatusError ?? "Dataset population failed"); - } - } - - if (flags.json) { - this.log(JSON.stringify({ runId: run.runId, status, rowCount }, null, 2)); - return; - } - - this.log(`Started populate run ${run.runId}`); - } -} diff --git a/cli/src/commands/rows.ts b/cli/src/commands/rows.ts deleted file mode 100644 index c731757..0000000 --- a/cli/src/commands/rows.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { getDataset, getRows } from "../client.js"; -import { buildCsv } from "../csv.js"; - -export default class RowsCommand extends Command { - static description = "Print dataset rows."; - - static args = { - datasetId: Args.string({ required: true }), - }; - - static flags = { - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - csv: Flags.boolean({ - default: false, - description: "print CSV", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(RowsCommand); - const [dataset, rows] = await Promise.all([ - getDataset(args.datasetId), - getRows(args.datasetId), - ]); - - if (flags.csv) { - this.log(buildCsv(dataset, rows)); - return; - } - - if (flags.json) { - this.log(JSON.stringify({ rows }, null, 2)); - return; - } - - this.log(buildCsv(dataset, rows)); - } -} diff --git a/cli/src/commands/status.ts b/cli/src/commands/status.ts deleted file mode 100644 index 7ba2946..0000000 --- a/cli/src/commands/status.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { getDataset } from "../client.js"; - -export default class StatusCommand extends Command { - static description = "Show one dataset's status."; - - static args = { - datasetId: Args.string({ required: true }), - }; - - static flags = { - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(StatusCommand); - const dataset = await getDataset(args.datasetId); - - if (flags.json) { - this.log(JSON.stringify({ dataset }, null, 2)); - return; - } - - this.log(`id: ${dataset._id}`); - this.log(`name: ${dataset.name}`); - this.log(`status: ${dataset.status}`); - this.log(`rows: ${dataset.rowCount ?? 0}`); - if (dataset.lastStatusError) this.log(`error: ${dataset.lastStatusError}`); - } -} diff --git a/cli/src/commands/stop.ts b/cli/src/commands/stop.ts deleted file mode 100644 index 3c23e5c..0000000 --- a/cli/src/commands/stop.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Args, Command, Flags } from "@oclif/core"; -import { stopDataset } from "../client.js"; - -export default class StopCommand extends Command { - static description = "Stop a running dataset population."; - - static args = { - datasetId: Args.string({ required: true }), - }; - - static flags = { - json: Flags.boolean({ - default: false, - description: "print machine-readable JSON", - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(StopCommand); - const result = await stopDataset(args.datasetId); - - if (flags.json) { - this.log(JSON.stringify(result, null, 2)); - return; - } - - this.log("Stop requested"); - } -} diff --git a/cli/src/config.ts b/cli/src/config.ts deleted file mode 100644 index 7df55e6..0000000 --- a/cli/src/config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { config as loadDotenv } from "dotenv"; -import { existsSync } from "node:fs"; -import { resolve } from "node:path"; - -const cwdEnv = resolve(process.cwd(), ".env"); -const parentEnv = resolve(process.cwd(), "..", ".env"); - -loadDotenv({ path: existsSync(cwdEnv) ? cwdEnv : parentEnv }); - -export function backendUrl(): string { - return ( - process.env.BIGSET_BACKEND_URL || - process.env.NEXT_PUBLIC_BACKEND_URL || - `http://localhost:${process.env.PORT || "3501"}` - ).replace(/\/$/, ""); -} diff --git a/cli/src/csv.ts b/cli/src/csv.ts deleted file mode 100644 index 21c07f1..0000000 --- a/cli/src/csv.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Dataset, DatasetRow } from "./client.js"; - -function csvEscape(value: unknown): string { - if (value === null || value === undefined) return ""; - const text = String(value); - if (/[",\n\r]/.test(text)) { - return `"${text.replace(/"/g, '""')}"`; - } - return text; -} - -export function buildCsv(dataset: Dataset, rows: DatasetRow[]): string { - const header = dataset.columns.map((column) => csvEscape(column.name)).join(","); - const body = rows.map((row) => - dataset.columns - .map((column) => csvEscape(row.data[column.name])) - .join(","), - ); - return [header, ...body].join("\n"); -} diff --git a/cli/src/index.ts b/cli/src/index.ts deleted file mode 100644 index 4f15876..0000000 --- a/cli/src/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import CreateCommand from "./commands/create.js"; -import ExportCommand from "./commands/export.js"; -import ListCommand from "./commands/list.js"; -import PopulateCommand from "./commands/populate.js"; -import RowsCommand from "./commands/rows.js"; -import StatusCommand from "./commands/status.js"; -import StopCommand from "./commands/stop.js"; - -const commands = { - create: CreateCommand, - export: ExportCommand, - list: ListCommand, - populate: PopulateCommand, - rows: RowsCommand, - status: StatusCommand, - stop: StopCommand, -}; - -function printHelp(): void { - console.log(`BigSet CLI MVP - -Usage: - bigset [args] [flags] - -Commands: - create Infer schema, create a dataset, and start populate - list List local CLI-scoped datasets - status Show dataset status - rows Print dataset rows - export Export dataset rows - populate Start population for an existing dataset - stop Stop a running populate/update - -Run "bigset --help" for command flags.`); -} - -const [commandName, ...argv] = process.argv.slice(2); - -if (!commandName || commandName === "help" || commandName === "--help" || commandName === "-h") { - printHelp(); - process.exit(0); -} - -const command = commands[commandName as keyof typeof commands]; -if (!command) { - console.error(`Unknown command: ${commandName}`); - printHelp(); - process.exit(1); -} - -try { - await command.run(argv); -} catch (err) { - const message = err instanceof Error ? err.message : String(err); - console.error(message); - process.exit(1); -} diff --git a/cli/tsconfig.json b/cli/tsconfig.json deleted file mode 100644 index 2d7992a..0000000 --- a/cli/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "dist", - "declaration": true, - "resolveJsonModule": true - }, - "include": ["src"] -} diff --git a/makefiles/Makefile b/makefiles/Makefile index 6fb05bf..cd7ef5e 100644 --- a/makefiles/Makefile +++ b/makefiles/Makefile @@ -10,7 +10,6 @@ all: dev install-deps: @cd frontend && npm install --silent @cd backend && npm install --silent - @cd cli && npm install --silent build-release: install-deps node scripts/build-release.mjs From 046138e9050404b02ade2515f36ac8bb7da63c6f Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Tue, 9 Jun 2026 13:39:16 -0700 Subject: [PATCH 6/7] fix cli stop orphan cleanup --- backend/src/index.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 5f486bc..870580d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1014,11 +1014,27 @@ fastify.post("/cli/datasets/:datasetId/stop", async (req, reply) => { const aborted = abortDataset(dataset._id); if (!aborted) { - await setDatasetPopulateStatus( - dataset._id, - "failed", - "Run interrupted: no active local CLI run registered", + req.log.warn( + { datasetId: dataset._id }, + "CLI stop requested for orphaned dataset (no active run registered); forcing to failed", ); + try { + if (dataset.status === "updating") { + await convex.mutation(internal.datasetRows.clearAllPendingUpdateStatus, { + datasetId: dataset._id, + }); + } + await setDatasetPopulateStatus( + dataset._id, + "failed", + "Run interrupted: no active local CLI run registered", + ); + } catch (statusErr) { + req.log.error( + { err: statusErr, datasetId: dataset._id }, + "Failed to force-transition orphaned CLI dataset to failed", + ); + } return reply.code(200).send({ success: true }); } return reply.code(202).send({ success: true }); From 49a975399c9bdee114a9c5dc83a9ba78bb6b5e6b Mon Sep 17 00:00:00 2001 From: pranavjanakiraman Date: Tue, 9 Jun 2026 14:40:43 -0700 Subject: [PATCH 7/7] release cli populate claim on failure --- backend/src/index.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/backend/src/index.ts b/backend/src/index.ts index 870580d..4d63f42 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -947,6 +947,7 @@ fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { return reply.code(400).send({ error: "datasetId is required" }); } + let claimedDatasetId: string | null = null; try { const dataset = await convex.query(internal.datasets.getOwnedInternal, { id: params.data.datasetId, @@ -964,6 +965,7 @@ fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { if (populateOutcome !== "started") { return reply.code(409).send({ error: `Cannot populate dataset: ${populateOutcome}` }); } + claimedDatasetId = dataset._id; const { getModelConfig } = await import("./config/models.js"); const modelConfig = await getModelConfig(ownerId); @@ -989,6 +991,20 @@ fastify.post("/cli/datasets/:datasetId/populate", async (req, reply) => { return reply.code(202).send({ success: true, runId: run.runId }); } catch (err) { req.log.error(err, "CLI populate failed"); + if (claimedDatasetId) { + try { + await setDatasetPopulateStatus( + claimedDatasetId, + "failed", + statusErrorMessage(err), + ); + } catch (statusErr) { + req.log.error( + { err: statusErr, datasetId: claimedDatasetId }, + "Failed to release CLI populate dataset claim", + ); + } + } return reply.code(502).send({ error: "Failed to populate dataset" }); } });