dataiku-sdk 3.8.0 → 3.9.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 +4 -2
- package/dist/build-metadata.json +1 -1
- package/dist/src/cli/command-syntax.json +36 -0
- package/dist/src/cli/commands/dataset.js +2 -2
- package/dist/src/cli/commands/job.js +34 -6
- package/dist/src/cli/commands/project.js +4 -3
- package/dist/src/cli/contract.js +2 -5
- package/dist/src/cli/output-schemas.js +27 -3
- package/dist/src/cli/project-map-output.d.ts +24 -0
- package/dist/src/cli/project-map-output.js +21 -0
- package/dist/src/cli/read-shapes.d.ts +14 -0
- package/dist/src/cli/read-shapes.js +114 -0
- package/dist/src/cli/result-shapes.d.ts +6 -0
- package/dist/src/cli/result-shapes.js +235 -0
- package/dist/src/cli/version.d.ts +2 -2
- package/dist/src/cli/version.js +2 -2
- package/dist/src/cli.js +11 -16
- package/dist/src/generated/action-output-schemas.json +224 -35
- package/dist/src/resources/jobs.d.ts +2 -1
- package/dist/src/resources/jobs.js +43 -4
- package/dist/src/utils/secret-sanitize.d.ts +11 -2
- package/dist/src/utils/secret-sanitize.js +23 -6
- package/package.json +4 -4
- package/plugin.json +1 -1
- package/skills/dataiku-dss/references/discovery.md +2 -2
- package/skills/dataiku-dss/references/troubleshooting.md +2 -2
|
@@ -80,7 +80,8 @@ export declare class JobsResource extends BaseResource {
|
|
|
80
80
|
/**
|
|
81
81
|
* Retrieve job log text.
|
|
82
82
|
* Returns the last `maxLogLines` lines (default 500) from the tail.
|
|
83
|
-
* Use `0` or `-1` to return the log without line truncation.
|
|
83
|
+
* Use `0` or `-1` to return the log without line truncation. Credential-like
|
|
84
|
+
* JSON pairs (`"jobTicketSecret":"..."`) are redacted.
|
|
84
85
|
*
|
|
85
86
|
* The download is byte-bounded and deadline-covered: at most `maxLogBytes`
|
|
86
87
|
* (default 10 MiB) of the *most recent* log output is retained, so a
|
|
@@ -2,6 +2,7 @@ import { ClientValidationError, DataikuError, } from "../errors.js";
|
|
|
2
2
|
import { JobSummaryArraySchema, } from "../schemas.js";
|
|
3
3
|
import { computeNextPollDelayMs, DEFAULT_POLL_INTERVAL_MS, DEFAULT_TIMEOUT_MS, isRequestDeadlineError, } from "../utils/polling.js";
|
|
4
4
|
import { asRecord, } from "../utils/records.js";
|
|
5
|
+
import { redactCredentialPairs, } from "../utils/secret-sanitize.js";
|
|
5
6
|
import { BaseResource, } from "./base.js";
|
|
6
7
|
const DEFAULT_MAX_LOG_LINES = 500;
|
|
7
8
|
/**
|
|
@@ -104,6 +105,41 @@ function dssRecordMessage(line) {
|
|
|
104
105
|
function isErrorContent(line) {
|
|
105
106
|
return ERROR_KEYWORDS.test(line) || ERROR_CLASS_NAME.test(line) || PY_FILE_FRAME.test(line);
|
|
106
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Remove embedded JSON object payloads (`{"...": ...}`) from a DSS record
|
|
110
|
+
* line. Structured payloads such as `SQL status {"statusWarnLevel":"ERROR",...}`
|
|
111
|
+
* carry level/class words as data, not as a failure of the record itself.
|
|
112
|
+
* An unbalanced payload (truncated line) is dropped to the end of the line.
|
|
113
|
+
*/
|
|
114
|
+
function stripJsonPayloads(line) {
|
|
115
|
+
let out = "";
|
|
116
|
+
let from = 0;
|
|
117
|
+
for (let start = line.indexOf('{"'); start !== -1; start = line.indexOf('{"', from)) {
|
|
118
|
+
out += line.slice(from, start);
|
|
119
|
+
let depth = 0;
|
|
120
|
+
let inString = false;
|
|
121
|
+
let end = line.length;
|
|
122
|
+
for (let i = start; i < line.length; i++) {
|
|
123
|
+
const ch = line[i];
|
|
124
|
+
if (inString) {
|
|
125
|
+
if (ch === "\\")
|
|
126
|
+
i++;
|
|
127
|
+
else if (ch === '"')
|
|
128
|
+
inString = false;
|
|
129
|
+
}
|
|
130
|
+
else if (ch === '"')
|
|
131
|
+
inString = true;
|
|
132
|
+
else if (ch === "{")
|
|
133
|
+
depth++;
|
|
134
|
+
else if (ch === "}" && --depth === 0) {
|
|
135
|
+
end = i + 1;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
from = end;
|
|
140
|
+
}
|
|
141
|
+
return out + line.slice(from);
|
|
142
|
+
}
|
|
107
143
|
function lineMatchesLogFilter(line, filter) {
|
|
108
144
|
const normalized = line.toLowerCase();
|
|
109
145
|
switch (filter) {
|
|
@@ -160,7 +196,7 @@ function filterJobLogToErrors(lines) {
|
|
|
160
196
|
pyTraceback = false;
|
|
161
197
|
}
|
|
162
198
|
if (isHeader) {
|
|
163
|
-
recordMatches = isErrorContent(line) || PY_FILE_FRAME.test(message);
|
|
199
|
+
recordMatches = isErrorContent(stripJsonPayloads(line)) || PY_FILE_FRAME.test(message);
|
|
164
200
|
if (recordMatches && (PY_TRACEBACK_HEAD.test(message) || PY_FILE_FRAME.test(message))) {
|
|
165
201
|
pyTraceback = true;
|
|
166
202
|
}
|
|
@@ -176,7 +212,7 @@ function filterJobLogToErrors(lines) {
|
|
|
176
212
|
if (recordMatches
|
|
177
213
|
&& !JVM_STACK_FRAME.test(line)
|
|
178
214
|
&& !JVM_MORE_FRAMES.test(line)
|
|
179
|
-
&& isErrorContent(line))
|
|
215
|
+
&& isErrorContent(stripJsonPayloads(line)))
|
|
180
216
|
kept.push(line);
|
|
181
217
|
}
|
|
182
218
|
return kept.join("\n");
|
|
@@ -283,7 +319,8 @@ export class JobsResource extends BaseResource {
|
|
|
283
319
|
/**
|
|
284
320
|
* Retrieve job log text.
|
|
285
321
|
* Returns the last `maxLogLines` lines (default 500) from the tail.
|
|
286
|
-
* Use `0` or `-1` to return the log without line truncation.
|
|
322
|
+
* Use `0` or `-1` to return the log without line truncation. Credential-like
|
|
323
|
+
* JSON pairs (`"jobTicketSecret":"..."`) are redacted.
|
|
287
324
|
*
|
|
288
325
|
* The download is byte-bounded and deadline-covered: at most `maxLogBytes`
|
|
289
326
|
* (default 10 MiB) of the *most recent* log output is retained, so a
|
|
@@ -298,7 +335,9 @@ export class JobsResource extends BaseResource {
|
|
|
298
335
|
const path = `/public/api/projects/${this.enc(opts?.projectKey)}/jobs/${jobEnc}/log/${query}`;
|
|
299
336
|
const maxLogBytes = Math.max(1, opts?.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES);
|
|
300
337
|
const { text, } = await this.client.getTextTailLimited(path, maxLogBytes);
|
|
301
|
-
|
|
338
|
+
// Single download choke point (logFromUrl and wait/summary go through here):
|
|
339
|
+
// DSS logs embed the job ticket secret in start_session JSON.
|
|
340
|
+
return limitJobLog(filterJobLog(redactCredentialPairs(text), opts?.logFilter), opts?.maxLogLines);
|
|
302
341
|
}
|
|
303
342
|
async logFromUrl(logUrl, opts) {
|
|
304
343
|
const parsed = new URL(logUrl, "http://dss.local");
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* - optionally redacts userinfo embedded in HTTP(S) URL authorities
|
|
8
8
|
* (`https://user:pass@host/...` -> `https://[redacted]@host/...`).
|
|
9
9
|
*
|
|
10
|
-
* The SDK resource layer forwards values verbatim to DSS over TLS;
|
|
11
|
-
* is a CLI presentation concern and must never leak secret
|
|
10
|
+
* The SDK resource layer forwards values verbatim to DSS over TLS; sanitizing
|
|
11
|
+
* structured output is a CLI presentation concern and must never leak secret
|
|
12
|
+
* material instead. DSS-produced read text that consumers never need secrets
|
|
13
|
+
* from (job logs) is scrubbed at the SDK download via `redactCredentialPairs`.
|
|
12
14
|
*/
|
|
13
15
|
/** Marker replacing every redacted value or secret occurrence. */
|
|
14
16
|
export declare const SECRET_REDACTED = "[redacted]";
|
|
@@ -46,6 +48,13 @@ export declare function redactUrlUserinfo(text: string): string;
|
|
|
46
48
|
* recurse, arrays recurse. Non-string scalars pass through unchanged.
|
|
47
49
|
*/
|
|
48
50
|
export declare function sanitizeSecrets<T>(value: T, options: SecretSanitizeOptions): T;
|
|
51
|
+
/**
|
|
52
|
+
* Redact the string value of every JSON-style `"key":"value"` pair in free
|
|
53
|
+
* text whose key is credential-like (e.g. the `jobTicketSecret` DSS writes
|
|
54
|
+
* into job logs). Bare `key` fields are not redacted here: in log text they
|
|
55
|
+
* name ordinary identifiers far more often than secrets.
|
|
56
|
+
*/
|
|
57
|
+
export declare function redactCredentialPairs(text: string): string;
|
|
49
58
|
/**
|
|
50
59
|
* Every string value under a credential-like key (any depth), plus bare
|
|
51
60
|
* `key` fields such as `authRealm.key`. Collected from a command's own JSON
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* - optionally redacts userinfo embedded in HTTP(S) URL authorities
|
|
8
8
|
* (`https://user:pass@host/...` -> `https://[redacted]@host/...`).
|
|
9
9
|
*
|
|
10
|
-
* The SDK resource layer forwards values verbatim to DSS over TLS;
|
|
11
|
-
* is a CLI presentation concern and must never leak secret
|
|
10
|
+
* The SDK resource layer forwards values verbatim to DSS over TLS; sanitizing
|
|
11
|
+
* structured output is a CLI presentation concern and must never leak secret
|
|
12
|
+
* material instead. DSS-produced read text that consumers never need secrets
|
|
13
|
+
* from (job logs) is scrubbed at the SDK download via `redactCredentialPairs`.
|
|
12
14
|
*/
|
|
13
15
|
/** Marker replacing every redacted value or secret occurrence. */
|
|
14
16
|
export const SECRET_REDACTED = "[redacted]";
|
|
@@ -92,6 +94,24 @@ const CREDENTIAL_KEY_FRAGMENTS = [
|
|
|
92
94
|
];
|
|
93
95
|
/** Keys naming how credentials work (`credentialsMode`, `passwordType`), not secrets. */
|
|
94
96
|
const CREDENTIAL_SETTING_SUFFIXES = ["mode", "type", "policy",];
|
|
97
|
+
/** Credential-like key (normalized fragment match, minus credential settings). */
|
|
98
|
+
function isCredentialKey(key) {
|
|
99
|
+
const normalized = normalizeSecretKey(key);
|
|
100
|
+
if (CREDENTIAL_SETTING_SUFFIXES.some((suffix) => normalized.endsWith(suffix)))
|
|
101
|
+
return false;
|
|
102
|
+
return CREDENTIAL_KEY_FRAGMENTS.some((fragment) => normalized.includes(fragment));
|
|
103
|
+
}
|
|
104
|
+
/** JSON-style `"key": "value"` pair with escape-aware string bodies. */
|
|
105
|
+
const JSON_STRING_PAIR = /"((?:[^"\\\n]|\\.)*)"(\s*:\s*)"((?:[^"\\\n]|\\.)*)"/g;
|
|
106
|
+
/**
|
|
107
|
+
* Redact the string value of every JSON-style `"key":"value"` pair in free
|
|
108
|
+
* text whose key is credential-like (e.g. the `jobTicketSecret` DSS writes
|
|
109
|
+
* into job logs). Bare `key` fields are not redacted here: in log text they
|
|
110
|
+
* name ordinary identifiers far more often than secrets.
|
|
111
|
+
*/
|
|
112
|
+
export function redactCredentialPairs(text) {
|
|
113
|
+
return text.replace(JSON_STRING_PAIR, (pair, key, separator, value) => value.length > 0 && isCredentialKey(key) ? `"${key}"${separator}"${SECRET_REDACTED}"` : pair);
|
|
114
|
+
}
|
|
95
115
|
/**
|
|
96
116
|
* Every string value under a credential-like key (any depth), plus bare
|
|
97
117
|
* `key` fields such as `authRealm.key`. Collected from a command's own JSON
|
|
@@ -114,10 +134,7 @@ export function credentialValues(value) {
|
|
|
114
134
|
if (node === null || typeof node !== "object")
|
|
115
135
|
return;
|
|
116
136
|
for (const [key, item,] of Object.entries(node)) {
|
|
117
|
-
|
|
118
|
-
const setting = CREDENTIAL_SETTING_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
|
|
119
|
-
visit(item, sensitive || (!setting && (normalized === "key"
|
|
120
|
-
|| CREDENTIAL_KEY_FRAGMENTS.some((fragment) => normalized.includes(fragment)))));
|
|
137
|
+
visit(item, sensitive || isCredentialKey(key) || normalizeSecretKey(key) === "key");
|
|
121
138
|
}
|
|
122
139
|
};
|
|
123
140
|
visit(value, false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dataiku-sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"node-stream-zip": "^1.16.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@oxlint/plugins": "1.
|
|
51
|
+
"@oxlint/plugins": "1.87.0",
|
|
52
52
|
"@types/bun": "^1.4.2",
|
|
53
53
|
"cross-env": "^10.1.0",
|
|
54
|
-
"dprint": "^0.
|
|
54
|
+
"dprint": "^0.61.1",
|
|
55
55
|
"js-tiktoken": "1.0.21",
|
|
56
|
-
"oxlint": "1.
|
|
56
|
+
"oxlint": "1.87.0",
|
|
57
57
|
"typescript": "^7.0.2"
|
|
58
58
|
},
|
|
59
59
|
"bundleDependencies": [
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "dataiku-sdk",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.9.0",
|
|
5
5
|
"description": "Dataiku DSS SDK and agent-oriented CLI for programmatic access to DSS REST APIs.",
|
|
6
6
|
"homepage": "https://github.com/clssck/dataiku-sdk",
|
|
7
7
|
"repository": "https://github.com/clssck/dataiku-sdk",
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
## Contract
|
|
4
4
|
|
|
5
5
|
- Stdout: exactly one compact JSON value; void success: `{ok:true}`. No prompts, help screens, tables, banners, or prose.
|
|
6
|
-
- Dispatch/runtime failure: stdout error object (`type:"error"`, `
|
|
6
|
+
- Dispatch/runtime failure: stdout error object (`type:"error"`, `error`, `code`, `category`, `exitCode`; `projectKey` when resolved), nonzero exit. `doctor`/`batch`/`cleanup` failures instead return direct result objects; inspect exit code and fields.
|
|
7
7
|
- Stderr: JSONL diagnostics only; `type:"warning"` / `type:"trace"` for warnings / `--verbose` HTTP traces, flushed before success or failure output.
|
|
8
8
|
- Exits: 0 success, 1 usage/configuration, 2 DSS/internal/permission-or-environment, 3 transient/retryable DSS error, 4 failed long-running result/assertion.
|
|
9
9
|
- Recipe payload stdout: JSON string. With `--output PATH`: exact bytes to file, JSON string equal to `PATH` on stdout.
|
|
10
10
|
- General `--fields a,b,c`: object projection, element-wise for object arrays. Dotted paths traverse nested objects; missing fields become `null`; strings/scalars pass through.
|
|
11
|
-
- `*.list
|
|
11
|
+
- Compact by default: `*.list` (ids, kind, next-step fields such as recipe `inputs`/`outputs`), `job get` (no log tail), `dataset get`/`info`, `project get`; `--full`/`--fields` use DSS objects. Lists: `--contains TEXT` filters ids; `--limit N` caps (`list_truncated` warning).
|
|
12
12
|
|
|
13
13
|
## Discovery
|
|
14
14
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Platform, builds, data
|
|
4
4
|
|
|
5
5
|
- Code/SQL: use `--file`/`--sql-file`; shells, especially PowerShell, mangle quotes, `$`, and multiline text. Non-UTF-8 consoles (e.g. Windows cp1252): write non-ASCII results to UTF-8 files or `--output PATH`, not the console.
|
|
6
|
-
- Build errors: `dss job log <id> --errors-only` selects errors/tracebacks.
|
|
6
|
+
- Build errors: `dss job log <id> --errors-only` selects errors/tracebacks. Plain `job log` prints the last 100 lines (`log_truncated`); for full logs, use `--output PATH` without `--errors-only`. Logs may arrive as one JVM-noisy line; `Error in Python process: At line <N>` identifies the payload source line.
|
|
7
7
|
- Schemas: code recipes set output schemas at run time. Visual/SQL query recipes: `recipe create` fills created or empty outputs (`outputSchemaUpdated`); `recipe set-payload` updates empty or DSS-derived ones, warns `recipe_output_schema_outdated` for hand-edited ones. Apply with `dss recipe update-schema NAME` (one recipe), `dss flow propagate-schema DATASET --wait` (every downstream recipe), or build `--auto-update-schema`. `built_dataset_has_no_columns`: the DONE build wrote nothing. `nothing_to_build` (exit 4): no recipe writes the managed target. Manual columns: `dss dataset refresh-schema NAME --data-file columns.json`. `dss dataset validate-build` catches file-backed misconfiguration.
|
|
8
8
|
- `Inline` (editable) datasets: the public API cannot write rows, so `dataset create` rejects them; use `UploadedFiles` + `dataset upload-file`, or a python recipe output. `code run` is a scenario step; some DSS versions refuse dataset writes there.
|
|
9
9
|
- `dss dataset download`: default 100k rows; result `{ path, rows, truncated, limit }`. If truncated, raise `--limit`. Specify `--output` or read the `dataset_download_default_location` path warning. CSV is spreadsheet-safe; `--raw-data` retains formula prefixes. For large tables, aggregate in SQL or use a recipe.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
Dispatch/runtime failure: one compact stdout error:
|
|
24
24
|
```json
|
|
25
|
-
{"type":"error","
|
|
25
|
+
{"type":"error","error":"Missing API key.","code":"missing_required_flag","category":"usage","exitCode":1}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
`doctor`/`batch`/`cleanup` command failures return direct stdout result objects; check nonzero exit before interpreting. Recover from structured `code`, `category`, `exitCode`, `retryable`, `status`, `details`, never message scraping.
|