dune-sdk-generator
v1.3.0
Published
Generate strongly-typed TypeScript SDKs from CDF Data Models
Maintainers
Readme
Dune SDK Generator
Generate strongly-typed TypeScript SDKs that unify native CDF APIs and custom Data Modeling types with automatic relationship discovery.
Quick Start
Interactive Mode
CDF_PROJECT="lervik-industries" CDF_TOKEN=$(npx dune-key) npx dune-sdk-generator generateThis launches an interactive UI to browse and select views for SDK generation.
# Specify output location and name
npx dune-sdk-generator generate --output ./sdk --name equipment-sdk
# Regenerate from saved config
npx dune-sdk-generator generate --config dune.config.jsonNon-Interactive Mode (Agent-Friendly)
# 1. Search for views
npx dune-sdk-generator search "canvas" --limit 10 > views.json
# 2. Generate SDK non-interactively
npx dune-sdk-generator generate \
--views views.json \
--name canvas-sdk \
--output ./sdk \
--non-interactive \
--format jsonEnvironment Variables
| Variable | Required |
|----------|----------|
| CDF_PROJECT | ✅ |
| CDF_TOKEN | ✅ |
| CDF_CLUSTER | Optional (default: api) |
Features
🎯 Unified SDK Interface
The generator produces a unified SDK that combines:
- Native CDF APIs with narrower, InstanceId-focused interfaces (timeseries, files, datapoints, events, sequences, annotations, labels)
- Custom Data Modeling types discovered via introspection (Canvas, Equipment, etc.)
All accessible from a single entry point:
const sdk = initializeSDK(client);
// Native APIs with narrower interfaces
sdk.timeseries.create([{ space, externalId, name, unit }]);
sdk.files.upload(id, metadata, content);
sdk.datapoints.insert([{ id, datapoints }]);
// Custom DM types
sdk.canvas.create({ space, externalId, properties });
sdk.equipment.list({ filter });Key Benefits:
- Consistent patterns: All APIs use InstanceId instead of mixed id/externalId
- Type safety: Full TypeScript support across native and custom types
- Unified workflows: Seamlessly combine native APIs with Data Modeling
- Focused interfaces: Streamlined API surface removes unused parameters
🤖 Agent-Friendly CLI
The CLI is designed for both interactive use and programmatic/agentic workflows:
search- Search for views by keyword with JSON outputintrospect- Get detailed view definitionsgenerate- Generate SDKs interactively or non-interactively--format json- Machine-readable output for parsing--non-interactive- Skip all prompts for CI/CD and agent contexts
See CLI Reference for details.
🎯 Type-Safe CRUD Operations
Generated SDKs provide fully typed methods for:
create()- Create new instancesget()- Retrieve by IDupdate()- Update existing instancesdelete()- Delete instanceslist()- List with pagination and filteringquery()- Advanced queries with custom filters
🔗 Reverse Relationships
The SDK generator automatically discovers reverse relationships in your data model.
What are Reverse Relationships?
In data modeling, you often have one-way references:
CanvasAnnotationhas a propertycanvas: InstanceIdpointing toCanvasCanvasdoesn't explicitly know aboutCanvasAnnotation- But semantically, a Canvas "has many" CanvasAnnotations
The SDK generator discovers these implicit relationships and generates helper methods:
// Without reverse relationships (manual query)
const annotations = await client.canvasAnnotations.query({
filter: {
equals: {
property: CanvasAnnotationSDK.PROPERTY_PATHS.canvas,
value: { space: 'mySpace', externalId: 'canvas-1' }
}
}
});
// With reverse relationships (generated helper)
const annotations = await client.canvases.getAnnotations(
{ space: 'mySpace', externalId: 'canvas-1' }
);Interactive Configuration
When you generate an SDK, the CLI automatically discovers and prompts you to configure reverse relationships:
npx dune-sdk-generator generate
# After view selection and introspection...
🔗 Discovered 3 reverse relationship(s):
Canvas:
• getAnnotations() → CanvasAnnotation[]
from CanvasAnnotation.canvas
• getComments() → CanvasComment[]
from CanvasComment.canvas
Equipment:
• getMaintenanceRecords() → MaintenanceRecord[]
from MaintenanceRecord.equipment
? Enable reverse relationships in the generated SDK? (Y/n)
? Review and customize relationship names? (y/N)Interactive Customization:
If you choose to review/customize (or if conflicts are detected):
Canvas.getAnnotations()
From: CanvasAnnotation.canvas
Returns: CanvasAnnotation[]
? Configure this relationship:
❯ Keep default (getAnnotations)
Customize method name
Skip this relationship
# If you choose "Customize method name":
? Method name: getCanvasAnnotationsConflict Detection:
The CLI automatically detects naming conflicts:
⚠️ Naming conflicts detected:
Canvas.getAnnotations
Multiple relationships would generate the same method name
? Review and customize relationship names? (Y/n) # Required for conflictsConfiguration is Saved:
Your choices are automatically saved to dune.config.json for regeneration.
Manual Configuration (Advanced)
You can also edit dune.config.json directly:
{
"reverseRelationships": {
"Canvas": {
"annotations": {
"from": "CanvasAnnotation.canvas",
"methodName": "getAnnotations"
}
}
}
}When regenerating with --config, your existing relationship configuration is preserved.
Generated Methods
For each reverse relationship, the SDK generates:
/**
* Get all CanvasAnnotation that reference this Canvas
*
* This is a reverse relationship - CanvasAnnotation has a property 'canvas'
* that references Canvas, but Canvas doesn't explicitly define this relationship.
*
* @param id The Canvas instance to find related CanvasAnnotation for
* @param options Query options (limit, additional filters, cursor)
* @returns Array of CanvasAnnotation instances referencing this Canvas
*
* @performance This performs a filtered query on CanvasAnnotation
*/
async getAnnotations(
id: InstanceId,
options?: {
limit?: number;
additionalFilter?: any;
cursor?: string;
}
): Promise<CanvasAnnotation[]>Advanced Usage
Combine reverse relationships with additional filters:
// Get only annotations created in the last week
const recentAnnotations = await client.canvases.getAnnotations(
canvasId,
{
limit: 50,
additionalFilter: {
range: {
property: ['node', 'createdTime'],
gte: Date.now() - 7 * 24 * 60 * 60 * 1000
}
}
}
);📦 Dependency Resolution
The generator automatically:
- Discovers parent views (via
implements) - Generates type definitions for dependencies
- Resolves transitive dependencies
🔄 Regeneration
Save your configuration for easy regeneration:
# First generation saves config
npx dune-sdk-generator generate
# Later regeneration
npx dune-sdk-generator generate --config dune.config.jsonCLI Reference
search Command
Search for views by keyword and output results in JSON format compatible with --views.
dune-sdk-generator search <query> [options]Arguments:
<query>- Search query string (single query per command)
Options:
--limit <number>- Maximum number of results (default: 1000)
Output: JSON array of view references
[
{
"space": "cdf_industrial_canvas",
"externalId": "Canvas",
"version": "v1"
}
]Examples:
# Search for canvas-related views
dune-sdk-generator search "canvas" > canvas-views.json
# Search with custom limit
dune-sdk-generator search "equipment" --limit 50
# Multiple searches (run separately)
dune-sdk-generator search "annotation" > annotations.json
dune-sdk-generator search "comment" > comments.jsonintrospect Command
Get detailed view definitions including properties, constraints, and relationships.
dune-sdk-generator introspect <references...>Arguments:
<references...>- One or more view references in format:space/externalId/version
Output: JSON array of complete view definitions
Examples:
# Introspect single view
dune-sdk-generator introspect cdf_industrial_canvas/Canvas/v1
# Introspect multiple views
dune-sdk-generator introspect \
cdf_industrial_canvas/Canvas/v1 \
cdf_industrial_canvas/CanvasAnnotation/v1generate Command
Generate SDK with interactive or non-interactive view selection.
dune-sdk-generator generate [options]Options:
--views <path|json>- Views to generate SDK for (file path or inline JSON array)--output <path>- Output directory (default:./sdk)--name <name>- SDK name for generated file (default:generated-sdk)--reverse-relationships <mode>- Reverse relationship mode:auto,none, orconfig(default:auto)--non-interactive, -y- Skip all prompts (use defaults or fail)--format <format>- Output format:textorjson(default:text)--config <path>- Regenerate from existing config file
Native API Configuration:
During interactive generation, you'll be prompted:
? Include native CDF API wrappers (timeseries, files, datapoints, etc.)? (Y/n)By default, all native APIs are included. You can configure which APIs to include in dune.config.json:
{
"includeNativeAPIs": true,
"nativeAPIs": {
"timeseries": true,
"files": true,
"datapoints": true,
"events": true,
"sequences": true,
"annotations": true,
"labels": true
}
}Modes:
- Interactive Mode (default): Browse and select views with UI
- Non-Interactive Mode: Provide views via
--viewsflag - Regeneration Mode: Use existing config with
--config
Examples:
# Interactive mode
dune-sdk-generator generate
# Non-interactive with views from file
dune-sdk-generator generate \
--views views.json \
--name my-sdk \
--output ./sdk \
--non-interactive
# Non-interactive with inline JSON
dune-sdk-generator generate \
--views '[{"space":"x","externalId":"y","version":"v1"}]' \
--non-interactive \
--format json
# Regenerate from config
dune-sdk-generator generate --config dune.config.json
# Non-interactive without reverse relationships
dune-sdk-generator generate \
--views views.json \
--reverse-relationships none \
--non-interactiveJSON Output Format (when --format json):
{
"success": true,
"sdkName": "my-sdk",
"outputDirectory": "./sdk",
"files": [
"./sdk/my-sdk.ts",
"./sdk/my-sdk.test.ts"
],
"configPath": "./dune.config.json",
"stats": {
"sdkViews": 5,
"typeOnlyViews": 3,
"reverseRelationships": 8
}
}Generated SDK Structure
The generated SDK provides a unified interface combining both native CDF APIs and custom Data Modeling types:
// Initialize SDK
import { CogniteClient } from '@cognite/sdk';
import { initializeSDK } from './sdk/my-sdk';
const client = new CogniteClient({ ... });
const sdk = initializeSDK(client);
// ============================================================================
// Native CDF APIs (Focused Interfaces)
// ============================================================================
// Time Series - InstanceId-based interface
const timeseries = await sdk.timeseries.create([{
space: 'mySpace',
externalId: 'temp-sensor-1',
name: 'Temperature Sensor',
unit: 'C',
isStep: false
}]);
// Data Points - Insert and retrieve
await sdk.datapoints.insert([{
id: { space: 'mySpace', externalId: 'temp-sensor-1' },
datapoints: [
{ timestamp: Date.now(), value: 23.5 },
{ timestamp: Date.now() + 60000, value: 24.1 }
]
}]);
const datapoints = await sdk.datapoints.retrieve({
items: [{ id: { space: 'mySpace', externalId: 'temp-sensor-1' } }],
start: Date.now() - 3600000,
end: Date.now()
});
// Files - Upload and download
await sdk.files.upload(
{ space: 'mySpace', externalId: 'manual-1' },
{ name: 'equipment-manual.pdf', mimeType: 'application/pdf' },
fileBuffer
);
const fileData = await sdk.files.download({ space: 'mySpace', externalId: 'manual-1' });
// Events - Create with instance linkage
await sdk.events.create([{
type: 'maintenance',
description: 'Routine inspection',
startTime: new Date(),
linkedInstance: { space: 'mySpace', externalId: 'equipment-1' }
}]);
// Find events for an instance
const events = await sdk.events.findByInstance(
{ space: 'mySpace', externalId: 'equipment-1' }
);
// ============================================================================
// Custom Data Modeling Types
// ============================================================================
// Create canvas instance
const canvas = await sdk.canvas.create({
space: 'mySpace',
externalId: 'canvas-1',
properties: {
name: 'My Canvas',
width: 1920,
height: 1080
}
});
// Use reverse relationships
const annotations = await sdk.canvas.getAnnotations(
{ space: canvas.space, externalId: canvas.externalId }
);
// Query with filters
const results = await sdk.canvas.query({
filter: {
equals: {
property: ['mySpace', 'Canvas/v1', 'name'],
value: 'My Canvas'
}
},
limit: 100
});
// ============================================================================
// Combined Workflows
// ============================================================================
// Create equipment with time series and files
const equipment = await sdk.equipment.create({
space: 'industrial',
externalId: 'pump-001',
properties: {
name: 'Main Pump',
location: 'Building A'
}
});
// Create associated time series
await sdk.timeseries.create([{
space: 'industrial',
externalId: 'pump-001-pressure',
name: 'Pump 001 Pressure',
unit: 'bar'
}]);
// Upload equipment manual
await sdk.files.upload(
{ space: 'industrial', externalId: 'pump-001-manual' },
{ name: 'pump-manual.pdf' },
manualBuffer
);
// Log maintenance event
await sdk.events.create([{
type: 'maintenance',
description: 'Pressure sensor calibration',
linkedInstance: { space: 'industrial', externalId: 'pump-001' }
}]);Agentic Usage Guide
This section demonstrates how an AI agent can use the CLI to help users generate SDKs through a conversational workflow.
End-to-End Agentic Workflow
Here's a complete example of how an agent would interact with a user to generate an SDK:
- Understand Requirements - Ask user for keywords
- Search - Find matching views
- Introspect - Analyze view structure and relationships
- Present Options - Show recommendations based on analysis
- Prepare - Create filtered view list
- Configure - Get SDK preferences
- Generate - Create SDK non-interactively
- Report - Parse and display results
Step 1: Understand User Requirements
Agent: "What data models would you like to generate an SDK for? Please provide some keywords that describe the views you need (e.g., 'canvas', 'equipment', 'work orders')."
User: "I need views related to industrial canvases and annotations"
Agent: "Got it! Let me search for views matching those keywords..."Step 2: Search for Views
# Agent executes: Search for "canvas" views
dune-sdk-generator search "canvas" --limit 50 > canvas-results.json
# Agent executes: Search for "annotation" views
dune-sdk-generator search "annotation" --limit 50 > annotation-results.jsonStep 3: Introspect Views to Understand Structure
# Agent parses search results and introspects top candidates
dune-sdk-generator introspect \
cdf_industrial_canvas/Canvas/v1 \
cdf_industrial_canvas/CanvasConfig/v1 \
cdf_apps_shared/Canvas/v1 \
cdf_industrial_canvas/CanvasAnnotation/v1 \
cdf_apps_shared/Annotation/v1 > view-details.jsonAgent analyzes the introspection results:
- Canvas (cdf_industrial_canvas): 15 properties, implements CogniteDescribableNode
- Has relationships to CanvasAnnotation, CanvasComment
- Properties: name, width, height, data, etc.
- CanvasAnnotation (cdf_industrial_canvas): 8 properties
- References Canvas via 'canvas' property (direct relation)
- Properties: text, position, color, etc.
- CanvasConfig (cdf_industrial_canvas): 5 properties
- Configuration for canvas display settings
- Canvas (cdf_apps_shared): Similar to cdf_industrial_canvas/Canvas
- Likely a duplicate/different version
- Annotation (cdf_apps_shared): Generic annotation
- May not be specific to CanvasStep 4: Present Analyzed Results to User
Agent: "I found the following views that match your criteria. After analyzing their structure, here are my recommendations:
✅ Recommended (tightly related to industrial canvases):
• cdf_industrial_canvas/Canvas/v1
- Main canvas view with 15 properties
- Core entity for canvas operations
• cdf_industrial_canvas/CanvasAnnotation/v1
- Annotations linked to Canvas (8 properties)
- Has direct relationship to Canvas
• cdf_industrial_canvas/CanvasConfig/v1
- Canvas display configuration (5 properties)
- Likely needed for Canvas functionality
⚠️ Alternative options (may be duplicates):
• cdf_apps_shared/Canvas/v1
- Similar to the industrial canvas version
- Consider if you need both
• cdf_apps_shared/Annotation/v1
- Generic annotation, not canvas-specific
My recommendation: Start with the three cdf_industrial_canvas views. They form a cohesive set for canvas and annotation management.
Would you like to:
a) Generate SDK with my recommended views
b) Include the cdf_apps_shared views as well
c) Let me search for additional related views
Please choose an option."
User: "Option a, that looks perfect"
Agent: "Excellent! I'll generate an SDK with these three cohesive views:
• Canvas (main entity)
• CanvasAnnotation (linked annotations)
• CanvasConfig (configuration)
This will include automatic reverse relationships, so you'll be able to:
- Get all annotations for a canvas: canvas.getAnnotations()
- Standard CRUD operations on all three types
- Type-safe access to all properties"Step 5: Prepare View List
# Agent creates a filtered views.json file
cat > views.json << 'EOF'
[
{
"space": "cdf_industrial_canvas",
"externalId": "Canvas",
"version": "v1"
},
{
"space": "cdf_industrial_canvas",
"externalId": "CanvasConfig",
"version": "v1"
},
{
"space": "cdf_industrial_canvas",
"externalId": "CanvasAnnotation",
"version": "v1"
}
]
EOFStep 6: Ask for Configuration Preferences
Agent: "Great! Now I need a few configuration details:
1. What should I name the SDK? (default: generated-sdk)
2. Where should I output the files? (default: ./sdk)
3. Should I include reverse relationships? (recommended: yes)
Please provide your preferences or type 'use defaults'."
User: "Name it canvas-sdk, output to ./src/sdk, and yes to reverse relationships"Step 7: Generate SDK Non-Interactively
# Agent executes the generation
dune-sdk-generator generate \
--views views.json \
--name canvas-sdk \
--output ./src/sdk \
--reverse-relationships auto \
--non-interactive \
--format json > generation-result.jsonStep 8: Report Results
# Agent parses the JSON output
cat generation-result.json{
"success": true,
"sdkName": "canvas-sdk",
"outputDirectory": "./src/sdk",
"files": [
"./src/sdk/canvas-sdk.ts",
"./src/sdk/canvas-sdk.test.ts"
],
"configPath": "./dune.config.json",
"stats": {
"sdkViews": 3,
"typeOnlyViews": 2,
"reverseRelationships": 5
}
}Agent: "✅ SDK generated successfully!
Generated files:
• ./src/sdk/canvas-sdk.ts (main SDK)
• ./src/sdk/canvas-sdk.test.ts (test suite)
• ./dune.config.json (configuration for regeneration)
SDK Stats:
• 3 main views with full CRUD operations
• 2 type-only dependency views
• 5 reverse relationships discovered
You can now import and use your SDK:
import { initializeSDK } from './src/sdk/canvas-sdk';
To regenerate later with the same configuration:
dune-sdk-generator generate --config dune.config.json"Agent Implementation Example
Here's how to implement the agentic workflow in code:
// Example agent implementation
async function generateSDKWithAgent(userRequest: string) {
// 1. Extract keywords from user request
const keywords = extractKeywords(userRequest); // e.g., ["canvas", "annotation"]
// 2. Search for each keyword
const allViews = [];
for (const keyword of keywords) {
const { stdout } = await exec(
`dune-sdk-generator search "${keyword}" --limit 50`
);
const views = JSON.parse(stdout);
allViews.push(...views);
}
// 3. Remove duplicates
const uniqueViews = deduplicateViews(allViews);
// 4. Introspect views to understand their structure
const viewRefs = uniqueViews.map(v => `${v.space}/${v.externalId}/${v.version}`);
const { stdout: introspectData } = await exec(
`dune-sdk-generator introspect ${viewRefs.join(' ')}`
);
const viewDetails = JSON.parse(introspectData);
// 5. Analyze views to make recommendations
const analysis = analyzeViews(viewDetails);
// - Check for relationships between views
// - Count properties
// - Identify core vs auxiliary views
// - Detect duplicates across spaces
// 6. Present recommendations to user
const recommendations = await presentRecommendations(analysis, uniqueViews);
const selectedViews = await askUserToConfirm(recommendations);
// 7. Write selected views to file
await writeFile('views.json', JSON.stringify(selectedViews, null, 2));
// 8. Get configuration preferences
const config = await askUserForConfig();
// 9. Generate SDK
const { stdout } = await exec(
`dune-sdk-generator generate \
--views views.json \
--name ${config.name} \
--output ${config.output} \
--reverse-relationships auto \
--non-interactive \
--format json`
);
// 10. Parse and report results
const result = JSON.parse(stdout);
return result;
}
// Helper function to analyze view relationships
function analyzeViews(viewDetails: any[]): ViewAnalysis {
const relationships = new Map();
for (const view of viewDetails) {
// Check for direct relations pointing to other views
for (const [propName, prop] of Object.entries(view.properties || {})) {
if (prop.type?.type === 'direct') {
const target = findViewByContainer(viewDetails, prop.type.source);
if (target) {
relationships.set(
`${view.externalId}->${target.externalId}`,
{ source: view, target, property: propName }
);
}
}
}
}
return {
views: viewDetails,
relationships: Array.from(relationships.values()),
clusters: clusterRelatedViews(viewDetails, relationships),
};
}Quick Scripts for Agents
Simple view search with introspection:
#!/bin/bash
# search-and-select.sh
KEYWORD=$1
echo "Searching for views matching: $KEYWORD"
# Search
dune-sdk-generator search "$KEYWORD" --limit 20 > results.json
# Show results
echo "Found views:"
jq -r '.[] | " • \(.space)/\(.externalId)/\(.version)"' results.json
# Introspect top 5 views to understand structure
echo ""
echo "Analyzing top 5 views..."
VIEW_REFS=$(jq -r 'limit(5; .[]) | "\(.space)/\(.externalId)/\(.version)"' results.json | tr '\n' ' ')
dune-sdk-generator introspect $VIEW_REFS > introspect-results.json
# Show analysis
echo ""
echo "View details:"
jq -r '.[] | " \(.externalId): \(.properties | length) properties"' introspect-results.json
echo ""
echo "Full results saved to:"
echo " - results.json (search results)"
echo " - introspect-results.json (detailed view info)"
echo ""
echo "To generate SDK: dune-sdk-generator generate --views results.json --non-interactive"Batch SDK generation:
#!/bin/bash
# batch-generate.sh
for config in ./configs/*.json; do
sdk_name=$(basename "$config" .json)
echo "Generating SDK: $sdk_name"
dune-sdk-generator generate \
--views "$config" \
--name "$sdk_name" \
--output "./sdks/$sdk_name" \
--non-interactive \
--format json > "./results/${sdk_name}-result.json"
echo "✓ $sdk_name complete"
doneCI/CD Integration
Using in GitHub Actions:
# .github/workflows/generate-sdk.yml
name: Generate SDK
on:
push:
paths:
- 'config/views.json'
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Generate SDK
run: |
npx dune-sdk-generator generate \
--views ./config/views.json \
--name production-sdk \
--output ./src/sdk \
--non-interactive \
--format json > result.json
env:
CDF_PROJECT: ${{ secrets.CDF_PROJECT }}
CDF_TOKEN: ${{ secrets.CDF_TOKEN }}
- name: Check result
run: |
if jq -e '.success == true' result.json > /dev/null; then
echo "✅ SDK generated successfully"
jq '.stats' result.json
else
echo "❌ SDK generation failed"
jq '.error' result.json
exit 1
fi
- name: Commit generated SDK
run: |
git config user.name "SDK Generator Bot"
git config user.email "[email protected]"
git add src/sdk/
git commit -m "chore: regenerate SDK"
git pushBest Practices
Reverse Relationships
- Start with Auto-Discovery: Enable
autoDiscoverReverseRelationships: trueto see all possible relationships - Refine Manually: Switch to manual configuration to control which relationships are exposed
- Name Thoughtfully: Use descriptive method names that reflect the business domain
- Document Performance: Reverse relationships perform queries - be mindful of limits and pagination
Conflict Resolution
The CLI automatically detects naming conflicts:
⚠️ Naming conflicts detected:
Canvas.getItems
Multiple relationships would generate the same method name
? Review and customize relationship names? (Y/n) # Defaults to YesWhen conflicts are detected:
- You'll be prompted to customize (required to proceed)
- The CLI suggests the conflicting relationships
- You provide unique names for each
Performance
- Use
limitparameter to control result set size - Leverage
cursorfor pagination - Add
additionalFilterto narrow results - Consider caching frequently accessed relationships
Development
Reference SDK Source
For development, you can clone the Cognite SDK source for reference:
mkdir -p reference
git clone https://github.com/cognitedata/cognite-sdk-js.git reference/cognite-sdkThis directory is gitignored and read-only (via Cursor rules).
License
MIT
