dataiku-sdk 3.1.0 → 3.3.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 (149) hide show
  1. package/README.md +253 -38
  2. package/dist/build-metadata.json +1 -1
  3. package/dist/packages/types/src/index.d.ts +102 -0
  4. package/dist/packages/types/src/index.js +76 -0
  5. package/dist/src/auth.js +1 -1
  6. package/dist/src/cli/coerce.js +11 -2
  7. package/dist/src/cli/commands/analysis.js +4 -3
  8. package/dist/src/cli/commands/api-service.js +5 -3
  9. package/dist/src/cli/commands/app.js +61 -18
  10. package/dist/src/cli/commands/bundle.js +3 -2
  11. package/dist/src/cli/commands/code-env.js +23 -16
  12. package/dist/src/cli/commands/connection.d.ts +9 -0
  13. package/dist/src/cli/commands/connection.js +267 -1
  14. package/dist/src/cli/commands/dashboard.js +7 -6
  15. package/dist/src/cli/commands/data-collection.d.ts +2 -0
  16. package/dist/src/cli/commands/data-collection.js +164 -0
  17. package/dist/src/cli/commands/data-quality.js +9 -8
  18. package/dist/src/cli/commands/dataset.js +170 -40
  19. package/dist/src/cli/commands/doctor.js +4 -2
  20. package/dist/src/cli/commands/flow-zone.js +9 -8
  21. package/dist/src/cli/commands/folder.js +9 -8
  22. package/dist/src/cli/commands/future.js +4 -3
  23. package/dist/src/cli/commands/group.d.ts +2 -0
  24. package/dist/src/cli/commands/group.js +109 -0
  25. package/dist/src/cli/commands/index.js +151 -78
  26. package/dist/src/cli/commands/insight.js +8 -7
  27. package/dist/src/cli/commands/job.js +4 -3
  28. package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
  29. package/dist/src/cli/commands/knowledge-bank.js +80 -0
  30. package/dist/src/cli/commands/llm.d.ts +2 -0
  31. package/dist/src/cli/commands/llm.js +93 -0
  32. package/dist/src/cli/commands/macro.d.ts +2 -0
  33. package/dist/src/cli/commands/macro.js +221 -0
  34. package/dist/src/cli/commands/meaning.js +5 -4
  35. package/dist/src/cli/commands/ml-task.js +21 -8
  36. package/dist/src/cli/commands/model-evaluation-store.js +4 -3
  37. package/dist/src/cli/commands/notebook.js +17 -12
  38. package/dist/src/cli/commands/plugin.d.ts +2 -0
  39. package/dist/src/cli/commands/plugin.js +799 -0
  40. package/dist/src/cli/commands/project-folder.d.ts +2 -0
  41. package/dist/src/cli/commands/project-folder.js +197 -0
  42. package/dist/src/cli/commands/project-git.js +14 -54
  43. package/dist/src/cli/commands/project-library.js +7 -6
  44. package/dist/src/cli/commands/project.js +55 -8
  45. package/dist/src/cli/commands/recipe.js +50 -14
  46. package/dist/src/cli/commands/saved-model.d.ts +5 -0
  47. package/dist/src/cli/commands/saved-model.js +392 -4
  48. package/dist/src/cli/commands/scenario.js +147 -9
  49. package/dist/src/cli/commands/sql.js +3 -2
  50. package/dist/src/cli/commands/statistics.js +7 -6
  51. package/dist/src/cli/commands/user.d.ts +8 -0
  52. package/dist/src/cli/commands/user.js +296 -0
  53. package/dist/src/cli/commands/variable.js +2 -1
  54. package/dist/src/cli/commands/webapp.js +2 -1
  55. package/dist/src/cli/commands/wiki.js +7 -6
  56. package/dist/src/cli/contract.d.ts +2 -3
  57. package/dist/src/cli/contract.js +1167 -163
  58. package/dist/src/cli/flags.d.ts +4 -0
  59. package/dist/src/cli/flags.js +50 -2
  60. package/dist/src/cli/helpers/app-successor.d.ts +19 -4
  61. package/dist/src/cli/helpers/app-successor.js +90 -30
  62. package/dist/src/cli/helpers/cleanup.js +3 -1
  63. package/dist/src/cli/helpers/flow-zone.js +1 -2
  64. package/dist/src/cli/output.d.ts +1 -1
  65. package/dist/src/cli/output.js +23 -10
  66. package/dist/src/cli/usage.d.ts +1 -0
  67. package/dist/src/cli/usage.js +47 -1
  68. package/dist/src/cli.js +225 -95
  69. package/dist/src/client.d.ts +86 -6
  70. package/dist/src/client.js +263 -50
  71. package/dist/src/errors.d.ts +3 -0
  72. package/dist/src/errors.js +4 -0
  73. package/dist/src/index.d.ts +16 -7
  74. package/dist/src/index.js +11 -2
  75. package/dist/src/resources/analyses.js +1 -8
  76. package/dist/src/resources/applications.d.ts +8 -6
  77. package/dist/src/resources/applications.js +21 -21
  78. package/dist/src/resources/base.d.ts +4 -0
  79. package/dist/src/resources/base.js +15 -0
  80. package/dist/src/resources/bundles.d.ts +9 -4
  81. package/dist/src/resources/bundles.js +10 -5
  82. package/dist/src/resources/connections.d.ts +78 -1
  83. package/dist/src/resources/connections.js +132 -1
  84. package/dist/src/resources/data-collections.d.ts +118 -0
  85. package/dist/src/resources/data-collections.js +49 -0
  86. package/dist/src/resources/datasets.d.ts +69 -0
  87. package/dist/src/resources/datasets.js +99 -1
  88. package/dist/src/resources/folders.js +2 -1
  89. package/dist/src/resources/futures.d.ts +4 -1
  90. package/dist/src/resources/futures.js +38 -6
  91. package/dist/src/resources/groups.d.ts +47 -0
  92. package/dist/src/resources/groups.js +56 -0
  93. package/dist/src/resources/jobs.d.ts +8 -13
  94. package/dist/src/resources/jobs.js +41 -16
  95. package/dist/src/resources/knowledge-banks.d.ts +58 -0
  96. package/dist/src/resources/knowledge-banks.js +61 -0
  97. package/dist/src/resources/llms.d.ts +92 -0
  98. package/dist/src/resources/llms.js +62 -0
  99. package/dist/src/resources/macros.d.ts +121 -0
  100. package/dist/src/resources/macros.js +231 -0
  101. package/dist/src/resources/ml-tasks.d.ts +37 -2
  102. package/dist/src/resources/ml-tasks.js +100 -20
  103. package/dist/src/resources/model-evaluation-stores.js +1 -8
  104. package/dist/src/resources/notebooks.d.ts +43 -7
  105. package/dist/src/resources/notebooks.js +53 -13
  106. package/dist/src/resources/plugins.d.ts +265 -0
  107. package/dist/src/resources/plugins.js +458 -0
  108. package/dist/src/resources/project-folders.d.ts +71 -0
  109. package/dist/src/resources/project-folders.js +57 -0
  110. package/dist/src/resources/project-git.d.ts +6 -0
  111. package/dist/src/resources/project-git.js +69 -42
  112. package/dist/src/resources/project-library.d.ts +0 -1
  113. package/dist/src/resources/project-library.js +1 -17
  114. package/dist/src/resources/projects.d.ts +19 -3
  115. package/dist/src/resources/projects.js +30 -18
  116. package/dist/src/resources/recipes.d.ts +15 -1
  117. package/dist/src/resources/recipes.js +73 -6
  118. package/dist/src/resources/saved-models.d.ts +134 -1
  119. package/dist/src/resources/saved-models.js +214 -6
  120. package/dist/src/resources/scenarios.d.ts +105 -0
  121. package/dist/src/resources/scenarios.js +232 -106
  122. package/dist/src/resources/sql.js +5 -1
  123. package/dist/src/resources/users.d.ts +129 -0
  124. package/dist/src/resources/users.js +128 -0
  125. package/dist/src/resources/webapps.d.ts +15 -2
  126. package/dist/src/resources/webapps.js +18 -4
  127. package/dist/src/schemas.d.ts +2 -2
  128. package/dist/src/schemas.js +1 -1
  129. package/dist/src/skill.d.ts +13 -4
  130. package/dist/src/skill.js +75 -24
  131. package/dist/src/utils/deep-merge.d.ts +1 -1
  132. package/dist/src/utils/polling.d.ts +20 -0
  133. package/dist/src/utils/polling.js +27 -0
  134. package/dist/src/utils/response-file.d.ts +2 -0
  135. package/dist/src/utils/response-file.js +34 -0
  136. package/dist/src/utils/secret-sanitize.d.ts +54 -0
  137. package/dist/src/utils/secret-sanitize.js +101 -0
  138. package/package.json +8 -5
  139. package/packages/types/dist/index.d.ts +102 -0
  140. package/packages/types/dist/index.js +76 -0
  141. package/plugin.json +1 -1
  142. package/skills/dataiku-dss/SKILL.md +47 -225
  143. package/skills/dataiku-dss/references/app-releases.md +28 -0
  144. package/skills/dataiku-dss/references/authentication.md +39 -0
  145. package/skills/dataiku-dss/references/coding.md +8 -0
  146. package/skills/dataiku-dss/references/discovery.md +30 -0
  147. package/skills/dataiku-dss/references/flow-maps.md +12 -0
  148. package/skills/dataiku-dss/references/mutations.md +33 -0
  149. package/skills/dataiku-dss/references/troubleshooting.md +29 -0
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, } from "node:fs";
2
- import { mkdir, writeFile, } from "node:fs/promises";
2
+ import { mkdir, } from "node:fs/promises";
3
3
  import { dirname, resolve, } from "node:path";
4
+ import { writeResponseToFile, } from "../../utils/response-file.js";
4
5
  import { readStdinText, stripUtf8Bom, } from "../coerce.js";
5
6
  import { enqueueCliWarning, } from "../output.js";
6
7
  import { UsageError, } from "../usage.js";
@@ -121,7 +122,7 @@ export const sqlCommands = {
121
122
  }
122
123
  const outputPath = resolve(outputFile);
123
124
  await mkdir(dirname(outputPath), { recursive: true, });
124
- await writeFile(outputPath, `${JSON.stringify(result, null, 2)}\n`, "utf-8");
125
+ await writeResponseToFile(outputPath, new Response(`${JSON.stringify(result, null, 2)}\n`), "preserve");
125
126
  return {
126
127
  queryId: result.queryId,
127
128
  schema: result.schema,
@@ -1,4 +1,5 @@
1
1
  import { requiredJsonInput, } from "../coerce.js";
2
+ import { executionMode, } from "../flags.js";
2
3
  import { encodedProjectEndpoint, planResult, } from "../output.js";
3
4
  import { requireArgs, } from "../usage.js";
4
5
  const STATISTICS_PLAN_EXIT_CODES = { usage: 1, error: 2, transient: 3, };
@@ -51,7 +52,7 @@ export const statisticsCommands = {
51
52
  requireArgs(a, 1, "dss statistics create-worksheet <dataset> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]");
52
53
  const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (worksheet definition).");
53
54
  const projectKey = f["project-key"];
54
- if (f["dry-run"] === true) {
55
+ if (executionMode(f).dryRun) {
55
56
  return statisticsPlan("create-worksheet", {
56
57
  method: "POST",
57
58
  endpoint: statisticsWorksheetsEndpoint(c, a[0], projectKey),
@@ -72,7 +73,7 @@ export const statisticsCommands = {
72
73
  requireArgs(a, 2, "dss statistics update-worksheet <dataset> <worksheetId> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]");
73
74
  const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (worksheet definition).");
74
75
  const projectKey = f["project-key"];
75
- if (f["dry-run"] === true) {
76
+ if (executionMode(f).dryRun) {
76
77
  return statisticsPlan("update-worksheet", {
77
78
  method: "PUT",
78
79
  endpoint: statisticsWorksheetEndpoint(c, a[0], a[1], projectKey),
@@ -90,7 +91,7 @@ export const statisticsCommands = {
90
91
  handler: async (c, a, f) => {
91
92
  requireArgs(a, 2, "dss statistics delete-worksheet <dataset> <worksheetId> [--dry-run] [--project-key KEY]");
92
93
  const projectKey = f["project-key"];
93
- if (f["dry-run"] === true) {
94
+ if (executionMode(f).dryRun) {
94
95
  return statisticsPlan("delete-worksheet", {
95
96
  method: "DELETE",
96
97
  endpoint: statisticsWorksheetEndpoint(c, a[0], a[1], projectKey),
@@ -108,7 +109,7 @@ export const statisticsCommands = {
108
109
  handler: async (c, a, f) => {
109
110
  requireArgs(a, 2, "dss statistics run-worksheet <dataset> <worksheetId> [--dry-run] [--project-key KEY]");
110
111
  const projectKey = f["project-key"];
111
- if (f["dry-run"] === true) {
112
+ if (executionMode(f).dryRun) {
112
113
  return statisticsPlan("run-worksheet", {
113
114
  method: "POST",
114
115
  endpoint: `${statisticsWorksheetEndpoint(c, a[0], a[1], projectKey)}/actions/run-card`,
@@ -127,7 +128,7 @@ export const statisticsCommands = {
127
128
  requireArgs(a, 2, "dss statistics run-card <dataset> <worksheetId> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]");
128
129
  const card = requiredJsonInput(f, "--data, --data-file, or --stdin is required (card settings).");
129
130
  const projectKey = f["project-key"];
130
- if (f["dry-run"] === true) {
131
+ if (executionMode(f).dryRun) {
131
132
  return statisticsPlan("run-card", {
132
133
  method: "POST",
133
134
  endpoint: `${statisticsWorksheetEndpoint(c, a[0], a[1], projectKey)}/actions/run-card`,
@@ -147,7 +148,7 @@ export const statisticsCommands = {
147
148
  requireArgs(a, 2, "dss statistics run-computation <dataset> <worksheetId> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]");
148
149
  const computation = requiredJsonInput(f, "--data, --data-file, or --stdin is required (computation settings).");
149
150
  const projectKey = f["project-key"];
150
- if (f["dry-run"] === true) {
151
+ if (executionMode(f).dryRun) {
151
152
  return statisticsPlan("run-computation", {
152
153
  method: "POST",
153
154
  endpoint: `${statisticsWorksheetEndpoint(c, a[0], a[1], projectKey)}/actions/run-computation`,
@@ -0,0 +1,8 @@
1
+ import type { CommandMeta } from "../types.js";
2
+ export declare function sanitizeUserSecrets<T>(value: T, secrets?: string[]): T;
3
+ /**
4
+ * Extract every exact password value in a user body (top-level or nested) so
5
+ * errors echoing the payload can be scrubbed.
6
+ */
7
+ export declare function userSecretsFromBody(body: unknown): string[];
8
+ export declare const userCommands: Record<string, CommandMeta>;
@@ -0,0 +1,296 @@
1
+ import { normalizeSecretKey, sanitizeSecrets, } from "../../utils/secret-sanitize.js";
2
+ import { requiredJsonInput, splitCsvFlag, } from "../coerce.js";
3
+ import { executionMode, } from "../flags.js";
4
+ import { readIfExists, skipResult, } from "../output.js";
5
+ import { requireArgs, UsageError, } from "../usage.js";
6
+ /** Credential-bearing user keys, matched after stripping -/_ and lowercasing. */
7
+ const SENSITIVE_USER_NORMALIZED_KEYS = {
8
+ password: true,
9
+ userpassword: true,
10
+ };
11
+ /**
12
+ * Key check for the user secrets family: `secrets` entries (DSS user settings
13
+ * carry a secrets array of credential values) and password-like keys are
14
+ * redacted regardless of nesting depth.
15
+ */
16
+ function isSensitiveUserKey(normalizedKey) {
17
+ return SENSITIVE_USER_NORMALIZED_KEYS[normalizedKey] === true || normalizedKey === "secrets";
18
+ }
19
+ export function sanitizeUserSecrets(value, secrets = []) {
20
+ return sanitizeSecrets(value, {
21
+ sensitiveKeys: SENSITIVE_USER_NORMALIZED_KEYS,
22
+ isSensitiveKey: (normalizedKey) => normalizedKey === "secrets",
23
+ secrets,
24
+ });
25
+ }
26
+ /**
27
+ * Extract every exact password value in a user body (top-level or nested) so
28
+ * errors echoing the payload can be scrubbed.
29
+ */
30
+ export function userSecretsFromBody(body) {
31
+ if (body === undefined || body === null || typeof body !== "object" || Array.isArray(body)) {
32
+ return [];
33
+ }
34
+ const found = [];
35
+ const visit = (value) => {
36
+ if (Array.isArray(value)) {
37
+ value.forEach(visit);
38
+ return;
39
+ }
40
+ if (value !== null && typeof value === "object") {
41
+ for (const [key, item,] of Object.entries(value)) {
42
+ const normalizedKey = normalizeSecretKey(key);
43
+ if (isSensitiveUserKey(normalizedKey)) {
44
+ // `secrets` may be an array of credential values or a single
45
+ // string; passwords are single strings.
46
+ if (typeof item === "string")
47
+ found.push(item);
48
+ else if (Array.isArray(item)) {
49
+ for (const entry of item) {
50
+ if (typeof entry === "string")
51
+ found.push(entry);
52
+ else
53
+ visit(entry);
54
+ }
55
+ }
56
+ else
57
+ visit(item);
58
+ }
59
+ else {
60
+ visit(item);
61
+ }
62
+ }
63
+ }
64
+ };
65
+ visit(body);
66
+ return found;
67
+ }
68
+ function requiredUserJson(flags, usage) {
69
+ const body = requiredJsonInput(flags, `--data, --data-file, or --stdin is required (user definition). Usage: ${usage}`);
70
+ return body;
71
+ }
72
+ /** Login identifiers must be non-empty before any request or dry-run preview. */
73
+ function requireLogin(value, usage) {
74
+ const trimmed = value?.trim() ?? "";
75
+ if (trimmed.length === 0) {
76
+ throw new UsageError(`login must be a non-empty string.\nUsage: ${usage}`, "validation_failed");
77
+ }
78
+ return trimmed;
79
+ }
80
+ export const userCommands = {
81
+ list: {
82
+ handler: (c, _a, f) => c.users.list(f["connected"] === true ? { connected: true, } : undefined),
83
+ usage: "dss user list [--connected]",
84
+ description: "List DSS users (admin). --connected reports currently connected users via WebSockets, which may under-report when WebSockets are disabled.",
85
+ examples: ["dss user list", "dss user list --connected",],
86
+ },
87
+ get: {
88
+ handler: (c, a) => {
89
+ const usage = "dss user get <login>";
90
+ requireArgs(a, 1, usage);
91
+ return c.users.get(requireLogin(a[0], usage));
92
+ },
93
+ usage: "dss user get <login>",
94
+ description: "Get a DSS user (admin).",
95
+ examples: ["dss user get admin",],
96
+ },
97
+ create: {
98
+ handler: async (c, _a, f) => {
99
+ const usage = "dss user create (--data JSON|--data-file PATH|--stdin)";
100
+ const body = requiredUserJson(f, usage);
101
+ if (executionMode(f).dryRun) {
102
+ return {
103
+ dryRun: true,
104
+ action: "create",
105
+ resource: "user",
106
+ login: typeof body.login === "string" ? body.login : undefined,
107
+ endpoint: "/public/api/admin/users",
108
+ method: "POST",
109
+ payload: sanitizeUserSecrets(body),
110
+ };
111
+ }
112
+ const result = await c.users.create(body);
113
+ return sanitizeUserSecrets(result, userSecretsFromBody(body));
114
+ },
115
+ usage: "dss user create (--data JSON|--data-file PATH|--stdin) [--dry-run]",
116
+ description: 'Create a DSS user (admin). The JSON body follows the DSS User schema, e.g. {"login":"UserA","sourceType":"LOCAL","displayName":"User A","groups":[],"userProfile":"DATA_ANALYST","password":"..."}. Secrets are never echoed in output.',
117
+ examples: [
118
+ `dss user create --data '{"login":"UserA","sourceType":"LOCAL","displayName":"User A","groups":[],"userProfile":"DATA_ANALYST","password":"s3cret"}'`,
119
+ ],
120
+ },
121
+ update: {
122
+ handler: async (c, a, f) => {
123
+ const usage = "dss user update <login> (--data JSON|--data-file PATH|--stdin) [--dry-run]";
124
+ requireArgs(a, 1, usage);
125
+ const login = requireLogin(a[0], usage);
126
+ const body = requiredUserJson(f, usage);
127
+ if (executionMode(f).dryRun) {
128
+ return {
129
+ dryRun: true,
130
+ action: "update",
131
+ resource: "user",
132
+ login,
133
+ method: "PUT",
134
+ endpoint: `/public/api/admin/users/${encodeURIComponent(login)}`,
135
+ payload: sanitizeUserSecrets(body),
136
+ note: "PUT replaces the whole User object; the body must come from `dss user get`. No GET was issued in dry-run.",
137
+ };
138
+ }
139
+ const result = await c.users.update(login, body);
140
+ return sanitizeUserSecrets(result, userSecretsFromBody(body));
141
+ },
142
+ usage: "dss user update <login> (--data JSON|--data-file PATH|--stdin) [--dry-run]",
143
+ description: "Update a DSS user (admin). The body MUST be the User object obtained from `dss user get` (PUT semantics); pass undocumented attributes through unchanged. Use --dry-run to preview the merged result without writing.",
144
+ examples: [
145
+ `dss user update UserA --data '{"login":"UserA","displayName":"User A2","groups":["administrators"],"codeAllowed":true}'`,
146
+ ],
147
+ },
148
+ delete: {
149
+ handler: async (c, a, f) => {
150
+ const usage = "dss user delete <login> [--if-exists] [--dry-run]";
151
+ requireArgs(a, 1, usage);
152
+ const login = requireLogin(a[0], usage);
153
+ if (executionMode(f).dryRun) {
154
+ return {
155
+ dryRun: true,
156
+ action: "delete",
157
+ resource: "user",
158
+ login,
159
+ method: "DELETE",
160
+ endpoint: `/public/api/admin/users/${encodeURIComponent(login)}`,
161
+ ...(f["if-exists"] === true
162
+ ? { note: "Existence not checked in dry-run; live --if-exists issues a GET probe first.", }
163
+ : {}),
164
+ };
165
+ }
166
+ if (f["if-exists"] === true) {
167
+ const current = await readIfExists(() => c.users.get(login));
168
+ if (!current)
169
+ return skipResult("user", login, "missing");
170
+ }
171
+ return c.users.delete(login);
172
+ },
173
+ usage: "dss user delete <login> [--if-exists] [--dry-run]",
174
+ description: "Delete a DSS user (admin). Destructive and irreversible.",
175
+ examples: ["dss user delete UserA", "dss user delete UserA --if-exists",],
176
+ },
177
+ resync: {
178
+ handler: async (c, a, f) => {
179
+ const usage = "dss user resync <login>";
180
+ requireArgs(a, 1, usage);
181
+ const login = requireLogin(a[0], usage);
182
+ if (executionMode(f).dryRun) {
183
+ return {
184
+ dryRun: true,
185
+ action: "resync",
186
+ resource: "user",
187
+ login,
188
+ method: "POST",
189
+ endpoint: `/public/api/admin/users/${encodeURIComponent(login)}/actions/resync`,
190
+ };
191
+ }
192
+ return c.users.resync(login);
193
+ },
194
+ usage: "dss user resync <login> [--dry-run]",
195
+ description: "Resync one DSS user (admin). Returns a DSS future; poll with `dss future wait <jobId>` when hasResult is false.",
196
+ examples: ["dss user resync UserA",],
197
+ },
198
+ "resync-multi": {
199
+ handler: async (c, _a, f) => {
200
+ const logins = splitCsvFlag(f["logins"]);
201
+ if (logins.length === 0) {
202
+ throw new UsageError("--logins is required. Usage: dss user resync-multi --logins user1,user2", "missing_required_flag");
203
+ }
204
+ if (executionMode(f).dryRun) {
205
+ return {
206
+ dryRun: true,
207
+ action: "resync-multi",
208
+ resource: "user",
209
+ count: logins.length,
210
+ method: "POST",
211
+ endpoint: "/public/api/admin/users/actions/resync-multi",
212
+ payload: logins,
213
+ };
214
+ }
215
+ return c.users.resyncMulti(logins);
216
+ },
217
+ usage: "dss user resync-multi --logins CSV [--dry-run]",
218
+ description: "Resync multiple DSS users (admin). Returns a DSS future.",
219
+ examples: ["dss user resync-multi --logins user1,user2,user3",],
220
+ },
221
+ "external-users": {
222
+ handler: async (c, _a, f) => {
223
+ if (executionMode(f).dryRun) {
224
+ return {
225
+ dryRun: true,
226
+ action: "external-users",
227
+ resource: "user",
228
+ method: "GET",
229
+ endpoint: "/public/api/admin/users/actions/external-users",
230
+ note: "Mints a DSS future that enumerates the external identity supplier.",
231
+ };
232
+ }
233
+ return c.users.externalUsers();
234
+ },
235
+ usage: "dss user external-users [--dry-run]",
236
+ description: "Fetch externally sourced users and their provisioning status (admin). Returns a DSS future whose result is the external user list when hasResult is true.",
237
+ examples: ["dss user external-users",],
238
+ },
239
+ "external-groups": {
240
+ handler: async (c, _a, f) => {
241
+ if (executionMode(f).dryRun) {
242
+ return {
243
+ dryRun: true,
244
+ action: "external-groups",
245
+ resource: "user",
246
+ method: "GET",
247
+ endpoint: "/public/api/admin/users/actions/external-groups",
248
+ note: "Mints a DSS future that enumerates the external identity supplier's groups.",
249
+ };
250
+ }
251
+ return c.users.externalGroups();
252
+ },
253
+ usage: "dss user external-groups [--dry-run]",
254
+ description: "Fetch externally sourced groups (admin). Returns a DSS future whose result is the external group name list when hasResult is true.",
255
+ examples: ["dss user external-groups",],
256
+ },
257
+ provision: {
258
+ handler: async (c, _a, f) => {
259
+ const usage = "dss user provision (--data JSON|--data-file PATH|--stdin)";
260
+ const body = requiredJsonInput(f, `--data, --data-file, or --stdin is required (provisioning request). Usage: ${usage}`);
261
+ if (executionMode(f).dryRun) {
262
+ return {
263
+ dryRun: true,
264
+ action: "provision",
265
+ resource: "user",
266
+ userSourceType: typeof body.userSourceType === "string" ? body.userSourceType : undefined,
267
+ method: "POST",
268
+ endpoint: "/public/api/admin/users/actions/provision",
269
+ payload: body,
270
+ };
271
+ }
272
+ return c.users.provision(body);
273
+ },
274
+ usage: "dss user provision (--data JSON|--data-file PATH|--stdin) [--dry-run]",
275
+ description: "Provision users from an external source (admin). Body is the documented {userSourceType, users[]} request; returns a DSS future with a provisioning summary.",
276
+ examples: [
277
+ `dss user provision --data '{"userSourceType":"AZURE_AD","users":[{"login":"bob","displayName":"Bob","sourceGroupNames":["externalGroupA"],"dkuGroupNames":["dssGroupA"]}]}'`,
278
+ ],
279
+ },
280
+ activity: {
281
+ handler: (c) => c.users.activityAll(),
282
+ usage: "dss user activity",
283
+ description: "Get last activity timestamps for all DSS users (admin).",
284
+ examples: ["dss user activity",],
285
+ },
286
+ "activity-get": {
287
+ handler: (c, a) => {
288
+ const usage = "dss user activity-get <login>";
289
+ requireArgs(a, 1, usage);
290
+ return c.users.activity(requireLogin(a[0], usage));
291
+ },
292
+ usage: "dss user activity-get <login>",
293
+ description: "Get last activity timestamps for one DSS user (admin).",
294
+ examples: ["dss user activity-get UserA",],
295
+ },
296
+ };
@@ -1,4 +1,5 @@
1
1
  import { json, } from "../coerce.js";
2
+ import { executionMode, } from "../flags.js";
2
3
  import { UsageError, } from "../usage.js";
3
4
  export const variableCommands = {
4
5
  get: {
@@ -15,7 +16,7 @@ export const variableCommands = {
15
16
  if (standard === undefined && local === undefined) {
16
17
  throw new UsageError("--standard and/or --local is required.");
17
18
  }
18
- if (f["dry-run"] === true) {
19
+ if (executionMode(f).dryRun) {
19
20
  const current = await c.variables.get(pk);
20
21
  const next = f["replace"] === true
21
22
  ? { standard: standard ?? {}, local: local ?? {}, }
@@ -2,6 +2,7 @@ import { ClientValidationError, } from "../../errors.js";
2
2
  import { deepMerge, } from "../../utils/deep-merge.js";
3
3
  import { stableHash, } from "../../utils/stable-hash.js";
4
4
  import { num, requiredJsonInput, } from "../coerce.js";
5
+ import { executionMode, } from "../flags.js";
5
6
  import { requireArgs, } from "../usage.js";
6
7
  const UPDATE_SETTINGS_USAGE = "dss webapp update-settings <webappId> (--data JSON|--data-file PATH|--stdin) [--expect-hash SHA256] [--dry-run] [--project-key KEY]";
7
8
  const RESTART_USAGE = "dss webapp restart-backend <webappId> [--wait] [--timeout MS] [--poll-interval MS] [--project-key KEY]";
@@ -44,7 +45,7 @@ export const webappCommands = {
44
45
  const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (webapp settings).");
45
46
  const projectKey = f["project-key"];
46
47
  const expectHash = validateExpectHash(f["expect-hash"]);
47
- if (f["dry-run"] === true) {
48
+ if (executionMode(f).dryRun) {
48
49
  const current = await c.webapps.getSettings(a[0], projectKey);
49
50
  const currentHash = stableHash(current);
50
51
  if (expectHash !== undefined && currentHash !== expectHash.toLowerCase()) {
@@ -1,5 +1,6 @@
1
1
  import { deepMerge, } from "../../utils/deep-merge.js";
2
2
  import { jsonInput, textInput, } from "../coerce.js";
3
+ import { executionMode, } from "../flags.js";
3
4
  import { readIfExists, skipResult, } from "../output.js";
4
5
  import { requireArgs, UsageError, } from "../usage.js";
5
6
  export const wikiCommands = {
@@ -31,12 +32,12 @@ export const wikiCommands = {
31
32
  throw new UsageError("--name is required. Usage: dss wiki create --name NAME");
32
33
  const content = textInput(f);
33
34
  const pk = f["project-key"];
34
- if (f["if-not-exists"] === true || f["dry-run"] === true) {
35
+ if (f["if-not-exists"] === true || executionMode(f).dryRun) {
35
36
  const existing = (await c.wiki.list(pk)).find((article) => article.article.name === name);
36
- if (existing && f["if-not-exists"] === true && f["dry-run"] !== true) {
37
+ if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
37
38
  return skipResult("wiki", existing.article.id, "exists", { current: existing, });
38
39
  }
39
- if (f["dry-run"] === true) {
40
+ if (executionMode(f).dryRun) {
40
41
  return {
41
42
  dryRun: true,
42
43
  action: "create",
@@ -75,7 +76,7 @@ export const wikiCommands = {
75
76
  if (!data && content === undefined && name === undefined) {
76
77
  throw new UsageError("--name, --content, --file, --data, --data-file, or --stdin is required.");
77
78
  }
78
- if (f["dry-run"] === true) {
79
+ if (executionMode(f).dryRun) {
79
80
  const current = await c.wiki.get(a[0], f["project-key"]);
80
81
  const next = deepMerge(current, data ?? {});
81
82
  if (name !== undefined) {
@@ -105,11 +106,11 @@ export const wikiCommands = {
105
106
  handler: async (c, a, f) => {
106
107
  requireArgs(a, 1, "dss wiki delete <id-or-name>");
107
108
  const pk = f["project-key"];
108
- if (f["dry-run"] === true || f["if-exists"] === true) {
109
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
109
110
  const current = await readIfExists(() => c.wiki.get(a[0], pk));
110
111
  if (!current)
111
112
  return skipResult("wiki", a[0], "missing");
112
- if (f["dry-run"] === true) {
113
+ if (executionMode(f).dryRun) {
113
114
  return { dryRun: true, action: "delete", resource: "wiki", article: a[0], current, };
114
115
  }
115
116
  }
@@ -99,13 +99,12 @@ export declare const AGENT_CONTRACT_USAGE = "dss agent contract [--fields PATHS]
99
99
  export declare const CLEANUP_USAGE = "dss cleanup --file PATH [--dry-run|--apply] [--continue-on-error]";
100
100
  export declare function isAllowedCleanupAction(resource: string, action: string): boolean;
101
101
  export declare function buildCommandSchemas(resource: string, action: string, flags: CommandFlagMetadata[], requiredFlags: string[], requiredOneOf: CommandFlagChoice[], payloadSchema: CommandPayloadSchema | undefined, outputShape: CommandOutputShape, usage: string): CommandAgentSchemas;
102
- export declare function inferRequiresProject(resource: string, action: string, usage: string): boolean;
103
102
  export declare function inferInputContract(usage: string): CommandInputContract;
104
103
  export declare function supportsCleanupLedger(resource: string, action: string): boolean;
105
104
  export declare function inferCleanupHint(resource: string, action: string): string | undefined;
106
- export declare function buildCommandRegistry(): Record<string, Record<string, CommandRegistryEntry>>;
105
+ export declare function buildCommandRegistry(resourceFilter?: string): Record<string, Record<string, CommandRegistryEntry>>;
107
106
  export declare function agentContractJsonSchema(): Record<string, unknown>;
108
- export declare function commandActionSummary(registry: Record<string, Record<string, CommandRegistryEntry>>): Record<string, string[]>;
107
+ export declare function commandActionSummary(): Record<string, string[]>;
109
108
  export declare function buildAgentContract(): Record<string, unknown>;
110
109
  export declare function commandPlanShape(resource: string, action: string, args: string[], flags: Record<string, string | boolean>, entry: CommandRegistryEntry, projectKey: string | undefined): {
111
110
  endpoint?: string;