@kweaver-ai/kweaver-sdk 0.7.1 → 0.7.3

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 (43) hide show
  1. package/README.md +34 -4
  2. package/README.zh.md +27 -2
  3. package/dist/api/datasources.d.ts +7 -0
  4. package/dist/api/datasources.js +8 -0
  5. package/dist/api/skills.js +10 -8
  6. package/dist/api/toolboxes.d.ts +2 -0
  7. package/dist/api/toolboxes.js +2 -1
  8. package/dist/cli.js +65 -17
  9. package/dist/commands/auth.js +85 -10
  10. package/dist/commands/bkn-ops.d.ts +6 -1
  11. package/dist/commands/bkn-ops.js +202 -93
  12. package/dist/commands/bkn-utils.d.ts +26 -2
  13. package/dist/commands/bkn-utils.js +66 -9
  14. package/dist/commands/config.js +8 -0
  15. package/dist/commands/context-loader.js +112 -36
  16. package/dist/commands/dataflow.js +194 -20
  17. package/dist/commands/ds.d.ts +23 -1
  18. package/dist/commands/ds.js +135 -27
  19. package/dist/commands/import-csv.d.ts +0 -2
  20. package/dist/commands/import-csv.js +2 -4
  21. package/dist/commands/skill.js +26 -6
  22. package/dist/commands/tool.d.ts +1 -0
  23. package/dist/commands/tool.js +12 -0
  24. package/dist/config/stateless.d.ts +13 -0
  25. package/dist/config/stateless.js +20 -0
  26. package/dist/config/store.d.ts +1 -0
  27. package/dist/config/store.js +17 -0
  28. package/dist/resources/toolboxes.d.ts +2 -0
  29. package/dist/templates/bkn/document/manifest.json +12 -0
  30. package/dist/templates/bkn/document/template.json +757 -0
  31. package/dist/templates/dataflow/unstructured/manifest.json +11 -0
  32. package/dist/templates/dataflow/unstructured/template.json +63 -0
  33. package/dist/templates/dataset/document/manifest.json +10 -0
  34. package/dist/templates/dataset/document/template.json +23 -0
  35. package/dist/templates/dataset/document-content/manifest.json +10 -0
  36. package/dist/templates/dataset/document-content/template.json +29 -0
  37. package/dist/templates/dataset/document-element/manifest.json +10 -0
  38. package/dist/templates/dataset/document-element/template.json +21 -0
  39. package/dist/utils/skill-bundle.d.ts +5 -0
  40. package/dist/utils/skill-bundle.js +74 -0
  41. package/dist/utils/template-loader.d.ts +40 -0
  42. package/dist/utils/template-loader.js +129 -0
  43. package/package.json +2 -1
@@ -2,12 +2,28 @@ import { ensureValidToken, formatHttpError, resolveActivePlatform, with401Refres
2
2
  import { callTool, searchSchema, queryObjectInstance, queryInstanceSubgraph, getLogicPropertiesValues, getActionInfo, findSkills, listTools, listResources, readResource, listResourceTemplates, listPrompts, getPrompt, } from "../api/context-loader.js";
3
3
  import { knSearchHttp, semanticSearch } from "../api/semantic-search.js";
4
4
  import { addContextLoaderEntry, getCurrentContextLoaderKn, loadContextLoaderConfig, removeContextLoaderEntry, resolveBusinessDomain, setCurrentContextLoader, } from "../config/store.js";
5
+ import { assertNotStatelessForWrite } from "../config/stateless.js";
6
+ const CONTEXT_LOADER_CONFIG_DEPRECATION = "[deprecated] `kweaver context-loader config ...` will be removed in a future release. " +
7
+ "Pass <kn-id> as the first positional to runtime subcommands instead, e.g. " +
8
+ "`kweaver context-loader tools <kn-id>` (or use the `--kn-id <id>` flag).";
5
9
  const MCP_NOT_CONFIGURED = "Context-loader MCP is not configured. Run: kweaver context-loader config set --kn-id <kn-id>";
6
- function ensureContextLoaderConfig() {
10
+ const MCP_PATH = "/api/agent-retrieval/v1/mcp";
11
+ function ensureContextLoaderConfig(knIdOverride) {
7
12
  const active = resolveActivePlatform();
8
13
  if (!active) {
9
14
  throw new Error("No platform selected. Set KWEAVER_BASE_URL or run: kweaver auth <platform-url>");
10
15
  }
16
+ // Override path (positional <kn-id> or --kn-id flag): derive MCP URL from
17
+ // the active platform; do not touch the deprecated saved config.
18
+ if (knIdOverride) {
19
+ return {
20
+ baseUrl: active.url,
21
+ mcpUrl: active.url.replace(/\/+$/, "") + MCP_PATH,
22
+ knId: knIdOverride,
23
+ accessToken: "",
24
+ businessDomain: resolveBusinessDomain(active.url),
25
+ };
26
+ }
11
27
  const kn = getCurrentContextLoaderKn();
12
28
  if (!kn) {
13
29
  throw new Error(MCP_NOT_CONFIGURED);
@@ -20,6 +36,50 @@ function ensureContextLoaderConfig() {
20
36
  businessDomain: resolveBusinessDomain(active.url),
21
37
  };
22
38
  }
39
+ // Subcommands that consult `ensureContextLoaderConfig`. The number is the
40
+ // minimum non-flag positional count expected by the handler itself (after
41
+ // kn-id is extracted). When the leading non-flag positional count exceeds
42
+ // this minimum, the first one is treated as <kn-id>.
43
+ const RUNTIME_MIN_POSITIONALS = {
44
+ tools: 0,
45
+ resources: 0,
46
+ templates: 0,
47
+ prompts: 0,
48
+ prompt: 1,
49
+ resource: 1,
50
+ "search-schema": 1,
51
+ "tool-call": 1,
52
+ "kn-search": 1,
53
+ "kn-schema-search": 1,
54
+ "query-object-instance": 1,
55
+ "query-instance-subgraph": 1,
56
+ "get-logic-properties": 1,
57
+ "get-action-info": 1,
58
+ "find-skills": 1,
59
+ };
60
+ function extractKnIdOverride(subcommand, rest) {
61
+ // 1) Explicit flag wins. `--kn-id <id>` / `-k <id>` is allowed for every
62
+ // runtime subcommand and is consumed before the handler sees `rest`.
63
+ for (let i = 0; i < rest.length; i += 1) {
64
+ if ((rest[i] === "--kn-id" || rest[i] === "-k") && rest[i + 1]) {
65
+ const id = rest[i + 1];
66
+ rest.splice(i, 2);
67
+ return id;
68
+ }
69
+ }
70
+ // 2) Positional <kn-id> as the first non-flag arg, when leading non-flag
71
+ // positional count exceeds what the handler itself requires.
72
+ const min = RUNTIME_MIN_POSITIONALS[subcommand];
73
+ if (min === undefined)
74
+ return undefined;
75
+ let cut = 0;
76
+ while (cut < rest.length && !rest[cut].startsWith("-"))
77
+ cut += 1;
78
+ if (cut > min) {
79
+ return rest.shift();
80
+ }
81
+ return undefined;
82
+ }
23
83
  function formatOutput(value, pretty) {
24
84
  const json = JSON.stringify(value, null, pretty ? 2 : 0);
25
85
  return json;
@@ -29,34 +89,38 @@ export async function runContextLoaderCommand(args) {
29
89
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
30
90
  console.log(`kweaver context-loader
31
91
 
92
+ KN selection (for runtime subcommands below):
93
+ Pass <kn-id> as the FIRST positional, e.g. \`kweaver context-loader tools <kn-id>\`,
94
+ or use the global \`--kn-id <id>\` / \`-k <id>\` flag. When omitted, falls back to
95
+ the deprecated saved config managed by \`kweaver context-loader config\`.
96
+
32
97
  Subcommands:
33
- config set --kn-id <id> [--name n] Add or update kn config (MCP URL derived from platform)
34
- config use <name> Switch current config
35
- config list List all configs and current
36
- config remove <name> Remove a config
37
- config show Show current config (knId + mcpUrl)
38
- tools tools/list - list available tools
39
- resources resources/list - list resources
40
- resource <uri> resources/read - read resource by URI
41
- templates resources/templates/list - list resource templates
42
- prompts prompts/list - list prompts
43
- prompt <name> [--args json] prompts/get - get prompt by name
44
- search-schema <query> [options] MCP search_schema (object/relation/action/metric types)
45
- tool-call <name> --args '<json>' MCP tools/call for any server tool
46
- kn-search <query> [--only-schema] Compatibility: HTTP kn_search
47
- kn-schema-search <query> [--max N] Compatibility: HTTP semantic-search
48
- query-object-instance <json> Layer 2: Query instances (args as JSON)
49
- query-instance-subgraph <json> Layer 2: Query subgraph (args as JSON)
50
- get-logic-properties <json> Layer 3: Get logic property values (args as JSON)
51
- get-action-info <json> Layer 3: Get action info (args as JSON)
52
- find-skills <ot_id> [options] Layer 3: Recall skills for an object type
98
+ config set --kn-id <id> [--name n] [deprecated] Add or update kn config
99
+ config use <name> [deprecated] Switch current config
100
+ config list [deprecated] List all configs and current
101
+ config remove <name> [deprecated] Remove a config
102
+ config show [deprecated] Show current config (knId + mcpUrl)
103
+ tools <kn-id> tools/list - list available tools
104
+ resources <kn-id> resources/list - list resources
105
+ resource <kn-id> <uri> resources/read - read resource by URI
106
+ templates <kn-id> resources/templates/list - list resource templates
107
+ prompts <kn-id> prompts/list - list prompts
108
+ prompt <kn-id> <name> [--args json] prompts/get - get prompt by name
109
+ search-schema <kn-id> <query> [opts] MCP search_schema (object/relation/action/metric)
110
+ tool-call <kn-id> <name> --args '<json>' MCP tools/call for any server tool
111
+ kn-search <kn-id> <query> [--only-schema] Compatibility: HTTP kn_search
112
+ kn-schema-search <kn-id> <query> [--max N] Compatibility: HTTP semantic-search
113
+ query-object-instance <kn-id> <json> Layer 2: Query instances
114
+ query-instance-subgraph <kn-id> <json> Layer 2: Query subgraph
115
+ get-logic-properties <kn-id> <json> Layer 3: Get logic property values
116
+ get-action-info <kn-id> <json> Layer 3: Get action info
117
+ find-skills <kn-id> <ot_id> [options] Layer 3: Recall skills for an object type
53
118
 
54
119
  Examples:
55
- kweaver context-loader config set --kn-id d5iv6c9818p72mpje8pg
56
- kweaver context-loader config set --kn-id xyz123 --name project-a
57
- kweaver context-loader search-schema "利润率" --scope object,metric --max 5
58
- kweaver context-loader tool-call search_schema --args '{"query":"利润率"}'
59
- kweaver context-loader kn-search "高血压 治疗 药品" --only-schema --pretty`);
120
+ kweaver context-loader tools d5iv6c9818p72mpje8pg
121
+ kweaver context-loader search-schema d5iv6c9818p72mpje8pg "利润率" --scope object,metric --max 5
122
+ kweaver context-loader tool-call d5iv6c9818p72mpje8pg search_schema --args '{"query":"利润率"}'
123
+ kweaver context-loader kn-search d5iv6c9818p72mpje8pg "高血压 治疗 药品" --only-schema --pretty`);
60
124
  return 0;
61
125
  }
62
126
  if (subcommand === "config") {
@@ -68,9 +132,12 @@ Examples:
68
132
  pretty = true;
69
133
  rest.splice(prettyIdx, 1);
70
134
  }
135
+ // Extract `<kn-id>` (positional or --kn-id/-k flag) before per-subcommand
136
+ // arg parsing. When provided it bypasses the deprecated saved config.
137
+ const knIdOverride = extractKnIdOverride(subcommand, rest);
71
138
  const dispatch = async () => {
72
139
  const token = await ensureValidToken();
73
- const base = ensureContextLoaderConfig();
140
+ const base = ensureContextLoaderConfig(knIdOverride);
74
141
  const options = { ...base, accessToken: token.accessToken };
75
142
  if (subcommand === "tools")
76
143
  return runListTools(options, rest, pretty);
@@ -122,16 +189,30 @@ Examples:
122
189
  async function runConfigCommand(args) {
123
190
  const [action, ...rest] = args;
124
191
  if (!action || action === "--help" || action === "-h") {
125
- console.log(`kweaver context-loader config
192
+ console.log(`kweaver context-loader config [deprecated]
126
193
 
127
194
  Subcommands:
128
195
  set --kn-id <id> [--name <name>] Add or update kn config (default name: default)
129
196
  use <name> Switch current config
130
197
  list List all configs and current
131
198
  remove <name> Remove a config
132
- show Show current config (knId + mcpUrl)`);
199
+ show Show current config (knId + mcpUrl)
200
+
201
+ Note: this command group is deprecated and will be removed in a future release.
202
+ It is disabled entirely in stateless mode (\`--token\`).`);
133
203
  return 0;
134
204
  }
205
+ // Stateless mode (`--token`) does not support any context-loader config
206
+ // operations; the saved config lives under `~/.kweaver/` and is foreign
207
+ // to the stateless paradigm.
208
+ try {
209
+ assertNotStatelessForWrite(`context-loader config ${action}`);
210
+ }
211
+ catch (err) {
212
+ console.error(err instanceof Error ? err.message : String(err));
213
+ return 1;
214
+ }
215
+ console.warn(CONTEXT_LOADER_CONFIG_DEPRECATION);
135
216
  const active = resolveActivePlatform();
136
217
  if (!active) {
137
218
  console.error("No platform selected. Set KWEAVER_BASE_URL or run: kweaver auth <platform-url>");
@@ -435,29 +516,24 @@ async function runToolCall(options, args, pretty) {
435
516
  async function runKnSearch(options, args, pretty) {
436
517
  let query;
437
518
  let onlySchema = false;
438
- let knIdOverride;
439
519
  for (let i = 0; i < args.length; i += 1) {
440
520
  const arg = args[i];
441
521
  if (arg === "--only-schema") {
442
522
  onlySchema = true;
443
523
  }
444
- else if ((arg === "--kn-id" || arg === "-k") && args[i + 1]) {
445
- knIdOverride = args[i + 1];
446
- i += 1;
447
- }
448
524
  else if (!arg.startsWith("-") && !query) {
449
525
  query = arg;
450
526
  }
451
527
  }
452
528
  if (!query) {
453
- console.error("Usage: kweaver context-loader kn-search <query> [--kn-id <id>] [--only-schema]");
529
+ console.error("Usage: kweaver context-loader kn-search <kn-id> <query> [--only-schema]");
454
530
  return 1;
455
531
  }
456
532
  const raw = await knSearchHttp({
457
533
  baseUrl: options.baseUrl,
458
534
  accessToken: options.accessToken,
459
535
  businessDomain: options.businessDomain,
460
- knId: knIdOverride ?? options.knId,
536
+ knId: options.knId,
461
537
  query,
462
538
  onlySchema,
463
539
  });
@@ -7,6 +7,9 @@ import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/
7
7
  import { resolveBusinessDomain } from "../config/store.js";
8
8
  import { getDataflowLogsPage, listDataflowRuns, listDataflows, runDataflowWithFile, runDataflowWithRemoteUrl, } from "../api/dataflow2.js";
9
9
  import { createDataflow } from "../api/dataflow.js";
10
+ import { createVegaResource } from "../api/vega.js";
11
+ import { createKnowledgeNetwork } from "../api/knowledge-networks.js";
12
+ import { loadTemplate, listTemplates, renderTemplate, generateSourceIdentifier, getTemplatesDir, } from "../utils/template-loader.js";
10
13
  function renderTable(rows) {
11
14
  if (rows.length === 0)
12
15
  return "";
@@ -101,26 +104,6 @@ export async function runDataflowCommand(args) {
101
104
  .strict()
102
105
  .fail((message, error) => {
103
106
  throw error ?? new Error(message);
104
- })
105
- .command("create <json>", "Create a new dataflow (DAG) from a JSON definition", (command) => command
106
- .positional("json", {
107
- type: "string",
108
- describe: "JSON body string or @file-path to read from file",
109
- })
110
- .option("biz-domain", { alias: "bd", type: "string" }), async (argv) => {
111
- exitCode = await with401RefreshRetry(async () => {
112
- const base = await requireTokenAndBusinessDomain(argv.bizDomain);
113
- let raw = argv.json;
114
- if (raw.startsWith("@")) {
115
- const filePath = raw.slice(1);
116
- await access(filePath, constants.R_OK);
117
- raw = (await readFile(filePath, "utf8")).toString();
118
- }
119
- const body = JSON.parse(raw);
120
- const dagId = await createDataflow({ ...base, body });
121
- console.log(JSON.stringify({ id: dagId }, null, 2));
122
- return 0;
123
- });
124
107
  })
125
108
  .command("list", "List all dataflows", (command) => command
126
109
  .option("biz-domain", {
@@ -280,6 +263,197 @@ export async function runDataflowCommand(args) {
280
263
  }
281
264
  return 0;
282
265
  });
266
+ })
267
+ .command("templates", "List all available templates", {
268
+ json: { type: "boolean", default: false, describe: "Output as JSON" },
269
+ }, (argv) => {
270
+ const templatesDir = getTemplatesDir();
271
+ return Promise.all([
272
+ listTemplates("dataset", templatesDir),
273
+ listTemplates("bkn", templatesDir),
274
+ listTemplates("dataflow", templatesDir),
275
+ ]).then(([datasetTemplates, bknTemplates, dataflowTemplates]) => {
276
+ if (argv.json) {
277
+ console.log(JSON.stringify({
278
+ dataset: datasetTemplates,
279
+ bkn: bknTemplates,
280
+ dataflow: dataflowTemplates,
281
+ }, null, 2));
282
+ }
283
+ else {
284
+ console.log("Dataset Templates:");
285
+ for (const t of datasetTemplates) {
286
+ console.log(` - ${t.name.padEnd(18)} ${t.description}`);
287
+ }
288
+ console.log("");
289
+ console.log("BKN Templates:");
290
+ for (const t of bknTemplates) {
291
+ console.log(` - ${t.name.padEnd(18)} ${t.description}`);
292
+ }
293
+ console.log("");
294
+ console.log("Dataflow Templates:");
295
+ for (const t of dataflowTemplates) {
296
+ console.log(` - ${t.name.padEnd(18)} ${t.description}`);
297
+ }
298
+ }
299
+ });
300
+ })
301
+ .command("create-dataset", "Create a dataset from a template", (command) => command
302
+ .option("template", { type: "string", demandOption: true, describe: "Template name" })
303
+ .option("set", { type: "array", string: true, describe: "Set parameter (key=value), can be used multiple times" })
304
+ .option("json", { type: "boolean", default: false, describe: "Output as JSON" })
305
+ .option("biz-domain", { alias: "bd", type: "string" }), async (argv) => {
306
+ exitCode = await with401RefreshRetry(async () => {
307
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
308
+ const templatesDir = getTemplatesDir();
309
+ // Parse --set arguments
310
+ const args = {};
311
+ if (argv.set) {
312
+ for (const item of argv.set) {
313
+ const eqIdx = item.indexOf("=");
314
+ if (eqIdx > 0) {
315
+ const key = item.slice(0, eqIdx);
316
+ const value = item.slice(eqIdx + 1);
317
+ args[key] = value;
318
+ }
319
+ }
320
+ }
321
+ // Load template
322
+ const loaded = await loadTemplate(argv.template, "dataset", templatesDir);
323
+ if (!loaded) {
324
+ console.error(`Template not found: ${argv.template}`);
325
+ return 1;
326
+ }
327
+ // Auto-generate source_identifier if not provided
328
+ if (!args["source_identifier"]) {
329
+ const prefixMap = {
330
+ "document": "dataflow_document",
331
+ "document-content": "dataflow_content",
332
+ "document-element": "dataflow_element",
333
+ };
334
+ const prefix = prefixMap[loaded.manifest.name] || "dataflow";
335
+ args["source_identifier"] = generateSourceIdentifier(prefix);
336
+ }
337
+ // Render template
338
+ const rendered = renderTemplate(loaded.template, loaded.manifest, args);
339
+ // Create dataset via API
340
+ const response = await createVegaResource({
341
+ ...base,
342
+ body: JSON.stringify(rendered),
343
+ });
344
+ const result = JSON.parse(response);
345
+ if (argv.json) {
346
+ console.log(JSON.stringify({ success: true, id: result.id, name: args.name }, null, 2));
347
+ }
348
+ else {
349
+ console.log(`dataset created: id=${result.id}`);
350
+ }
351
+ return 0;
352
+ });
353
+ })
354
+ .command("create-bkn", "Create a BKN (knowledge network) from a template", (command) => command
355
+ .option("template", { type: "string", demandOption: true, describe: "Template name" })
356
+ .option("set", { type: "array", string: true, describe: "Set parameter (key=value), can be used multiple times" })
357
+ .option("json", { type: "boolean", default: false, describe: "Output as JSON" })
358
+ .option("biz-domain", { alias: "bd", type: "string" }), async (argv) => {
359
+ exitCode = await with401RefreshRetry(async () => {
360
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
361
+ const templatesDir = getTemplatesDir();
362
+ // Parse --set arguments
363
+ const args = {};
364
+ if (argv.set) {
365
+ for (const item of argv.set) {
366
+ const eqIdx = item.indexOf("=");
367
+ if (eqIdx > 0) {
368
+ const key = item.slice(0, eqIdx);
369
+ const value = item.slice(eqIdx + 1);
370
+ args[key] = value;
371
+ }
372
+ }
373
+ }
374
+ // Load template
375
+ const loaded = await loadTemplate(argv.template, "bkn", templatesDir);
376
+ if (!loaded) {
377
+ console.error(`Template not found: ${argv.template}`);
378
+ return 1;
379
+ }
380
+ // Render template
381
+ const rendered = renderTemplate(loaded.template, loaded.manifest, args);
382
+ rendered.business_domain = base.businessDomain;
383
+ // Create BKN via API
384
+ const response = await createKnowledgeNetwork({
385
+ ...base,
386
+ body: JSON.stringify(rendered),
387
+ validate_dependency: false,
388
+ });
389
+ const result = JSON.parse(response);
390
+ if (argv.json) {
391
+ console.log(JSON.stringify({ success: true, id: result.id, name: args.name }, null, 2));
392
+ }
393
+ else {
394
+ console.log(`bkn created: id=${result.id}`);
395
+ }
396
+ return 0;
397
+ });
398
+ })
399
+ .command("create [json]", "Create a new dataflow (DAG) from a JSON definition or template", (command) => command
400
+ .positional("json", {
401
+ type: "string",
402
+ describe: "JSON body string or @file-path to read from file",
403
+ })
404
+ .option("template", { type: "string", describe: "Template name (use instead of json)" })
405
+ .option("set", { type: "array", string: true, describe: "Set parameter (key=value), can be used multiple times" })
406
+ .option("biz-domain", { alias: "bd", type: "string" })
407
+ .check((argv) => {
408
+ const hasJson = typeof argv.json === "string";
409
+ const hasTemplate = typeof argv.template === "string";
410
+ if (hasJson && hasTemplate) {
411
+ throw new Error("Cannot use both json and --template");
412
+ }
413
+ if (!hasJson && !hasTemplate) {
414
+ throw new Error("Either json or --template is required");
415
+ }
416
+ return true;
417
+ }), async (argv) => {
418
+ exitCode = await with401RefreshRetry(async () => {
419
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
420
+ let body;
421
+ if (argv.template) {
422
+ // Use template
423
+ const templatesDir = getTemplatesDir();
424
+ // Parse --set arguments
425
+ const args = {};
426
+ if (argv.set) {
427
+ for (const item of argv.set) {
428
+ const eqIdx = item.indexOf("=");
429
+ if (eqIdx > 0) {
430
+ const key = item.slice(0, eqIdx);
431
+ const value = item.slice(eqIdx + 1);
432
+ args[key] = value;
433
+ }
434
+ }
435
+ }
436
+ const loaded = await loadTemplate(argv.template, "dataflow", templatesDir);
437
+ if (!loaded) {
438
+ console.error(`Template not found: ${argv.template}`);
439
+ return 1;
440
+ }
441
+ body = renderTemplate(loaded.template, loaded.manifest, args);
442
+ }
443
+ else {
444
+ // Use JSON
445
+ let raw = argv.json;
446
+ if (raw.startsWith("@")) {
447
+ const filePath = raw.slice(1);
448
+ await access(filePath, constants.R_OK);
449
+ raw = (await readFile(filePath, "utf8")).toString();
450
+ }
451
+ body = JSON.parse(raw);
452
+ }
453
+ const dagId = await createDataflow({ ...base, body });
454
+ console.log(JSON.stringify({ id: dagId }, null, 2));
455
+ return 0;
456
+ });
283
457
  })
284
458
  .demandCommand(1);
285
459
  try {
@@ -5,13 +5,35 @@ export declare function parseDsListArgs(args: string[]): {
5
5
  businessDomain: string;
6
6
  pretty: boolean;
7
7
  };
8
+ /**
9
+ * Match candidate signature against a list response (the kind returned by
10
+ * `listDatasources`). Connection metadata lives under `bin_data` — host,
11
+ * port, account, plus type-specific fields (`database_name` for MySQL etc.).
12
+ *
13
+ * Exported for unit testing.
14
+ */
15
+ export interface DsMatchSignature {
16
+ type: string;
17
+ host: string;
18
+ port: number;
19
+ database: string;
20
+ account: string;
21
+ name?: string;
22
+ }
23
+ export interface DsMatchHit {
24
+ id: string;
25
+ name: string;
26
+ matchedByName: boolean;
27
+ matchedByTuple: boolean;
28
+ }
29
+ export declare function findExistingDatasource(listBody: string, sig: DsMatchSignature): DsMatchHit | undefined;
30
+ export declare function findDatasourceIdByName(listBody: string, name: string): string | undefined;
8
31
  export declare function parseImportCsvArgs(args: string[]): {
9
32
  datasourceId: string;
10
33
  files: string;
11
34
  tablePrefix: string;
12
35
  batchSize: number;
13
36
  businessDomain: string;
14
- recreate: boolean;
15
37
  };
16
38
  export declare function resolveFiles(pattern: string): Promise<string[]>;
17
39
  export interface ImportCsvResult {