@graphit/cli 0.2.113 → 0.2.136

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +2 -0
  5. package/bin/graphit +1 -1
  6. package/bin/graphit.ps1 +1 -1
  7. package/dist/commands/connector.js +66 -2
  8. package/dist/commands/connector.js.map +1 -1
  9. package/dist/commands/ds-config.d.ts +45 -0
  10. package/dist/commands/ds-config.js +122 -0
  11. package/dist/commands/ds-config.js.map +1 -0
  12. package/dist/commands/ds.d.ts +0 -45
  13. package/dist/commands/ds.js +4 -128
  14. package/dist/commands/ds.js.map +1 -1
  15. package/dist/commands/governance.js +1 -17
  16. package/dist/commands/governance.js.map +1 -1
  17. package/dist/commands/kb-create.js +2 -2
  18. package/dist/commands/kb-create.js.map +1 -1
  19. package/dist/commands/kb-update.js +2 -2
  20. package/dist/commands/kb-update.js.map +1 -1
  21. package/dist/commands/query.d.ts +38 -0
  22. package/dist/commands/query.js +145 -18
  23. package/dist/commands/query.js.map +1 -1
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/skill-guard.d.ts +4 -0
  27. package/dist/skill-guard.js +75 -0
  28. package/dist/skill-guard.js.map +1 -0
  29. package/package.json +1 -1
  30. package/scripts/plugin-status.mjs +48 -0
  31. package/skills/graphit/SKILL.md +15 -14
  32. package/skills/graphit/VERSION.json +1 -1
  33. package/skills/graphit/cursor/graphit-sql-reference.mdc +2 -2
  34. package/skills/graphit/graphit.mdc +6 -9
  35. package/skills/graphit/references/data-sources.md +7 -3
  36. package/skills/graphit/references/governance-explained.md +2 -2
  37. package/skills/graphit/references/governance.md +22 -17
  38. package/skills/graphit/references/kb-actions.md +2 -3
  39. package/skills/graphit/references/kb-traversal.md +1 -1
  40. package/skills/graphit/references/runtime.md +5 -1
  41. package/skills/graphit/references/sql-reference.md +15 -2
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Graphit CLI plugin for AI coding assistants",
10
- "version": "0.2.113"
10
+ "version": "0.2.136"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -16,9 +16,9 @@
16
16
  "source": {
17
17
  "source": "npm",
18
18
  "package": "@graphit/cli",
19
- "version": "0.2.113"
19
+ "version": "0.2.136"
20
20
  },
21
- "version": "0.2.113",
21
+ "version": "0.2.136",
22
22
  "category": "data-visualization",
23
23
  "tags": [
24
24
  "bi",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphit",
3
- "version": "0.2.113",
3
+ "version": "0.2.136",
4
4
  "description": "Build custom HTML dashboards from real data using the Graphit CLI. KB-aware queries, entity wrapping, cached data sources.",
5
5
  "author": {
6
6
  "name": "Graphit",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphit",
3
- "version": "0.2.113",
3
+ "version": "0.2.136",
4
4
  "description": "Build custom HTML dashboards from real data using the Graphit CLI. KB-aware queries, entity wrapping, cached data sources.",
5
5
  "author": {
6
6
  "name": "Graphit",
package/README.md CHANGED
@@ -51,6 +51,8 @@ graphit setup
51
51
 
52
52
  Either path installs the Graphit skill so the assistant can drive the CLI on your behalf.
53
53
 
54
+ > **Start a new session after installing the plugin.** Claude Code (and Codex) load plugin skills and commands at session start, so the Graphit skill, `/graphit`, and the `graphit` commands are not available in the session you installed from. Open a new session first - otherwise the assistant will not have the skill loaded and will improvise instead of following the guided workflow.
55
+
54
56
  ## Authenticate
55
57
 
56
58
  ```bash
package/bin/graphit CHANGED
@@ -14,7 +14,7 @@ if [ -z "${GRAPHIT_PLUGIN_ROOT:-}" ]; then
14
14
  fi
15
15
 
16
16
  # graphit:floor (stamped by scripts/sync-plugin-version.mjs from cli/package.json)
17
- FLOOR_VERSION="0.2.113"
17
+ FLOOR_VERSION="0.2.136"
18
18
 
19
19
  PACKAGE_NAME="@graphit/cli"
20
20
  # Strict semver: anything else is rejected so a tampered cache cannot inject.
package/bin/graphit.ps1 CHANGED
@@ -7,7 +7,7 @@ if (-not $env:GRAPHIT_PLUGIN_ROOT) {
7
7
  }
8
8
 
9
9
  # graphit:floor (stamped by scripts/sync-plugin-version.mjs from cli/package.json)
10
- $FloorVersion = "0.2.113"
10
+ $FloorVersion = "0.2.136"
11
11
 
12
12
  $PackageName = "@graphit/cli"
13
13
  # Strict semver: anything else is rejected so a tampered cache cannot inject.
@@ -1,18 +1,48 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
2
  import { apiClient } from "../api/client.js";
3
3
  import { output, errorOutput } from "../output/format.js";
4
+ // Mirrors the backend SA-key required fields; the client-side check gives fast
5
+ // feedback (bad path / not JSON / wrong key file) before the network call. The
6
+ // backend re-validates authoritatively.
7
+ const SA_KEY_REQUIRED_FIELDS = [
8
+ "type", "project_id", "private_key_id", "private_key",
9
+ "client_email", "client_id", "auth_uri", "token_uri",
10
+ ];
11
+ function parseServiceAccountKey(raw) {
12
+ let key;
13
+ try {
14
+ key = JSON.parse(raw);
15
+ }
16
+ catch {
17
+ throw new Error("Invalid JSON in key file. Pass the full service-account key file with --key-file.");
18
+ }
19
+ if (typeof key !== "object" || key === null || Array.isArray(key)) {
20
+ throw new Error("Service-account key must be a JSON object.");
21
+ }
22
+ if (key.type !== "service_account") {
23
+ throw new Error(`Expected key type 'service_account', got '${key.type ?? "(missing)"}'.`);
24
+ }
25
+ const missing = SA_KEY_REQUIRED_FIELDS.filter((f) => !(f in key));
26
+ if (missing.length > 0) {
27
+ throw new Error(`Service-account key is missing required fields: ${missing.sort().join(", ")}`);
28
+ }
29
+ return key;
30
+ }
4
31
  export function registerConnectorCommands(program) {
5
32
  const connector = program
6
33
  .command("connector")
7
34
  .description("Connection management. OAuth and GitHub connections are set up in the Graphit web app.");
8
35
  connector
9
36
  .command("list")
10
- .description("List active connections")
37
+ .description("List active connections (Snowflake + BigQuery)")
11
38
  .action(async function () {
12
39
  try {
13
40
  const resp = await apiClient.get("/api/v1/cli/connectors");
41
+ // Warehouse-agnostic columns: `type` distinguishes the warehouse;
42
+ // per-type fields (account/warehouse vs project/dataset) fill in where
43
+ // present and render blank where not applicable.
14
44
  output(this, resp.connectors, {
15
- columns: ["id", "name", "account", "auth_type"],
45
+ columns: ["id", "name", "type", "account", "warehouse", "project", "dataset"],
16
46
  });
17
47
  }
18
48
  catch (err) {
@@ -56,6 +86,40 @@ export function registerConnectorCommands(program) {
56
86
  errorOutput(err);
57
87
  }
58
88
  });
89
+ add
90
+ .command("bigquery-serviceaccount")
91
+ .description("Add BigQuery via a service-account key (org admin only)")
92
+ .requiredOption("--key-file <path>", "Path to the service-account key JSON file")
93
+ .option("--project <id>", "GCP project for billing/queries (defaults to the key's project_id)")
94
+ .option("--dataset <name>", "Default BigQuery dataset")
95
+ .option("--location <loc>", "BigQuery location, e.g. US or EU (must match the dataset region)")
96
+ .option("--name <name>", "Connection name (defaults to the project id)")
97
+ .option("--max-bytes-billed <n>", "Per-query scan cap in bytes (default 10 GiB)")
98
+ .action(async function () {
99
+ try {
100
+ const opts = this.opts();
101
+ const keyStats = await stat(opts.keyFile);
102
+ const mode = keyStats.mode & 0o777;
103
+ if (mode > 0o600) {
104
+ console.error(`Warning: Key file has loose permissions (${mode.toString(8)}). Consider chmod 600.`);
105
+ }
106
+ const raw = await readFile(opts.keyFile, "utf-8");
107
+ const key = parseServiceAccountKey(raw);
108
+ const projectId = opts.project || key.project_id;
109
+ const resp = await apiClient.post("/api/v1/cli/connectors/bigquery-serviceaccount", {
110
+ name: opts.name || projectId,
111
+ sa_key_json: raw,
112
+ project_id: projectId || null,
113
+ dataset: opts.dataset || null,
114
+ location: opts.location || null,
115
+ max_bytes_billed: opts.maxBytesBilled ? Number(opts.maxBytesBilled) : null,
116
+ });
117
+ output(this, resp, { keyValue: true });
118
+ }
119
+ catch (err) {
120
+ errorOutput(err);
121
+ }
122
+ });
59
123
  connector
60
124
  .command("test <id>")
61
125
  .description("Test a connection")
@@ -1 +1 @@
1
- {"version":3,"file":"connector.js","sourceRoot":"","sources":["../../src/commands/connector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../../src/commands/connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAS1D,+EAA+E;AAC/E,+EAA+E;AAC/E,wCAAwC;AACxC,MAAM,sBAAsB,GAAG;IAC7B,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa;IACrD,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW;CACrD,CAAC;AAEF,SAAS,sBAAsB,CAAC,GAAW;IACzC,IAAI,GAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,6CAA6C,GAAG,CAAC,IAAI,IAAI,WAAW,IAAI,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mDAAmD,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,SAAS,GAAG,OAAO;SACtB,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,wFAAwF,CAAC,CAAC;IAEzG,SAAS;SACN,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,KAAK;QACX,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAC9B,wBAAwB,CACzB,CAAC;YACF,kEAAkE;YAClE,uEAAuE;YACvE,iDAAiD;YACjD,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;gBAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC;aAC9E,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,MAAM,GAAG,GAAG,SAAS;SAClB,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,2FAA2F,CAAC,CAAC;IAE5G,GAAG;SACA,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,gCAAgC,CAAC;SAC7C,cAAc,CAAC,qBAAqB,EAAE,8BAA8B,CAAC;SACrE,cAAc,CAAC,eAAe,EAAE,oBAAoB,CAAC;SACrD,cAAc,CAAC,cAAc,EAAE,gCAAgC,CAAC;SAChE,MAAM,CAAC,eAAe,EAAE,uDAAuD,CAAC;SAChF,cAAc,CAAC,kBAAkB,EAAE,mBAAmB,CAAC;SACvD,cAAc,CAAC,eAAe,EAAE,cAAc,CAAC;SAC/C,cAAc,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;SACrD,MAAM,CAAC,KAAK;QACX,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC;YACnC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,4CAA4C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC;YACtG,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAEjD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAC/B,0CAA0C,EAC1C;gBACE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,eAAe,EAAE,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;gBACvB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;gBACjC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;aAChC,CACF,CAAC;YACF,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,GAAG;SACA,OAAO,CAAC,yBAAyB,CAAC;SAClC,WAAW,CAAC,yDAAyD,CAAC;SACtE,cAAc,CAAC,mBAAmB,EAAE,2CAA2C,CAAC;SAChF,MAAM,CAAC,gBAAgB,EAAE,oEAAoE,CAAC;SAC9F,MAAM,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;SACtD,MAAM,CAAC,kBAAkB,EAAE,kEAAkE,CAAC;SAC9F,MAAM,CAAC,eAAe,EAAE,8CAA8C,CAAC;SACvE,MAAM,CAAC,wBAAwB,EAAE,8CAA8C,CAAC;SAChF,MAAM,CAAC,KAAK;QACX,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC;YACnC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,4CAA4C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC;YACtG,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAExC,MAAM,SAAS,GAAuB,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC;YAErE,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAC/B,gDAAgD,EAChD;gBACE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;gBAC5B,WAAW,EAAE,GAAG;gBAChB,UAAU,EAAE,SAAS,IAAI,IAAI;gBAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;gBAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;gBAC/B,gBAAgB,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI;aAC3E,CACF,CAAC;YACF,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,SAAS;SACN,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,mBAAmB,CAAC;SAChC,MAAM,CAAC,KAAK,WAA0B,EAAU;QAC/C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAC/B,0BAA0B,EAAE,OAAO,EACnC,EAAE,CACH,CAAC;YACF,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,SAAS;SACN,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,sCAAsC,CAAC;SACnD,cAAc,CAAC,OAAO,EAAE,iBAAiB,CAAC;SAC1C,MAAM,CAAC,KAAK,WAA0B,EAAU;QAC/C,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,MAAM,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,45 @@
1
+ export type CreateMode = {
2
+ kind: "file";
3
+ } | {
4
+ kind: "sql";
5
+ } | {
6
+ kind: "error";
7
+ message: string;
8
+ };
9
+ /**
10
+ * Decide whether `ds create` runs in file-upload or SQL mode (Feature #645).
11
+ * Pure + exported so it is unit-testable without a backend.
12
+ */
13
+ export declare function resolveCreateMode(opts: {
14
+ file?: unknown;
15
+ sql?: unknown;
16
+ name?: unknown;
17
+ }): CreateMode;
18
+ /**
19
+ * Parse one `--table-lookback` spec of the form `TABLE:COLUMN:DAYS`. Split from
20
+ * the RIGHT so schema-qualified table names (`DB.SCHEMA.TABLE`) pass through
21
+ * verbatim - column names can never contain `:` (server-validated identifiers).
22
+ * Pure + exported so it is unit-testable without a backend.
23
+ */
24
+ export declare function parseTableLookback(spec: string): {
25
+ table: string;
26
+ column: string;
27
+ lookback_days: number;
28
+ };
29
+ /**
30
+ * Validate `ds refresh-config` flags and build the PATCH body. Pure + exported
31
+ * so it is unit-testable without a backend. Throws on invalid input; the action
32
+ * surfaces the message via errorOutput. Sets the COMPLETE incremental config -
33
+ * omitted fields fall back to server defaults (merge window 3, reconciliation
34
+ * off, table lookback windows cleared).
35
+ */
36
+ export declare function buildRefreshConfigBody(opts: {
37
+ mode?: unknown;
38
+ watermarkColumn?: unknown;
39
+ watermarkType?: unknown;
40
+ mergeKey?: unknown;
41
+ lookback?: unknown;
42
+ mergeWindow?: unknown;
43
+ reconciliation?: unknown;
44
+ tableLookback?: unknown;
45
+ }): Record<string, unknown>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Decide whether `ds create` runs in file-upload or SQL mode (Feature #645).
3
+ * Pure + exported so it is unit-testable without a backend.
4
+ */
5
+ export function resolveCreateMode(opts) {
6
+ if (opts.file) {
7
+ if (opts.sql) {
8
+ return { kind: "error", message: "Use either --file or --sql, not both." };
9
+ }
10
+ return { kind: "file" };
11
+ }
12
+ if (!opts.sql || !opts.name) {
13
+ return {
14
+ kind: "error",
15
+ message: "Provide --file <path> to upload a local Excel/CSV, or both --sql <query> and --name <name> for a SQL data source.",
16
+ };
17
+ }
18
+ return { kind: "sql" };
19
+ }
20
+ /**
21
+ * Parse one `--table-lookback` spec of the form `TABLE:COLUMN:DAYS`. Split from
22
+ * the RIGHT so schema-qualified table names (`DB.SCHEMA.TABLE`) pass through
23
+ * verbatim - column names can never contain `:` (server-validated identifiers).
24
+ * Pure + exported so it is unit-testable without a backend.
25
+ */
26
+ export function parseTableLookback(spec) {
27
+ const last = spec.lastIndexOf(":");
28
+ const mid = last === -1 ? -1 : spec.lastIndexOf(":", last - 1);
29
+ const column = mid === -1 ? "" : spec.slice(mid + 1, last);
30
+ if (mid <= 0 || !column) {
31
+ throw new Error(`Invalid --table-lookback "${spec}". Use TABLE:COLUMN:DAYS (e.g. "ANALYTICS.PUBLIC.EVENTS:EVENT_DATE:30").`);
32
+ }
33
+ const days = Number(spec.slice(last + 1));
34
+ if (!Number.isInteger(days) || days < 1 || days > 3650) {
35
+ throw new Error(`Invalid days in --table-lookback "${spec}". Must be an integer between 1 and 3650.`);
36
+ }
37
+ return { table: spec.slice(0, mid), column, lookback_days: days };
38
+ }
39
+ // Mirrors the server's canonical table matching: last dotted segment, uppercased.
40
+ function canonicalTableKey(table) {
41
+ return (table.split(".").pop() ?? table).toUpperCase();
42
+ }
43
+ /**
44
+ * Validate `ds refresh-config` flags and build the PATCH body. Pure + exported
45
+ * so it is unit-testable without a backend. Throws on invalid input; the action
46
+ * surfaces the message via errorOutput. Sets the COMPLETE incremental config -
47
+ * omitted fields fall back to server defaults (merge window 3, reconciliation
48
+ * off, table lookback windows cleared).
49
+ */
50
+ export function buildRefreshConfigBody(opts) {
51
+ const mode = opts.mode;
52
+ if (!mode) {
53
+ throw new Error('--mode is required. Use "full" or "incremental".');
54
+ }
55
+ if (mode !== "full" && mode !== "incremental") {
56
+ throw new Error(`Invalid --mode "${mode}". Use "full" or "incremental".`);
57
+ }
58
+ // Mirror the backend 400 client-side so the user fixes it in one turn.
59
+ if (mode === "incremental" && (!opts.watermarkColumn || !opts.watermarkType)) {
60
+ throw new Error("Incremental mode requires --watermark-column and --watermark-type.");
61
+ }
62
+ const WM_TYPES = new Set(["timestamp", "date", "integer"]);
63
+ if (opts.watermarkType && !WM_TYPES.has(opts.watermarkType)) {
64
+ throw new Error(`Invalid --watermark-type "${opts.watermarkType}". Use timestamp, date, or integer.`);
65
+ }
66
+ const RECON = new Set(["daily", "weekly", "monthly", "off"]);
67
+ if (opts.reconciliation !== undefined && !RECON.has(opts.reconciliation)) {
68
+ throw new Error(`Invalid --reconciliation "${opts.reconciliation}". Use daily, weekly, monthly, or off.`);
69
+ }
70
+ // Clean break: the pre-rename --lookback flag set the merge window, while
71
+ // "lookback" now means the per-table read windows - silently honoring
72
+ // either meaning would be a trap. Hidden from help; always errors with the fix.
73
+ if (opts.lookback !== undefined) {
74
+ throw new Error("--lookback was renamed to --merge-window (it sets the merge window - how far back " +
75
+ "each run re-fetches and upserts). For per-table lookback windows use --table-lookback.");
76
+ }
77
+ const body = { refresh_mode: mode };
78
+ if (opts.watermarkColumn)
79
+ body.watermark_column = opts.watermarkColumn;
80
+ if (opts.watermarkType)
81
+ body.watermark_type = opts.watermarkType;
82
+ if (opts.mergeKey)
83
+ body.merge_key = opts.mergeKey;
84
+ if (opts.mergeWindow !== undefined) {
85
+ const n = parseInt(opts.mergeWindow, 10);
86
+ if (isNaN(n)) {
87
+ throw new Error(`Invalid --merge-window "${opts.mergeWindow}". Must be a number.`);
88
+ }
89
+ body.lookback_periods = n;
90
+ }
91
+ if (opts.reconciliation !== undefined) {
92
+ // "off" disables the reconciliation full-refresh backstop.
93
+ body.reconciliation_schedule = opts.reconciliation === "off" ? null : opts.reconciliation;
94
+ }
95
+ // Per-table lookback windows (repeatable TABLE:COLUMN:DAYS). The server
96
+ // clears windows SILENTLY in full mode and rejects integer watermarks -
97
+ // both are mirrored loudly here so the user fixes the call in one turn.
98
+ const specs = (opts.tableLookback ?? []);
99
+ if (specs.length > 0) {
100
+ if (mode !== "incremental") {
101
+ throw new Error("--table-lookback requires --mode incremental.");
102
+ }
103
+ if (opts.watermarkType === "integer") {
104
+ throw new Error("--table-lookback needs --watermark-type date or timestamp (lookback windows are day-based).");
105
+ }
106
+ if (specs.length > 20) {
107
+ throw new Error(`Too many --table-lookback windows (${specs.length}); max 20.`);
108
+ }
109
+ const windows = specs.map(parseTableLookback);
110
+ const seen = new Set();
111
+ for (const w of windows) {
112
+ const key = canonicalTableKey(w.table);
113
+ if (seen.has(key)) {
114
+ throw new Error(`Duplicate --table-lookback for table "${w.table}" (tables match on the last dotted segment, case-insensitive).`);
115
+ }
116
+ seen.add(key);
117
+ }
118
+ body.table_lookback_windows = windows;
119
+ }
120
+ return body;
121
+ }
122
+ //# sourceMappingURL=ds-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ds-config.js","sourceRoot":"","sources":["../../src/commands/ds-config.ts"],"names":[],"mappings":"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"}
@@ -1,47 +1,2 @@
1
1
  import { Command } from "commander";
2
- export type CreateMode = {
3
- kind: "file";
4
- } | {
5
- kind: "sql";
6
- } | {
7
- kind: "error";
8
- message: string;
9
- };
10
- /**
11
- * Decide whether `ds create` runs in file-upload or SQL mode (Feature #645).
12
- * Pure + exported so it is unit-testable without a backend.
13
- */
14
- export declare function resolveCreateMode(opts: {
15
- file?: unknown;
16
- sql?: unknown;
17
- name?: unknown;
18
- }): CreateMode;
19
- /**
20
- * Parse one `--table-lookback` spec of the form `TABLE:COLUMN:DAYS`. Split from
21
- * the RIGHT so schema-qualified table names (`DB.SCHEMA.TABLE`) pass through
22
- * verbatim - column names can never contain `:` (server-validated identifiers).
23
- * Pure + exported so it is unit-testable without a backend.
24
- */
25
- export declare function parseTableLookback(spec: string): {
26
- table: string;
27
- column: string;
28
- lookback_days: number;
29
- };
30
- /**
31
- * Validate `ds refresh-config` flags and build the PATCH body. Pure + exported
32
- * so it is unit-testable without a backend. Throws on invalid input; the action
33
- * surfaces the message via errorOutput. Sets the COMPLETE incremental config -
34
- * omitted fields fall back to server defaults (merge window 3, reconciliation
35
- * off, table lookback windows cleared).
36
- */
37
- export declare function buildRefreshConfigBody(opts: {
38
- mode?: unknown;
39
- watermarkColumn?: unknown;
40
- watermarkType?: unknown;
41
- mergeKey?: unknown;
42
- lookback?: unknown;
43
- mergeWindow?: unknown;
44
- reconciliation?: unknown;
45
- tableLookback?: unknown;
46
- }): Record<string, unknown>;
47
2
  export declare function registerDSCommands(program: Command): void;
@@ -6,6 +6,7 @@ import { apiClient } from "../api/client.js";
6
6
  import { output, errorOutput, note } from "../output/format.js";
7
7
  import { getFrontendUrl } from "../config.js";
8
8
  import { reconcilePoll, NOT_FOUND_STATUS } from "./ds-poll.js";
9
+ import { buildRefreshConfigBody, resolveCreateMode } from "./ds-config.js";
9
10
  import { registerRefreshHistoryCommand } from "./ds/refresh-history.js";
10
11
  // Project #244: print soft, non-blocking shape advisories. Print-and-proceed -
11
12
  // these never gate a create or verify.
@@ -215,127 +216,6 @@ async function scanAndPrintSummary(dsId, dsName, rowCount, force = false, accept
215
216
  return null;
216
217
  }
217
218
  }
218
- /**
219
- * Decide whether `ds create` runs in file-upload or SQL mode (Feature #645).
220
- * Pure + exported so it is unit-testable without a backend.
221
- */
222
- export function resolveCreateMode(opts) {
223
- if (opts.file) {
224
- if (opts.sql) {
225
- return { kind: "error", message: "Use either --file or --sql, not both." };
226
- }
227
- return { kind: "file" };
228
- }
229
- if (!opts.sql || !opts.name) {
230
- return {
231
- kind: "error",
232
- message: "Provide --file <path> to upload a local Excel/CSV, or both --sql <query> and --name <name> for a SQL data source.",
233
- };
234
- }
235
- return { kind: "sql" };
236
- }
237
- /**
238
- * Parse one `--table-lookback` spec of the form `TABLE:COLUMN:DAYS`. Split from
239
- * the RIGHT so schema-qualified table names (`DB.SCHEMA.TABLE`) pass through
240
- * verbatim - column names can never contain `:` (server-validated identifiers).
241
- * Pure + exported so it is unit-testable without a backend.
242
- */
243
- export function parseTableLookback(spec) {
244
- const last = spec.lastIndexOf(":");
245
- const mid = last === -1 ? -1 : spec.lastIndexOf(":", last - 1);
246
- const column = mid === -1 ? "" : spec.slice(mid + 1, last);
247
- if (mid <= 0 || !column) {
248
- throw new Error(`Invalid --table-lookback "${spec}". Use TABLE:COLUMN:DAYS (e.g. "ANALYTICS.PUBLIC.EVENTS:EVENT_DATE:30").`);
249
- }
250
- const days = Number(spec.slice(last + 1));
251
- if (!Number.isInteger(days) || days < 1 || days > 3650) {
252
- throw new Error(`Invalid days in --table-lookback "${spec}". Must be an integer between 1 and 3650.`);
253
- }
254
- return { table: spec.slice(0, mid), column, lookback_days: days };
255
- }
256
- // Mirrors the server's canonical table matching: last dotted segment, uppercased.
257
- function canonicalTableKey(table) {
258
- return (table.split(".").pop() ?? table).toUpperCase();
259
- }
260
- /**
261
- * Validate `ds refresh-config` flags and build the PATCH body. Pure + exported
262
- * so it is unit-testable without a backend. Throws on invalid input; the action
263
- * surfaces the message via errorOutput. Sets the COMPLETE incremental config -
264
- * omitted fields fall back to server defaults (merge window 3, reconciliation
265
- * off, table lookback windows cleared).
266
- */
267
- export function buildRefreshConfigBody(opts) {
268
- const mode = opts.mode;
269
- if (!mode) {
270
- throw new Error('--mode is required. Use "full" or "incremental".');
271
- }
272
- if (mode !== "full" && mode !== "incremental") {
273
- throw new Error(`Invalid --mode "${mode}". Use "full" or "incremental".`);
274
- }
275
- // Mirror the backend 400 client-side so the user fixes it in one turn.
276
- if (mode === "incremental" && (!opts.watermarkColumn || !opts.watermarkType)) {
277
- throw new Error("Incremental mode requires --watermark-column and --watermark-type.");
278
- }
279
- const WM_TYPES = new Set(["timestamp", "date", "integer"]);
280
- if (opts.watermarkType && !WM_TYPES.has(opts.watermarkType)) {
281
- throw new Error(`Invalid --watermark-type "${opts.watermarkType}". Use timestamp, date, or integer.`);
282
- }
283
- const RECON = new Set(["daily", "weekly", "monthly", "off"]);
284
- if (opts.reconciliation !== undefined && !RECON.has(opts.reconciliation)) {
285
- throw new Error(`Invalid --reconciliation "${opts.reconciliation}". Use daily, weekly, monthly, or off.`);
286
- }
287
- // Clean break: the pre-rename --lookback flag set the merge window, while
288
- // "lookback" now means the per-table read windows - silently honoring
289
- // either meaning would be a trap. Hidden from help; always errors with the fix.
290
- if (opts.lookback !== undefined) {
291
- throw new Error("--lookback was renamed to --merge-window (it sets the merge window - how far back " +
292
- "each run re-fetches and upserts). For per-table lookback windows use --table-lookback.");
293
- }
294
- const body = { refresh_mode: mode };
295
- if (opts.watermarkColumn)
296
- body.watermark_column = opts.watermarkColumn;
297
- if (opts.watermarkType)
298
- body.watermark_type = opts.watermarkType;
299
- if (opts.mergeKey)
300
- body.merge_key = opts.mergeKey;
301
- if (opts.mergeWindow !== undefined) {
302
- const n = parseInt(opts.mergeWindow, 10);
303
- if (isNaN(n)) {
304
- throw new Error(`Invalid --merge-window "${opts.mergeWindow}". Must be a number.`);
305
- }
306
- body.lookback_periods = n;
307
- }
308
- if (opts.reconciliation !== undefined) {
309
- // "off" disables the reconciliation full-refresh backstop.
310
- body.reconciliation_schedule = opts.reconciliation === "off" ? null : opts.reconciliation;
311
- }
312
- // Per-table lookback windows (repeatable TABLE:COLUMN:DAYS). The server
313
- // clears windows SILENTLY in full mode and rejects integer watermarks -
314
- // both are mirrored loudly here so the user fixes the call in one turn.
315
- const specs = (opts.tableLookback ?? []);
316
- if (specs.length > 0) {
317
- if (mode !== "incremental") {
318
- throw new Error("--table-lookback requires --mode incremental.");
319
- }
320
- if (opts.watermarkType === "integer") {
321
- throw new Error("--table-lookback needs --watermark-type date or timestamp (lookback windows are day-based).");
322
- }
323
- if (specs.length > 20) {
324
- throw new Error(`Too many --table-lookback windows (${specs.length}); max 20.`);
325
- }
326
- const windows = specs.map(parseTableLookback);
327
- const seen = new Set();
328
- for (const w of windows) {
329
- const key = canonicalTableKey(w.table);
330
- if (seen.has(key)) {
331
- throw new Error(`Duplicate --table-lookback for table "${w.table}" (tables match on the last dotted segment, case-insensitive).`);
332
- }
333
- seen.add(key);
334
- }
335
- body.table_lookback_windows = windows;
336
- }
337
- return body;
338
- }
339
219
  /**
340
220
  * Create a data source from a local Excel/CSV file (Feature #645). Uploads the
341
221
  * file as multipart; the server parses, runs the inline AI scan, encrypts, and
@@ -393,8 +273,8 @@ export function registerDSCommands(program) {
393
273
  .description("Create a data source from a SQL query (--sql) or a local Excel/CSV file (--file)")
394
274
  .option("--sql <query>", "Source SQL query")
395
275
  .option("--name <name>", "Data source name (defaults to the file name in --file mode)")
396
- .option("--connection <id>", "Snowflake connection ID (SQL mode)")
397
- .option("--schema <name>", "Schema name (SQL mode)")
276
+ .option("--connection <id>", "Warehouse connection ID - Snowflake or BigQuery (SQL mode)")
277
+ .option("--schema <name>", "Schema (Snowflake) or dataset (BigQuery) name (SQL mode)")
398
278
  .option("--skip-scan", "Skip schema scan after creation (SQL mode)")
399
279
  .option("--detect-tables", "Print the tables detected in the SQL and exit, without creating (SQL mode preview)")
400
280
  .option("--source-tables <list>", "Comma-separated override for the tables referenced in the SQL (SQL mode; omit to auto-detect)")
@@ -647,16 +527,12 @@ export function registerDSCommands(program) {
647
527
  }
648
528
  });
649
529
  ds.command("update <id>")
650
- .description("Update data source governance settings")
651
- .option("--governed-mode <mode>", "Governance mode: on|off")
530
+ .description("Update a data source row cap")
652
531
  .option("--max-rows <n>", "Max rows limit (0 to remove)")
653
532
  .action(async function (id) {
654
533
  try {
655
534
  const opts = this.opts();
656
535
  const body = {};
657
- if (opts.governedMode !== undefined) {
658
- body.governed_mode = opts.governedMode === "on";
659
- }
660
536
  if (opts.maxRows !== undefined) {
661
537
  const val = parseInt(opts.maxRows, 10);
662
538
  if (isNaN(val))