From 7074790393663e9845b655a91c4ffd4b3b2b5f3d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 10 Jun 2026 16:45:24 +0000 Subject: [PATCH] feat: wmill datatable create + actionable sql extension error Co-Authored-By: Claude Fable 5 --- cli/src/commands/datatable/datatable.ts | 69 +++++++++++++++++++ cli/src/guidance/skills.gen.ts | 3 + cli/src/utils/script_common.ts | 10 ++- .../auto-generated/cli/cli-commands.md | 3 + system_prompts/auto-generated/prompts.ts | 3 + .../skills/cli-commands/SKILL.md | 3 + 6 files changed, 90 insertions(+), 1 deletion(-) diff --git a/cli/src/commands/datatable/datatable.ts b/cli/src/commands/datatable/datatable.ts index c86c971a90..1291d4cc13 100644 --- a/cli/src/commands/datatable/datatable.ts +++ b/cli/src/commands/datatable/datatable.ts @@ -41,6 +41,61 @@ async function run( await runCatalogQuery(opts, "datatable", name, sql); } +async function create( + opts: GlobalOptions & { resource?: string; force?: boolean }, + name?: string, +) { + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + const dtName = name ?? DEFAULT_DATATABLE_NAME; + + const existing = await wmill.listDataTables({ + workspace: workspace.workspaceId, + }); + if (existing.some((d) => d.name === dtName)) { + throw new Error(`Datatable '${dtName}' already exists in this workspace`); + } + // edit_datatable_config replaces the whole settings object, and fork + // metadata on existing datatables can't be read back through the API — + // so only touch a non-empty config when explicitly asked to. + if (existing.length > 0 && !opts.force) { + throw new Error( + `Workspace already has datatable(s): ${existing + .map((d) => d.name) + .join(", ")}. Re-run with --force to add '${dtName}' ` + + "(note: fork metadata on existing datatables is not preserved)", + ); + } + + const datatables: Record< + string, + { database: { resource_type: "postgresql" | "instance"; resource_path?: string } } + > = {}; + for (const d of existing) { + datatables[d.name] = { + database: { + resource_type: d.resource_type as "postgresql" | "instance", + resource_path: d.resource_path ?? undefined, + }, + }; + } + datatables[dtName] = opts.resource + ? { database: { resource_type: "postgresql", resource_path: opts.resource } } + : { database: { resource_type: "instance", resource_path: "datatable_db" } }; + + await wmill.editDataTableConfig({ + workspace: workspace.workspaceId, + requestBody: { settings: { datatables } }, + }); + log.info( + `Datatable '${dtName}' created (${ + opts.resource + ? `postgresql resource ${opts.resource}` + : "instance-backed" + }). Scripts can now use datatable://${dtName}.`, + ); +} + async function serve( opts: GlobalOptions & { port?: number; host?: string; password?: string }, ) { @@ -69,6 +124,20 @@ const command = new Command() "Output only the final result as JSON. Useful for scripting.", ) .action(run as any) + .command( + "create", + "register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable://", + ) + .arguments("[name:string]") + .option( + "--resource ", + "Back the datatable with an existing postgresql resource path instead of the instance database", + ) + .option( + "--force", + "Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved)", + ) + .action(create as any) .command( "serve", "Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string", diff --git a/cli/src/guidance/skills.gen.ts b/cli/src/guidance/skills.gen.ts index 61d92755fb..cdb0c19234 100644 --- a/cli/src/guidance/skills.gen.ts +++ b/cli/src/guidance/skills.gen.ts @@ -6102,6 +6102,9 @@ datatable related commands - \`datatable run \` - run a SQL query on a datatable - \`-n --name \` - Datatable name (default: main) - \`-s --silent\` - Output only the final result as JSON. Useful for scripting. +- \`datatable create [name:string]\` - register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable:// + - \`--resource \` - Back the datatable with an existing postgresql resource path instead of the instance database + - \`--force\` - Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved) - \`datatable serve\` - Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string - \`--port \` - Port to listen on (default: first free port in 5433-5500) - \`--host \` - Bind address (default: 127.0.0.1) diff --git a/cli/src/utils/script_common.ts b/cli/src/utils/script_common.ts index f314fe0e5d..a713e41782 100644 --- a/cli/src/utils/script_common.ts +++ b/cli/src/utils/script_common.ts @@ -110,8 +110,16 @@ export function inferContentTypeFromFilePath( return "rlang"; // for related places search: ADD_NEW_LANG } else { + const ext = contentPath.substring(contentPath.lastIndexOf(".")); + let hint = ""; + if (ext === ".sql") { + hint = + "\nBare .sql is ambiguous — use a dialect extension: .pg.sql (postgresql), .my.sql (mysql), .bq.sql (bigquery), .sf.sql (snowflake), .ms.sql (mssql), .odb.sql (oracledb), .duckdb.sql (duckdb)"; + } throw new Error( - "Invalid language: " + contentPath.substring(contentPath.lastIndexOf(".")) + `Cannot infer script language from extension '${ext}' (file ${contentPath}).` + + hint + + "\nSupported extensions: .ts (bun/deno), .py, .go, .sh, .ps1, .php, .rs, .cs, .nu, .java, .rb, .r, .gql, .playbook.yml, .pg.sql, .my.sql, .bq.sql, .sf.sql, .ms.sql, .odb.sql, .duckdb.sql" ); } } diff --git a/system_prompts/auto-generated/cli/cli-commands.md b/system_prompts/auto-generated/cli/cli-commands.md index 19e1144c95..ac1a5b45f6 100644 --- a/system_prompts/auto-generated/cli/cli-commands.md +++ b/system_prompts/auto-generated/cli/cli-commands.md @@ -77,6 +77,9 @@ datatable related commands - `datatable run ` - run a SQL query on a datatable - `-n --name ` - Datatable name (default: main) - `-s --silent` - Output only the final result as JSON. Useful for scripting. +- `datatable create [name:string]` - register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable:// + - `--resource ` - Back the datatable with an existing postgresql resource path instead of the instance database + - `--force` - Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved) - `datatable serve` - Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string - `--port ` - Port to listen on (default: first free port in 5433-5500) - `--host ` - Bind address (default: 127.0.0.1) diff --git a/system_prompts/auto-generated/prompts.ts b/system_prompts/auto-generated/prompts.ts index 3d8819c4fe..dc0f3702f7 100644 --- a/system_prompts/auto-generated/prompts.ts +++ b/system_prompts/auto-generated/prompts.ts @@ -2627,6 +2627,9 @@ datatable related commands - \`datatable run \` - run a SQL query on a datatable - \`-n --name \` - Datatable name (default: main) - \`-s --silent\` - Output only the final result as JSON. Useful for scripting. +- \`datatable create [name:string]\` - register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable:// + - \`--resource \` - Back the datatable with an existing postgresql resource path instead of the instance database + - \`--force\` - Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved) - \`datatable serve\` - Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string - \`--port \` - Port to listen on (default: first free port in 5433-5500) - \`--host \` - Bind address (default: 127.0.0.1) diff --git a/system_prompts/auto-generated/skills/cli-commands/SKILL.md b/system_prompts/auto-generated/skills/cli-commands/SKILL.md index dc2ab710ed..f67d475856 100644 --- a/system_prompts/auto-generated/skills/cli-commands/SKILL.md +++ b/system_prompts/auto-generated/skills/cli-commands/SKILL.md @@ -82,6 +82,9 @@ datatable related commands - `datatable run ` - run a SQL query on a datatable - `-n --name ` - Datatable name (default: main) - `-s --silent` - Output only the final result as JSON. Useful for scripting. +- `datatable create [name:string]` - register a datatable database in the workspace (default: instance-backed 'main') so scripts can use datatable:// + - `--resource ` - Back the datatable with an existing postgresql resource path instead of the instance database + - `--force` - Allow adding to a workspace that already has datatables (fork metadata on existing ones is not preserved) - `datatable serve` - Serve all datatables as a Postgres-wire endpoint (psql, DBeaver, pgAdmin); the client picks the datatable via the database name in its connection string - `--port ` - Port to listen on (default: first free port in 5433-5500) - `--host ` - Bind address (default: 127.0.0.1)