mcpField
T
toolkit

toolkit

github.com/stahura开发24193 次安装26 周前更新
代码仓库

关于 toolkit

@domoinc/toolkit 是面向 Domo 服务的 TypeScript 类型化客户端库,供 Domo 应用开发者调用各类服务 API。它涵盖存储、身份、SQL、AI、文件和工作流等客户端。

主要功能

  1. 01Domo 服务的类型化客户端
  2. 02AppDB 文档与集合操作
  3. 03身份、用户和群组 API
  4. 04SQL 查询与文件上传
  5. 05AI 生成与工作流访问

适用场景

  1. 01创建和查询 AppDB 文档
  2. 02获取当前登录用户身份
  3. 03对数据集执行 SQL 查询
  4. 04调用 AI 服务或 Domo 工作流

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/stahura/domo-ai-vibe-rules --skill toolkit

安装后在 Claude Code 中输入/toolkit确认已加载。

详细介绍

@domoinc/toolkit - 服务客户端

该工具包为 Domo 服务提供带类型的客户端类。

yarn add @domoinc/toolkit
import {
  AppDBClient,
  AIClient,
  IdentityClient,
  SqlClient,
  UserClient,
  GroupClient,
  FileClient,
  CodeEngineClient,
  WorkflowClient,
  DomoClient
} from '@domoinc/toolkit';

响应包装器

大多数工具包方法返回一个响应包装器:

interface ToolkitResponse<T> {
  body: T;        // The actual data
  status: number; // HTTP status code
  headers?: Record<string, string>;
}

// Usage
const response = await IdentityClient.get();
const user = response.body;  // Access the data
const status = response.status;

例外: AIClient 通常将负载返回在 response.data 下。处理 AI 响应时使用:

const body = response.data || response.body || response;
const output = body.output || body.choices?.[0]?.output;

AppDBClient - NoSQL 文档存储

推荐用于 CRUD 应用和长文本存储。 集合可以同步到 Domo 数据集。

DocumentsClient
// Define your document type
interface Task {
  title: string;
  description: string;
  status: 'active' | 'completed';
  priority: 'Low' | 'High' | 'Urgent';
  dueDate?: string;
}

// Create client for a collection
const tasksClient = new AppDBClient.DocumentsClient<Task>('TasksCollection');

创建文档:

// Create single document
const response = await tasksClient.create({
  title: 'New Task',
  description: 'Task description',
  status: 'active',
  priority: 'High'
});
const newTask = response.body;
// Returns: { id: 'uuid', content: {...}, owner: 'userId', createdOn: '...', updatedOn: '...' }

// Create multiple documents
const response = await tasksClient.create([
  { title: 'Task 1', status: 'active', priority: 'Low' },
  { title: 'Task 2', status: 'active', priority: 'High' }
]);

读取/查询文档:

// Get all documents
const response = await tasksClient.get();
const tasks = response.body;

// Filter with MongoDB-style queries
const activeTasks = await tasksClient.get({
  status: { $eq: 'active' }
});

const highPriority = await tasksClient.get({
  priority: { $in: ['High', 'Urgent'] },
  status: { $ne: 'completed' }
});

// Complex queries
const filtered = await tasksClient.get({
  $and: [
    { status: { $eq: 'active' } },
    { priority: { $in: ['High', 'Urgent'] } }
  ]
});

const either = await tasksClient.get({
  $or: [
    { priority: { $eq: 'Urgent' } },
    { status: { $eq: 'completed' } }
  ]
});

// With pagination and sorting
const paginated = await tasksClient.get(
  { status: { $eq: 'active' } },
  {
    limit: 10,
    offset: 0,
    orderby: ['priority', 'dueDate']
  }
);

// Aggregations
const statusCounts = await tasksClient.get(
  {},
  {
    groupby: ['status'],
    count: 'id'
  }
);

const priorityStats = await tasksClient.get(
  {},
  {
    groupby: ['priority'],
    count: 'id',
    avg: ['estimatedHours']
  }
);

关键的响应结构说明:

  • .get() 返回带有元数据的文档包装器;你的应用字段位于 doc.content 中。
  • 不要假设使用 docs[0].fieldName;应使用 docs[0].content.fieldName。
const response = await tasksClient.get();
const rawDocs = response.body || response;
const docs = Array.isArray(rawDocs) ? rawDocs : [];

const parsed = docs.map((doc) => ({
  id: doc.id,
  ...doc.content
}));

MongoDB 查询运算符:

  • $eq - 等于
  • $ne - 不等于
  • $gt - 大于
  • $gte - 大于或等于
  • $lt - 小于
  • $lte - 小于或等于
  • $in - 在数组中
  • $nin - 不在数组中
  • $regex - 正则表达式匹配
  • $and - 逻辑 AND
  • $or - 逻辑 OR

更新文档:

// Full update (replace content)
const response = await tasksClient.update({
  id: 'document-uuid',
  content: {
    title: 'Updated Title',
    description: 'Updated description',
    status: 'completed',
    priority: 'Low'
  }
});

// Bulk update
const bulkResponse = await tasksClient.update([
  { id: 'uuid-1', content: { title: 'Updated 1', status: 'active', priority: 'Low' } },
  { id: 'uuid-2', content: { title: 'Updated 2', status: 'active', priority: 'High' } }
]);
// Returns: { body: { updated: 2, inserted: 0 } }

// Partial update with MongoDB operators
const count = await tasksClient.partialUpdate(
  { status: { $eq: 'active' } },  // Filter
  { $set: { status: 'archived' } }  // Operation
);
// Returns number of documents updated

// Partial update operators
await tasksClient.partialUpdate(
  { priority: { $eq: 'Low' } },
  {
    $set: { priority: 'Medium', updatedAt: new Date().toISOString() },
    $unset: { tempField: 1 },
    $inc: { viewCount: 1 }
  }
);

MongoDB 更新运算符:

  • $set - 设置字段值
  • $unset - 移除字段
  • $inc - 递增数值字段
  • $push - 添加到数组字段

删除文档:

// Delete single document
await tasksClient.delete('document-uuid');

// Bulk delete
const response = await tasksClient.delete(['uuid-1', 'uuid-2', 'uuid-3']);
// Returns: { body: { deleted: 3 } }
CollectionsClient
const collectionsClient = new AppDBClient.CollectionsClient();

// List all collections
const collections = await collectionsClient.list();

// Create collection
await collectionsClient.create({
  name: 'NewCollection',
  schema: {
    columns: [
      { name: 'field1', type: 'STRING' },
      { name: 'field2', type: 'LONG' }
    ]
  }
});

// Delete collection
await collectionsClient.delete('CollectionName');

// Export all collections
const exportData = await collectionsClient.export();

IdentityClient - 当前用户(安全)

在安全敏感操作中,请使用此客户端而不是 Domo.env。

// Get current authenticated user
const response = await IdentityClient.get();
const user = response.body;
// {
//   id: 12345,
//   displayName: 'John Doe',
//   userName: 'jdoe',
//   emailAddress: '[email protected]',
//   avatarKey: 'abc123',
//   role: 'Admin',
//   groups: [{ id: 1, name: 'Everyone' }, ...]
// }

// With options
const response = await IdentityClient.get(
  5000,  // timeout in ms
  false  // includeGroups - set false for faster response
);

SqlClient - 直接 SQL 查询

注意: SQL 端点不遵循页面筛选器。对于嵌入仪表板的应用,请使用 Query 端点。

const sqlClient = new SqlClient();

// Execute SQL query
const response = await sqlClient.get(
  'sales-dataset-alias',
  'SELECT region, SUM(amount) as total FROM sales-dataset-alias GROUP BY region ORDER BY total DESC'
);
const result = response.body;
// {
//   columns: ['region', 'total'],
//   rows: [{ region: 'North', total: 50000 }, ...],
//   numRows: 4,
//   numColumns: 2
// }

// Complex query
const response = await sqlClient.get(
  'sales',
  `SELECT 
    DATE_TRUNC('month', order_date) as month,
    product_category,
    SUM(revenue) as revenue,
    COUNT(*) as orders
   FROM sales
   WHERE order_date >= '2024-01-01'
   GROUP BY 1, 2
   ORDER BY 1, 3 DESC`
);

// Parse page filters into SQL (static method)
const predicates = SqlClient.parsePageFilters(['dataset1', 'dataset2']);
// Returns: { 'dataset1': [{ where: '"column" = \'value\'', having: '' }], ... }

// Get concatenated clauses
const clauses = SqlClient.parsePageFilters(['dataset1'], true);
// Returns: { 'dataset1': { whereClause: 'WHERE ...', havingClause: '' } }

UserClient - 用户 API

// Get paginated list of users
const response = await UserClient.get(
  50,    // limit
  0,     // offset
  true   // includeDetails
);
const users = response.body;

// Get specific user
const response = await UserClient.getUser(
  12345,  // user ID
  true    // includeDetails
);
const user = response.body;

// Get user avatar
const avatarBlob = await UserClient.getAvatar(
  'avatar-key-from-user',
  'medium'  // 'small' | 'medium' | 'large'
);

GroupClient - 组 API

// Get all groups
const response = await GroupClient.get(50, 0);  // limit, offset
const groups = response.body;

// Get specific group
const response = await GroupClient.getGroup(456);
const group = response.body;

// Get group members
const response = await GroupClient.getMembers(456);
const members = response.body;  // Array of User objects

FileClient - 文件存储

const fileClient = new FileClient();

// Upload file
const response = await fileClient.upload(
  fileObject,        // File object
  'report.pdf',      // Display name
  'Monthly report',  // Description (optional)
  true,              // isPublic (optional, default true)
  10000              // timeout (optional)
);
const fileId = response.body.id;

// Upload new revision
const response = await fileClient.uploadRevision(
  newFileObject,
  existingFileId
);

// Download file
const blob = await fileClient.download(
  fileId,
  'filename.pdf',
  revisionId  // optional, latest if omitted
);

// List files with details
const response = await fileClient.detailsList(
  [123, 456],  // File IDs (empty for all)
  ['metadata', 'permissions', 'revisions']  // Expand options
);

// Get/update permissions
const perms = await fileClient.getPermissions(fileId);
await fileClient.updatePermissions(fileId, JSON.stringify({
  public: false,
  users: [123, 456],
  groups: [789]
}));

// Delete file revision
await fileClient.delete(fileId, revisionId);

AIClient - AI 服务

所有方法都是静态的,并使用 snake_case 命名。

响应结构: 重要: AIClient 使用 data 而不是 body 作为响应负载!工具包将响应包装在 { data, status, statusCode } 中。API 响应同时包含顶层 output 字段和 choices 数组。当可用时,优先使用顶层 output 字段。

// Text generation
const response = await AIClient.generate_text(
  'Explain this sales trend in simple terms',
  { template: 'You are a business analyst. ${input}' },  // promptTemplate
  { tone: 'professional' },  // parameters for template
  'model-id',  // optional model override
  { temperature: 0.7 }  // model configuration
);

// Response structure: { data: { output: string, choices: [{ output: string }], ... }, status: "OK", statusCode: 200 }
// NOTE: AIClient uses 'data' not 'body'!
// Prefer top-level output field (more reliable)
const responseBody = response.data || response.body || response;
const text = responseBody.output || responseBody.choices?.[0]?.output;

// Text to SQL
const response = await AIClient.text_to_sql(
  'Show me total sales by region for Q4',
  [{
    dataSourceName: 'Sales',
    description: 'Sales transactions table',
    columns: [
      { name: 'region', type: 'string' },
      { name: 'amount', type: 'number' },
      { name: 'order_date', type: 'date' }
    ]
  }]
);
const responseBody = response.data || response.body || response;
const sql = responseBody.output || responseBody.choices?.[0]?.output;
// NOTE: second argument must be DataSourceSchema[] (array), not a single schema object.
// Passing an array allows multi-dataset SQL generation.

// Text to Beast Mode (calculated field)
const response = await AIClient.text_to_beastmode(
  'Calculate year over year growth percentage',
  {
    dataSourceName: 'Revenue',
    columns: [
      { name: 'revenue', type: 'number' },
      { name: 'date', type: 'date' }
    ]
  }
);
const responseBody = response.data || response.body || response;
const beastMode = responseBody.output || responseBody.choices?.[0]?.output;

// Text summarization
const response = await AIClient.summarize(
  longTextContent,
  undefined,  // promptTemplate
  undefined,  // parameters
  undefined,  // model
  undefined,  // modelConfiguration
  undefined,  // system prompt
  [],         // chatContext
  { separatorType: 'paragraph' },  // chunkingConfiguration
  'bullets',  // outputStyle: 'paragraph' | 'bullets'
  100         // outputWordLength
);
const responseBody = response.data || response.body || response;
const summary = responseBody.output || responseBody.choices?.[0]?.output;

CodeEngineClient - 无服务器函数

在应用实现中,优先使用通过 domo.post 和 packagesMapping 契约的直接调用模式。 如果你使用 Toolkit 文档示例,请在你的环境中验证运行时行为。

import domo from 'ryuu.js';

// Execute function by alias
const response = await domo.post('/domo/codeengine/v2/packages/calculateTax', {
  amount: 1000,
  state: 'CA'
});

// Inspect first run response shape
console.log('Code Engine response:', response);

const body = response?.body ?? response?.data ?? response;
const result =
  body?.output ??
  body?.result ??
  body?.value ??
  body;

Manifest 说明:使用 packagesMapping(带 s),并包含完整的参数/输出模式字段。

WorkflowClient - Domo 工作流

// List available workflow models
const response = await WorkflowClient.getAllModels();
const models = response.body;
const modelsWithPermissions = await WorkflowClient.getAllModels(true);

// Get model details by workflow alias from manifest workflowMapping.alias
const response = await WorkflowClient.getModelDetails('myWorkflow');
const model = response.body;

// Start workflow instance by alias (NOT UUID)
const response = await WorkflowClient.startModel(
  'myWorkflow',
  { inputVar: 'value', anotherVar: 123 }
);
const instance = response.body;
// { id: 'instance-uuid', modelId: '...', status: 'RUNNING', ... }

// Check instance status by alias + instanceId
const response = await WorkflowClient.getInstance('myWorkflow', 'instance-uuid');
const status = response.body;
// { id: '...', status: 'COMPLETED' | 'RUNNING' | 'FAILED', ... }

在应用代码中,所有 WorkflowClient 方法都基于别名:

  • WorkflowClient.startModel(workflowAlias, variables)
  • WorkflowClient.getAllModels() / WorkflowClient.getAllModels(true)
  • WorkflowClient.getModelDetails(workflowAlias)
  • WorkflowClient.getInstance(workflowAlias, instanceId)

工作流 UUID 位于 manifest.json 中的 workflowMapping[].modelId 下;运行时客户端调用传递别名。

DomoClient - ryuu.js 的替代方案

// Same as Domo.get/post/put/delete but from toolkit
const data = await DomoClient.get('/data/v1/sales');
await DomoClient.post('/api/items', { name: 'test' });
await DomoClient.put('/api/items/123', { name: 'updated' });
await DomoClient.delete('/api/items/123');

评论

支持 Markdown