dataiku-sdk 3.1.0 → 3.2.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.
Files changed (58) hide show
  1. package/README.md +124 -12
  2. package/dist/build-metadata.json +1 -1
  3. package/dist/src/auth.js +1 -1
  4. package/dist/src/cli/coerce.js +11 -2
  5. package/dist/src/cli/commands/analysis.js +4 -3
  6. package/dist/src/cli/commands/api-service.js +5 -3
  7. package/dist/src/cli/commands/app.js +4 -3
  8. package/dist/src/cli/commands/bundle.js +2 -1
  9. package/dist/src/cli/commands/code-env.js +10 -9
  10. package/dist/src/cli/commands/dashboard.js +7 -6
  11. package/dist/src/cli/commands/data-quality.js +9 -8
  12. package/dist/src/cli/commands/dataset.js +9 -8
  13. package/dist/src/cli/commands/doctor.js +4 -2
  14. package/dist/src/cli/commands/flow-zone.js +9 -8
  15. package/dist/src/cli/commands/folder.js +9 -8
  16. package/dist/src/cli/commands/future.js +2 -1
  17. package/dist/src/cli/commands/insight.js +8 -7
  18. package/dist/src/cli/commands/job.js +4 -3
  19. package/dist/src/cli/commands/meaning.js +5 -4
  20. package/dist/src/cli/commands/ml-task.js +3 -2
  21. package/dist/src/cli/commands/model-evaluation-store.js +4 -3
  22. package/dist/src/cli/commands/notebook.js +11 -10
  23. package/dist/src/cli/commands/project-library.js +7 -6
  24. package/dist/src/cli/commands/project.js +3 -2
  25. package/dist/src/cli/commands/recipe.js +14 -13
  26. package/dist/src/cli/commands/saved-model.js +4 -3
  27. package/dist/src/cli/commands/scenario.js +9 -8
  28. package/dist/src/cli/commands/sql.js +3 -2
  29. package/dist/src/cli/commands/statistics.js +7 -6
  30. package/dist/src/cli/commands/variable.js +2 -1
  31. package/dist/src/cli/commands/webapp.js +2 -1
  32. package/dist/src/cli/commands/wiki.js +7 -6
  33. package/dist/src/cli/contract.d.ts +1 -1
  34. package/dist/src/cli/contract.js +100 -82
  35. package/dist/src/cli/flags.d.ts +4 -0
  36. package/dist/src/cli/flags.js +20 -1
  37. package/dist/src/cli.js +92 -68
  38. package/dist/src/client.d.ts +3 -1
  39. package/dist/src/client.js +78 -21
  40. package/dist/src/errors.js +2 -0
  41. package/dist/src/resources/folders.js +2 -1
  42. package/dist/src/resources/projects.d.ts +0 -2
  43. package/dist/src/resources/projects.js +3 -17
  44. package/dist/src/resources/sql.js +1 -1
  45. package/dist/src/skill.d.ts +13 -4
  46. package/dist/src/skill.js +75 -24
  47. package/dist/src/utils/response-file.d.ts +2 -0
  48. package/dist/src/utils/response-file.js +34 -0
  49. package/package.json +6 -4
  50. package/plugin.json +1 -1
  51. package/skills/dataiku-dss/SKILL.md +44 -224
  52. package/skills/dataiku-dss/references/app-releases.md +27 -0
  53. package/skills/dataiku-dss/references/authentication.md +39 -0
  54. package/skills/dataiku-dss/references/coding.md +8 -0
  55. package/skills/dataiku-dss/references/discovery.md +30 -0
  56. package/skills/dataiku-dss/references/flow-maps.md +12 -0
  57. package/skills/dataiku-dss/references/mutations.md +33 -0
  58. package/skills/dataiku-dss/references/troubleshooting.md +29 -0
@@ -76,15 +76,6 @@ function isTransientError(status, body) {
76
76
  function shouldRetryMethod(method) {
77
77
  return method.toUpperCase() === "GET";
78
78
  }
79
- function concatBytes(chunks, total) {
80
- const out = new Uint8Array(total);
81
- let offset = 0;
82
- for (const chunk of chunks) {
83
- out.set(chunk, offset);
84
- offset += chunk.byteLength;
85
- }
86
- return out;
87
- }
88
79
  /**
89
80
  * Drop a trailing incomplete UTF-8 multibyte sequence so the bytes end on a
90
81
  * character boundary. Guarantees the decoded string re-encodes to no more bytes
@@ -133,7 +124,7 @@ function buildBodyReadTimeoutError(timeoutMs) {
133
124
  * Read the next chunk of a response body under a hard deadline.
134
125
  * When `remainingMs` elapses, the stream is cancelled and the caller's
135
126
  * promise rejects with a DataikuError, so a stalled body cannot hang the
136
- * buffered consumers after headers already arrived.
127
+ * consumers after headers already arrived.
137
128
  */
138
129
  async function readChunkWithDeadline(reader, remainingMs, timeoutMs) {
139
130
  const { promise, resolve, reject, } = Promise.withResolvers();
@@ -506,11 +497,12 @@ export class DataikuClient {
506
497
  return text;
507
498
  }
508
499
  async postStream(path, body) {
509
- return this.fetchWithRetry(`${this.baseUrl}${path}`, {
500
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
510
501
  method: "POST",
511
502
  headers: { ...this.getAnyHeaders(), "Content-Type": "application/json", },
512
503
  body: body !== undefined ? JSON.stringify(body) : undefined,
513
504
  });
505
+ return this.withBodyDeadline(res);
514
506
  }
515
507
  async put(path, body) {
516
508
  const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
@@ -551,10 +543,51 @@ export class DataikuClient {
551
543
  return this.parseJsonResponse(response);
552
544
  }
553
545
  async stream(path) {
554
- return this.fetchWithRetry(`${this.baseUrl}${path}`, {
546
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
555
547
  method: "GET",
556
548
  headers: this.getAnyHeaders(),
557
549
  });
550
+ return this.withBodyDeadline(res);
551
+ }
552
+ /** Bound each pending read, not total transfer time; preserve large downloads and backpressure. */
553
+ withBodyDeadline(res) {
554
+ if (!res.body)
555
+ return res;
556
+ const reader = res.body.getReader();
557
+ const timeoutMs = this.requestTimeoutMs;
558
+ const body = new ReadableStream({
559
+ async pull(controller) {
560
+ try {
561
+ const { done, value, } = await readChunkWithDeadline(reader, timeoutMs, timeoutMs);
562
+ if (done) {
563
+ controller.close();
564
+ reader.releaseLock();
565
+ }
566
+ else {
567
+ controller.enqueue(value);
568
+ }
569
+ }
570
+ catch (error) {
571
+ controller.error(error);
572
+ void reader.cancel(error).catch(() => { });
573
+ }
574
+ },
575
+ cancel(reason) {
576
+ void reader.cancel(reason).catch(() => { });
577
+ },
578
+ });
579
+ const response = new Response(body, {
580
+ status: res.status,
581
+ statusText: res.statusText,
582
+ headers: res.headers,
583
+ });
584
+ // ResponseInit has no fields for these fetch response properties.
585
+ Object.defineProperties(response, {
586
+ url: { value: res.url, },
587
+ redirected: { value: res.redirected, },
588
+ type: { value: res.type, },
589
+ });
590
+ return response;
558
591
  }
559
592
  /* ---- private: headers ---- */
560
593
  getHeaders() {
@@ -658,7 +691,34 @@ export class DataikuClient {
658
691
  if (!res.body)
659
692
  return { text: "", truncated: false, };
660
693
  const reader = res.body.getReader();
661
- const chunks = [];
694
+ const decoder = new TextDecoder();
695
+ const parts = [];
696
+ // Keep the final UTF-8 sequence undecoded until truncation is known.
697
+ // Four bytes also cover malformed sequences handled by trimToUtf8Boundary.
698
+ const tail = new Uint8Array(4);
699
+ let tailLength = 0;
700
+ const append = (chunk) => {
701
+ if (chunk.byteLength >= tail.length) {
702
+ if (tailLength > 0) {
703
+ parts.push(decoder.decode(tail.subarray(0, tailLength), { stream: true, }));
704
+ }
705
+ const end = chunk.byteLength - tail.length;
706
+ if (end > 0)
707
+ parts.push(decoder.decode(chunk.subarray(0, end), { stream: true, }));
708
+ tail.set(chunk.subarray(end));
709
+ tailLength = tail.length;
710
+ }
711
+ else {
712
+ const excess = Math.max(0, tailLength + chunk.byteLength - tail.length);
713
+ if (excess > 0) {
714
+ parts.push(decoder.decode(tail.subarray(0, excess), { stream: true, }));
715
+ tail.copyWithin(0, excess, tailLength);
716
+ tailLength -= excess;
717
+ }
718
+ tail.set(chunk, tailLength);
719
+ tailLength += chunk.byteLength;
720
+ }
721
+ };
662
722
  let bytesRead = 0;
663
723
  let truncated = false;
664
724
  const startedAt = Date.now();
@@ -672,12 +732,12 @@ export class DataikuClient {
672
732
  break;
673
733
  const room = limit - bytesRead;
674
734
  if (value.byteLength > room) {
675
- chunks.push(value.slice(0, room));
735
+ append(value.subarray(0, room));
676
736
  bytesRead += room;
677
737
  truncated = true;
678
738
  break;
679
739
  }
680
- chunks.push(value);
740
+ append(value);
681
741
  bytesRead += value.byteLength;
682
742
  }
683
743
  if (!truncated && bytesRead >= limit) {
@@ -693,12 +753,9 @@ export class DataikuClient {
693
753
  finally {
694
754
  void reader.cancel().catch(() => { });
695
755
  }
696
- const collected = concatBytes(chunks, bytesRead);
697
- // On truncation, drop any trailing partial UTF-8 character so the decoded
698
- // text never re-encodes beyond the byte cap (a stream-flush would otherwise
699
- // emit a 3-byte replacement char for a split multibyte sequence).
700
- const usable = truncated ? trimToUtf8Boundary(collected) : collected;
701
- return { text: new TextDecoder().decode(usable), truncated, };
756
+ const remaining = tail.subarray(0, tailLength);
757
+ parts.push(decoder.decode(truncated ? trimToUtf8Boundary(remaining) : remaining));
758
+ return { text: parts.join(""), truncated, };
702
759
  }
703
760
  /**
704
761
  * Read a 2xx response body keeping only the last `maxBytes` bytes.
@@ -240,6 +240,8 @@ export function classifyDataikuError(status, body) {
240
240
  && (lowerBody.includes("column_not_found")
241
241
  || lowerBody.includes("table_not_found")
242
242
  || lowerBody.includes("no_such_table")
243
+ || lowerBody.includes("no such column")
244
+ || lowerBody.includes("no such table")
243
245
  || lowerBody.includes("column does not exist"));
244
246
  if (isSqlEngineValidation) {
245
247
  return {
@@ -1,6 +1,7 @@
1
1
  import * as nodePath from "node:path";
2
2
  import { FolderDetailsSchema, FolderItemArraySchema, FolderSummaryArraySchema, } from "../schemas.js";
3
3
  import { deepMerge, } from "../utils/deep-merge.js";
4
+ import { writeResponseToFile, } from "../utils/response-file.js";
4
5
  import { sanitizeFileName, } from "../utils/sanitize.js";
5
6
  import { BaseResource, } from "./base.js";
6
7
  import { resolveAdminManagedStorageConnection, } from "./connections.js";
@@ -73,7 +74,7 @@ export class FoldersResource extends BaseResource {
73
74
  throw new Error("folders.download response did not include a body");
74
75
  const dest = opts?.localPath
75
76
  ?? nodePath.resolve(process.cwd(), inferDownloadFileName(normalizedPath));
76
- await Bun.write(dest, new Response(res.body), { createPath: false, });
77
+ await writeResponseToFile(dest, res);
77
78
  return dest;
78
79
  }
79
80
  upload(folderId, path, localPath, projectKey) {
@@ -87,8 +87,6 @@ export interface ProjectImportResult extends ProjectImportProcessResult {
87
87
  projectIncarnationHash?: string;
88
88
  }
89
89
  export declare class ProjectsResource extends BaseResource {
90
- private httpInternals;
91
- private postStream;
92
90
  /** Create a new project. */
93
91
  createProject(projectKey: string, name: string, ownerLogin: string, settings?: ProjectLifecycleSettings | null): Promise<Record<string, unknown>>;
94
92
  /** Delete a project using the lifecycle endpoint. */
@@ -1,4 +1,5 @@
1
1
  import { ClientValidationError, DataikuError, } from "../errors.js";
2
+ import { writeResponseToFile, } from "../utils/response-file.js";
2
3
  import { ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, } from "../schemas.js";
3
4
  import { analyzeFlowMap, flowTopologyFingerprint, renderFlowMap, } from "../utils/flow-analysis.js";
4
5
  import { normalizeFlowGraph, } from "../utils/flow-map.js";
@@ -104,21 +105,6 @@ const DEFAULT_MAX_EDGES = 600;
104
105
  const DEFAULT_METADATA_TIMEOUT_MS = 1_500;
105
106
  const PROJECT_IMPORT_AMBIGUOUS_REMEDIATION = "Import processing left DSS state indeterminate. Inspect the archive with `dss project inspect-archive <file>` and the live projects with `dss project list` before retrying the import.";
106
107
  export class ProjectsResource extends BaseResource {
107
- httpInternals() {
108
- return this.client;
109
- }
110
- async postStream(path, body) {
111
- const http = this.httpInternals();
112
- return http.fetchWithRetry(`${http.baseUrl}${path}`, {
113
- method: "POST",
114
- headers: {
115
- Authorization: `Bearer ${http.apiKey}`,
116
- Accept: "*/*",
117
- "Content-Type": "application/json",
118
- },
119
- body: JSON.stringify(body),
120
- });
121
- }
122
108
  /** Create a new project. */
123
109
  async createProject(projectKey, name, ownerLogin, settings) {
124
110
  return this.client.post("/public/api/projects/", {
@@ -155,10 +141,10 @@ export class ProjectsResource extends BaseResource {
155
141
  }
156
142
  /** Export a project archive to a local file. */
157
143
  async exportArchive(projectKey, filePath, options) {
158
- const res = await this.postStream(`/public/api/projects/${encodeURIComponent(projectKey)}/export`, options ?? {});
144
+ const res = await this.client.postStream(`/public/api/projects/${encodeURIComponent(projectKey)}/export`, options ?? {});
159
145
  if (!res.body)
160
146
  throw new Error("projects.exportArchive response did not include a body");
161
- await Bun.write(filePath, new Response(res.body), { createPath: false, });
147
+ await writeResponseToFile(filePath, res);
162
148
  }
163
149
  /** Upload a project archive and return its temporary import handle. */
164
150
  async prepareProjectImport(filePath) {
@@ -101,7 +101,7 @@ function withSqlErrorContext(error) {
101
101
  catch {
102
102
  body = `SQL query failed: ${detail}\n${error.body}`;
103
103
  }
104
- throw new DataikuError(error.status, error.statusText, body, error.retry);
104
+ throw new DataikuError(error.status, error.statusText, body, error.retry, error.requestId, { target: error.trustedTarget, elapsedMs: error.trustedElapsedMs, });
105
105
  }
106
106
  }
107
107
  throw error;
@@ -29,18 +29,27 @@ export declare function detectAgents(): DetectedAgent[];
29
29
  */
30
30
  export declare function findWorkspaceRoot(startDir: string): string;
31
31
  export type SkillStatus = "missing" | "stale" | "current";
32
+ export interface InstalledSkillFile {
33
+ relativePath: string;
34
+ path: string;
35
+ status: SkillStatus;
36
+ changed: boolean;
37
+ expectedSha256: string;
38
+ actualSha256?: string;
39
+ }
32
40
  export interface InstallResult {
33
41
  agent: string;
34
42
  path: string;
35
43
  via: DetectedAgent["via"];
36
- /** Deterministic state of the destination file relative to the canonical skill content. */
44
+ /** Missing entrypoint, incomplete/different bundle, or entirely current bundle. */
37
45
  status: SkillStatus;
38
- /** Whether an install run would (or did) write the destination file. */
46
+ /** Whether any bundled file would (or did) change. Unmanaged files are preserved. */
39
47
  changed: boolean;
40
- /** SHA-256 hex of the canonical skill bytes this installation writes. */
48
+ /** SHA-256 of the canonical SKILL.md entrypoint, not the whole bundle. */
41
49
  expectedSha256: string;
42
- /** SHA-256 hex of the destination file when it exists; absent when status is "missing". */
50
+ /** Hash of the existing entrypoint; absent when that file is missing. */
43
51
  actualSha256?: string;
52
+ files: InstalledSkillFile[];
44
53
  }
45
54
  export declare function planSkillInstalls(agents: DetectedAgent[], opts: {
46
55
  global: boolean;
package/dist/src/skill.js CHANGED
@@ -2,17 +2,21 @@ import { createHash, } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
+ import { fileURLToPath, } from "node:url";
5
6
  const SKILL_URLS = [
6
7
  new URL("../skills/dataiku-dss/SKILL.md", import.meta.url),
7
8
  new URL("../../skills/dataiku-dss/SKILL.md", import.meta.url),
8
9
  ];
9
- function skillContent() {
10
+ function skillDirectory() {
10
11
  for (const skillUrl of SKILL_URLS) {
11
12
  if (fs.existsSync(skillUrl))
12
- return fs.readFileSync(skillUrl, "utf-8");
13
+ return new URL("./", skillUrl);
13
14
  }
14
15
  throw new Error(`Bundled Dataiku skill not found. Checked: ${SKILL_URLS.map((url) => url.pathname).join(", ")}`);
15
16
  }
17
+ function skillContent() {
18
+ return fs.readFileSync(new URL("SKILL.md", skillDirectory()), "utf-8");
19
+ }
16
20
  export const AGENTS = {
17
21
  claude: {
18
22
  name: "Claude Code",
@@ -110,7 +114,27 @@ function skillState(target, expectedSha256) {
110
114
  actualSha256,
111
115
  };
112
116
  }
113
- export function planSkillInstalls(agents, opts) {
117
+ /** Load the packaged tree, keeping portable relative paths in the report. */
118
+ function skillFiles() {
119
+ const root = fileURLToPath(skillDirectory());
120
+ const files = new Map();
121
+ function visit(relativeDir) {
122
+ const dir = path.join(root, relativeDir);
123
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true, }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
124
+ const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
125
+ if (entry.isDirectory())
126
+ visit(relativePath);
127
+ else if (entry.isFile()) {
128
+ files.set(relativePath, fs.readFileSync(path.join(root, relativePath)));
129
+ }
130
+ else
131
+ throw new Error(`Unsupported bundled skill entry: ${relativePath}`);
132
+ }
133
+ }
134
+ visit("");
135
+ return files;
136
+ }
137
+ function skillInstallPlan(agents, opts, bundle) {
114
138
  const home = os.homedir();
115
139
  const results = [];
116
140
  for (const { id, def, via, } of agents) {
@@ -121,38 +145,65 @@ export function planSkillInstalls(agents, opts) {
121
145
  : undefined;
122
146
  if (!dir)
123
147
  continue;
124
- const target = path.join(dir, def.filename);
125
- const expectedSha256 = sha256Hex(def.content());
126
- const state = skillState(target, expectedSha256);
148
+ const files = [];
149
+ for (const [sourcePath, source,] of bundle) {
150
+ const relativePath = sourcePath === "SKILL.md" ? def.filename : sourcePath;
151
+ const target = path.join(dir, relativePath);
152
+ const expectedSha256 = sha256Hex(sourcePath === "SKILL.md" ? def.content() : source);
153
+ const state = skillState(target, expectedSha256);
154
+ files.push({
155
+ relativePath,
156
+ path: target,
157
+ ...state,
158
+ changed: state.status !== "current",
159
+ expectedSha256,
160
+ });
161
+ }
162
+ const entrypoint = files.find((file) => file.relativePath === def.filename);
163
+ const changed = files.some((file) => file.changed);
127
164
  results.push({
128
165
  agent: id,
129
- path: target,
166
+ path: entrypoint.path,
130
167
  via,
131
- ...state,
132
- changed: state.status !== "current",
133
- expectedSha256,
168
+ status: entrypoint.status === "missing" ? "missing" : changed ? "stale" : "current",
169
+ changed,
170
+ expectedSha256: entrypoint.expectedSha256,
171
+ ...(entrypoint.actualSha256 !== undefined ? { actualSha256: entrypoint.actualSha256, } : {}),
172
+ files,
134
173
  });
135
174
  }
136
175
  return results;
137
176
  }
177
+ export function planSkillInstalls(agents, opts) {
178
+ return skillInstallPlan(agents, opts, skillFiles());
179
+ }
180
+ function writeSkillFile(file, content) {
181
+ if (!file.changed)
182
+ return;
183
+ const dir = path.dirname(file.path);
184
+ fs.mkdirSync(dir, { recursive: true, });
185
+ const tmpPath = path.join(dir, `.${path.basename(file.path)}.tmp-${process.pid}-${Date.now().toString(36)}`);
186
+ try {
187
+ fs.writeFileSync(tmpPath, content);
188
+ fs.renameSync(tmpPath, file.path);
189
+ }
190
+ finally {
191
+ fs.rmSync(tmpPath, { force: true, });
192
+ }
193
+ }
138
194
  export function installSkill(agents, opts) {
139
- const results = planSkillInstalls(agents, opts);
195
+ const bundle = skillFiles();
196
+ const results = skillInstallPlan(agents, opts, bundle);
140
197
  for (const result of results) {
141
- const def = AGENTS[result.agent];
142
- if (!def || !result.changed)
198
+ if (!result.changed)
143
199
  continue;
144
- const content = def.content();
145
- const dir = path.dirname(result.path);
146
- fs.mkdirSync(dir, { recursive: true, });
147
- const tmpPath = path.join(dir, `.${path.basename(result.path)}.tmp-${process.pid}-${Date.now().toString(36)}`);
148
- fs.writeFileSync(tmpPath, content, "utf-8");
149
- try {
150
- fs.renameSync(tmpPath, result.path);
151
- }
152
- catch (error) {
153
- fs.rmSync(tmpPath, { force: true, });
154
- throw error;
200
+ const def = agents.find((agent) => agent.id === result.agent).def;
201
+ // Publish referenced files first; never expose a new entrypoint with missing references.
202
+ for (const file of result.files) {
203
+ if (file.relativePath !== def.filename)
204
+ writeSkillFile(file, bundle.get(file.relativePath));
155
205
  }
206
+ writeSkillFile(result.files.find((file) => file.relativePath === def.filename), def.content());
156
207
  }
157
208
  return results;
158
209
  }
@@ -0,0 +1,2 @@
1
+ /** Stream with backpressure; publish only a complete download, preserving existing output on failure. */
2
+ export declare function writeResponseToFile(path: string, response: Response, permissions?: "private" | "preserve"): Promise<number>;
@@ -0,0 +1,34 @@
1
+ import { randomUUID, } from "node:crypto";
2
+ import { createWriteStream, } from "node:fs";
3
+ import { chmod, rename, rm, stat, } from "node:fs/promises";
4
+ import { basename, dirname, join, } from "node:path";
5
+ import { Readable, } from "node:stream";
6
+ import { pipeline, } from "node:stream/promises";
7
+ /** Stream with backpressure; publish only a complete download, preserving existing output on failure. */
8
+ export async function writeResponseToFile(path, response, permissions = "private") {
9
+ let mode;
10
+ if (permissions === "preserve") {
11
+ try {
12
+ mode = (await stat(path)).mode & 0o777;
13
+ }
14
+ catch (error) {
15
+ if (error.code !== "ENOENT")
16
+ throw error;
17
+ mode = 0o666 & ~process.umask();
18
+ }
19
+ }
20
+ const input = Readable.fromWeb(response.body);
21
+ const temporaryPath = join(dirname(path), `.${basename(path)}.tmp-${randomUUID()}`);
22
+ const output = createWriteStream(temporaryPath, { flags: "wx", mode: 0o600, });
23
+ try {
24
+ await pipeline(input, output);
25
+ // Keep partial contents private; restore publication permissions only after completion.
26
+ if (mode !== undefined)
27
+ await chmod(temporaryPath, mode);
28
+ await rename(temporaryPath, path);
29
+ return output.bytesWritten;
30
+ }
31
+ finally {
32
+ await rm(temporaryPath, { force: true, });
33
+ }
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataiku-sdk",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -25,13 +25,15 @@
25
25
  "build": "tsc && bun scripts/generate-build-metadata.mjs && tsc -p packages/types/tsconfig.json",
26
26
  "prepack": "bun scripts/sync-plugin-version.mjs --check && tsc && bun scripts/generate-build-metadata.mjs && tsc -p packages/types/tsconfig.json",
27
27
  "prepublishOnly": "bun scripts/sync-plugin-version.mjs --check && tsc && bun scripts/generate-build-metadata.mjs && tsc -p packages/types/tsconfig.json",
28
- "check": "tsc --noEmit && bun scripts/sync-plugin-version.mjs --check",
28
+ "check": "tsc --noEmit && bun run check:live && bun scripts/sync-plugin-version.mjs --check",
29
29
  "version": "bun scripts/sync-plugin-version.mjs && git add plugin.json",
30
- "lint": "oxlint src/",
31
- "lint:fix": "oxlint --fix src/",
30
+ "lint": "oxlint src/ scripts/live-suite.ts tests/live-*.ts",
31
+ "lint:fix": "oxlint --fix src/ scripts/live-suite.ts tests/live-*.ts",
32
32
  "format": "dprint fmt",
33
33
  "format:check": "dprint check",
34
34
  "test": "bun test",
35
+ "test:live": "bun --no-env-file scripts/live-suite.ts",
36
+ "check:live": "tsc --noEmit -p tsconfig.live.json",
35
37
  "test:tokens": "bun test tests/cli/agent-token-budget.test.ts",
36
38
  "test:platform": "bun scripts/platform-smoke.mjs",
37
39
  "test:integration": "cross-env RUN_DATAIKU_INTEGRATION=1 bun test tests/integration-playground.test.ts",
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.1.0",
4
+ "version": "3.2.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",