@credibledata/cred-cli 0.0.100 → 0.0.103

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 (225) hide show
  1. package/README-DEV.md +2 -2
  2. package/README.md +1 -1
  3. package/coverage/coverage-summary.json +40 -0
  4. package/coverage/tmp/coverage-2152-1789496974457-0.json +1 -0
  5. package/coverage/tmp/coverage-2153-1789496954404-0.json +1 -0
  6. package/dist/client/api.js +6151 -2254
  7. package/dist/client/api.js.map +1 -1
  8. package/dist/client/base.js +1 -1
  9. package/dist/client/common.js +1 -1
  10. package/dist/client/configuration.js +1 -1
  11. package/dist/client/index.js +1 -1
  12. package/dist/clientconfiguration.js +114 -26
  13. package/dist/clientconfiguration.js.map +1 -1
  14. package/dist/clientconfiguration.spec.js +159 -37
  15. package/dist/clientconfiguration.spec.js.map +1 -1
  16. package/dist/commands/add.js +36 -5
  17. package/dist/commands/add.js.map +1 -1
  18. package/dist/commands/connection/createconnections.js +4 -4
  19. package/dist/commands/connection/createconnections.js.map +1 -1
  20. package/dist/commands/connection/createconnections.spec.js +11 -1
  21. package/dist/commands/connection/createconnections.spec.js.map +1 -1
  22. package/dist/commands/connection/deleteconnection.js +4 -4
  23. package/dist/commands/connection/deleteconnection.js.map +1 -1
  24. package/dist/commands/connection/deleteconnection.spec.js +1 -1
  25. package/dist/commands/connection/deleteconnection.spec.js.map +1 -1
  26. package/dist/commands/connection/listconnections.js +4 -4
  27. package/dist/commands/connection/listconnections.js.map +1 -1
  28. package/dist/commands/connection/listconnections.spec.js +1 -1
  29. package/dist/commands/connection/listconnections.spec.js.map +1 -1
  30. package/dist/commands/{project/createproject.js → environment/createenvironment.js} +17 -14
  31. package/dist/commands/environment/createenvironment.js.map +1 -0
  32. package/dist/commands/{project/createproject.spec.js → environment/createenvironment.spec.js} +46 -46
  33. package/dist/commands/environment/createenvironment.spec.js.map +1 -0
  34. package/dist/commands/{project/deleteproject.js → environment/deleteenvironment.js} +9 -9
  35. package/dist/commands/environment/deleteenvironment.js.map +1 -0
  36. package/dist/commands/{project/deleteproject.spec.js → environment/deleteenvironment.spec.js} +29 -29
  37. package/dist/commands/environment/deleteenvironment.spec.js.map +1 -0
  38. package/dist/commands/{project/getproject.js → environment/getenvironment.js} +8 -8
  39. package/dist/commands/environment/getenvironment.js.map +1 -0
  40. package/dist/commands/{project/getproject.spec.js → environment/getenvironment.spec.js} +32 -33
  41. package/dist/commands/environment/getenvironment.spec.js.map +1 -0
  42. package/dist/commands/{project/listprojects.js → environment/listenvironments.js} +10 -10
  43. package/dist/commands/environment/listenvironments.js.map +1 -0
  44. package/dist/commands/{project/listprojects.spec.js → environment/listenvironments.spec.js} +35 -35
  45. package/dist/commands/environment/listenvironments.spec.js.map +1 -0
  46. package/dist/commands/{project/setproject.js → environment/setenvironment.js} +6 -5
  47. package/dist/commands/environment/setenvironment.js.map +1 -0
  48. package/dist/commands/{project/setproject.spec.js → environment/setenvironment.spec.js} +9 -9
  49. package/dist/commands/environment/setenvironment.spec.js.map +1 -0
  50. package/dist/commands/environment/updateenvironment.js +130 -0
  51. package/dist/commands/environment/updateenvironment.js.map +1 -0
  52. package/dist/commands/environment/updateenvironment.spec.js +213 -0
  53. package/dist/commands/environment/updateenvironment.spec.js.map +1 -0
  54. package/dist/commands/get.js +66 -5
  55. package/dist/commands/get.js.map +1 -1
  56. package/dist/commands/legacy.js +9 -0
  57. package/dist/commands/legacy.js.map +1 -0
  58. package/dist/commands/list.js +95 -4
  59. package/dist/commands/list.js.map +1 -1
  60. package/dist/commands/materialization/cancelrun.js +62 -0
  61. package/dist/commands/materialization/cancelrun.js.map +1 -0
  62. package/dist/commands/materialization/getindex.js +52 -0
  63. package/dist/commands/materialization/getindex.js.map +1 -0
  64. package/dist/commands/materialization/getmaterialization.js +54 -0
  65. package/dist/commands/materialization/getmaterialization.js.map +1 -0
  66. package/dist/commands/materialization/getpreaggregate.js +55 -0
  67. package/dist/commands/materialization/getpreaggregate.js.map +1 -0
  68. package/dist/commands/materialization/getrun.js +54 -0
  69. package/dist/commands/materialization/getrun.js.map +1 -0
  70. package/dist/commands/materialization/listindexes.js +58 -0
  71. package/dist/commands/materialization/listindexes.js.map +1 -0
  72. package/dist/commands/materialization/listmaterializations.js +52 -0
  73. package/dist/commands/materialization/listmaterializations.js.map +1 -0
  74. package/dist/commands/materialization/listpreaggregates.js +61 -0
  75. package/dist/commands/materialization/listpreaggregates.js.map +1 -0
  76. package/dist/commands/materialization/listruns.js +60 -0
  77. package/dist/commands/materialization/listruns.js.map +1 -0
  78. package/dist/commands/materialization/rerun.js +111 -0
  79. package/dist/commands/materialization/rerun.js.map +1 -0
  80. package/dist/commands/misc/login.js +16 -2
  81. package/dist/commands/misc/login.js.map +1 -1
  82. package/dist/commands/misc/login.spec.js +133 -5
  83. package/dist/commands/misc/login.spec.js.map +1 -1
  84. package/dist/commands/package/deletepackage.js +4 -4
  85. package/dist/commands/package/deletepackage.js.map +1 -1
  86. package/dist/commands/package/deletepackage.spec.js +1 -1
  87. package/dist/commands/package/deletepackage.spec.js.map +1 -1
  88. package/dist/commands/package/listpackages.js +11 -4
  89. package/dist/commands/package/listpackages.js.map +1 -1
  90. package/dist/commands/package/listpackages.spec.js +13 -1
  91. package/dist/commands/package/listpackages.spec.js.map +1 -1
  92. package/dist/commands/package/publish.js +36 -13
  93. package/dist/commands/package/publish.js.map +1 -1
  94. package/dist/commands/package/publish.spec.js +27 -1
  95. package/dist/commands/package/publish.spec.js.map +1 -1
  96. package/dist/commands/package/updatepackage.js +4 -4
  97. package/dist/commands/package/updatepackage.js.map +1 -1
  98. package/dist/commands/package/updatepackage.spec.js +1 -1
  99. package/dist/commands/package/updatepackage.spec.js.map +1 -1
  100. package/dist/commands/permission/createpermission.js +123 -0
  101. package/dist/commands/permission/createpermission.js.map +1 -0
  102. package/dist/commands/permission/createpermission.spec.js +360 -0
  103. package/dist/commands/permission/createpermission.spec.js.map +1 -0
  104. package/dist/commands/permission/deletepermission.js +86 -0
  105. package/dist/commands/permission/deletepermission.js.map +1 -0
  106. package/dist/commands/permission/getpermission.js +85 -0
  107. package/dist/commands/permission/getpermission.js.map +1 -0
  108. package/dist/commands/permission/listpermissions.js +75 -0
  109. package/dist/commands/permission/listpermissions.js.map +1 -0
  110. package/dist/commands/permission/scope.js +140 -0
  111. package/dist/commands/permission/scope.js.map +1 -0
  112. package/dist/commands/rm.js +26 -4
  113. package/dist/commands/rm.js.map +1 -1
  114. package/dist/commands/set.js +12 -4
  115. package/dist/commands/set.js.map +1 -1
  116. package/dist/commands/update.js +29 -10
  117. package/dist/commands/update.js.map +1 -1
  118. package/dist/commands/version/archive-unarchive-version.js +4 -5
  119. package/dist/commands/version/archive-unarchive-version.js.map +1 -1
  120. package/dist/commands/version/archive-unarchive-version.spec.js +4 -4
  121. package/dist/commands/version/archive-unarchive-version.spec.js.map +1 -1
  122. package/dist/commands/version/listversions.js +4 -4
  123. package/dist/commands/version/listversions.js.map +1 -1
  124. package/dist/commands/version/listversions.spec.js +1 -1
  125. package/dist/commands/version/listversions.spec.js.map +1 -1
  126. package/dist/common/UserGroupId.js +1 -1
  127. package/dist/common/UserGroupId.js.map +1 -1
  128. package/dist/common/UserGroupId.spec.js +2 -4
  129. package/dist/common/UserGroupId.spec.js.map +1 -1
  130. package/dist/constants.js +95 -19
  131. package/dist/constants.js.map +1 -1
  132. package/dist/constants.spec.js +90 -0
  133. package/dist/constants.spec.js.map +1 -0
  134. package/dist/index.js +128 -15
  135. package/dist/index.js.map +1 -1
  136. package/dist/logger.js +5 -1
  137. package/dist/logger.js.map +1 -1
  138. package/openapitools.json +4 -1
  139. package/package.json +10 -9
  140. package/src/client/api.ts +9363 -3093
  141. package/src/client/base.ts +1 -1
  142. package/src/client/common.ts +1 -1
  143. package/src/client/configuration.ts +1 -1
  144. package/src/client/index.ts +1 -1
  145. package/src/clientconfiguration.spec.ts +238 -113
  146. package/src/clientconfiguration.ts +131 -30
  147. package/src/commands/add.ts +78 -12
  148. package/src/commands/connection/createconnections.spec.ts +11 -1
  149. package/src/commands/connection/createconnections.ts +4 -4
  150. package/src/commands/connection/deleteconnection.spec.ts +1 -1
  151. package/src/commands/connection/deleteconnection.ts +4 -4
  152. package/src/commands/connection/listconnections.spec.ts +1 -1
  153. package/src/commands/connection/listconnections.ts +4 -4
  154. package/src/commands/{project/createproject.spec.ts → environment/createenvironment.spec.ts} +56 -54
  155. package/src/commands/{project/createproject.ts → environment/createenvironment.ts} +17 -14
  156. package/src/commands/{project/deleteproject.spec.ts → environment/deleteenvironment.spec.ts} +59 -30
  157. package/src/commands/{project/deleteproject.ts → environment/deleteenvironment.ts} +10 -10
  158. package/src/commands/environment/getenvironment.spec.ts +110 -0
  159. package/src/commands/{project/getproject.ts → environment/getenvironment.ts} +9 -9
  160. package/src/commands/{project/listprojects.spec.ts → environment/listenvironments.spec.ts} +37 -35
  161. package/src/commands/{project/listprojects.ts → environment/listenvironments.ts} +13 -8
  162. package/src/commands/{project/setproject.spec.ts → environment/setenvironment.spec.ts} +8 -8
  163. package/src/commands/{project/setproject.ts → environment/setenvironment.ts} +4 -4
  164. package/src/commands/environment/updateenvironment.spec.ts +386 -0
  165. package/src/commands/environment/updateenvironment.ts +168 -0
  166. package/src/commands/get.ts +131 -9
  167. package/src/commands/legacy.ts +5 -0
  168. package/src/commands/list.ts +204 -9
  169. package/src/commands/materialization/cancelrun.ts +70 -0
  170. package/src/commands/materialization/getindex.ts +55 -0
  171. package/src/commands/materialization/getmaterialization.ts +57 -0
  172. package/src/commands/materialization/getpreaggregate.ts +58 -0
  173. package/src/commands/materialization/getrun.ts +55 -0
  174. package/src/commands/materialization/listindexes.ts +69 -0
  175. package/src/commands/materialization/listmaterializations.ts +53 -0
  176. package/src/commands/materialization/listpreaggregates.ts +74 -0
  177. package/src/commands/materialization/listruns.ts +77 -0
  178. package/src/commands/materialization/rerun.ts +123 -0
  179. package/src/commands/misc/login.spec.ts +165 -7
  180. package/src/commands/misc/login.ts +20 -2
  181. package/src/commands/package/deletepackage.spec.ts +1 -1
  182. package/src/commands/package/deletepackage.ts +4 -4
  183. package/src/commands/package/listpackages.spec.ts +17 -1
  184. package/src/commands/package/listpackages.ts +11 -4
  185. package/src/commands/package/publish.spec.ts +46 -1
  186. package/src/commands/package/publish.ts +38 -13
  187. package/src/commands/package/updatepackage.spec.ts +1 -1
  188. package/src/commands/package/updatepackage.ts +4 -4
  189. package/src/commands/permission/createpermission.spec.ts +507 -0
  190. package/src/commands/permission/createpermission.ts +181 -0
  191. package/src/commands/permission/deletepermission.ts +117 -0
  192. package/src/commands/permission/getpermission.ts +115 -0
  193. package/src/commands/permission/listpermissions.ts +101 -0
  194. package/src/commands/permission/scope.ts +180 -0
  195. package/src/commands/rm.ts +54 -9
  196. package/src/commands/set.ts +13 -4
  197. package/src/commands/update.ts +66 -23
  198. package/src/commands/version/archive-unarchive-version.spec.ts +4 -4
  199. package/src/commands/version/archive-unarchive-version.ts +4 -7
  200. package/src/commands/version/listversions.spec.ts +1 -1
  201. package/src/commands/version/listversions.ts +4 -4
  202. package/src/common/UserGroupId.spec.ts +2 -4
  203. package/src/common/UserGroupId.ts +1 -1
  204. package/src/constants.spec.ts +111 -0
  205. package/src/constants.ts +131 -19
  206. package/src/index.ts +187 -18
  207. package/src/logger.ts +5 -1
  208. package/bun.lock +0 -1158
  209. package/dist/commands/project/createproject.js.map +0 -1
  210. package/dist/commands/project/createproject.spec.js.map +0 -1
  211. package/dist/commands/project/deleteproject.js.map +0 -1
  212. package/dist/commands/project/deleteproject.spec.js.map +0 -1
  213. package/dist/commands/project/getproject.js.map +0 -1
  214. package/dist/commands/project/getproject.spec.js.map +0 -1
  215. package/dist/commands/project/listprojects.js.map +0 -1
  216. package/dist/commands/project/listprojects.spec.js.map +0 -1
  217. package/dist/commands/project/setproject.js.map +0 -1
  218. package/dist/commands/project/setproject.spec.js.map +0 -1
  219. package/dist/commands/project/updateproject.js +0 -68
  220. package/dist/commands/project/updateproject.js.map +0 -1
  221. package/dist/commands/project/updateproject.spec.js +0 -129
  222. package/dist/commands/project/updateproject.spec.js.map +0 -1
  223. package/src/commands/project/getproject.spec.ts +0 -109
  224. package/src/commands/project/updateproject.spec.ts +0 -217
  225. package/src/commands/project/updateproject.ts +0 -82
@@ -0,0 +1,55 @@
1
+ import { RunsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { prettifyJson, printOutError } from "../../utils";
6
+
7
+ const runsApi = new RunsApi(new Configuration());
8
+
9
+ /**
10
+ * Get a single build/refresh run by id, including its stateful build plan. The
11
+ * `buildPlan.nodes` carry each produced unit's live state — per source node the
12
+ * physical `tableName` and `rowCount`, per index node the dimension and its
13
+ * status — and `buildPlan.edges` wire sources to the indexes that depend on
14
+ * them. This is the detailed view (`listRuns` omits the graph); use it to see
15
+ * exactly which physical tables a run produced and how the units relate.
16
+ */
17
+ export async function getRun(
18
+ controlPlane: string,
19
+ packagePath: string,
20
+ runId: string,
21
+ debug: boolean,
22
+ ) {
23
+ const clientConfig = new ClientConfiguration();
24
+ const organizationName = clientConfig.getOrganization();
25
+ const environmentName = clientConfig.getProject();
26
+
27
+ if (!clientConfig.doesConfigExists()) {
28
+ logger.error("Please, make sure that you are logged in");
29
+ return;
30
+ }
31
+
32
+ if (!environmentName) {
33
+ logger.error("Please, make sure to set an environment");
34
+ return;
35
+ }
36
+
37
+ try {
38
+ const packageName = packagePath.replace("/", "");
39
+ const response = await runsApi.getRun(
40
+ organizationName,
41
+ environmentName,
42
+ packageName,
43
+ runId,
44
+ {
45
+ baseURL: controlPlane,
46
+ headers: clientConfig.getHeaders(),
47
+ },
48
+ );
49
+
50
+ console.log(prettifyJson(response.data));
51
+ } catch (e) {
52
+ logger.error("Failed to get run");
53
+ printOutError(e, debug);
54
+ }
55
+ }
@@ -0,0 +1,69 @@
1
+ import { IndexesApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import {
6
+ prettifyJson,
7
+ printOutError,
8
+ parsePaginationParams,
9
+ extractTotalCount,
10
+ } from "../../utils";
11
+
12
+ const indexesApi = new IndexesApi(new Configuration());
13
+
14
+ /**
15
+ * List the dimensional search indexes bound to a package version (the per-`#(index)`
16
+ * serving anchors). Each entry prints its `status` and, when FAILED, the `error`;
17
+ * plus the serving `rowCount`, `lastIndexedAt`, freshness policy, and the
18
+ * `stale`/`staleReasons` triple — so a failing or stale index is diagnosable
19
+ * from the CLI. The total available count is logged to stderr.
20
+ */
21
+ export async function listIndexes(
22
+ controlPlane: string,
23
+ packagePath: string,
24
+ versionId: string,
25
+ debug: boolean,
26
+ page?: string | number,
27
+ pageSize?: string | number,
28
+ ) {
29
+ const clientConfig = new ClientConfiguration();
30
+ const organizationName = clientConfig.getOrganization();
31
+ const environmentName = clientConfig.getProject();
32
+
33
+ if (!clientConfig.doesConfigExists()) {
34
+ logger.error("Please, make sure that you are logged in");
35
+ return;
36
+ }
37
+
38
+ if (!environmentName) {
39
+ logger.error("Please, make sure to set an environment");
40
+ return;
41
+ }
42
+
43
+ try {
44
+ const packageName = packagePath.replace("/", "");
45
+ const { limit, offset } = parsePaginationParams(page, pageSize);
46
+ const response = await indexesApi.listIndexes(
47
+ organizationName,
48
+ environmentName,
49
+ packageName,
50
+ versionId,
51
+ limit,
52
+ offset,
53
+ {
54
+ baseURL: controlPlane,
55
+ headers: clientConfig.getHeaders(),
56
+ },
57
+ );
58
+
59
+ const totalCount = extractTotalCount(response.headers);
60
+ if (totalCount !== undefined) {
61
+ logger.info(`Total indexes available: ${totalCount}`);
62
+ }
63
+
64
+ console.log(prettifyJson(response.data));
65
+ } catch (e) {
66
+ logger.error("Failed to list indexes");
67
+ printOutError(e, debug);
68
+ }
69
+ }
@@ -0,0 +1,53 @@
1
+ import { MaterializationsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { prettifyJson, printOutError } from "../../utils";
6
+
7
+ const materializationsApi = new MaterializationsApi(new Configuration());
8
+
9
+ /**
10
+ * List the Malloy Persistence materializations bound to a package version.
11
+ * Each materialization prints its `status` and, when FAILED, the `error`
12
+ * explaining why the build failed (e.g. a warehouse permission denial) — so a
13
+ * failing materialization is diagnosable from the CLI without opening the app.
14
+ */
15
+ export async function listMaterializations(
16
+ controlPlane: string,
17
+ packagePath: string,
18
+ versionId: string,
19
+ debug: boolean,
20
+ ) {
21
+ const clientConfig = new ClientConfiguration();
22
+ const organizationName = clientConfig.getOrganization();
23
+ const environmentName = clientConfig.getProject();
24
+
25
+ if (!clientConfig.doesConfigExists()) {
26
+ logger.error("Please, make sure that you are logged in");
27
+ return;
28
+ }
29
+
30
+ if (!environmentName) {
31
+ logger.error("Please, make sure to set an environment");
32
+ return;
33
+ }
34
+
35
+ try {
36
+ const packageName = packagePath.replace("/", "");
37
+ const response = await materializationsApi.listMaterializations(
38
+ organizationName,
39
+ environmentName,
40
+ packageName,
41
+ versionId,
42
+ {
43
+ baseURL: controlPlane,
44
+ headers: clientConfig.getHeaders(),
45
+ },
46
+ );
47
+
48
+ console.log(prettifyJson(response.data));
49
+ } catch (e) {
50
+ logger.error("Failed to list materializations");
51
+ printOutError(e, debug);
52
+ }
53
+ }
@@ -0,0 +1,74 @@
1
+ import { PreaggregatesApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import {
6
+ prettifyJson,
7
+ printOutError,
8
+ parsePaginationParams,
9
+ extractTotalCount,
10
+ } from "../../utils";
11
+
12
+ const preaggregatesApi = new PreaggregatesApi(new Configuration());
13
+
14
+ /**
15
+ * List the pre-aggregated measures of a package version (one entry per `#@ preaggregate`
16
+ * measure). Each entry prints its `status` and, when FAILED, the `error`; plus the
17
+ * `grainDimensions` it is rolled up to, the serving `rowCount`, `lastBuiltAt`, freshness
18
+ * policy, and the `stale`/`staleReasons` triple. `--source` narrows to the measures of one
19
+ * authored source. The total available count is logged to stderr.
20
+ *
21
+ * <p>One rollup table can serve several measures, so entries here outnumber the tables
22
+ * behind them: the measure is what a modeler wrote, and it is the unit this lists.
23
+ */
24
+ export async function listPreaggregates(
25
+ controlPlane: string,
26
+ packagePath: string,
27
+ versionId: string,
28
+ sourceName: string | undefined,
29
+ debug: boolean,
30
+ page?: string | number,
31
+ pageSize?: string | number,
32
+ ) {
33
+ const clientConfig = new ClientConfiguration();
34
+ const organizationName = clientConfig.getOrganization();
35
+ const environmentName = clientConfig.getProject();
36
+
37
+ if (!clientConfig.doesConfigExists()) {
38
+ logger.error("Please, make sure that you are logged in");
39
+ return;
40
+ }
41
+
42
+ if (!environmentName) {
43
+ logger.error("Please, make sure to set an environment");
44
+ return;
45
+ }
46
+
47
+ try {
48
+ const packageName = packagePath.replace("/", "");
49
+ const { limit, offset } = parsePaginationParams(page, pageSize);
50
+ const response = await preaggregatesApi.listPreaggregates(
51
+ organizationName,
52
+ environmentName,
53
+ packageName,
54
+ versionId,
55
+ sourceName,
56
+ limit,
57
+ offset,
58
+ {
59
+ baseURL: controlPlane,
60
+ headers: clientConfig.getHeaders(),
61
+ },
62
+ );
63
+
64
+ const totalCount = extractTotalCount(response.headers);
65
+ if (totalCount !== undefined) {
66
+ logger.info(`Total pre-aggregations available: ${totalCount}`);
67
+ }
68
+
69
+ console.log(prettifyJson(response.data));
70
+ } catch (e) {
71
+ logger.error("Failed to list pre-aggregations");
72
+ printOutError(e, debug);
73
+ }
74
+ }
@@ -0,0 +1,77 @@
1
+ import { RunsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import {
6
+ prettifyJson,
7
+ printOutError,
8
+ parsePaginationParams,
9
+ extractTotalCount,
10
+ } from "../../utils";
11
+
12
+ const runsApi = new RunsApi(new Configuration());
13
+
14
+ /**
15
+ * List the build/refresh runs for a package (most recent first). Runs are one
16
+ * package-scoped resource spanning both families — materialized sources and
17
+ * dimensional indexes. Optional filters narrow the list: `versionId` limits to
18
+ * the runs that impacted a version; `sourceName`/`dimension`/`measure` limit to runs
19
+ * targeting a specific unit (Phase C per-unit dispatch). Each run prints its
20
+ * `status`, `trigger`, `buildNumber`, target scope, and, when failed, its
21
+ * `error`. The per-unit build-plan graph is available via `cred get run`.
22
+ */
23
+ export async function listRuns(
24
+ controlPlane: string,
25
+ packagePath: string,
26
+ versionId: string | undefined,
27
+ debug: boolean,
28
+ sourceName?: string,
29
+ dimension?: string,
30
+ measure?: string,
31
+ page?: string | number,
32
+ pageSize?: string | number,
33
+ ) {
34
+ const clientConfig = new ClientConfiguration();
35
+ const organizationName = clientConfig.getOrganization();
36
+ const environmentName = clientConfig.getProject();
37
+
38
+ if (!clientConfig.doesConfigExists()) {
39
+ logger.error("Please, make sure that you are logged in");
40
+ return;
41
+ }
42
+
43
+ if (!environmentName) {
44
+ logger.error("Please, make sure to set an environment");
45
+ return;
46
+ }
47
+
48
+ try {
49
+ const packageName = packagePath.replace("/", "");
50
+ const { limit, offset } = parsePaginationParams(page, pageSize);
51
+ const response = await runsApi.listRuns(
52
+ organizationName,
53
+ environmentName,
54
+ packageName,
55
+ versionId,
56
+ sourceName,
57
+ measure,
58
+ dimension,
59
+ limit,
60
+ offset,
61
+ {
62
+ baseURL: controlPlane,
63
+ headers: clientConfig.getHeaders(),
64
+ },
65
+ );
66
+
67
+ const totalCount = extractTotalCount(response.headers);
68
+ if (totalCount !== undefined) {
69
+ logger.info(`Total runs available: ${totalCount}`);
70
+ }
71
+
72
+ console.log(prettifyJson(response.data));
73
+ } catch (e) {
74
+ logger.error("Failed to list runs");
75
+ printOutError(e, debug);
76
+ }
77
+ }
@@ -0,0 +1,123 @@
1
+ import { RunsApi, CreateRunRequest } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { getConfirmation, prettifyJson, printOutError } from "../../utils";
6
+
7
+ const runsApi = new RunsApi(new Configuration());
8
+
9
+ export interface RerunScope {
10
+ source?: string;
11
+ dimension?: string;
12
+ measure?: string;
13
+ modelFile?: string;
14
+ includeUpstream?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Trigger an on-demand run (rebuild/refresh) for a package version. With no
19
+ * scope selectors this is a full run: re-materialize every persist source and
20
+ * rebuild every indexed dimension. The optional `--source` / `--dimension` /
21
+ * `--measure` / `--model-file` selectors narrow it to a single unit (Phase C
22
+ * per-unit dispatch); `--measure` rebuilds the rollup serving that
23
+ * pre-aggregated measure, and fails when the measure is declared at more than
24
+ * one grain (pass `--source` to disambiguate); `--include-upstream` additionally force-rebuilds the target's
25
+ * upstream persisted closure. Prints the started run (poll it with
26
+ * `cred get run <package> <runId>`).
27
+ */
28
+ export async function rerun(
29
+ controlPlane: string,
30
+ packagePath: string,
31
+ versionId: string,
32
+ scope: RerunScope,
33
+ debug: boolean,
34
+ autoConfirm: boolean,
35
+ ) {
36
+ const clientConfig = new ClientConfiguration();
37
+ const organizationName = clientConfig.getOrganization();
38
+ const environmentName = clientConfig.getProject();
39
+
40
+ if (!clientConfig.doesConfigExists()) {
41
+ logger.error("Please, make sure that you are logged in");
42
+ return;
43
+ }
44
+
45
+ if (!environmentName) {
46
+ logger.error("Please, make sure to set an environment");
47
+ return;
48
+ }
49
+
50
+ if (!versionId) {
51
+ logger.error("Please provide a version ID");
52
+ return;
53
+ }
54
+
55
+ // Caught here rather than at the server so the message can name the flags. A
56
+ // source may carry a dimension and a measure of the same name, so which one
57
+ // you pass decides what gets rebuilt — there is no safe default.
58
+ if (scope.dimension && scope.measure) {
59
+ logger.error(
60
+ "--dimension and --measure both narrow a run to one field; pass only one",
61
+ );
62
+ return;
63
+ }
64
+
65
+ const packageName = packagePath.replace("/", "");
66
+
67
+ const request: CreateRunRequest = { versionId };
68
+ if (scope.source) request.sourceName = scope.source;
69
+ if (scope.dimension) request.dimension = scope.dimension;
70
+ if (scope.measure) request.measure = scope.measure;
71
+ if (scope.modelFile) request.modelFilePath = scope.modelFile;
72
+ if (scope.includeUpstream) request.includeUpstream = true;
73
+
74
+ const targetLabel = describeScope(scope);
75
+
76
+ if (!autoConfirm) {
77
+ const confirmation = await getConfirmation(
78
+ `Trigger a run for ${packageName}/${versionId} (${targetLabel})?`,
79
+ );
80
+ if (!confirmation) {
81
+ return;
82
+ }
83
+ }
84
+
85
+ logger.info(`Starting run for ${packageName}/${versionId} (${targetLabel})`);
86
+ try {
87
+ const response = await runsApi.createRun(
88
+ organizationName,
89
+ environmentName,
90
+ packageName,
91
+ request,
92
+ {
93
+ baseURL: controlPlane,
94
+ headers: clientConfig.getHeaders(),
95
+ },
96
+ );
97
+
98
+ console.log(prettifyJson(response.data));
99
+ } catch (e) {
100
+ logger.error("Failed to start run");
101
+ printOutError(e, debug);
102
+ }
103
+ }
104
+
105
+ function describeScope(scope: RerunScope): string {
106
+ const parts: string[] = [];
107
+ if (scope.source) parts.push(`source=${scope.source}`);
108
+ if (scope.dimension) parts.push(`dimension=${scope.dimension}`);
109
+ if (scope.measure) parts.push(`measure=${scope.measure}`);
110
+ if (scope.modelFile) parts.push(`modelFile=${scope.modelFile}`);
111
+ if (parts.length === 0) {
112
+ return "full run — all sources, indexes and pre-aggregations";
113
+ }
114
+ if (scope.includeUpstream) parts.push("includeUpstream");
115
+ const scoped = `scoped: ${parts.join(", ")}`;
116
+ // A measure names a COLUMN in a rollup table, and the whole table is what
117
+ // gets rebuilt — so `measure=revenue` alone understates what the caller is
118
+ // agreeing to. This string is the confirmation prompt, so the sibling
119
+ // measures have to be named as consequences before the y/n, not after.
120
+ return scope.measure
121
+ ? `${scoped} — rebuilds the measure's whole rollup table, and so every measure declared at the same grain`
122
+ : scoped;
123
+ }
@@ -1,12 +1,13 @@
1
+ import { spawn } from "child_process";
1
2
  import axios from "axios";
2
3
  import { expect } from "chai";
3
4
  import fs from "fs";
4
5
  import os from "os";
5
- import { join } from "path";
6
+ import { join, resolve } from "path";
6
7
  import sinon from "sinon";
7
8
  import * as credConstants from "../../constants";
8
9
  import { ClientConfiguration } from "../../clientconfiguration";
9
- import { login } from "./login";
10
+ import { isLoginInvocation, login } from "./login";
10
11
  import { OrganizationsApi } from "../../client/api";
11
12
  import * as loopbackOAuth from "./loopbackOAuth";
12
13
 
@@ -19,6 +20,7 @@ function createFakeIdToken(payload: Record<string, unknown>): string {
19
20
  }
20
21
 
21
22
  describe("login", () => {
23
+ let tmpConfigDir: string;
22
24
  let tmpConfigFilePath: string;
23
25
  let axiosStub: sinon.SinonStub;
24
26
  let orgPermissionsStub: sinon.SinonStub;
@@ -51,8 +53,26 @@ describe("login", () => {
51
53
  };
52
54
 
53
55
  beforeEach(() => {
54
- tmpConfigFilePath = join(os.tmpdir(), "cred.json");
55
- fs.writeFileSync(tmpConfigFilePath, "{}");
56
+ // mkdtempSync atomically creates a private (mode 0700) directory
57
+ // with an unguessable suffix, sidestepping the symlink / clobber
58
+ // class that CodeQL flags as js/insecure-temporary-file when a
59
+ // fixed name is reused inside the shared OS tmpdir.
60
+ tmpConfigDir = fs.mkdtempSync(join(os.tmpdir(), "cred-login-spec-"));
61
+ tmpConfigFilePath = join(tmpConfigDir, "cred.json");
62
+ // Seed with a valid ConfigFile. loadConfig now throws on malformed
63
+ // input rather than silently resetting, so an empty `{}` is no longer
64
+ // an acceptable starting state.
65
+ fs.writeFileSync(
66
+ tmpConfigFilePath,
67
+ JSON.stringify({
68
+ configPerEnvironment: {
69
+ production: {},
70
+ staging: {},
71
+ localhost: {},
72
+ },
73
+ activeEnvironment: "production",
74
+ }),
75
+ );
56
76
  axiosStub = sinon.stub(axios, "request");
57
77
  orgPermissionsStub = sinon
58
78
  .stub(OrganizationsApi.prototype, "getOrganization")
@@ -60,7 +80,7 @@ describe("login", () => {
60
80
  });
61
81
 
62
82
  afterEach(() => {
63
- fs.rmSync(tmpConfigFilePath);
83
+ fs.rmSync(tmpConfigDir, { recursive: true, force: true });
64
84
  sinon.restore();
65
85
  });
66
86
 
@@ -159,7 +179,7 @@ describe("login", () => {
159
179
  "https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
160
180
  tmpConfigFilePath,
161
181
  );
162
- expect(orgIdStub.calledOnceWithExactly("videoamp")).to.be.true;
182
+ expect(orgIdStub.calledOnceWithExactly("videoamp", undefined)).to.be.true;
163
183
  expect(loopbackStub.calledOnce).to.be.true;
164
184
  const call = loopbackStub.getCall(0);
165
185
  expect(call.args[0].authorizeUrl).to.contain(
@@ -190,12 +210,35 @@ describe("login", () => {
190
210
  "https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
191
211
  tmpConfigFilePath,
192
212
  );
193
- expect(orgIdStub.calledOnceWithExactly("VideoAmp")).to.be.true;
213
+ expect(orgIdStub.calledOnceWithExactly("VideoAmp", undefined)).to.be.true;
194
214
  expect(loopbackStub.getCall(0).args[0].authorizeUrl).to.contain(
195
215
  "organization=org_nMRKafeSuxOiuJeG",
196
216
  );
197
217
  });
198
218
 
219
+ it("forwards the cluster arg to getAuth0OrganizationIdForCliLogin (so -c aws triggers PKCE)", async function () {
220
+ this.timeout(2000);
221
+ const orgIdStub = sinon
222
+ .stub(credConstants, "getAuth0OrganizationIdForCliLogin")
223
+ .returns("org_nMRKafeSuxOiuJeG");
224
+ sinon
225
+ .stub(loopbackOAuth, "authorizationCodeViaLoopback")
226
+ .callsFake(async (opts) => {
227
+ opts.onListening?.();
228
+ return { code: "test-pkce-auth-code" };
229
+ });
230
+ axiosStub.onFirstCall().resolves(mockedAccessTokenResponse);
231
+ const config = new ClientConfiguration(tmpConfigFilePath);
232
+ config.setEnvironment("videoamp");
233
+ await login(
234
+ "videoamp",
235
+ "https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
236
+ tmpConfigFilePath,
237
+ "aws",
238
+ );
239
+ expect(orgIdStub.calledOnceWithExactly("videoamp", "aws")).to.be.true;
240
+ });
241
+
199
242
  it("should not pass Auth0 organization for unmapped org names", async function () {
200
243
  this.timeout(2000);
201
244
  axiosStub
@@ -211,3 +254,118 @@ describe("login", () => {
211
254
  );
212
255
  });
213
256
  });
257
+
258
+ describe("isLoginInvocation", () => {
259
+ it("is true for a `cred login <org>` invocation", () => {
260
+ expect(isLoginInvocation(["node", "/x/index.js", "login", "myorg"])).to.be
261
+ .true;
262
+ });
263
+
264
+ it("is false for other commands (they stay fail-loud on a corrupt config)", () => {
265
+ expect(isLoginInvocation(["node", "/x/index.js", "status"])).to.be.false;
266
+ expect(isLoginInvocation(["node", "/x/index.js", "publish"])).to.be.false;
267
+ expect(isLoginInvocation(["node", "/x/index.js"])).to.be.false;
268
+ });
269
+ });
270
+
271
+ // Drives the real binary entrypoint (src/index.ts), not recoverIfCorrupt() in
272
+ // isolation. index.ts reads the active environment at module load (axios
273
+ // baseURL + command registration), so a corrupt ~/.cred used to throw before
274
+ // the login action ran — dead-ending the documented recovery path. This guards
275
+ // that ordering: with HOME pointed at a fixture dir holding a corrupt config,
276
+ // `index.ts login <org>` must reset the file rather than crash with the
277
+ // structure error.
278
+ describe("entrypoint recovery for a corrupt ~/.cred (real index.ts)", () => {
279
+ let tmpHome: string;
280
+ const indexPath = resolve(__dirname, "../../index.ts");
281
+ const cliRoot = resolve(__dirname, "../../..");
282
+
283
+ beforeEach(() => {
284
+ tmpHome = fs.mkdtempSync(join(os.tmpdir(), "cred-entrypoint-"));
285
+ });
286
+
287
+ afterEach(() => {
288
+ fs.rmSync(tmpHome, { recursive: true, force: true });
289
+ });
290
+
291
+ const findBackup = (): string | undefined =>
292
+ fs.readdirSync(tmpHome).find((f) => f.startsWith(".cred.corrupt-"));
293
+
294
+ it("backs up the corrupt file and resets instead of crashing", async function () {
295
+ this.timeout(60000);
296
+
297
+ // A structurally-invalid config: parses as JSON but has neither
298
+ // activeEnvironment nor configPerEnvironment — exactly what triggered the
299
+ // customer's "re-run cred login" loop.
300
+ const credPath = join(tmpHome, ".cred");
301
+ fs.writeFileSync(credPath, JSON.stringify({ garbage: true }));
302
+
303
+ const child = spawn(
304
+ process.execPath,
305
+ ["-r", "ts-node/register", indexPath, "login", "testorg"],
306
+ {
307
+ cwd: cliRoot,
308
+ env: {
309
+ ...process.env,
310
+ HOME: tmpHome,
311
+ USERPROFILE: tmpHome,
312
+ // Runtime behavior is what we're testing; skip type-checking the
313
+ // whole import graph so an unrelated type error can't derail it.
314
+ TS_NODE_TRANSPILE_ONLY: "1",
315
+ },
316
+ },
317
+ );
318
+
319
+ let output = "";
320
+ child.stdout.on("data", (d) => (output += d.toString()));
321
+ child.stderr.on("data", (d) => (output += d.toString()));
322
+
323
+ // Recovery runs synchronously at module load, before login() touches the
324
+ // network. It renames the corrupt file aside (.cred.corrupt-*) and THEN
325
+ // rewrites .cred to a valid default. Gate on the rewrite, not the backup:
326
+ // the backup appears first, so killing the instant it shows can catch the
327
+ // child in the gap before .cred is rewritten — a flaky ENOENT on the read
328
+ // below. resetComplete() only goes true once .cred is the valid default
329
+ // (which implies the backup already happened). Then stop the process
330
+ // (login otherwise blocks on the device-code flow).
331
+ const resetComplete = (): boolean => {
332
+ try {
333
+ return (
334
+ JSON.parse(fs.readFileSync(credPath, "utf8"))
335
+ .activeEnvironment === "production"
336
+ );
337
+ } catch {
338
+ return false; // .cred absent or mid-rewrite
339
+ }
340
+ };
341
+ await new Promise<void>((resolveWait) => {
342
+ const deadline = Date.now() + 55000;
343
+ const timer = setInterval(() => {
344
+ if (resetComplete() || Date.now() > deadline) {
345
+ clearInterval(timer);
346
+ resolveWait();
347
+ }
348
+ }, 200);
349
+ child.on("exit", () => {
350
+ clearInterval(timer);
351
+ resolveWait();
352
+ });
353
+ });
354
+ child.kill("SIGKILL");
355
+
356
+ const backup = findBackup();
357
+ expect(backup, "corrupt config should be backed up aside").to.be.a(
358
+ "string",
359
+ );
360
+ // The bad contents are preserved in the backup, not destroyed.
361
+ expect(
362
+ JSON.parse(fs.readFileSync(join(tmpHome, backup!), "utf8")),
363
+ ).to.deep.equal({ garbage: true });
364
+ // ~/.cred is rewritten to a valid default the next read can load.
365
+ const reset = JSON.parse(fs.readFileSync(credPath, "utf8"));
366
+ expect(reset.activeEnvironment).to.equal("production");
367
+ expect(reset.configPerEnvironment).to.be.an("object");
368
+ // The startup read no longer dead-ends on the structure error.
369
+ expect(output).to.not.match(/Invalid config structure/);
370
+ });
371
+ });