Skip to content

Commit fbdeb0c

Browse files
anandgupta42claude
andcommitted
merge: resolve conflict with origin/main in pyproject.toml
Keep both our dev dependencies and main's new security/docker/tunneling optional dependency groups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2 parents 0449ac8 + 8a57d73 commit fbdeb0c

38 files changed

Lines changed: 1705 additions & 4101 deletions

packages/altimate-code/src/agent/agent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export namespace Agent {
113113
lineage_check: "allow",
114114
warehouse_list: "allow",
115115
warehouse_test: "allow",
116+
warehouse_discover: "allow",
116117
schema_inspect: "allow",
117118
schema_index: "allow",
118119
schema_search: "allow",
@@ -169,6 +170,7 @@ export namespace Agent {
169170
lineage_check: "allow",
170171
warehouse_list: "allow",
171172
warehouse_test: "allow",
173+
warehouse_discover: "allow",
172174
schema_inspect: "allow",
173175
schema_index: "allow",
174176
schema_search: "allow",

packages/altimate-code/src/bridge/protocol.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,50 @@ export interface WarehouseTestResult {
218218
error?: string
219219
}
220220

221+
// --- Warehouse Management ---
222+
223+
export interface WarehouseAddParams {
224+
name: string
225+
config: Record<string, unknown>
226+
}
227+
228+
export interface WarehouseAddResult {
229+
success: boolean
230+
name: string
231+
type: string
232+
error?: string
233+
}
234+
235+
export interface WarehouseRemoveParams {
236+
name: string
237+
}
238+
239+
export interface WarehouseRemoveResult {
240+
success: boolean
241+
error?: string
242+
}
243+
244+
// --- Docker Discovery ---
245+
246+
export interface DockerContainer {
247+
container_id: string
248+
name: string
249+
image: string
250+
db_type: string
251+
host: string
252+
port: number
253+
user?: string
254+
password?: string
255+
database?: string
256+
status: string
257+
}
258+
259+
export interface WarehouseDiscoverResult {
260+
containers: DockerContainer[]
261+
container_count: number
262+
error?: string
263+
}
264+
221265
// --- Schema Cache (Indexing & Search) ---
222266

223267
export interface SchemaIndexParams {
@@ -1000,6 +1044,9 @@ export const BridgeMethods = {
10001044
"dbt.lineage": {} as { params: DbtLineageParams; result: DbtLineageResult },
10011045
"warehouse.list": {} as { params: WarehouseListParams; result: WarehouseListResult },
10021046
"warehouse.test": {} as { params: WarehouseTestParams; result: WarehouseTestResult },
1047+
"warehouse.add": {} as { params: WarehouseAddParams; result: WarehouseAddResult },
1048+
"warehouse.remove": {} as { params: WarehouseRemoveParams; result: WarehouseRemoveResult },
1049+
"warehouse.discover": {} as { params: Record<string, never>; result: WarehouseDiscoverResult },
10031050
"finops.query_history": {} as { params: QueryHistoryParams; result: QueryHistoryResult },
10041051
"finops.analyze_credits": {} as { params: CreditAnalysisParams; result: CreditAnalysisResult },
10051052
"finops.expensive_queries": {} as { params: ExpensiveQueriesParams; result: ExpensiveQueriesResult },

packages/altimate-code/src/tool/registry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ import { SqlTranslateTool } from "./sql-translate"
3232
import { LineageCheckTool } from "./lineage-check"
3333
import { WarehouseListTool } from "./warehouse-list"
3434
import { WarehouseTestTool } from "./warehouse-test"
35+
import { WarehouseAddTool } from "./warehouse-add"
36+
import { WarehouseRemoveTool } from "./warehouse-remove"
37+
import { WarehouseDiscoverTool } from "./warehouse-discover"
3538
import { SqlRecordFeedbackTool } from "./sql-record-feedback"
3639
import { SqlPredictCostTool } from "./sql-predict-cost"
3740
import { DbtRunTool } from "./dbt-run"
@@ -190,6 +193,9 @@ export namespace ToolRegistry {
190193
LineageCheckTool,
191194
WarehouseListTool,
192195
WarehouseTestTool,
196+
WarehouseAddTool,
197+
WarehouseRemoveTool,
198+
WarehouseDiscoverTool,
193199
SqlRecordFeedbackTool,
194200
SqlPredictCostTool,
195201
DbtRunTool,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import z from "zod"
2+
import { Tool } from "./tool"
3+
import { Bridge } from "../bridge/client"
4+
5+
export const WarehouseAddTool = Tool.define("warehouse_add", {
6+
description:
7+
"Add a new warehouse connection. Stores credentials securely in OS keyring when available, metadata in connections.json.",
8+
parameters: z.object({
9+
name: z.string().describe("Name for the warehouse connection"),
10+
config: z
11+
.record(z.string(), z.unknown())
12+
.describe(
13+
'Connection configuration. Must include "type" (postgres, snowflake, duckdb, etc). Example: {"type": "postgres", "host": "localhost", "port": 5432, "database": "mydb", "user": "admin", "password": "secret"}',
14+
),
15+
}),
16+
async execute(args, ctx) {
17+
if (!args.config.type) {
18+
return {
19+
title: `Add '${args.name}': FAILED`,
20+
metadata: { success: false, name: args.name, type: "" },
21+
output: `Missing required field "type" in config. Specify the database type (postgres, snowflake, duckdb, mysql, sqlserver, bigquery, databricks, redshift).`,
22+
}
23+
}
24+
25+
try {
26+
const result = await Bridge.call("warehouse.add", {
27+
name: args.name,
28+
config: args.config,
29+
})
30+
31+
if (result.success) {
32+
return {
33+
title: `Add '${args.name}': OK`,
34+
metadata: { success: true, name: result.name, type: result.type },
35+
output: `Successfully added warehouse '${result.name}' (type: ${result.type}).\n\nUse warehouse_test to verify connectivity.`,
36+
}
37+
}
38+
39+
return {
40+
title: `Add '${args.name}': FAILED`,
41+
metadata: { success: false, name: args.name, type: "" },
42+
output: `Failed to add warehouse '${args.name}'.\nError: ${result.error ?? "Unknown error"}`,
43+
}
44+
} catch (e) {
45+
const msg = e instanceof Error ? e.message : String(e)
46+
return {
47+
title: `Add '${args.name}': ERROR`,
48+
metadata: { success: false, name: args.name, type: "" },
49+
output: `Failed to add warehouse: ${msg}`,
50+
}
51+
}
52+
},
53+
})
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import z from "zod"
2+
import { Tool } from "./tool"
3+
import { Bridge } from "../bridge/client"
4+
5+
export const WarehouseDiscoverTool = Tool.define("warehouse_discover", {
6+
description:
7+
"Discover database containers running in Docker. Detects PostgreSQL, MySQL/MariaDB, and SQL Server containers and extracts connection details from port mappings and environment variables.",
8+
parameters: z.object({}),
9+
async execute(args, ctx) {
10+
try {
11+
const result = await Bridge.call("warehouse.discover", {})
12+
13+
if (result.error) {
14+
return {
15+
title: "Discover: ERROR",
16+
metadata: { count: 0 },
17+
output: `Docker discovery failed: ${result.error}`,
18+
}
19+
}
20+
21+
if (result.container_count === 0) {
22+
return {
23+
title: "Discover: no containers found",
24+
metadata: { count: 0 },
25+
output: "No supported database containers found running in Docker.\n\nSupported types: PostgreSQL, MySQL/MariaDB, SQL Server.\nEnsure Docker is running and containers have published ports.",
26+
}
27+
}
28+
29+
const lines: string[] = [
30+
"Container | Type | Host:Port | User | Database | Status",
31+
"----------|------|-----------|------|----------|-------",
32+
]
33+
for (const c of result.containers) {
34+
lines.push(
35+
`${c.name} | ${c.db_type} | ${c.host}:${c.port} | ${c.user ?? "-"} | ${c.database ?? "-"} | ${c.status}`,
36+
)
37+
}
38+
lines.push("")
39+
lines.push("Use warehouse_add to save any of these as a connection.")
40+
41+
return {
42+
title: `Discover: ${result.container_count} container(s) found`,
43+
metadata: { count: result.container_count },
44+
output: lines.join("\n"),
45+
}
46+
} catch (e) {
47+
const msg = e instanceof Error ? e.message : String(e)
48+
return {
49+
title: "Discover: ERROR",
50+
metadata: { count: 0 },
51+
output: `Failed to discover containers: ${msg}\n\nEnsure Docker is running and the docker Python package is installed.`,
52+
}
53+
}
54+
},
55+
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import z from "zod"
2+
import { Tool } from "./tool"
3+
import { Bridge } from "../bridge/client"
4+
5+
export const WarehouseRemoveTool = Tool.define("warehouse_remove", {
6+
description: "Remove a warehouse connection. Deletes both the config entry and any stored keyring credentials.",
7+
parameters: z.object({
8+
name: z.string().describe("Name of the warehouse connection to remove"),
9+
}),
10+
async execute(args, ctx) {
11+
try {
12+
const result = await Bridge.call("warehouse.remove", { name: args.name })
13+
14+
if (result.success) {
15+
return {
16+
title: `Remove '${args.name}': OK`,
17+
metadata: { success: true },
18+
output: `Successfully removed warehouse '${args.name}' and its stored credentials.`,
19+
}
20+
}
21+
22+
return {
23+
title: `Remove '${args.name}': FAILED`,
24+
metadata: { success: false },
25+
output: `Failed to remove warehouse '${args.name}'.\nError: ${result.error ?? "Connection not found or is defined via environment variable"}`,
26+
}
27+
} catch (e) {
28+
const msg = e instanceof Error ? e.message : String(e)
29+
return {
30+
title: `Remove '${args.name}': ERROR`,
31+
metadata: { success: false },
32+
output: `Failed to remove warehouse: ${msg}`,
33+
}
34+
}
35+
},
36+
})

packages/altimate-engine/pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ classifiers = [
2525
]
2626
dependencies = [
2727
"pydantic>=2.0",
28-
"sqlglot>=25.0",
2928
"pyyaml>=6.0",
3029
]
3130

@@ -47,6 +46,9 @@ warehouses = [
4746
"mysql-connector-python>=8.0",
4847
"pyodbc>=5.0",
4948
]
49+
security = ["keyring>=24.0"]
50+
docker = ["docker>=7.0"]
51+
tunneling = ["sshtunnel>=0.4", "paramiko>=3.0"]
5052
dev = [
5153
"pytest>=7.0",
5254
"ruff>=0.4",

packages/altimate-engine/src/altimate_engine/ci/cost_gate.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""CI cost gate — scan changed SQL files for critical issues.
22
3-
Reads SQL files, runs existing analyze_sql(), and returns
3+
Reads SQL files, runs lint analysis, and returns
44
pass/fail based on whether CRITICAL severity issues are found.
55
66
Skips:
77
- Jinja templates ({{ }}, {% %})
8-
- sqlglot parse errors (likely Jinja or non-standard SQL)
8+
- Parse errors (likely Jinja or non-standard SQL)
99
- Non-SQL files
1010
"""
1111

@@ -15,7 +15,7 @@
1515
import re
1616
from typing import Any
1717

18-
from altimate_engine.sql.analyzer import analyze_sql
18+
from altimate_engine.sql.guard import guard_lint
1919

2020

2121
# Jinja pattern: {{ ... }} or {% ... %} or {# ... #}
@@ -119,19 +119,19 @@ def scan_files(
119119
file_issues: list[dict[str, Any]] = []
120120

121121
for stmt in statements:
122-
# Run analyzer
123-
analysis = analyze_sql(stmt, dialect)
124-
if not analysis.get("success", True):
122+
# Run lint analysis
123+
lint_result = guard_lint(stmt)
124+
if lint_result.get("error"):
125125
# Parse error — skip this statement (likely incomplete SQL)
126126
continue
127127

128-
for issue in analysis.get("issues", []):
129-
severity = issue.get("severity", "warning")
128+
for finding in lint_result.get("findings", lint_result.get("issues", [])):
129+
severity = finding.get("severity", "warning")
130130
file_issues.append({
131-
"type": issue.get("type", "UNKNOWN"),
131+
"type": finding.get("rule", finding.get("type", "UNKNOWN")),
132132
"severity": severity,
133-
"message": issue.get("message", ""),
134-
"source": "analyzer",
133+
"message": finding.get("message", ""),
134+
"source": "lint",
135135
})
136136
total_issues += 1
137137
if severity in ("error", "critical"):

packages/altimate-engine/src/altimate_engine/connections.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66
from typing import Any
77

88
from altimate_engine.connectors.base import Connector
9+
from altimate_engine.credential_store import resolve_config
10+
from altimate_engine.ssh_tunnel import start, stop
11+
12+
SSH_FIELDS = {
13+
"ssh_host",
14+
"ssh_port",
15+
"ssh_user",
16+
"ssh_auth_type",
17+
"ssh_key_path",
18+
"ssh_password",
19+
}
920

1021

1122
class ConnectionRegistry:
@@ -44,7 +55,33 @@ def get(cls, name: str) -> Connector:
4455
if name not in cls._connections:
4556
raise ValueError(f"Connection '{name}' not found in registry")
4657

47-
config = cls._connections[name]
58+
config = dict(cls._connections[name])
59+
config = resolve_config(name, config)
60+
61+
ssh_host = config.get("ssh_host")
62+
if ssh_host:
63+
if config.get("connection_string"):
64+
raise ValueError(
65+
"SSH tunneling requires explicit host/port — "
66+
"cannot be used with connection_string"
67+
)
68+
ssh_config = {
69+
k: config.pop(k) for k in list(config.keys()) if k in SSH_FIELDS
70+
}
71+
local_port = start(
72+
name=name,
73+
ssh_host=ssh_config.get("ssh_host", ""),
74+
remote_host=config.get("host", "localhost"),
75+
remote_port=config.get("port", 5432),
76+
ssh_port=ssh_config.get("ssh_port", 22),
77+
ssh_user=ssh_config.get("ssh_user"),
78+
ssh_auth_type=ssh_config.get("ssh_auth_type", "key"),
79+
ssh_key_path=ssh_config.get("ssh_key_path"),
80+
ssh_password=ssh_config.get("ssh_password"),
81+
)
82+
config["host"] = "127.0.0.1"
83+
config["port"] = local_port
84+
4885
dialect = config.get("type", "duckdb")
4986

5087
if dialect == "duckdb":
@@ -219,3 +256,26 @@ def test(cls, name: str) -> dict[str, Any]:
219256
return {"connected": True, "error": None}
220257
except Exception as e:
221258
return {"connected": False, "error": str(e)}
259+
finally:
260+
stop(name)
261+
262+
@classmethod
263+
def add(cls, name: str, config: dict[str, Any]) -> dict[str, Any]:
264+
from altimate_engine.credential_store import save_connection
265+
266+
result = save_connection(name, config)
267+
cls._loaded = False
268+
return result
269+
270+
@classmethod
271+
def remove(cls, name: str) -> bool:
272+
from altimate_engine.credential_store import remove_connection
273+
274+
result = remove_connection(name)
275+
cls._loaded = False
276+
return result
277+
278+
@classmethod
279+
def reload(cls) -> None:
280+
cls._loaded = False
281+
cls._connections.clear()

0 commit comments

Comments
 (0)