dataiku-sdk 0.3.1 → 0.4.0

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.
@@ -233,6 +233,16 @@ export declare const ScenarioStatusSchema: import("@sinclair/typebox").TObject<{
233
233
  }>>;
234
234
  }>;
235
235
  export type ScenarioStatus = Static<typeof ScenarioStatusSchema>;
236
+ export declare const ScenarioWaitResultSchema: import("@sinclair/typebox").TObject<{
237
+ scenarioId: import("@sinclair/typebox").TString;
238
+ runId: import("@sinclair/typebox").TString;
239
+ outcome: import("@sinclair/typebox").TString;
240
+ success: import("@sinclair/typebox").TBoolean;
241
+ elapsedMs: import("@sinclair/typebox").TNumber;
242
+ pollCount: import("@sinclair/typebox").TNumber;
243
+ timedOut: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
244
+ }>;
245
+ export type ScenarioWaitResult = Static<typeof ScenarioWaitResultSchema>;
236
246
  export declare const FolderSummarySchema: import("@sinclair/typebox").TObject<{
237
247
  id: import("@sinclair/typebox").TString;
238
248
  name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -284,6 +284,15 @@ export const ScenarioStatusSchema = Type.Object({
284
284
  }, { additionalProperties: true, })),
285
285
  }, { additionalProperties: true, })),
286
286
  }, { additionalProperties: true, });
287
+ export const ScenarioWaitResultSchema = Type.Object({
288
+ scenarioId: Type.String(),
289
+ runId: Type.String(),
290
+ outcome: Type.String(),
291
+ success: Type.Boolean(),
292
+ elapsedMs: Type.Number(),
293
+ pollCount: Type.Number(),
294
+ timedOut: Type.Optional(Type.Boolean()),
295
+ });
287
296
  // ---------------------------------------------------------------------------
288
297
  // Folders
289
298
  // ---------------------------------------------------------------------------
package/dist/src/cli.js CHANGED
@@ -222,6 +222,8 @@ const BOOLEAN_FLAGS = new Set([
222
222
  "include-payload",
223
223
  "include-logs",
224
224
  "replace",
225
+ "dry-run",
226
+ "if-not-exists",
225
227
  ]);
226
228
  const SHORT_FLAGS = {
227
229
  h: "help",
@@ -302,18 +304,26 @@ const commands = {
302
304
  list: {
303
305
  handler: (c) => c.projects.list(),
304
306
  usage: "dss project list",
307
+ description: "List all accessible projects.",
308
+ examples: ["dss project list", "dss project list -f table",],
305
309
  },
306
310
  get: {
307
311
  handler: (c, _a, f) => c.projects.get(f["project-key"]),
308
312
  usage: "dss project get [--project-key KEY]",
313
+ description: "Get project settings and metadata.",
314
+ examples: ["dss project get", "dss project get --project-key MYPROJ",],
309
315
  },
310
316
  metadata: {
311
317
  handler: (c, _a, f) => c.projects.metadata(f["project-key"]),
312
318
  usage: "dss project metadata [--project-key KEY]",
319
+ description: "Get project-level metadata (tags, labels, custom fields).",
320
+ examples: ["dss project metadata", "dss project metadata --project-key MYPROJ",],
313
321
  },
314
322
  flow: {
315
323
  handler: (c, _a, f) => c.projects.flow(f["project-key"]),
316
324
  usage: "dss project flow [--project-key KEY]",
325
+ description: "Get the raw flow graph (all datasets, recipes, and edges).",
326
+ examples: ["dss project flow", "dss project flow --project-key MYPROJ -f quiet",],
317
327
  },
318
328
  map: {
319
329
  handler: (c, _a, f) => c.projects.map({
@@ -322,12 +332,24 @@ const commands = {
322
332
  includeRaw: f["include-raw"] === true,
323
333
  }),
324
334
  usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw]",
335
+ description: "Get a summarized, truncated flow map.",
336
+ examples: [
337
+ "dss project map",
338
+ "dss project map --max-nodes 50 --max-edges 100",
339
+ "dss project map --include-raw",
340
+ ],
325
341
  },
326
342
  },
327
343
  dataset: {
328
344
  list: {
329
345
  handler: (c, _a, f) => c.datasets.list(f["project-key"]),
330
346
  usage: "dss dataset list [--project-key KEY]",
347
+ description: "List all datasets in a project.",
348
+ examples: [
349
+ "dss dataset list",
350
+ "dss dataset list -f table",
351
+ "dss dataset list --project-key MYPROJ",
352
+ ],
331
353
  },
332
354
  get: {
333
355
  handler: (c, a, f) => {
@@ -335,6 +357,8 @@ const commands = {
335
357
  return c.datasets.get(a[0], f["project-key"]);
336
358
  },
337
359
  usage: "dss dataset get <name> [--project-key KEY]",
360
+ description: "Get full settings for a dataset.",
361
+ examples: ["dss dataset get orders", "dss dataset get orders --project-key MYPROJ",],
338
362
  },
339
363
  schema: {
340
364
  handler: (c, a, f) => {
@@ -342,6 +366,8 @@ const commands = {
342
366
  return c.datasets.schema(a[0], f["project-key"]);
343
367
  },
344
368
  usage: "dss dataset schema <name> [--project-key KEY]",
369
+ description: "Show the column schema of a dataset.",
370
+ examples: ["dss dataset schema orders", "dss dataset schema orders -f table",],
345
371
  },
346
372
  preview: {
347
373
  handler: (c, a, f) => {
@@ -353,6 +379,8 @@ const commands = {
353
379
  });
354
380
  },
355
381
  usage: "dss dataset preview <name> [--max-rows N] [--project-key KEY] [--timeout MS]",
382
+ description: "Preview dataset rows.",
383
+ examples: ["dss dataset preview orders", "dss dataset preview orders --max-rows 5",],
356
384
  },
357
385
  metadata: {
358
386
  handler: (c, a, f) => {
@@ -360,6 +388,8 @@ const commands = {
360
388
  return c.datasets.metadata(a[0], f["project-key"]);
361
389
  },
362
390
  usage: "dss dataset metadata <name> [--project-key KEY]",
391
+ description: "Get dataset-level metadata.",
392
+ examples: ["dss dataset metadata orders",],
363
393
  },
364
394
  download: {
365
395
  handler: (c, a, f) => {
@@ -370,39 +400,69 @@ const commands = {
370
400
  });
371
401
  },
372
402
  usage: "dss dataset download <name> [--output PATH] [--project-key KEY]",
403
+ description: "Download dataset contents as CSV.",
404
+ examples: ["dss dataset download orders", "dss dataset download orders --output ./data/",],
373
405
  },
374
406
  create: {
375
- handler: (c, _a, f) => c.datasets.create({
376
- datasetName: f["name"],
377
- connection: f["connection"],
378
- dsType: f["type"],
379
- projectKey: f["project-key"],
380
- }),
407
+ handler: async (c, _a, f) => {
408
+ const pk = f["project-key"];
409
+ const name = f["name"];
410
+ if (f["if-not-exists"] === true) {
411
+ const list = await c.datasets.list(pk);
412
+ const existing = list.find((d) => d.name === name);
413
+ if (existing)
414
+ return { exists: true, ...existing, };
415
+ }
416
+ return c.datasets.create({
417
+ datasetName: name,
418
+ connection: f["connection"],
419
+ dsType: f["type"],
420
+ projectKey: pk,
421
+ });
422
+ },
381
423
  usage: "dss dataset create --name NAME --connection CONN --type TYPE [--project-key KEY]",
424
+ description: "Create a new dataset.",
425
+ examples: ["dss dataset create --name orders --connection filesystem --type Filesystem",],
382
426
  },
383
427
  delete: {
384
- handler: (c, a, f) => {
428
+ handler: async (c, a, f) => {
385
429
  requireArgs(a, 1, "dss dataset delete <name>");
386
- return c.datasets.delete(a[0], f["project-key"]);
430
+ const pk = f["project-key"];
431
+ if (f["dry-run"] === true) {
432
+ const current = await c.datasets.get(a[0], pk);
433
+ return { dryRun: true, action: "delete", resource: "dataset", name: a[0], current, };
434
+ }
435
+ await c.datasets.delete(a[0], pk);
436
+ return { deleted: a[0], resource: "dataset", };
387
437
  },
388
438
  usage: "dss dataset delete <name> [--project-key KEY]",
439
+ description: "Delete a dataset.",
440
+ examples: ["dss dataset delete orders",],
389
441
  },
390
442
  update: {
391
- handler: (c, a, f) => {
443
+ handler: async (c, a, f) => {
392
444
  requireArgs(a, 1, "dss dataset update <name> [--data '{...}' | --data-file PATH | --stdin]");
393
445
  const data = jsonInput(f);
394
446
  if (!data) {
395
447
  throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss dataset update <name> [--data '{...}' | --data-file PATH | --stdin]");
396
448
  }
397
- return c.datasets.update(a[0], data, f["project-key"]);
449
+ await c.datasets.update(a[0], data, f["project-key"]);
450
+ return { updated: a[0], resource: "dataset", };
398
451
  },
399
452
  usage: "dss dataset update <name> [--data '{...}' | --data-file PATH | --stdin] [--project-key KEY]",
453
+ description: "Update dataset settings via JSON merge.",
454
+ examples: [
455
+ 'dss dataset update orders --data \'{"tags":["production"]}\'',
456
+ "echo '{\"tags\":[]}' | dss dataset update orders --stdin",
457
+ ],
400
458
  },
401
459
  },
402
460
  recipe: {
403
461
  list: {
404
462
  handler: (c, _a, f) => c.recipes.list(f["project-key"]),
405
463
  usage: "dss recipe list [--project-key KEY]",
464
+ description: "List all recipes in a project.",
465
+ examples: ["dss recipe list", "dss recipe list -f table",],
406
466
  },
407
467
  get: {
408
468
  handler: (c, a, f) => {
@@ -412,13 +472,23 @@ const commands = {
412
472
  });
413
473
  },
414
474
  usage: "dss recipe get <name> [--include-payload]",
475
+ description: "Get recipe settings.",
476
+ examples: ["dss recipe get compute_orders", "dss recipe get compute_orders --include-payload",],
415
477
  },
416
478
  delete: {
417
- handler: (c, a, f) => {
479
+ handler: async (c, a, f) => {
418
480
  requireArgs(a, 1, "dss recipe delete <name>");
419
- return c.recipes.delete(a[0], f["project-key"]);
481
+ const pk = f["project-key"];
482
+ if (f["dry-run"] === true) {
483
+ const current = await c.recipes.get(a[0]);
484
+ return { dryRun: true, action: "delete", resource: "recipe", name: a[0], current, };
485
+ }
486
+ await c.recipes.delete(a[0], pk);
487
+ return { deleted: a[0], resource: "recipe", };
420
488
  },
421
489
  usage: "dss recipe delete <name> [--project-key KEY]",
490
+ description: "Delete a recipe.",
491
+ examples: ["dss recipe delete compute_orders",],
422
492
  },
423
493
  download: {
424
494
  handler: (c, a, f) => {
@@ -429,6 +499,11 @@ const commands = {
429
499
  });
430
500
  },
431
501
  usage: "dss recipe download <name> [--output PATH] [--project-key KEY]",
502
+ description: "Download recipe definition as JSON.",
503
+ examples: [
504
+ "dss recipe download compute_orders",
505
+ "dss recipe download compute_orders -o recipe.json",
506
+ ],
432
507
  },
433
508
  "download-code": {
434
509
  handler: (c, a, f) => {
@@ -439,9 +514,14 @@ const commands = {
439
514
  });
440
515
  },
441
516
  usage: "dss recipe download-code <name> [--output PATH] [--project-key KEY]",
517
+ description: "Download the code payload of a recipe.",
518
+ examples: [
519
+ "dss recipe download-code compute_orders",
520
+ "dss recipe download-code compute_orders -o code.py",
521
+ ],
442
522
  },
443
523
  create: {
444
- handler: (c, _a, f) => {
524
+ handler: async (c, _a, f) => {
445
525
  const type = f["type"];
446
526
  if (!type) {
447
527
  throw new UsageError("--type is required. Usage: dss recipe create --type TYPE --input DS --output DS");
@@ -450,16 +530,29 @@ const commands = {
450
530
  if (!outputDataset) {
451
531
  throw new UsageError("--output is required. Usage: dss recipe create --type TYPE --input DS --output DS");
452
532
  }
533
+ const name = f["name"];
534
+ const pk = f["project-key"];
535
+ if (f["if-not-exists"] === true && name) {
536
+ const list = await c.recipes.list(pk);
537
+ const existing = list.find((r) => r.name === name);
538
+ if (existing)
539
+ return { exists: true, ...existing, };
540
+ }
453
541
  return c.recipes.create({
454
542
  type,
455
- name: f["name"],
543
+ name,
456
544
  inputDatasets: f["input"] ? [f["input"],] : undefined,
457
545
  outputDataset,
458
546
  outputConnection: f["output-connection"],
459
- projectKey: f["project-key"],
547
+ projectKey: pk,
460
548
  });
461
549
  },
462
550
  usage: "dss recipe create --type TYPE --input DS --output DS [--output-connection CONN] [--project-key KEY]",
551
+ description: "Create a new recipe.",
552
+ examples: [
553
+ "dss recipe create --type python --input orders --output orders_clean",
554
+ "dss recipe create --type python --input orders --output orders_clean --output-connection filesystem",
555
+ ],
463
556
  },
464
557
  diff: {
465
558
  handler: async (c, a, f) => {
@@ -479,17 +572,25 @@ const commands = {
479
572
  return formatLineDiff(a[0], filePath, result.payload, localContent);
480
573
  },
481
574
  usage: "dss recipe diff <name> --file PATH [--project-key KEY]",
575
+ description: "Show differences between local file and remote recipe code.",
576
+ examples: ["dss recipe diff compute_orders --file code.py",],
482
577
  },
483
578
  update: {
484
- handler: (c, a, f) => {
579
+ handler: async (c, a, f) => {
485
580
  requireArgs(a, 1, "dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin]");
486
581
  const data = jsonInput(f);
487
582
  if (!data) {
488
583
  throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin]");
489
584
  }
490
- return c.recipes.update(a[0], data, f["project-key"]);
585
+ await c.recipes.update(a[0], data, f["project-key"]);
586
+ return { updated: a[0], resource: "recipe", };
491
587
  },
492
588
  usage: "dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin] [--project-key KEY]",
589
+ description: "Update recipe settings via JSON merge.",
590
+ examples: [
591
+ "dss recipe update compute_orders --data-file settings.json",
592
+ "cat settings.json | dss recipe update compute_orders --stdin",
593
+ ],
493
594
  },
494
595
  "get-payload": {
495
596
  handler: async (c, a, f) => {
@@ -504,6 +605,11 @@ const commands = {
504
605
  return payload;
505
606
  },
506
607
  usage: "dss recipe get-payload <name> [--output PATH] [--project-key KEY]",
608
+ description: "Print the recipe code payload to stdout.",
609
+ examples: [
610
+ "dss recipe get-payload compute_orders",
611
+ "dss recipe get-payload compute_orders -o code.py",
612
+ ],
507
613
  },
508
614
  "set-payload": {
509
615
  handler: async (c, a, f) => {
@@ -515,14 +621,19 @@ const commands = {
515
621
  await c.recipes.setPayload(a[0], content, {
516
622
  projectKey: f["project-key"],
517
623
  });
624
+ return { updated: a[0], resource: "recipe", file: filePath, };
518
625
  },
519
626
  usage: "dss recipe set-payload <name> --file PATH [--project-key KEY]",
627
+ description: "Upload recipe code from a local file.",
628
+ examples: ["dss recipe set-payload compute_orders --file code.py",],
520
629
  },
521
630
  },
522
631
  job: {
523
632
  list: {
524
633
  handler: (c, _a, f) => c.jobs.list(f["project-key"]),
525
634
  usage: "dss job list [--project-key KEY]",
635
+ description: "List recent jobs.",
636
+ examples: ["dss job list", "dss job list -f table",],
526
637
  },
527
638
  get: {
528
639
  handler: (c, a, f) => {
@@ -530,6 +641,8 @@ const commands = {
530
641
  return c.jobs.get(a[0], f["project-key"]);
531
642
  },
532
643
  usage: "dss job get <id> [--project-key KEY]",
644
+ description: "Get job details.",
645
+ examples: ["dss job get JOB_ID",],
533
646
  },
534
647
  log: {
535
648
  handler: (c, a, f) => {
@@ -540,6 +653,8 @@ const commands = {
540
653
  });
541
654
  },
542
655
  usage: "dss job log <id> [--activity NAME] [--max-lines N]",
656
+ description: "Get log output for a job.",
657
+ examples: ["dss job log JOB_ID", "dss job log JOB_ID --activity main --max-lines 200",],
543
658
  },
544
659
  build: {
545
660
  handler: (c, a, f) => {
@@ -549,6 +664,8 @@ const commands = {
549
664
  });
550
665
  },
551
666
  usage: "dss job build <dataset> [--build-mode MODE]",
667
+ description: "Start a dataset build (returns immediately).",
668
+ examples: ["dss job build orders", "dss job build orders --build-mode RECURSIVE_BUILD",],
552
669
  },
553
670
  "build-and-wait": {
554
671
  handler: (c, a, f) => {
@@ -560,6 +677,12 @@ const commands = {
560
677
  });
561
678
  },
562
679
  usage: "dss job build-and-wait <dataset> [--build-mode MODE] [--include-logs] [--timeout MS]",
680
+ description: "Build a dataset and wait for completion.",
681
+ examples: [
682
+ "dss job build-and-wait orders",
683
+ "dss job build-and-wait orders --include-logs",
684
+ "dss job build-and-wait orders --timeout 300000",
685
+ ],
563
686
  },
564
687
  wait: {
565
688
  handler: (c, a, f) => {
@@ -570,6 +693,8 @@ const commands = {
570
693
  });
571
694
  },
572
695
  usage: "dss job wait <id> [--include-logs] [--timeout MS]",
696
+ description: "Wait for an existing job to complete.",
697
+ examples: ["dss job wait JOB_ID", "dss job wait JOB_ID --include-logs --timeout 60000",],
573
698
  },
574
699
  abort: {
575
700
  handler: (c, a, f) => {
@@ -577,12 +702,16 @@ const commands = {
577
702
  return c.jobs.abort(a[0], f["project-key"]);
578
703
  },
579
704
  usage: "dss job abort <id> [--project-key KEY]",
705
+ description: "Abort a running job.",
706
+ examples: ["dss job abort JOB_ID",],
580
707
  },
581
708
  },
582
709
  scenario: {
583
710
  list: {
584
711
  handler: (c, _a, f) => c.scenarios.list(f["project-key"]),
585
712
  usage: "dss scenario list [--project-key KEY]",
713
+ description: "List all scenarios in a project.",
714
+ examples: ["dss scenario list", "dss scenario list -f table",],
586
715
  },
587
716
  get: {
588
717
  handler: (c, a, _f) => {
@@ -590,6 +719,8 @@ const commands = {
590
719
  return c.scenarios.get(a[0]);
591
720
  },
592
721
  usage: "dss scenario get <id>",
722
+ description: "Get scenario definition.",
723
+ examples: ["dss scenario get my_scenario",],
593
724
  },
594
725
  run: {
595
726
  handler: (c, a, f) => {
@@ -597,6 +728,23 @@ const commands = {
597
728
  return c.scenarios.run(a[0], f["project-key"]);
598
729
  },
599
730
  usage: "dss scenario run <id> [--project-key KEY]",
731
+ description: "Trigger a scenario run (returns immediately).",
732
+ examples: ["dss scenario run my_scenario",],
733
+ },
734
+ "run-and-wait": {
735
+ handler: (c, a, f) => {
736
+ requireArgs(a, 1, "dss scenario run-and-wait <id>");
737
+ return c.scenarios.runAndWait(a[0], {
738
+ timeoutMs: num(f["timeout"]),
739
+ projectKey: f["project-key"],
740
+ });
741
+ },
742
+ usage: "dss scenario run-and-wait <id> [--timeout MS] [--project-key KEY]",
743
+ description: "Run a scenario and wait for completion.",
744
+ examples: [
745
+ "dss scenario run-and-wait my_scenario",
746
+ "dss scenario run-and-wait my_scenario --timeout 300000",
747
+ ],
600
748
  },
601
749
  status: {
602
750
  handler: (c, a, f) => {
@@ -604,40 +752,68 @@ const commands = {
604
752
  return c.scenarios.status(a[0], f["project-key"]);
605
753
  },
606
754
  usage: "dss scenario status <id> [--project-key KEY]",
755
+ description: "Get the current run status of a scenario.",
756
+ examples: ["dss scenario status my_scenario",],
607
757
  },
608
758
  delete: {
609
- handler: (c, a, f) => {
759
+ handler: async (c, a, f) => {
610
760
  requireArgs(a, 1, "dss scenario delete <id>");
611
- return c.scenarios.delete(a[0], f["project-key"]);
761
+ const pk = f["project-key"];
762
+ if (f["dry-run"] === true) {
763
+ const current = await c.scenarios.get(a[0]);
764
+ return { dryRun: true, action: "delete", resource: "scenario", id: a[0], current, };
765
+ }
766
+ await c.scenarios.delete(a[0], pk);
767
+ return { deleted: a[0], resource: "scenario", };
612
768
  },
613
769
  usage: "dss scenario delete <id> [--project-key KEY]",
770
+ description: "Delete a scenario.",
771
+ examples: ["dss scenario delete my_scenario",],
614
772
  },
615
773
  create: {
616
- handler: (c, a, f) => {
774
+ handler: async (c, a, f) => {
617
775
  requireArgs(a, 2, "dss scenario create <id> <name>");
618
- return c.scenarios.create(a[0], a[1], {
776
+ const pk = f["project-key"];
777
+ if (f["if-not-exists"] === true) {
778
+ const list = await c.scenarios.list(pk);
779
+ const existing = list.find((s) => s.id === a[0]);
780
+ if (existing)
781
+ return { exists: true, ...existing, };
782
+ }
783
+ await c.scenarios.create(a[0], a[1], {
619
784
  scenarioType: f["type"],
620
- projectKey: f["project-key"],
785
+ projectKey: pk,
621
786
  });
787
+ return { created: a[0], name: a[1], resource: "scenario", };
622
788
  },
623
789
  usage: "dss scenario create <id> <name> [--type step_based|custom_python] [--project-key KEY]",
790
+ description: "Create a new scenario.",
791
+ examples: [
792
+ 'dss scenario create my_scenario "My Scenario"',
793
+ 'dss scenario create my_scenario "My Scenario" --type custom_python',
794
+ ],
624
795
  },
625
796
  update: {
626
- handler: (c, a, f) => {
797
+ handler: async (c, a, f) => {
627
798
  requireArgs(a, 1, "dss scenario update <id> [--data '{...}' | --data-file PATH | --stdin]");
628
799
  const data = jsonInput(f);
629
800
  if (!data) {
630
801
  throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss scenario update <id> [--data '{...}' | --data-file PATH | --stdin]");
631
802
  }
632
- return c.scenarios.update(a[0], data, f["project-key"]);
803
+ await c.scenarios.update(a[0], data, f["project-key"]);
804
+ return { updated: a[0], resource: "scenario", };
633
805
  },
634
806
  usage: "dss scenario update <id> [--data '{...}' | --data-file PATH | --stdin] [--project-key KEY]",
807
+ description: "Update scenario settings via JSON merge.",
808
+ examples: ["dss scenario update my_scenario --data-file settings.json",],
635
809
  },
636
810
  },
637
811
  folder: {
638
812
  list: {
639
813
  handler: (c, _a, f) => c.folders.list(f["project-key"]),
640
814
  usage: "dss folder list [--project-key KEY]",
815
+ description: "List managed folders in a project.",
816
+ examples: ["dss folder list", "dss folder list -f table",],
641
817
  },
642
818
  get: {
643
819
  handler: async (c, a, f) => {
@@ -645,6 +821,8 @@ const commands = {
645
821
  return c.folders.get(await resolveFolderId(c, a[0], f), f["project-key"]);
646
822
  },
647
823
  usage: "dss folder get <name-or-id> [--project-key KEY]",
824
+ description: "Get managed folder settings.",
825
+ examples: ["dss folder get my_folder",],
648
826
  },
649
827
  contents: {
650
828
  handler: async (c, a, f) => {
@@ -654,6 +832,8 @@ const commands = {
654
832
  });
655
833
  },
656
834
  usage: "dss folder contents <name-or-id> [--project-key KEY]",
835
+ description: "List files in a managed folder.",
836
+ examples: ["dss folder contents my_folder",],
657
837
  },
658
838
  download: {
659
839
  handler: async (c, a, f) => {
@@ -665,26 +845,42 @@ const commands = {
665
845
  });
666
846
  },
667
847
  usage: "dss folder download <name-or-id> <remote-path> [local-path] [--output PATH] [--project-key KEY]",
848
+ description: "Download a file from a managed folder.",
849
+ examples: [
850
+ "dss folder download my_folder /data/report.csv",
851
+ "dss folder download my_folder /data/report.csv ./report.csv",
852
+ ],
668
853
  },
669
854
  upload: {
670
855
  handler: async (c, a, f) => {
671
856
  requireArgs(a, 3, "dss folder upload <name-or-id> <path> <localPath>");
672
- return c.folders.upload(await resolveFolderId(c, a[0], f), a[1], a[2], f["project-key"]);
857
+ await c.folders.upload(await resolveFolderId(c, a[0], f), a[1], a[2], f["project-key"]);
858
+ return { uploaded: a[1], folder: a[0], localPath: a[2], resource: "folder", };
673
859
  },
674
860
  usage: "dss folder upload <name-or-id> <path> <localPath> [--project-key KEY]",
861
+ description: "Upload a local file to a managed folder.",
862
+ examples: ["dss folder upload my_folder /data/report.csv ./report.csv",],
675
863
  },
676
864
  "delete-file": {
677
865
  handler: async (c, a, f) => {
678
866
  requireArgs(a, 2, "dss folder delete-file <name-or-id> <path>");
679
- return c.folders.deleteFile(await resolveFolderId(c, a[0], f), a[1], f["project-key"]);
867
+ if (f["dry-run"] === true) {
868
+ return { dryRun: true, action: "delete-file", resource: "folder", folder: a[0], path: a[1], };
869
+ }
870
+ await c.folders.deleteFile(await resolveFolderId(c, a[0], f), a[1], f["project-key"]);
871
+ return { deleted: a[1], folder: a[0], resource: "folder", };
680
872
  },
681
873
  usage: "dss folder delete-file <name-or-id> <path> [--project-key KEY]",
874
+ description: "Delete a file from a managed folder.",
875
+ examples: ["dss folder delete-file my_folder /data/report.csv",],
682
876
  },
683
877
  },
684
878
  variable: {
685
879
  get: {
686
880
  handler: (c, _a, f) => c.variables.get(f["project-key"]),
687
881
  usage: "dss variable get [--project-key KEY]",
882
+ description: "Get project variables (standard and local).",
883
+ examples: ["dss variable get", "dss variable get --project-key MYPROJ",],
688
884
  },
689
885
  set: {
690
886
  handler: (c, _a, f) => c.variables.set({
@@ -693,19 +889,28 @@ const commands = {
693
889
  replace: f["replace"] === true,
694
890
  projectKey: f["project-key"],
695
891
  }),
696
- usage: 'dss variable set --standard \'{"k":"v"}\' --local \'{"k":"v"}\' [--replace] [--project-key KEY]',
892
+ usage: `dss variable set --standard '{"k":"v"}' --local '{"k":"v"}' [--replace] [--project-key KEY]`,
893
+ description: "Set project variables via JSON merge (or full replace with --replace).",
894
+ examples: [
895
+ 'dss variable set --standard \'{"env":"staging"}\'',
896
+ "dss variable set --local '{\"debug\":true}' --replace",
897
+ ],
697
898
  },
698
899
  },
699
900
  connection: {
700
901
  list: {
701
902
  handler: (c) => c.connections.list(),
702
903
  usage: "dss connection list",
904
+ description: "List all connection names.",
905
+ examples: ["dss connection list",],
703
906
  },
704
907
  infer: {
705
908
  handler: (c, _a, f) => c.connections.infer({
706
909
  mode: f["mode"],
707
910
  }),
708
911
  usage: "dss connection infer [--mode fast|rich]",
912
+ description: "List connections with inferred types and metadata.",
913
+ examples: ["dss connection infer", "dss connection infer --mode rich",],
709
914
  },
710
915
  },
711
916
  "code-env": {
@@ -714,6 +919,8 @@ const commands = {
714
919
  envLang: f["lang"],
715
920
  }),
716
921
  usage: "dss code-env list [--lang LANG]",
922
+ description: "List code environments.",
923
+ examples: ["dss code-env list", "dss code-env list --lang PYTHON",],
717
924
  },
718
925
  get: {
719
926
  handler: (c, a) => {
@@ -721,6 +928,8 @@ const commands = {
721
928
  return c.codeEnvs.get(a[0], a[1]);
722
929
  },
723
930
  usage: "dss code-env get <lang> <name>",
931
+ description: "Get code environment details.",
932
+ examples: ["dss code-env get PYTHON my_env",],
724
933
  },
725
934
  },
726
935
  sql: {
@@ -741,12 +950,20 @@ const commands = {
741
950
  });
742
951
  },
743
952
  usage: SQL_QUERY_USAGE,
953
+ description: "Run a SQL query against a DSS connection or dataset.",
954
+ examples: [
955
+ "dss sql query 'SELECT * FROM orders LIMIT 10' --connection my_pg",
956
+ "dss sql query --sql-file query.sql --connection my_pg",
957
+ "echo 'SELECT 1' | dss sql query --stdin --dataset MYPROJ.orders",
958
+ ],
744
959
  },
745
960
  },
746
961
  notebook: {
747
962
  "list-jupyter": {
748
963
  handler: (c, _a, f) => c.notebooks.listJupyter(f["project-key"]),
749
964
  usage: "dss notebook list-jupyter [--project-key KEY]",
965
+ description: "List Jupyter notebooks.",
966
+ examples: ["dss notebook list-jupyter",],
750
967
  },
751
968
  "get-jupyter": {
752
969
  handler: (c, a, _f) => {
@@ -754,20 +971,32 @@ const commands = {
754
971
  return c.notebooks.getJupyter(a[0]);
755
972
  },
756
973
  usage: "dss notebook get-jupyter <name>",
974
+ description: "Get a Jupyter notebook.",
975
+ examples: ["dss notebook get-jupyter my_notebook",],
757
976
  },
758
977
  "delete-jupyter": {
759
- handler: (c, a, _f) => {
978
+ handler: async (c, a, f) => {
760
979
  requireArgs(a, 1, "dss notebook delete-jupyter <name>");
761
- return c.notebooks.deleteJupyter(a[0]);
980
+ if (f["dry-run"] === true) {
981
+ const current = await c.notebooks.getJupyter(a[0]);
982
+ return { dryRun: true, action: "delete", resource: "jupyter-notebook", name: a[0], current, };
983
+ }
984
+ await c.notebooks.deleteJupyter(a[0]);
985
+ return { deleted: a[0], resource: "jupyter-notebook", };
762
986
  },
763
987
  usage: "dss notebook delete-jupyter <name>",
988
+ description: "Delete a Jupyter notebook.",
989
+ examples: ["dss notebook delete-jupyter my_notebook",],
764
990
  },
765
991
  "clear-jupyter-outputs": {
766
- handler: (c, a, _f) => {
992
+ handler: async (c, a, _f) => {
767
993
  requireArgs(a, 1, "dss notebook clear-jupyter-outputs <name>");
768
- return c.notebooks.clearJupyterOutputs(a[0]);
994
+ await c.notebooks.clearJupyterOutputs(a[0]);
995
+ return { cleared: a[0], resource: "jupyter-notebook", };
769
996
  },
770
997
  usage: "dss notebook clear-jupyter-outputs <name>",
998
+ description: "Clear all cell outputs from a Jupyter notebook.",
999
+ examples: ["dss notebook clear-jupyter-outputs my_notebook",],
771
1000
  },
772
1001
  "sessions-jupyter": {
773
1002
  handler: (c, a, _f) => {
@@ -775,17 +1004,24 @@ const commands = {
775
1004
  return c.notebooks.listJupyterSessions(a[0]);
776
1005
  },
777
1006
  usage: "dss notebook sessions-jupyter <name>",
1007
+ description: "List active kernel sessions for a Jupyter notebook.",
1008
+ examples: ["dss notebook sessions-jupyter my_notebook",],
778
1009
  },
779
1010
  "unload-jupyter": {
780
- handler: (c, a, _f) => {
1011
+ handler: async (c, a, _f) => {
781
1012
  requireArgs(a, 2, "dss notebook unload-jupyter <name> <sessionId>");
782
- return c.notebooks.unloadJupyter(a[0], a[1]);
1013
+ await c.notebooks.unloadJupyter(a[0], a[1]);
1014
+ return { unloaded: a[0], sessionId: a[1], resource: "jupyter-notebook", };
783
1015
  },
784
1016
  usage: "dss notebook unload-jupyter <name> <sessionId>",
1017
+ description: "Unload a Jupyter notebook kernel session.",
1018
+ examples: ["dss notebook unload-jupyter my_notebook SESSION_ID",],
785
1019
  },
786
1020
  "list-sql": {
787
1021
  handler: (c, _a, f) => c.notebooks.listSql(f["project-key"]),
788
1022
  usage: "dss notebook list-sql [--project-key KEY]",
1023
+ description: "List SQL notebooks.",
1024
+ examples: ["dss notebook list-sql",],
789
1025
  },
790
1026
  "get-sql": {
791
1027
  handler: (c, a, _f) => {
@@ -793,13 +1029,22 @@ const commands = {
793
1029
  return c.notebooks.getSql(a[0]);
794
1030
  },
795
1031
  usage: "dss notebook get-sql <id>",
1032
+ description: "Get a SQL notebook.",
1033
+ examples: ["dss notebook get-sql my_sql_notebook",],
796
1034
  },
797
1035
  "delete-sql": {
798
- handler: (c, a, _f) => {
1036
+ handler: async (c, a, f) => {
799
1037
  requireArgs(a, 1, "dss notebook delete-sql <id>");
800
- return c.notebooks.deleteSql(a[0]);
1038
+ if (f["dry-run"] === true) {
1039
+ const current = await c.notebooks.getSql(a[0]);
1040
+ return { dryRun: true, action: "delete", resource: "sql-notebook", id: a[0], current, };
1041
+ }
1042
+ await c.notebooks.deleteSql(a[0]);
1043
+ return { deleted: a[0], resource: "sql-notebook", };
801
1044
  },
802
1045
  usage: "dss notebook delete-sql <id>",
1046
+ description: "Delete a SQL notebook.",
1047
+ examples: ["dss notebook delete-sql my_sql_notebook",],
803
1048
  },
804
1049
  "history-sql": {
805
1050
  handler: (c, a, _f) => {
@@ -807,39 +1052,56 @@ const commands = {
807
1052
  return c.notebooks.getSqlHistory(a[0]);
808
1053
  },
809
1054
  usage: "dss notebook history-sql <id>",
1055
+ description: "Get query history for a SQL notebook.",
1056
+ examples: ["dss notebook history-sql my_sql_notebook",],
810
1057
  },
811
1058
  "save-jupyter": {
812
- handler: (c, a, f) => {
1059
+ handler: async (c, a, f) => {
813
1060
  requireArgs(a, 1, "dss notebook save-jupyter <name> [--data '{...}' | --data-file PATH | --stdin]");
814
1061
  const data = jsonInput(f);
815
1062
  if (!data) {
816
1063
  throw new UsageError("--data, --data-file, or --stdin is required (notebook JSON content).");
817
1064
  }
818
- return c.notebooks.saveJupyter(a[0], data, f["project-key"]);
1065
+ await c.notebooks.saveJupyter(a[0], data, f["project-key"]);
1066
+ return { saved: a[0], resource: "jupyter-notebook", };
819
1067
  },
820
1068
  usage: "dss notebook save-jupyter <name> [--data '{...}' | --data-file PATH | --stdin] [--project-key KEY]",
1069
+ description: "Save content to a Jupyter notebook.",
1070
+ examples: [
1071
+ "dss notebook save-jupyter my_notebook --data-file notebook.json",
1072
+ "cat notebook.json | dss notebook save-jupyter my_notebook --stdin",
1073
+ ],
821
1074
  },
822
1075
  "save-sql": {
823
- handler: (c, a, f) => {
1076
+ handler: async (c, a, f) => {
824
1077
  requireArgs(a, 1, "dss notebook save-sql <id> [--data '{...}' | --data-file PATH | --stdin]");
825
1078
  const data = jsonInput(f);
826
1079
  if (!data) {
827
1080
  throw new UsageError("--data, --data-file, or --stdin is required (SQL notebook content JSON).");
828
1081
  }
829
- return c.notebooks.saveSql(a[0], data, f["project-key"]);
1082
+ await c.notebooks.saveSql(a[0], data, f["project-key"]);
1083
+ return { saved: a[0], resource: "sql-notebook", };
830
1084
  },
831
1085
  usage: "dss notebook save-sql <id> [--data '{...}' | --data-file PATH | --stdin] [--project-key KEY]",
1086
+ description: "Save content to a SQL notebook.",
1087
+ examples: ["dss notebook save-sql my_sql_notebook --data-file content.json",],
832
1088
  },
833
1089
  "clear-sql-history": {
834
- handler: (c, a, f) => {
1090
+ handler: async (c, a, f) => {
835
1091
  requireArgs(a, 1, "dss notebook clear-sql-history <id>");
836
- return c.notebooks.clearSqlHistory(a[0], {
1092
+ await c.notebooks.clearSqlHistory(a[0], {
837
1093
  cellId: f["cell-id"],
838
1094
  numRunsToRetain: num(f["retain"]),
839
1095
  projectKey: f["project-key"],
840
1096
  });
1097
+ return { cleared: a[0], resource: "sql-notebook", };
841
1098
  },
842
1099
  usage: "dss notebook clear-sql-history <id> [--cell-id CID] [--retain N] [--project-key KEY]",
1100
+ description: "Clear query history for a SQL notebook.",
1101
+ examples: [
1102
+ "dss notebook clear-sql-history my_sql_notebook",
1103
+ "dss notebook clear-sql-history my_sql_notebook --cell-id CELL1 --retain 5",
1104
+ ],
843
1105
  },
844
1106
  },
845
1107
  };
@@ -860,7 +1122,10 @@ function printTopLevelHelp() {
860
1122
  " --url URL Dataiku DSS base URL (env: DATAIKU_URL)",
861
1123
  " --api-key KEY API key (env: DATAIKU_API_KEY)",
862
1124
  " --project-key KEY Default project key (env: DATAIKU_PROJECT_KEY)",
863
- " --timeout MS Request timeout in ms (default: 30000)",
1125
+ " --timeout MS Operation timeout (build-and-wait, run-and-wait)",
1126
+ " --request-timeout MS HTTP request timeout in ms (default: 30000)",
1127
+ " --dry-run Preview destructive actions without executing",
1128
+ " --if-not-exists Skip create if resource already exists",
864
1129
  " --insecure Disable TLS certificate verification",
865
1130
  " --ca-cert PATH Extra PEM CA bundle (env: NODE_EXTRA_CA_CERTS)",
866
1131
  "",
@@ -884,11 +1149,14 @@ function printResourceHelp(resource) {
884
1149
  const actions = commands[resource];
885
1150
  if (!actions)
886
1151
  return;
1152
+ const maxName = Math.max(...Object.keys(actions).map((n) => n.length));
887
1153
  const lines = [
888
1154
  `Usage: dss ${resource} <action> [args...] [--flags]`,
889
1155
  "",
890
1156
  "Actions:",
891
- ...Object.entries(actions).map(([name, meta,]) => ` ${name}${meta.usage}`),
1157
+ ...Object.entries(actions).map(([name, meta,]) => ` ${name.padEnd(maxName + 2)}${meta.description ?? meta.usage}`),
1158
+ "",
1159
+ `Run 'dss ${resource} <action> --help' for details and examples.`,
892
1160
  ];
893
1161
  process.stderr.write(`${lines.join("\n")}\n`);
894
1162
  }
@@ -896,7 +1164,16 @@ function printActionHelp(resource, action) {
896
1164
  const meta = commands[resource]?.[action];
897
1165
  if (!meta)
898
1166
  return;
899
- process.stderr.write(`Usage: ${meta.usage}\n`);
1167
+ const lines = [];
1168
+ if (meta.description)
1169
+ lines.push(meta.description, "");
1170
+ lines.push(`Usage: ${meta.usage}`);
1171
+ if (meta.examples && meta.examples.length > 0) {
1172
+ lines.push("", "Examples:");
1173
+ for (const ex of meta.examples)
1174
+ lines.push(` ${ex}`);
1175
+ }
1176
+ process.stderr.write(`${lines.join("\n")}\n`);
900
1177
  }
901
1178
  // ---------------------------------------------------------------------------
902
1179
  // Validation
@@ -977,13 +1254,18 @@ const AUTH_ACTIONS = {
977
1254
  process.stderr.write(`Credentials saved to ${getCredentialsPath()}\n`);
978
1255
  },
979
1256
  usage: "dss auth login [--url URL] [--api-key KEY] [--project-key KEY] [--insecure] [--ca-cert PATH]",
1257
+ description: "Save DSS credentials (interactive or via flags).",
1258
+ examples: [
1259
+ "dss auth login --url https://dss.example.com --api-key YOUR_KEY",
1260
+ "dss auth login --url https://dss.example.com --api-key YOUR_KEY --project-key MYPROJ",
1261
+ ],
980
1262
  },
981
1263
  status: {
982
1264
  handler: async (flags) => {
983
1265
  const creds = loadCredentials();
984
1266
  if (!creds) {
985
1267
  process.stderr.write("No saved credentials. Run: dss auth login\n");
986
- return;
1268
+ process.exit(1);
987
1269
  }
988
1270
  const tlsSettings = resolveTlsSettings(flags, creds);
989
1271
  const lines = [
@@ -1001,10 +1283,14 @@ const AUTH_ACTIONS = {
1001
1283
  }
1002
1284
  else {
1003
1285
  process.stderr.write(`Connection: ✗ Failed (${result.error ?? "unknown error"})\n`);
1286
+ process.stderr.write(`Config: ${getCredentialsPath()}\n`);
1287
+ process.exit(1);
1004
1288
  }
1005
1289
  process.stderr.write(`Config: ${getCredentialsPath()}\n`);
1006
1290
  },
1007
1291
  usage: "dss auth status [--insecure] [--ca-cert PATH]",
1292
+ description: "Show saved credentials and verify the connection.",
1293
+ examples: ["dss auth status",],
1008
1294
  },
1009
1295
  logout: {
1010
1296
  handler: async (_flags) => {
@@ -1012,6 +1298,8 @@ const AUTH_ACTIONS = {
1012
1298
  process.stderr.write("Credentials removed.\n");
1013
1299
  },
1014
1300
  usage: "dss auth logout",
1301
+ description: "Remove saved credentials.",
1302
+ examples: ["dss auth logout",],
1015
1303
  },
1016
1304
  };
1017
1305
  // ---------------------------------------------------------------------------
@@ -1089,12 +1377,15 @@ async function main() {
1089
1377
  // Auth commands — dispatched before client creation
1090
1378
  if (resource === "auth") {
1091
1379
  const action = positional[1];
1092
- if (!action || flags["help"] === true) {
1380
+ if (!action) {
1381
+ const maxName = Math.max(...Object.keys(AUTH_ACTIONS).map((n) => n.length));
1093
1382
  const lines = [
1094
1383
  "Usage: dss auth <action> [--flags]",
1095
1384
  "",
1096
1385
  "Actions:",
1097
- ...Object.entries(AUTH_ACTIONS).map(([name, meta,]) => ` ${name} \u2192 ${meta.usage}`),
1386
+ ...Object.entries(AUTH_ACTIONS).map(([name, meta,]) => ` ${name.padEnd(maxName + 2)}${meta.description ?? meta.usage}`),
1387
+ "",
1388
+ "Run 'dss auth <action> --help' for details and examples.",
1098
1389
  ];
1099
1390
  process.stderr.write(`${lines.join("\n")}\n`);
1100
1391
  process.exit(flags["help"] === true ? 0 : 1);
@@ -1104,6 +1395,19 @@ async function main() {
1104
1395
  process.stderr.write(`Unknown action: auth ${action}\nAvailable: ${Object.keys(AUTH_ACTIONS).join(", ")}\n`);
1105
1396
  process.exit(1);
1106
1397
  }
1398
+ if (flags["help"] === true) {
1399
+ const lines = [];
1400
+ if (authMeta.description)
1401
+ lines.push(authMeta.description, "");
1402
+ lines.push(`Usage: ${authMeta.usage}`);
1403
+ if (authMeta.examples && authMeta.examples.length > 0) {
1404
+ lines.push("", "Examples:");
1405
+ for (const ex of authMeta.examples)
1406
+ lines.push(` ${ex}`);
1407
+ }
1408
+ process.stderr.write(`${lines.join("\n")}\n`);
1409
+ process.exit(0);
1410
+ }
1107
1411
  await authMeta.handler(flags);
1108
1412
  return;
1109
1413
  }
@@ -1167,6 +1471,30 @@ async function main() {
1167
1471
  }
1168
1472
  return;
1169
1473
  }
1474
+ // commands — machine-readable introspection (no auth needed)
1475
+ if (resource === "commands") {
1476
+ const registry = {};
1477
+ for (const [res, actions,] of Object.entries(commands)) {
1478
+ registry[res] = {};
1479
+ for (const [act, meta,] of Object.entries(actions)) {
1480
+ registry[res][act] = {
1481
+ usage: meta.usage,
1482
+ description: meta.description,
1483
+ examples: meta.examples,
1484
+ };
1485
+ }
1486
+ }
1487
+ registry["auth"] = {};
1488
+ for (const [act, meta,] of Object.entries(AUTH_ACTIONS)) {
1489
+ registry["auth"][act] = {
1490
+ usage: meta.usage,
1491
+ description: meta.description,
1492
+ examples: meta.examples,
1493
+ };
1494
+ }
1495
+ process.stdout.write(`${JSON.stringify(registry, null, 2)}\n`);
1496
+ process.exit(0);
1497
+ }
1170
1498
  // Unknown resource
1171
1499
  if (!commands[resource]) {
1172
1500
  if (flags["help"]) {
@@ -1205,7 +1533,7 @@ async function main() {
1205
1533
  if (!apiKey) {
1206
1534
  throw new UsageError("Missing API key. Set DATAIKU_API_KEY, pass --api-key, or run: dss auth login");
1207
1535
  }
1208
- const requestTimeoutMs = num(flags["timeout"]) ?? undefined;
1536
+ const requestTimeoutMs = num(flags["request-timeout"]) ?? num(flags["timeout"]) ?? undefined;
1209
1537
  const client = new DataikuClient({
1210
1538
  url,
1211
1539
  apiKey,
@@ -1222,7 +1550,7 @@ async function main() {
1222
1550
  }
1223
1551
  main().catch((err) => {
1224
1552
  if (err instanceof UsageError) {
1225
- process.stderr.write(`${err.message} \n`);
1553
+ process.stderr.write(`${JSON.stringify({ error: err.message, code: "usage", }, null, 2)}\n`);
1226
1554
  process.exit(1);
1227
1555
  }
1228
1556
  if (err instanceof DataikuError) {
@@ -1,4 +1,4 @@
1
- import type { ScenarioDetails, ScenarioStatus, ScenarioSummary } from "../schemas.js";
1
+ import type { ScenarioDetails, ScenarioStatus, ScenarioSummary, ScenarioWaitResult } from "../schemas.js";
2
2
  import { BaseResource } from "./base.js";
3
3
  export declare class ScenariosResource extends BaseResource {
4
4
  /** List all scenarios in a project. */
@@ -23,4 +23,13 @@ export declare class ScenariosResource extends BaseResource {
23
23
  update(scenarioId: string, data: Record<string, unknown>, projectKey?: string): Promise<void>;
24
24
  /** Delete a scenario. */
25
25
  delete(scenarioId: string, projectKey?: string): Promise<void>;
26
+ /**
27
+ * Run a scenario and poll until it finishes or times out.
28
+ * Returns `{ success: false, timedOut: true }` on timeout rather than throwing.
29
+ */
30
+ runAndWait(scenarioId: string, opts?: {
31
+ pollIntervalMs?: number;
32
+ timeoutMs?: number;
33
+ projectKey?: string;
34
+ }): Promise<ScenarioWaitResult>;
26
35
  }
@@ -1,6 +1,7 @@
1
1
  import { ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, } from "../schemas.js";
2
2
  import { deepMerge, } from "../utils/deep-merge.js";
3
3
  import { BaseResource, } from "./base.js";
4
+ import { computeNextPollDelayMs, } from "./jobs.js";
4
5
  export class ScenariosResource extends BaseResource {
5
6
  /** List all scenarios in a project. */
6
7
  async list(projectKey) {
@@ -54,4 +55,50 @@ export class ScenariosResource extends BaseResource {
54
55
  const scEnc = encodeURIComponent(scenarioId);
55
56
  await this.client.del(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/`);
56
57
  }
58
+ /**
59
+ * Run a scenario and poll until it finishes or times out.
60
+ * Returns `{ success: false, timedOut: true }` on timeout rather than throwing.
61
+ */
62
+ async runAndWait(scenarioId, opts) {
63
+ const { runId, } = await this.run(scenarioId, opts?.projectKey);
64
+ const baseIntervalMs = Math.max(1, opts?.pollIntervalMs ?? 2_000);
65
+ const adaptivePolling = opts?.pollIntervalMs === undefined;
66
+ const timeout = Math.max(baseIntervalMs, opts?.timeoutMs ?? 120_000);
67
+ const startedAt = Date.now();
68
+ let pollCount = 0;
69
+ while (true) {
70
+ pollCount += 1;
71
+ const st = await this.status(scenarioId, opts?.projectKey);
72
+ const elapsedMs = Date.now() - startedAt;
73
+ // Scenario finished when it's no longer running and lastRun matches our runId
74
+ if (!st.running && st.lastRun?.runId === runId) {
75
+ const outcome = st.lastRun?.outcome ?? "UNKNOWN";
76
+ return {
77
+ scenarioId,
78
+ runId,
79
+ outcome,
80
+ success: outcome === "SUCCESS",
81
+ elapsedMs,
82
+ pollCount,
83
+ };
84
+ }
85
+ if (elapsedMs >= timeout) {
86
+ return {
87
+ scenarioId,
88
+ runId,
89
+ outcome: "TIMEOUT",
90
+ success: false,
91
+ elapsedMs,
92
+ pollCount,
93
+ timedOut: true,
94
+ };
95
+ }
96
+ const nextDelayMs = computeNextPollDelayMs({
97
+ pollCount,
98
+ baseIntervalMs,
99
+ adaptiveEnabled: adaptivePolling,
100
+ });
101
+ await new Promise((r) => setTimeout(r, Math.min(nextDelayMs, timeout - elapsedMs)));
102
+ }
103
+ }
57
104
  }
@@ -3,5 +3,5 @@
3
3
  * The types package (packages/types/) owns the TypeBox schema definitions.
4
4
  * SDK consumers get everything through this re-export.
5
5
  */
6
- export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
7
- export type { BuildMode, CodeEnvDetails, CodeEnvSummary, ConnectionSummary, DatasetCreateOptions, DatasetDetails, DatasetSchema, DatasetSummary, FlowMapOptions, FolderDetails, FolderItem, FolderSummary, JobSummary, JobWaitResult, JupyterCell, JupyterNotebookContent, JupyterNotebookSummary, NotebookSession, ProjectDetails, ProjectMetadata, ProjectSummary, ProjectVariables, RecipeCreateOptions, RecipeCreateResult, RecipeDetails, RecipeSummary, SafeParseResult, ScenarioDetails, ScenarioStatus, ScenarioSummary, SqlNotebookCell, SqlNotebookContent, SqlNotebookSummary, SqlQueryResponse, SqlQueryResult, SqlQuerySchema, } from "../packages/types/src/index.js";
6
+ export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, ScenarioWaitResultSchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
7
+ export type { BuildMode, CodeEnvDetails, CodeEnvSummary, ConnectionSummary, DatasetCreateOptions, DatasetDetails, DatasetSchema, DatasetSummary, FlowMapOptions, FolderDetails, FolderItem, FolderSummary, JobSummary, JobWaitResult, JupyterCell, JupyterNotebookContent, JupyterNotebookSummary, NotebookSession, ProjectDetails, ProjectMetadata, ProjectSummary, ProjectVariables, RecipeCreateOptions, RecipeCreateResult, RecipeDetails, RecipeSummary, SafeParseResult, ScenarioDetails, ScenarioStatus, ScenarioSummary, ScenarioWaitResult, SqlNotebookCell, SqlNotebookContent, SqlNotebookSummary, SqlQueryResponse, SqlQueryResult, SqlQuerySchema, } from "../packages/types/src/index.js";
@@ -3,4 +3,4 @@
3
3
  * The types package (packages/types/) owns the TypeBox schema definitions.
4
4
  * SDK consumers get everything through this re-export.
5
5
  */
6
- export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
6
+ export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, ScenarioWaitResultSchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataiku-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -233,6 +233,16 @@ export declare const ScenarioStatusSchema: import("@sinclair/typebox").TObject<{
233
233
  }>>;
234
234
  }>;
235
235
  export type ScenarioStatus = Static<typeof ScenarioStatusSchema>;
236
+ export declare const ScenarioWaitResultSchema: import("@sinclair/typebox").TObject<{
237
+ scenarioId: import("@sinclair/typebox").TString;
238
+ runId: import("@sinclair/typebox").TString;
239
+ outcome: import("@sinclair/typebox").TString;
240
+ success: import("@sinclair/typebox").TBoolean;
241
+ elapsedMs: import("@sinclair/typebox").TNumber;
242
+ pollCount: import("@sinclair/typebox").TNumber;
243
+ timedOut: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
244
+ }>;
245
+ export type ScenarioWaitResult = Static<typeof ScenarioWaitResultSchema>;
236
246
  export declare const FolderSummarySchema: import("@sinclair/typebox").TObject<{
237
247
  id: import("@sinclair/typebox").TString;
238
248
  name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -284,6 +284,15 @@ export const ScenarioStatusSchema = Type.Object({
284
284
  }, { additionalProperties: true, })),
285
285
  }, { additionalProperties: true, })),
286
286
  }, { additionalProperties: true, });
287
+ export const ScenarioWaitResultSchema = Type.Object({
288
+ scenarioId: Type.String(),
289
+ runId: Type.String(),
290
+ outcome: Type.String(),
291
+ success: Type.Boolean(),
292
+ elapsedMs: Type.Number(),
293
+ pollCount: Type.Number(),
294
+ timedOut: Type.Optional(Type.Boolean()),
295
+ });
287
296
  // ---------------------------------------------------------------------------
288
297
  // Folders
289
298
  // ---------------------------------------------------------------------------