dataiku-sdk 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -8,21 +8,22 @@ Examples below assume the installed `dss` binary. From this checkout, use `./bin
8
8
  ## CLI contract
9
9
 
10
10
  - Success: stdout contains one JSON result.
11
- - Failure: stderr contains one JSON error envelope with `ok:false`, `error`, `code`, and `exitCode`.
12
- - `--verbose` may add HTTP trace lines to stderr.
11
+ - Failure: stderr contains one JSONL error event with `type:"error"`, `ok:false`, `error`, `code`, and `exitCode`.
12
+ - Non-fatal diagnostics and `--verbose` traces are JSONL stderr events (`type:"warning"` / `type:"trace"`); no prose trace lines are part of the contract.
13
13
  - `--fields a,b,c` projects those fields from an object or array-of-objects result; dotted paths (`a.b.c`) drill into nested objects, and missing fields become `null`; string and scalar results pass through unchanged.
14
14
  - No prompts, help screens, tables, banners, or prose output are part of the contract.
15
15
  - Exit codes: `0` success, `1` usage/configuration error, `2` DSS/internal error, `3` transient DSS error, `4` completed command with failed long-running DSS work.
16
16
  - The exit code is the success signal: chain mutations with `&&` and never infer success from piped output that discards the exit code (e.g. `dss ... 2>&1 | helper; echo done` reports success even when the command failed).
17
17
  - `--raw` is only for recipe payload commands. Without `--output`, stdout is raw bytes; with `--output PATH`, stdout is the JSON string equal to `PATH` and the file contains the exact raw bytes.
18
18
 
19
- Discover the complete machine-readable command surface:
19
+ Discover the agent protocol and complete machine-readable command surface:
20
20
 
21
21
  ```bash
22
+ dss agent contract
22
23
  dss commands run
23
24
  ```
24
25
 
25
- Agents should parse `commands run` before choosing syntax; inspect `flags`, `requiresAuth`, `requiresProject`, `sideEffect`, and `outputShape` instead of guessing.
26
+ Agents should parse `agent contract` once for protocol/schema compatibility, then parse `commands run` before choosing syntax; inspect `flags`, `structuredExamples`, `schemas`, `requiresAuth`, `requiresProject`, `sideEffect`, `unsafeOutputs`, and `outputShape` instead of guessing.
26
27
 
27
28
  ## Agent skill installation
28
29
 
package/dist/src/cli.js CHANGED
@@ -72,6 +72,9 @@ function gitRevision(packageRoot) {
72
72
  const PACKAGE_ROOT = findPackageRoot();
73
73
  const CLI_VERSION = packageVersion(PACKAGE_ROOT);
74
74
  const CLI_GIT_REVISION = gitRevision(PACKAGE_ROOT);
75
+ const AGENT_CONTRACT_VERSION = 1;
76
+ const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
77
+ const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v1.json";
75
78
  function cliVersionResult() {
76
79
  return { version: CLI_VERSION, gitRevision: CLI_GIT_REVISION ?? null, };
77
80
  }
@@ -1128,13 +1131,13 @@ let pendingCliWarnings = [];
1128
1131
  function enqueueCliWarning(warning) {
1129
1132
  pendingCliWarnings.push(warning);
1130
1133
  }
1131
- /** Emit queued warnings as a single `{ warnings: [...] }` object on stderr. Idempotent. */
1134
+ /** Emit queued warnings as one JSONL warning event on stderr. Idempotent. */
1132
1135
  function flushCliWarnings() {
1133
1136
  if (pendingCliWarnings.length === 0)
1134
1137
  return;
1135
1138
  const warnings = pendingCliWarnings;
1136
1139
  pendingCliWarnings = [];
1137
- process.stderr.write(`${JSON.stringify({ warnings, }, null, 2)}\n`);
1140
+ process.stderr.write(`${JSON.stringify({ type: "warning", warnings, })}\n`);
1138
1141
  }
1139
1142
  function resolveFieldPath(source, field) {
1140
1143
  let current = source;
@@ -1499,6 +1502,7 @@ const BOOLEAN_FLAGS = new Set([
1499
1502
  "errors-only",
1500
1503
  "keep",
1501
1504
  "full-log",
1505
+ "drop-data",
1502
1506
  ]);
1503
1507
  const SHORT_FLAGS = {
1504
1508
  v: "verbose",
@@ -1517,6 +1521,12 @@ const FLAG_ALIASES = {
1517
1521
  };
1518
1522
  const VALUE_FLAGS = new Set([
1519
1523
  "fields",
1524
+ "owner",
1525
+ "topic",
1526
+ "reply",
1527
+ "text",
1528
+ "partitions",
1529
+ "since",
1520
1530
  "activity",
1521
1531
  "agent",
1522
1532
  "api-key",
@@ -6473,6 +6483,8 @@ async function runFixtures(flags) {
6473
6483
  const READ_ACTIONS = new Set([
6474
6484
  "cat",
6475
6485
  "contents",
6486
+ "deployment-settings",
6487
+ "deployment-status",
6476
6488
  "diff",
6477
6489
  "download",
6478
6490
  "download-code",
@@ -6508,6 +6520,7 @@ const READ_ACTIONS = new Set([
6508
6520
  "status",
6509
6521
  "rules",
6510
6522
  "settings",
6523
+ "settings-get",
6511
6524
  "status-by-partition",
6512
6525
  "usages",
6513
6526
  ]);
@@ -6539,6 +6552,9 @@ const AUTHENTICATED_AGENT_FLAGS = [
6539
6552
  const COMMANDS_USAGE = "dss commands run [--json]";
6540
6553
  const COMMANDS_DESCRIPTION = "Print the machine-readable command registry for agent planning.";
6541
6554
  const COMMANDS_EXAMPLES = ["dss commands run", "dss commands run --json",];
6555
+ const AGENT_CONTRACT_USAGE = "dss agent contract";
6556
+ const AGENT_CONTRACT_DESCRIPTION = "Print the versioned JSON contract agents should use to drive dss.";
6557
+ const AGENT_CONTRACT_EXAMPLES = ["dss agent contract",];
6542
6558
  const VERSION_USAGE = "dss version";
6543
6559
  const VERSION_DESCRIPTION = "Print the CLI version and git revision as JSON.";
6544
6560
  const VERSION_EXAMPLES = ["dss version", "dss --version",];
@@ -6585,6 +6601,118 @@ function flagKind(name) {
6585
6601
  function registryKey(resource, action) {
6586
6602
  return `${resource}.${action}`;
6587
6603
  }
6604
+ function splitShellLike(input) {
6605
+ const tokens = [];
6606
+ let current = "";
6607
+ let quote;
6608
+ for (let index = 0; index < input.length; index++) {
6609
+ const char = input[index];
6610
+ if (quote) {
6611
+ if (char === quote) {
6612
+ quote = undefined;
6613
+ }
6614
+ else if (char === "\\" && quote === '"' && index + 1 < input.length) {
6615
+ current += input[++index];
6616
+ }
6617
+ else {
6618
+ current += char;
6619
+ }
6620
+ continue;
6621
+ }
6622
+ if (char === "'" || char === '"') {
6623
+ quote = char;
6624
+ continue;
6625
+ }
6626
+ if (/\s/.test(char)) {
6627
+ if (current.length > 0) {
6628
+ tokens.push(current);
6629
+ current = "";
6630
+ }
6631
+ continue;
6632
+ }
6633
+ if (char === "|" || char === "<" || char === ">" || char === ";" || char === "&") {
6634
+ return undefined;
6635
+ }
6636
+ current += char;
6637
+ }
6638
+ if (quote)
6639
+ return undefined;
6640
+ if (current.length > 0)
6641
+ tokens.push(current);
6642
+ return tokens;
6643
+ }
6644
+ function exampleArgv(example) {
6645
+ const tokens = splitShellLike(example);
6646
+ if (!tokens || tokens[0] !== "dss")
6647
+ return undefined;
6648
+ return tokens.slice(1);
6649
+ }
6650
+ function structuredExamples(examples, examplePayload) {
6651
+ return (examples ?? []).map((shell) => {
6652
+ const argv = exampleArgv(shell);
6653
+ return {
6654
+ shell,
6655
+ ...(argv ? { argv, } : {}),
6656
+ ...(examplePayload !== undefined && shell.includes("--data")
6657
+ ? { payload: examplePayload, }
6658
+ : {}),
6659
+ };
6660
+ });
6661
+ }
6662
+ function outputJsonSchema(shape) {
6663
+ if (shape === "array")
6664
+ return { type: "array", items: true, };
6665
+ if (shape === "string")
6666
+ return { type: "string", };
6667
+ return { type: "object", additionalProperties: true, };
6668
+ }
6669
+ function payloadJsonSchema(payloadSchema) {
6670
+ if (!payloadSchema)
6671
+ return undefined;
6672
+ return payloadSchema.jsonShape === "array"
6673
+ ? { type: "array", items: true, }
6674
+ : { type: "object", additionalProperties: true, };
6675
+ }
6676
+ function argvJsonSchema(resource, action, _flags, _positionals, _requiredFlags, _requiredOneOf) {
6677
+ return {
6678
+ type: "object",
6679
+ additionalProperties: true,
6680
+ required: ["argv",],
6681
+ properties: {
6682
+ argv: { type: "array", items: { type: "string", }, minItems: 1, },
6683
+ resource: { const: resource, },
6684
+ action: { const: action, },
6685
+ },
6686
+ };
6687
+ }
6688
+ function unsafeOutputs(resource, action, flags, producesLocalFile) {
6689
+ const outputs = [];
6690
+ if (flags.some((flag) => flag.name === "raw")) {
6691
+ outputs.push({
6692
+ condition: "--raw without --output",
6693
+ kind: "raw-stdout",
6694
+ detail: `${resource} ${action} can intentionally write raw payload bytes/text instead of JSON.`,
6695
+ safeAlternative: flags.some((flag) => flag.name === "output")
6696
+ ? "Pass --output PATH so stdout remains a JSON string containing the path."
6697
+ : "Omit --raw when a JSON stdout value is required.",
6698
+ });
6699
+ }
6700
+ if (producesLocalFile) {
6701
+ outputs.push({
6702
+ condition: "--output or --output-file",
6703
+ kind: "local-file",
6704
+ detail: "Command writes bytes to a local path and returns JSON metadata on stdout.",
6705
+ });
6706
+ }
6707
+ return outputs.length > 0 ? outputs : undefined;
6708
+ }
6709
+ function buildCommandSchemas(resource, action, flags, positionals, requiredFlags, requiredOneOf, payloadSchema, outputShape) {
6710
+ return {
6711
+ argv: argvJsonSchema(resource, action, flags, positionals, requiredFlags, requiredOneOf),
6712
+ ...(payloadSchema ? { input: payloadJsonSchema(payloadSchema), } : {}),
6713
+ output: outputJsonSchema(outputShape),
6714
+ };
6715
+ }
6588
6716
  const EXPLICIT_REGISTRY_OVERRIDES = {
6589
6717
  "dashboard.create": {
6590
6718
  examplePayload: { pages: [], },
@@ -6640,7 +6768,8 @@ function extractPositionals(usage) {
6640
6768
  function inferSideEffect(resource, action) {
6641
6769
  if (resource === "auth")
6642
6770
  return "auth";
6643
- if (resource === "doctor" || resource === "commands" || resource === "fixtures"
6771
+ if (resource === "agent" || resource === "doctor" || resource === "commands"
6772
+ || resource === "fixtures"
6644
6773
  || resource === "version") {
6645
6774
  return "read";
6646
6775
  }
@@ -6650,20 +6779,23 @@ function inferSideEffect(resource, action) {
6650
6779
  return "write";
6651
6780
  if (READ_ACTIONS.has(action))
6652
6781
  return "read";
6653
- if (/^(create|clone|restore|update|delete|set|save|upload|run|build|abort|move|refresh|clear|unload|install|login|logout|add|remove|publish|activate|deploy|import|export|preload|upgrade|stop|restart|duplicate|put|rename|reply|compute)/
6654
- .test(action)) {
6782
+ if (/^(create|clone|restore|update|delete|set|save|upload|run|build|abort|move|refresh|clear|unload|install|login|logout|add|remove|publish|activate|deploy|import|export|preload|upgrade|start|stop|restart|duplicate|put|rename|reply|compute|organize)/
6783
+ .test(action)
6784
+ // Compound actions whose mutating verb is a suffix (e.g. permissions-set, dataset-compute).
6785
+ || /-(set|compute)$/.test(action)) {
6655
6786
  return "write";
6656
6787
  }
6657
6788
  return "read";
6658
6789
  }
6659
6790
  function inferRequiresAuth(resource) {
6660
- return resource !== "auth"
6791
+ return resource !== "agent"
6792
+ && resource !== "auth"
6661
6793
  && resource !== "commands"
6662
6794
  && resource !== "install-skill"
6663
6795
  && resource !== "version";
6664
6796
  }
6665
6797
  function inferRequiresProject(resource, action, usage) {
6666
- if (resource === "auth" || resource === "doctor" || resource === "commands"
6798
+ if (resource === "agent" || resource === "auth" || resource === "doctor" || resource === "commands"
6667
6799
  || resource === "install-skill" || resource === "version") {
6668
6800
  return false;
6669
6801
  }
@@ -6681,6 +6813,7 @@ const ARRAY_OUTPUT_ACTIONS = new Set([
6681
6813
  "list",
6682
6814
  "list-jupyter",
6683
6815
  "list-sql",
6816
+ "project-timeline",
6684
6817
  "rules",
6685
6818
  "schemas",
6686
6819
  "sessions-jupyter",
@@ -6696,14 +6829,19 @@ const STRING_OUTPUT_ACTIONS = new Set([
6696
6829
  "log-url",
6697
6830
  ]);
6698
6831
  function inferOutputShape(resource, action) {
6699
- if (resource === "auth" || resource === "commands" || resource === "install-skill"
6832
+ if (resource === "agent" || resource === "auth" || resource === "commands"
6833
+ || resource === "install-skill"
6700
6834
  || resource === "version") {
6701
6835
  return "object";
6702
6836
  }
6837
+ if (/^list(-|$)/.test(action))
6838
+ return "array";
6703
6839
  if (ARRAY_OUTPUT_ACTIONS.has(action))
6704
6840
  return "array";
6705
6841
  if (resource === "dataset" && action === "download")
6706
6842
  return "object";
6843
+ if (resource === "project-library" && action === "get")
6844
+ return "string";
6707
6845
  if (STRING_OUTPUT_ACTIONS.has(action))
6708
6846
  return "string";
6709
6847
  return "object";
@@ -6936,47 +7074,58 @@ function buildRegistryEntry(resource, action, meta) {
6936
7074
  const cleanupCommand = meta.cleanupCommand
6937
7075
  ?? EXPLICIT_REGISTRY_OVERRIDES[registryKey(resource, action)]?.cleanupCommand
6938
7076
  ?? cleanupCommandFromDeleteUsage(resource, action);
7077
+ const flagMetadata = flags.map((name) => {
7078
+ const kind = flagKind(name);
7079
+ if (kind === "boolean")
7080
+ return { name, kind, };
7081
+ const hint = valueHints.get(name) ?? GLOBAL_FLAG_VALUE_HINTS[name];
7082
+ if (!hint)
7083
+ return { name, kind, };
7084
+ return {
7085
+ name,
7086
+ kind,
7087
+ valueType: hint.valueType,
7088
+ ...(hint.enumValues ? { enumValues: hint.enumValues, } : {}),
7089
+ };
7090
+ });
7091
+ const positionals = extractPositionals(meta.usage);
7092
+ const outputShape = inferOutputShape(resource, action);
7093
+ const producesLocalFile = meta.usage.includes("--output PATH")
7094
+ || meta.usage.includes("--output-file PATH");
7095
+ const uniqueRequiredFlags = uniqueStrings(requiredFlags);
7096
+ const uniqueOptionalFlags = uniqueStrings(optionalFlags);
7097
+ const unsafe = unsafeOutputs(resource, action, flagMetadata, producesLocalFile);
6939
7098
  return {
6940
7099
  resource,
6941
7100
  action,
6942
7101
  usage: meta.usage,
6943
7102
  description: meta.description,
6944
7103
  examples: meta.examples,
6945
- flags: flags.map((name) => {
6946
- const kind = flagKind(name);
6947
- if (kind === "boolean")
6948
- return { name, kind, };
6949
- const hint = valueHints.get(name) ?? GLOBAL_FLAG_VALUE_HINTS[name];
6950
- if (!hint)
6951
- return { name, kind, };
6952
- return {
6953
- name,
6954
- kind,
6955
- valueType: hint.valueType,
6956
- ...(hint.enumValues ? { enumValues: hint.enumValues, } : {}),
6957
- };
6958
- }),
6959
- positionals: extractPositionals(meta.usage),
7104
+ structuredExamples: structuredExamples(meta.examples, examplePayload),
7105
+ flags: flagMetadata,
7106
+ positionals,
6960
7107
  sideEffect,
6961
7108
  requiresAuth,
6962
7109
  requiresProject,
6963
- outputShape: inferOutputShape(resource, action),
7110
+ outputShape,
6964
7111
  inputContract,
6965
7112
  destructive,
6966
- producesLocalFile: meta.usage.includes("--output PATH")
6967
- || meta.usage.includes("--output-file PATH"),
7113
+ producesLocalFile,
6968
7114
  mutatesDss,
6969
7115
  async: asyncKind,
6970
7116
  idempotency: inferIdempotency(sideEffect, action, meta.usage),
6971
7117
  dryRun: meta.usage.includes("--dry-run"),
6972
- requiredFlags: uniqueStrings(requiredFlags),
6973
- optionalFlags: uniqueStrings(optionalFlags),
7118
+ requiredFlags: uniqueRequiredFlags,
7119
+ optionalFlags: uniqueOptionalFlags,
6974
7120
  ...(requiredOneOf.length > 0 ? { requiredOneOf, } : {}),
6975
7121
  ...(payloadSchema ? { payloadSchema, } : {}),
7122
+ schemas: buildCommandSchemas(resource, action, flagMetadata, positionals, uniqueRequiredFlags, requiredOneOf, payloadSchema, outputShape),
7123
+ ...(unsafe ? { unsafeOutputs: unsafe, } : {}),
6976
7124
  ...(examplePayload !== undefined ? { examplePayload, } : {}),
6977
7125
  ...(cleanupCommand ? { cleanupCommand, } : {}),
6978
7126
  exitCodes: inferExitCodes(asyncKind),
6979
7127
  ...(cleanupHint ? { cleanupHint, } : {}),
7128
+ agentContractVersion: AGENT_CONTRACT_VERSION,
6980
7129
  };
6981
7130
  }
6982
7131
  function buildCommandRegistry() {
@@ -6995,6 +7144,14 @@ function buildCommandRegistry() {
6995
7144
  examples: COMMANDS_EXAMPLES,
6996
7145
  }),
6997
7146
  };
7147
+ registry.agent = {
7148
+ contract: buildRegistryEntry("agent", "contract", {
7149
+ handler: async () => undefined,
7150
+ usage: AGENT_CONTRACT_USAGE,
7151
+ description: AGENT_CONTRACT_DESCRIPTION,
7152
+ examples: AGENT_CONTRACT_EXAMPLES,
7153
+ }),
7154
+ };
6998
7155
  registry.version = {
6999
7156
  run: buildRegistryEntry("version", "run", {
7000
7157
  handler: async () => undefined,
@@ -7049,6 +7206,191 @@ function buildCommandRegistry() {
7049
7206
  }
7050
7207
  return registry;
7051
7208
  }
7209
+ function commandFlagJsonSchema() {
7210
+ return {
7211
+ type: "object",
7212
+ required: ["name", "kind",],
7213
+ additionalProperties: false,
7214
+ properties: {
7215
+ name: { type: "string", },
7216
+ kind: { enum: ["boolean", "value",], },
7217
+ valueType: { type: "string", },
7218
+ enumValues: { type: "array", items: { type: "string", }, },
7219
+ },
7220
+ };
7221
+ }
7222
+ function commandRegistryEntryJsonSchema() {
7223
+ return {
7224
+ type: "object",
7225
+ required: [
7226
+ "resource",
7227
+ "action",
7228
+ "usage",
7229
+ "flags",
7230
+ "positionals",
7231
+ "sideEffect",
7232
+ "requiresAuth",
7233
+ "requiresProject",
7234
+ "outputShape",
7235
+ "inputContract",
7236
+ "schemas",
7237
+ "exitCodes",
7238
+ "agentContractVersion",
7239
+ ],
7240
+ additionalProperties: true,
7241
+ properties: {
7242
+ resource: { type: "string", },
7243
+ action: { type: "string", },
7244
+ usage: { type: "string", },
7245
+ description: { type: "string", },
7246
+ examples: { type: "array", items: { type: "string", }, },
7247
+ structuredExamples: { type: "array", items: { type: "object", additionalProperties: true, }, },
7248
+ flags: { type: "array", items: commandFlagJsonSchema(), },
7249
+ positionals: { type: "array", items: { type: "string", }, },
7250
+ sideEffect: { enum: ["read", "write", "auth",], },
7251
+ requiresAuth: { type: "boolean", },
7252
+ requiresProject: { type: "boolean", },
7253
+ outputShape: { enum: ["object", "array", "string", "void",], },
7254
+ inputContract: { type: "object", additionalProperties: { type: "boolean", }, },
7255
+ destructive: { enum: ["none", "reversible", "destructive",], },
7256
+ producesLocalFile: { type: "boolean", },
7257
+ mutatesDss: { type: "boolean", },
7258
+ async: { enum: ["none", "job", "future",], },
7259
+ idempotency: { enum: ["safe", "convergent", "if-not-exists", "if-exists", "none",], },
7260
+ dryRun: { type: "boolean", },
7261
+ requiredFlags: { type: "array", items: { type: "string", }, },
7262
+ optionalFlags: { type: "array", items: { type: "string", }, },
7263
+ schemas: { type: "object", additionalProperties: true, },
7264
+ unsafeOutputs: { type: "array", items: { type: "object", additionalProperties: true, }, },
7265
+ exitCodes: { type: "object", additionalProperties: { type: "number", }, },
7266
+ agentContractVersion: { const: AGENT_CONTRACT_VERSION, },
7267
+ },
7268
+ };
7269
+ }
7270
+ function commandRegistryJsonSchema() {
7271
+ return {
7272
+ $schema: JSON_SCHEMA_DRAFT,
7273
+ type: "object",
7274
+ additionalProperties: {
7275
+ type: "object",
7276
+ additionalProperties: commandRegistryEntryJsonSchema(),
7277
+ },
7278
+ };
7279
+ }
7280
+ function errorEnvelopeJsonSchema() {
7281
+ return {
7282
+ type: "object",
7283
+ required: ["type", "ok", "error", "code", "category", "exitCode",],
7284
+ additionalProperties: true,
7285
+ properties: {
7286
+ type: { const: "error", },
7287
+ ok: { const: false, },
7288
+ error: { type: "string", },
7289
+ code: { type: "string", },
7290
+ category: { enum: ["usage", "dss", "internal",], },
7291
+ exitCode: { type: "number", },
7292
+ resource: { type: "string", },
7293
+ action: { type: "string", },
7294
+ projectKey: { type: "string", },
7295
+ },
7296
+ };
7297
+ }
7298
+ function warningEventJsonSchema() {
7299
+ return {
7300
+ type: "object",
7301
+ required: ["type", "warnings",],
7302
+ additionalProperties: false,
7303
+ properties: {
7304
+ type: { const: "warning", },
7305
+ warnings: { type: "array", items: { type: "object", additionalProperties: true, }, },
7306
+ },
7307
+ };
7308
+ }
7309
+ function traceEventJsonSchema() {
7310
+ return {
7311
+ type: "object",
7312
+ required: ["type", "phase", "method", "url", "attempt", "maxAttempts",],
7313
+ additionalProperties: false,
7314
+ properties: {
7315
+ type: { const: "trace", },
7316
+ phase: { enum: ["request", "response", "error",], },
7317
+ method: { type: "string", },
7318
+ url: { type: "string", },
7319
+ attempt: { type: "number", },
7320
+ maxAttempts: { type: "number", },
7321
+ status: { type: "number", },
7322
+ elapsedMs: { type: "number", },
7323
+ detail: { type: "string", },
7324
+ },
7325
+ };
7326
+ }
7327
+ function agentContractJsonSchema() {
7328
+ return {
7329
+ $schema: JSON_SCHEMA_DRAFT,
7330
+ $id: AGENT_CONTRACT_SCHEMA_ID,
7331
+ type: "object",
7332
+ required: ["protocol", "agentContractVersion", "cli", "commands", "schemas", "stdio",],
7333
+ additionalProperties: true,
7334
+ properties: {
7335
+ protocol: { const: "dataiku-sdk-agent", },
7336
+ agentContractVersion: { const: AGENT_CONTRACT_VERSION, },
7337
+ cli: { type: "object", additionalProperties: true, },
7338
+ commands: { type: "object", additionalProperties: true, },
7339
+ schemas: { type: "object", additionalProperties: true, },
7340
+ stdio: { type: "object", additionalProperties: true, },
7341
+ },
7342
+ };
7343
+ }
7344
+ function commandActionSummary(registry) {
7345
+ const summary = {};
7346
+ for (const [resource, actions,] of Object.entries(registry)) {
7347
+ summary[resource] = Object.keys(actions).sort();
7348
+ }
7349
+ return summary;
7350
+ }
7351
+ function buildAgentContract() {
7352
+ const registry = buildCommandRegistry();
7353
+ return {
7354
+ protocol: "dataiku-sdk-agent",
7355
+ agentContractVersion: AGENT_CONTRACT_VERSION,
7356
+ cli: cliVersionResult(),
7357
+ commands: {
7358
+ discoveryCommand: "dss commands run",
7359
+ actions: commandActionSummary(registry),
7360
+ },
7361
+ schemas: {
7362
+ agentContract: agentContractJsonSchema(),
7363
+ commandRegistry: commandRegistryJsonSchema(),
7364
+ commandRegistryEntry: commandRegistryEntryJsonSchema(),
7365
+ errorEnvelope: errorEnvelopeJsonSchema(),
7366
+ warningEvent: warningEventJsonSchema(),
7367
+ traceEvent: traceEventJsonSchema(),
7368
+ },
7369
+ stdio: {
7370
+ stdout: {
7371
+ success: "single-json-value",
7372
+ rawEscapeHatches: [
7373
+ "recipe get-payload --raw without --output",
7374
+ "recipe cat --raw without --output",
7375
+ ],
7376
+ },
7377
+ stderr: {
7378
+ format: "jsonl",
7379
+ events: ["warning", "trace", "error",],
7380
+ error: "single-final-error-event-on-nonzero-exit",
7381
+ },
7382
+ },
7383
+ planning: {
7384
+ discoveryCommand: "dss commands run",
7385
+ contractCommand: AGENT_CONTRACT_USAGE,
7386
+ mutatingCommandsAdvertisePlan: true,
7387
+ },
7388
+ compatibility: {
7389
+ fieldsAreAdditiveWithinMajor: true,
7390
+ failFastWhenUnsupported: "Check agentContractVersion before planning commands.",
7391
+ },
7392
+ };
7393
+ }
7052
7394
  function exitCodesOnFailure(entry) {
7053
7395
  return {
7054
7396
  usage: entry.exitCodes.usage,
@@ -7609,6 +7951,31 @@ function commandPlanShape(resource, action, args, flags, entry, projectKey) {
7609
7951
  identifiers: { id, cellId: flags["cell-id"], },
7610
7952
  payload: { retain: num(flags["retain"]), },
7611
7953
  };
7954
+ case "project.permissions-set":
7955
+ return {
7956
+ method: "PUT",
7957
+ endpoint: projectEndpoint("/permissions"),
7958
+ payload: requiredPlanJsonInput(flags, entry.usage),
7959
+ };
7960
+ case "continuous-activity.start":
7961
+ return {
7962
+ method: "POST",
7963
+ endpoint: projectEndpoint(`/continuous-activities/${encodeURIComponent(id)}/start`),
7964
+ identifiers: { recipeId: id, },
7965
+ payload: jsonInput(flags) ?? {},
7966
+ };
7967
+ case "metrics.dataset-compute":
7968
+ return {
7969
+ method: "POST",
7970
+ endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}/actions/computeMetrics?partition=`),
7971
+ identifiers: { dataset: id, },
7972
+ };
7973
+ case "flow-zone.organize":
7974
+ return {
7975
+ method: "POST",
7976
+ endpoint: projectEndpoint("/flow/zones"),
7977
+ payload: jsonInput(flags),
7978
+ };
7612
7979
  default:
7613
7980
  return {
7614
7981
  method: action.startsWith("delete") || action === "abort" ? "DELETE" : "POST",
@@ -7958,6 +8325,7 @@ function buildErrorReport(err) {
7958
8325
  const exitCode = errorExitCode(err);
7959
8326
  if (err instanceof UsageError) {
7960
8327
  return {
8328
+ type: "error",
7961
8329
  ok: false,
7962
8330
  error: err.message,
7963
8331
  code: err.code,
@@ -7970,6 +8338,7 @@ function buildErrorReport(err) {
7970
8338
  }
7971
8339
  if (err instanceof CommandResultFailure) {
7972
8340
  return {
8341
+ type: "error",
7973
8342
  ok: false,
7974
8343
  error: err.message,
7975
8344
  code: "long_running_failure",
@@ -7981,6 +8350,7 @@ function buildErrorReport(err) {
7981
8350
  }
7982
8351
  if (err instanceof DataikuError) {
7983
8352
  return {
8353
+ type: "error",
7984
8354
  ok: false,
7985
8355
  error: err.message,
7986
8356
  code: dataikuErrorCode(err.category),
@@ -8001,6 +8371,7 @@ function buildErrorReport(err) {
8001
8371
  }
8002
8372
  const message = err instanceof Error ? err.message : String(err);
8003
8373
  return {
8374
+ type: "error",
8004
8375
  ok: false,
8005
8376
  error: message,
8006
8377
  code: "internal_error",
@@ -8010,7 +8381,7 @@ function buildErrorReport(err) {
8010
8381
  };
8011
8382
  }
8012
8383
  function writeErrorReport(err) {
8013
- process.stderr.write(`${JSON.stringify(buildErrorReport(err), null, 2)}\n`);
8384
+ process.stderr.write(`${JSON.stringify(buildErrorReport(err))}\n`);
8014
8385
  }
8015
8386
  // ---------------------------------------------------------------------------
8016
8387
  // Main
@@ -8122,6 +8493,16 @@ async function main() {
8122
8493
  });
8123
8494
  return;
8124
8495
  }
8496
+ if (resource === "agent") {
8497
+ const action = positional[1];
8498
+ if (!action)
8499
+ throw missingActionError("agent", ["contract",], AGENT_CONTRACT_USAGE);
8500
+ currentCommandContext.action = action;
8501
+ if (action !== "contract")
8502
+ throw unknownActionError("agent", action, ["contract",]);
8503
+ writeCommandResult(buildAgentContract());
8504
+ return;
8505
+ }
8125
8506
  if (resource === "commands") {
8126
8507
  const action = positional[1];
8127
8508
  if (!action)
@@ -29,6 +29,17 @@ import { VariablesResource } from "./resources/variables.js";
29
29
  import { WebappsResource } from "./resources/webapps.js";
30
30
  import { WikiResource } from "./resources/wiki.js";
31
31
  import { WorkspacesResource } from "./resources/workspaces.js";
32
+ export interface DataikuClientTraceEvent {
33
+ type: "trace";
34
+ phase: "request" | "response" | "error";
35
+ method: string;
36
+ url: string;
37
+ attempt: number;
38
+ maxAttempts: number;
39
+ status?: number;
40
+ elapsedMs?: number;
41
+ detail?: string;
42
+ }
32
43
  export interface DataikuClientConfig {
33
44
  /** DSS base URL (e.g. https://dss.example.com) */
34
45
  url: string;
@@ -40,8 +51,9 @@ export interface DataikuClientConfig {
40
51
  requestTimeoutMs?: number;
41
52
  /** Max retry attempts for idempotent requests (default 4, capped at 10) */
42
53
  retryMaxAttempts?: number;
43
- /** Emit HTTP request/response logs to stderr for CLI debugging. */
54
+ /** Emit HTTP request/response trace events. Defaults to JSONL on stderr when verbose is true. */
44
55
  verbose?: boolean;
56
+ onTrace?: (event: DataikuClientTraceEvent) => void;
45
57
  /** Override TLS certificate verification for HTTPS requests. */
46
58
  tlsRejectUnauthorized?: boolean;
47
59
  /** Extra PEM CA bundle to trust in addition to Bun's default trust store. */
@@ -62,6 +74,7 @@ export declare class DataikuClient {
62
74
  private readonly retryMaxAttempts;
63
75
  private readonly verbose;
64
76
  private readonly tlsOptions;
77
+ private readonly onTrace;
65
78
  private readonly onValidationWarning;
66
79
  private projectsResource?;
67
80
  private datasetsResource?;
@@ -143,7 +156,7 @@ export declare class DataikuClient {
143
156
  stream(path: string): Promise<Response>;
144
157
  private getHeaders;
145
158
  private getAnyHeaders;
146
- private logVerbose;
159
+ private logTrace;
147
160
  /**
148
161
  * Validate raw data against a TypeBox schema, throwing on structural mismatch.
149
162
  * Resources call this instead of bare `as T` casts for validated responses.
@@ -44,6 +44,9 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
44
44
  /* ------------------------------------------------------------------ */
45
45
  /* Helpers */
46
46
  /* ------------------------------------------------------------------ */
47
+ function defaultTrace(event) {
48
+ process.stderr.write(`${JSON.stringify(event)}\n`);
49
+ }
47
50
  function defaultValidationWarning(_method, _errors) {
48
51
  }
49
52
  function sleep(ms) {
@@ -139,6 +142,7 @@ export class DataikuClient {
139
142
  retryMaxAttempts;
140
143
  verbose;
141
144
  tlsOptions;
145
+ onTrace;
142
146
  onValidationWarning;
143
147
  /* Resource namespaces — lazily initialized to break circular imports */
144
148
  projectsResource;
@@ -280,6 +284,7 @@ export class DataikuClient {
280
284
  this.retryMaxAttempts = Math.min(Math.max(1, rawMax), MAX_RETRY_ATTEMPTS_CAP);
281
285
  this.verbose = config.verbose === true;
282
286
  this.tlsOptions = buildFetchTlsOptions(config);
287
+ this.onTrace = config.onTrace ?? defaultTrace;
283
288
  this.onValidationWarning = config.onValidationWarning ?? defaultValidationWarning;
284
289
  }
285
290
  getRequestTimeoutMs() {
@@ -423,9 +428,9 @@ export class DataikuClient {
423
428
  Accept: "*/*",
424
429
  };
425
430
  }
426
- logVerbose(message) {
431
+ logTrace(event) {
427
432
  if (this.verbose)
428
- process.stderr.write(`[dss] ${message}\n`);
433
+ this.onTrace({ type: "trace", ...event, });
429
434
  }
430
435
  /* ---- public: schema-validated parsing ---- */
431
436
  /**
@@ -498,7 +503,7 @@ export class DataikuClient {
498
503
  timedOut = true;
499
504
  controller.abort();
500
505
  }, this.requestTimeoutMs);
501
- this.logVerbose(`${method} ${url}`);
506
+ this.logTrace({ phase: "request", method, url, attempt, maxAttempts, });
502
507
  try {
503
508
  const requestInit = {
504
509
  ...init,
@@ -508,7 +513,15 @@ export class DataikuClient {
508
513
  if (this.tlsOptions)
509
514
  requestInit.tls = this.tlsOptions;
510
515
  const res = await fetch(url, requestInit);
511
- this.logVerbose(`${method} ${url} → ${res.status} (${Date.now() - startedAt}ms)`);
516
+ this.logTrace({
517
+ phase: "response",
518
+ method,
519
+ url,
520
+ attempt,
521
+ maxAttempts,
522
+ status: res.status,
523
+ elapsedMs: Date.now() - startedAt,
524
+ });
512
525
  if (!res.ok) {
513
526
  const text = await res.text();
514
527
  const canRetry = retryEnabled && attempt < maxAttempts && isTransientError(res.status, text);
@@ -537,7 +550,15 @@ export class DataikuClient {
537
550
  : error instanceof Error
538
551
  ? error.message
539
552
  : "Unknown transport error";
540
- this.logVerbose(`${method} ${url} → ERROR (${Date.now() - startedAt}ms) ${detail}`);
553
+ this.logTrace({
554
+ phase: "error",
555
+ method,
556
+ url,
557
+ attempt,
558
+ maxAttempts,
559
+ elapsedMs: Date.now() - startedAt,
560
+ detail,
561
+ });
541
562
  const statusText = timedOut ? "Request Timeout" : "Network Error";
542
563
  throw new DataikuError(0, statusText, detail, buildRetryMetadata(method, retryEnabled, maxAttempts, attempt, delaysMs, timedOut));
543
564
  }
@@ -1,4 +1,4 @@
1
- export { DataikuClient, type DataikuClientConfig, } from "./client.js";
1
+ export { DataikuClient, type DataikuClientConfig, type DataikuClientTraceEvent, } from "./client.js";
2
2
  export { type CredentialValidationOptions, type CredentialValidationResult, validateCredentials, } from "./auth.js";
3
3
  export { type DssCredentials, getConfigDir, getCredentialsPath, loadCredentials, saveCredentials, } from "./config.js";
4
4
  export { DataikuError, type DataikuErrorCategory, type DataikuErrorTaxonomy, type DataikuRetryMetadata, } from "./errors.js";
package/dist/src/skill.js CHANGED
@@ -11,8 +11,8 @@ If the installed \`dss\` binary is unavailable but the repository checkout is th
11
11
  ## Contract
12
12
 
13
13
  - Success writes exactly one JSON result to stdout.
14
- - Failure writes exactly one JSON error envelope to stderr with \`ok:false\`, \`error\`, \`code\`, and \`exitCode\`.
15
- - \`--verbose\` may add HTTP trace lines to stderr.
14
+ - Failure writes exactly one JSONL error event to stderr with \`type:"error"\`, \`ok:false\`, \`error\`, \`code\`, and \`exitCode\`.
15
+ - Non-fatal warnings and \`--verbose\` HTTP traces are JSONL stderr events (\`type:"warning"\` / \`type:"trace"\`), never prose.
16
16
  - No prompts, help screens, tables, banners, or prose output are part of the contract.
17
17
  - Exit codes: 0 success, 1 usage/configuration error, 2 DSS or internal error, 3 transient/retryable DSS error, 4 completed command with failed long-running DSS work.
18
18
  - \`--raw\` is the only stdout escape hatch: recipe payload commands emit raw bytes to stdout unless \`--output PATH\` is also set; with \`--output\`, stdout is the JSON string equal to \`PATH\` and the file receives exact raw bytes.
@@ -21,10 +21,11 @@ If the installed \`dss\` binary is unavailable but the repository checkout is th
21
21
  ## Discover commands
22
22
 
23
23
  \`\`\`bash
24
+ dss agent contract
24
25
  dss commands run
25
26
  \`\`\`
26
27
 
27
- The registry is the canonical schema for resources, actions, flags, positional arguments, side effects, auth requirements, output shape, idempotency, dry-run support, payload schemas, cleanup hints, and exit codes. Use it before choosing command syntax.
28
+ Use \`dss agent contract\` once to check \`agentContractVersion\`, stderr event schemas, non-JSON escape hatches, and compatibility rules. The command registry from \`dss commands run\` is the canonical schema for resources, actions, flags, positional arguments, side effects, auth requirements, output shape, idempotency, dry-run support, structured examples, payload schemas, unsafe outputs, cleanup hints, and exit codes. Use it before choosing command syntax.
28
29
  Credential lookup order is flags first, then \`DATAIKU_*\` environment variables, then saved credentials.
29
30
  Set \`DATAIKU_DISABLE_ENV=1\` when a test must ignore both \`.env\` files and \`DATAIKU_*\` environment variables.
30
31
  When \`.env\` loading is enabled, the CLI reads \`.env\` from the command's current working directory first and then the CLI build/root directory; the invocation directory wins on conflicting keys. Put test-specific \`.env\` files in the directory where you invoke \`dss\`.
@@ -91,10 +92,11 @@ Mutations print a small JSON ack to stdout and exit 0 on success (e.g. \`{"updat
91
92
 
92
93
  ## Error envelope
93
94
 
94
- Parse stderr as JSON when exit code is non-zero:
95
+ Parse each stderr line as JSON; on non-zero exit the final line is an error event:
95
96
 
96
97
  \`\`\`json
97
98
  {
99
+ "type": "error",
98
100
  "ok": false,
99
101
  "error": "Missing API key.",
100
102
  "code": "usage_error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataiku-sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
5
5
  "type": "module",
6
6
  "workspaces": [