@celonis/content-cli 1.17.0 → 1.18.1

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 (41) hide show
  1. package/commands/configuration-management/api/package-version-api.js +9 -0
  2. package/commands/configuration-management/module.js +10 -0
  3. package/commands/configuration-management/package-version-command.service.js +5 -0
  4. package/commands/configuration-management/package-version.service.js +16 -0
  5. package/commands/sdk/module.js +29 -13
  6. package/commands/sdk/preview-certificate.js +82 -0
  7. package/commands/sdk/preview-headers.js +25 -0
  8. package/commands/sdk/preview-inline-scripts.js +50 -0
  9. package/commands/sdk/preview-proxy.js +182 -0
  10. package/commands/sdk/sdk-api.js +3 -13
  11. package/commands/sdk/sdk-preview.service.js +98 -0
  12. package/commands/sdk/sdk.service.js +18 -15
  13. package/commands/t2tc/api/t2tc-package-api.js +10 -0
  14. package/commands/t2tc/batch-export-import.constants.js +1 -0
  15. package/commands/t2tc/module.js +5 -1
  16. package/commands/t2tc/t2tc-command.service.js +2 -2
  17. package/commands/t2tc/t2tc-package.service.js +39 -10
  18. package/content-cli.js +2 -2
  19. package/core/command/module-handler.js +4 -7
  20. package/core/http/http-client.js +3 -2
  21. package/core/profile/profile.service.js +45 -1
  22. package/core/utils/logger.js +1 -0
  23. package/lib/commands/configuration-management/api/package-version-api.d.ts +1 -0
  24. package/lib/commands/configuration-management/module.d.ts +1 -0
  25. package/lib/commands/configuration-management/package-version-command.service.d.ts +1 -0
  26. package/lib/commands/configuration-management/package-version.service.d.ts +1 -0
  27. package/lib/commands/sdk/module.d.ts +2 -1
  28. package/lib/commands/sdk/preview-certificate.d.ts +5 -0
  29. package/lib/commands/sdk/preview-headers.d.ts +5 -0
  30. package/lib/commands/sdk/preview-inline-scripts.d.ts +13 -0
  31. package/lib/commands/sdk/preview-proxy.d.ts +14 -0
  32. package/lib/commands/sdk/sdk-api.d.ts +5 -2
  33. package/lib/commands/sdk/sdk-preview.service.d.ts +11 -0
  34. package/lib/commands/sdk/sdk.service.d.ts +4 -3
  35. package/lib/commands/t2tc/api/t2tc-package-api.d.ts +1 -0
  36. package/lib/commands/t2tc/batch-export-import.constants.d.ts +2 -1
  37. package/lib/commands/t2tc/t2tc-command.service.d.ts +1 -1
  38. package/lib/commands/t2tc/t2tc-package.service.d.ts +3 -1
  39. package/lib/core/http/http-client.d.ts +1 -1
  40. package/lib/core/profile/profile.service.d.ts +3 -0
  41. package/package.json +6 -5
@@ -24,6 +24,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
24
24
  constructor(context) {
25
25
  this.httpClient = () => context.httpClient;
26
26
  }
27
+ findAll(packageKey) {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ return this.httpClient()
30
+ .get(`/pacman/api/core/packages/${packageKey}/versions`)
31
+ .catch(e => {
32
+ throw new logger_1.FatalError(`Problem listing versions of Package with key '${packageKey}': ${e}`);
33
+ });
34
+ });
35
+ }
27
36
  findOne(packageKey, version) {
28
37
  return __awaiter(this, void 0, void 0, function* () {
29
38
  return this.httpClient()
@@ -202,6 +202,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
202
202
  .requiredOption("--packageVersion <packageVersion>", "Version of the package. Can be a specific version or \"LATEST\" keyword, which will return the most recently created version.")
203
203
  .option("--json", "Return the response as a JSON file")
204
204
  .action(this.getPackageVersion);
205
+ configVersionCommand.command("list")
206
+ .description("List all created versions of a package or branch")
207
+ .requiredOption("--packageKey <packageKey>", "Identifier of the package. Use <projectKey>@<branchKey> for a branch.")
208
+ .option("--json", "Return the response as a JSON file")
209
+ .action(this.listPackageVersions);
205
210
  configVersionCommand.command("create")
206
211
  .description("Create a new version for a package")
207
212
  .requiredOption("--packageKey <packageKey>", "Identifier of the package")
@@ -421,6 +426,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
421
426
  yield new package_version_command_service_1.PackageVersionCommandService(context).getPackageVersion(options.packageKey, options.packageVersion, options.json);
422
427
  });
423
428
  }
429
+ listPackageVersions(context, command, options) {
430
+ return __awaiter(this, void 0, void 0, function* () {
431
+ yield new package_version_command_service_1.PackageVersionCommandService(context).listPackageVersions(options.packageKey, options.json);
432
+ });
433
+ }
424
434
  createPackageVersion(context, command, options) {
425
435
  return __awaiter(this, void 0, void 0, function* () {
426
436
  const hasExplicitVersion = !!options.packageVersion;
@@ -29,6 +29,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
29
29
  yield this.packageVersionService.findPackageVersion(packageKey, version, jsonResponse);
30
30
  });
31
31
  }
32
+ listPackageVersions(packageKey, jsonResponse) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ yield this.packageVersionService.listPackageVersions(packageKey, jsonResponse);
35
+ });
36
+ }
32
37
  createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse) {
33
38
  return __awaiter(this, void 0, void 0, function* () {
34
39
  yield this.packageVersionService.createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse);
@@ -42,6 +42,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  }
43
43
  });
44
44
  }
45
+ listPackageVersions(packageKey, jsonResponse) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const packageVersions = yield this.packageVersionApi.findAll(packageKey);
48
+ if (jsonResponse) {
49
+ const filename = (0, uuid_1.v4)() + ".json";
50
+ const writtenFilename = yield this.cuiFileService.writeToFileWithGivenName(JSON.stringify(packageVersions, null, 2), filename);
51
+ logger_1.logger.info(file_service_1.FileService.fileDownloadedMessage + writtenFilename);
52
+ }
53
+ else if (packageVersions.length === 0) {
54
+ logger_1.logger.info(`Package ${packageKey} has no versions.`);
55
+ }
56
+ else {
57
+ packageVersions.forEach(packageVersion => logger_1.logger.info(JSON.stringify(packageVersion)));
58
+ }
59
+ });
60
+ }
45
61
  createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse) {
46
62
  return __awaiter(this, void 0, void 0, function* () {
47
63
  const request = {
@@ -13,40 +13,56 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  if (v !== undefined) module.exports = v;
14
14
  }
15
15
  else if (typeof define === "function" && define.amd) {
16
- define(["require", "exports", "../../core/command/module-handler", "./sdk.service"], factory);
16
+ define(["require", "exports", "../../core/command/module-handler", "./sdk-preview.service", "./sdk.service"], factory);
17
17
  }
18
18
  })(function (require, exports) {
19
19
  "use strict";
20
20
  const module_handler_1 = require("../../core/command/module-handler");
21
+ const sdk_preview_service_1 = require("./sdk-preview.service");
21
22
  const sdk_service_1 = require("./sdk.service");
22
23
  class Module extends module_handler_1.IModule {
23
24
  register(context, configurator) {
24
25
  const sdkCommand = configurator
25
26
  .command("sdk")
26
27
  .beta()
27
- .description("Implement custom apps for Codespace assets using the Celonis SDK");
28
- sdkCommand
29
- .command("download")
30
- .beta()
31
- .description("Download the latest Celonis SDK generated for a Codespace asset")
32
- .requiredOption("--appId <appId>", "Identifier of the app to download the SDK for")
33
- .action(this.downloadSdk);
28
+ .description("Implement custom apps for Codehub assets using the Celonis SDK");
34
29
  sdkCommand
35
30
  .command("publish")
36
31
  .beta()
37
- .description("Publish a build of a custom app as a new version of its Codespace asset")
32
+ .description("Publish a build of a custom app as a new version of its Codehub asset")
38
33
  .requiredOption("--appId <appId>", "Identifier of the app to publish the build for")
39
34
  .requiredOption("--dist <dist>", "Directory containing the build of the app")
35
+ .option("--buildVersion <buildVersion>", "Semantic version of the build. Defaults to the next major version")
36
+ .option("--message <message>", "Message describing the build, at most 500 characters")
40
37
  .action(this.publishApp);
38
+ sdkCommand
39
+ .command("preview")
40
+ .beta()
41
+ .skipProfileLoading()
42
+ .description("Proxy your local dev server to preview the app inside Celonis")
43
+ .option("--target <target>", "URL of the local dev server to proxy", "http://localhost:4200")
44
+ .option("--port <port>", "Target port on which the preview is hosted", "4300")
45
+ .option("--allow-inline-scripts", "Allow inline scripts in your dev server's HTML")
46
+ .action(this.previewApp);
47
+ sdkCommand
48
+ .command("token")
49
+ .beta()
50
+ .description("Print a token with read access to the Celonis SDK, e.g. to authenticate against the npm registry")
51
+ .action(this.printToken);
41
52
  }
42
- downloadSdk(context, command, options) {
53
+ publishApp(context, command, options) {
43
54
  return __awaiter(this, void 0, void 0, function* () {
44
- yield new sdk_service_1.SdkService(context).downloadSdk(options.appId);
55
+ yield new sdk_service_1.SdkService(context).publishApp(options.appId, options.dist, options.buildVersion, options.message);
45
56
  });
46
57
  }
47
- publishApp(context, command, options) {
58
+ previewApp(context, command, options) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ yield new sdk_preview_service_1.SdkPreviewService().previewUntilStopped(options.target, options.port, options.allowInlineScripts === true);
61
+ });
62
+ }
63
+ printToken(context) {
48
64
  return __awaiter(this, void 0, void 0, function* () {
49
- yield new sdk_service_1.SdkService(context).publishApp(options.appId, options.dist);
65
+ yield new sdk_service_1.SdkService(context).printToken();
50
66
  });
51
67
  }
52
68
  }
@@ -0,0 +1,82 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ (function (factory) {
11
+ if (typeof module === "object" && typeof module.exports === "object") {
12
+ var v = factory(require, exports);
13
+ if (v !== undefined) module.exports = v;
14
+ }
15
+ else if (typeof define === "function" && define.amd) {
16
+ define(["require", "exports", "node:crypto", "node:fs", "node:path", "selfsigned"], factory);
17
+ }
18
+ })(function (require, exports) {
19
+ "use strict";
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.loadOrCreatePreviewCertificate = void 0;
22
+ const node_crypto_1 = require("node:crypto");
23
+ const node_fs_1 = require("node:fs");
24
+ const node_path_1 = require("node:path");
25
+ const selfsigned_1 = require("selfsigned");
26
+ const DAY_MS = 24 * 60 * 60 * 1000;
27
+ const CERT_FILE = "localhost-cert.pem";
28
+ const KEY_FILE = "localhost-key.pem";
29
+ function loadOrCreatePreviewCertificate(directory, now = new Date()) {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ const existing = readUsableCertificate(directory, now);
32
+ if (existing) {
33
+ return existing;
34
+ }
35
+ const generated = yield (0, selfsigned_1.generate)([{ name: "commonName", value: "localhost" }], {
36
+ keyType: "ec",
37
+ curve: "P-256",
38
+ algorithm: "sha256",
39
+ notBeforeDate: now,
40
+ notAfterDate: new Date(now.getTime() + 365 * DAY_MS),
41
+ extensions: [
42
+ {
43
+ name: "subjectAltName",
44
+ altNames: [
45
+ { type: 2, value: "localhost" },
46
+ { type: 7, ip: "127.0.0.1" },
47
+ { type: 7, ip: "::1" },
48
+ ],
49
+ },
50
+ ],
51
+ });
52
+ const certificate = { key: generated.private, cert: generated.cert };
53
+ (0, node_fs_1.mkdirSync)(directory, { recursive: true });
54
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(directory, CERT_FILE), certificate.cert);
55
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(directory, KEY_FILE), certificate.key, { mode: 0o600 });
56
+ return certificate;
57
+ });
58
+ }
59
+ exports.loadOrCreatePreviewCertificate = loadOrCreatePreviewCertificate;
60
+ function readUsableCertificate(directory, now) {
61
+ const certPath = (0, node_path_1.join)(directory, CERT_FILE);
62
+ const keyPath = (0, node_path_1.join)(directory, KEY_FILE);
63
+ if (!(0, node_fs_1.existsSync)(certPath) || !(0, node_fs_1.existsSync)(keyPath)) {
64
+ return null;
65
+ }
66
+ try {
67
+ const cert = (0, node_fs_1.readFileSync)(certPath, "utf8");
68
+ const key = (0, node_fs_1.readFileSync)(keyPath, "utf8");
69
+ const x509 = new node_crypto_1.X509Certificate(cert);
70
+ if (!x509.checkPrivateKey((0, node_crypto_1.createPrivateKey)(key))) {
71
+ return null;
72
+ }
73
+ if (new Date(x509.validTo).getTime() - now.getTime() <= DAY_MS) {
74
+ return null;
75
+ }
76
+ return { key, cert };
77
+ }
78
+ catch (_a) {
79
+ return null;
80
+ }
81
+ }
82
+ });
@@ -0,0 +1,25 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.LOCAL_PREVIEW_HEADERS = void 0;
13
+ /**
14
+ * Copied from `localPreviewHeaders()` in ems-frontend `libs/common/fragments/sandbox/src/lib/policy.ts`,
15
+ * whose spec fails when the gateway policy drifts. Update both together.
16
+ */
17
+ exports.LOCAL_PREVIEW_HEADERS = {
18
+ "content-security-policy": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; media-src 'self' blob:; connect-src 'none'; worker-src 'none'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors https://*.celonis.cloud http://localhost:*; sandbox allow-scripts",
19
+ "permissions-policy": "camera=(), microphone=(), geolocation=(), payment=(), usb=(), serial=(), hid=(), clipboard-read=(), clipboard-write=(), display-capture=()",
20
+ "access-control-allow-origin": "*",
21
+ "referrer-policy": "no-referrer",
22
+ "x-content-type-options": "nosniff",
23
+ "cache-control": "no-store",
24
+ };
25
+ });
@@ -0,0 +1,50 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports", "node:crypto"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.allowInlineScripts = exports.inlineScriptHashes = void 0;
13
+ const node_crypto_1 = require("node:crypto");
14
+ const SCRIPT_ELEMENT = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
15
+ const SRC_ATTRIBUTE = /(?:^|[\s/])src(?=[\s=/]|$)/i;
16
+ const EXCERPT_LENGTH = 60;
17
+ /**
18
+ * The inline scripts (`<script>` without `src`) of an HTML document, distinct by hash and in document order.
19
+ * Each hash covers the script text as the browser parses it for CSP (line endings normalized to LF).
20
+ */
21
+ function inlineScriptHashes(html) {
22
+ const scripts = new Map();
23
+ for (const [, attributes, text] of html.matchAll(SCRIPT_ELEMENT)) {
24
+ if (SRC_ATTRIBUTE.test(attributes) || text.length === 0) {
25
+ continue;
26
+ }
27
+ // Browsers hash the parsed text, and the HTML parser turns CRLF and lone CR into LF.
28
+ const parsedText = text.replace(/\r\n?/g, "\n");
29
+ const hash = `'sha256-${(0, node_crypto_1.createHash)("sha256").update(parsedText, "utf8").digest("base64")}'`;
30
+ if (!scripts.has(hash)) {
31
+ scripts.set(hash, { hash, excerpt: text.replace(/\s+/g, " ").trim().slice(0, EXCERPT_LENGTH) });
32
+ }
33
+ }
34
+ return [...scripts.values()];
35
+ }
36
+ exports.inlineScriptHashes = inlineScriptHashes;
37
+ /** Appends the hashes to the policy's `script-src` directive and leaves every other directive unchanged. */
38
+ function allowInlineScripts(csp, hashes) {
39
+ if (hashes.length === 0) {
40
+ return csp;
41
+ }
42
+ return csp
43
+ .split(";")
44
+ .map(directive => /^\s*script-src(\s|$)/i.test(directive)
45
+ ? `${directive.trimEnd()} ${hashes.join(" ")}`
46
+ : directive)
47
+ .join(";");
48
+ }
49
+ exports.allowInlineScripts = allowInlineScripts;
50
+ });
@@ -0,0 +1,182 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports", "node:http", "node:https", "node:net", "node:tls", "../../core/utils/logger", "./preview-inline-scripts"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.startPreviewProxy = void 0;
13
+ const http = require("node:http");
14
+ const https = require("node:https");
15
+ const net = require("node:net");
16
+ const tls = require("node:tls");
17
+ const logger_1 = require("../../core/utils/logger");
18
+ const preview_inline_scripts_1 = require("./preview-inline-scripts");
19
+ const REPLACED_HEADERS = ["content-security-policy", "content-security-policy-report-only", "x-frame-options"];
20
+ const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "[::1]"]);
21
+ const BODILESS_STATUSES = new Set([204, 304]);
22
+ function inlineScriptWarning(path, excerpt, allowed) {
23
+ return allowed
24
+ ? `Allowing an inline script in ${path} for the local preview: "${excerpt}". ` +
25
+ "Inline scripts are disabled for published apps, so they can only be used during development."
26
+ : `Blocking an inline script in ${path}: "${excerpt}". Inline scripts are disabled for published apps. You can allow inline scripts during development by passing --allow-inline-scripts.`;
27
+ }
28
+ function startPreviewProxy(options) {
29
+ const { target, port, certificate, headers, allowInlineScripts: allowInline } = options;
30
+ const secureTarget = target.protocol === "https:";
31
+ const targetHostname = target.hostname.replace(/^\[(.*)\]$/, "$1");
32
+ const targetPort = Number(target.port) || (secureTarget ? 443 : 80);
33
+ const rejectUnauthorized = !LOOPBACK_HOSTNAMES.has(target.hostname);
34
+ const agent = secureTarget
35
+ ? new https.Agent({ keepAlive: true, rejectUnauthorized })
36
+ : new http.Agent({ keepAlive: true });
37
+ const removedHeaders = new Set([...REPLACED_HEADERS, ...Object.keys(headers).map(name => name.toLowerCase())]);
38
+ const unreachableMessage = `content-cli sdk preview can't reach ${target.origin}. Start your dev server or pass --target.`;
39
+ const openSockets = new Set();
40
+ const warnedInlineScripts = new Set();
41
+ let warnedUnreachable = false;
42
+ function track(socket) {
43
+ openSockets.add(socket);
44
+ socket.once("close", () => openSockets.delete(socket));
45
+ }
46
+ function forwardRequest(req, res) {
47
+ const proxyReq = (secureTarget ? https : http).request({
48
+ protocol: target.protocol,
49
+ hostname: targetHostname,
50
+ port: targetPort,
51
+ method: req.method,
52
+ path: req.url,
53
+ headers: Object.assign(Object.assign({}, req.headers), { host: target.host, "accept-encoding": "identity" }),
54
+ agent,
55
+ });
56
+ let responded = false;
57
+ proxyReq.on("response", proxyRes => {
58
+ var _a;
59
+ responded = true;
60
+ warnedUnreachable = false;
61
+ const responseHeaders = {};
62
+ for (const [name, value] of Object.entries(proxyRes.headers)) {
63
+ if (!removedHeaders.has(name.toLowerCase())) {
64
+ responseHeaders[name] = value;
65
+ }
66
+ }
67
+ proxyRes.on("error", () => res.destroy());
68
+ proxyRes.on("aborted", () => res.destroy());
69
+ const isHtml = /^text\/html\b/i.test((_a = proxyRes.headers["content-type"]) !== null && _a !== void 0 ? _a : "");
70
+ const hasBody = req.method !== "HEAD" && !BODILESS_STATUSES.has(proxyRes.statusCode);
71
+ if (!isHtml || !hasBody) {
72
+ res.writeHead(proxyRes.statusCode, Object.assign(Object.assign({}, responseHeaders), headers));
73
+ proxyRes.pipe(res);
74
+ return;
75
+ }
76
+ const chunks = [];
77
+ proxyRes.on("data", (chunk) => chunks.push(chunk));
78
+ proxyRes.on("end", () => {
79
+ if (res.destroyed) {
80
+ return;
81
+ }
82
+ const body = Buffer.concat(chunks);
83
+ const scripts = (0, preview_inline_scripts_1.inlineScriptHashes)(body.toString("utf8"));
84
+ for (const { hash, excerpt } of scripts) {
85
+ if (!warnedInlineScripts.has(hash)) {
86
+ warnedInlineScripts.add(hash);
87
+ logger_1.logger.warn(inlineScriptWarning(req.url, excerpt, allowInline));
88
+ }
89
+ }
90
+ if (proxyRes.headers["content-length"] !== undefined) {
91
+ responseHeaders["content-length"] = body.length;
92
+ }
93
+ const csp = headers["content-security-policy"];
94
+ res.writeHead(proxyRes.statusCode, Object.assign(Object.assign(Object.assign({}, responseHeaders), headers), (csp === undefined || !allowInline ? {} : {
95
+ "content-security-policy": (0, preview_inline_scripts_1.allowInlineScripts)(csp, scripts.map(script => script.hash)),
96
+ })));
97
+ res.end(body);
98
+ });
99
+ });
100
+ proxyReq.on("error", () => {
101
+ if (res.destroyed) {
102
+ return;
103
+ }
104
+ if (responded || res.headersSent) {
105
+ res.destroy();
106
+ return;
107
+ }
108
+ if (!warnedUnreachable) {
109
+ warnedUnreachable = true;
110
+ logger_1.logger.warn(unreachableMessage);
111
+ }
112
+ res.writeHead(502, Object.assign(Object.assign({}, headers), { "content-type": "text/plain" }));
113
+ res.end(unreachableMessage);
114
+ });
115
+ res.on("close", () => {
116
+ if (!res.writableFinished) {
117
+ proxyReq.destroy();
118
+ }
119
+ });
120
+ req.pipe(proxyReq);
121
+ }
122
+ function forwardUpgrade(req, socket, head) {
123
+ const targetSocket = secureTarget
124
+ ? tls.connect({
125
+ host: targetHostname,
126
+ port: targetPort,
127
+ servername: net.isIP(targetHostname) ? undefined : targetHostname,
128
+ rejectUnauthorized,
129
+ })
130
+ : net.connect({ host: targetHostname, port: targetPort });
131
+ track(targetSocket);
132
+ const destroyBoth = () => {
133
+ socket.destroy();
134
+ targetSocket.destroy();
135
+ };
136
+ socket.on("error", destroyBoth);
137
+ targetSocket.on("error", destroyBoth);
138
+ socket.on("close", () => targetSocket.destroy());
139
+ targetSocket.on("close", () => socket.destroy());
140
+ targetSocket.once(secureTarget ? "secureConnect" : "connect", () => {
141
+ const lines = [`${req.method} ${req.url} HTTP/${req.httpVersion}`];
142
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
143
+ const name = req.rawHeaders[i];
144
+ lines.push(`${name}: ${name.toLowerCase() === "host" ? target.host : req.rawHeaders[i + 1]}`);
145
+ }
146
+ targetSocket.write(`${lines.join("\r\n")}\r\n\r\n`);
147
+ if (head.length > 0) {
148
+ targetSocket.write(head);
149
+ }
150
+ targetSocket.pipe(socket);
151
+ socket.pipe(targetSocket);
152
+ });
153
+ }
154
+ const server = https.createServer({ key: certificate.key, cert: certificate.cert }, forwardRequest);
155
+ server.on("connection", track);
156
+ server.on("upgrade", forwardUpgrade);
157
+ function close() {
158
+ return new Promise(closed => {
159
+ server.close(() => closed());
160
+ openSockets.forEach(socket => socket.destroy());
161
+ agent.destroy();
162
+ });
163
+ }
164
+ return new Promise((resolve, reject) => {
165
+ const onListenError = (error) => {
166
+ agent.destroy();
167
+ reject(new logger_1.FatalError(error.code === "EADDRINUSE"
168
+ ? `Port ${port} is already in use. Stop the other process or pass --port.`
169
+ : `Could not start the preview proxy on port ${port}: ${error.message}`));
170
+ };
171
+ server.once("error", onListenError);
172
+ server.listen({ port, host: "localhost" }, () => {
173
+ server.off("error", onListenError);
174
+ resolve({
175
+ url: `https://localhost:${server.address().port}`,
176
+ close,
177
+ });
178
+ });
179
+ });
180
+ }
181
+ exports.startPreviewProxy = startPreviewProxy;
182
+ });
@@ -24,21 +24,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
24
24
  constructor(context) {
25
25
  this.httpClient = () => context.httpClient;
26
26
  }
27
- downloadSdk(appId) {
27
+ publishApp(appId, bundle, options) {
28
28
  return __awaiter(this, void 0, void 0, function* () {
29
29
  return this.httpClient()
30
- .getFile(`/pro-code-app-manager/api/npm/@celonis-sdk/${appId}/-/${appId}-latest.tgz`)
31
- .catch((e) => {
32
- const problem = `Problem downloading SDK for app ${appId}`;
33
- throw new logger_1.FatalError(e ? `${problem}: ${e}` : problem);
34
- });
35
- });
36
- }
37
- publishApp(appId, bundle) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- return this.httpClient()
40
- .postBinary(`/pro-code-app-manager/api/codespace/apps/${appId}/publish`, bundle, "application/zip")
41
- .catch((e) => {
30
+ .postBinary(`/pro-code-app-manager/api/codespace/apps/${appId}/publish`, bundle, "application/zip", options)
31
+ .catch(e => {
42
32
  throw new logger_1.FatalError(`Problem publishing app ${appId}: ${e}`);
43
33
  });
44
34
  });
@@ -0,0 +1,98 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ (function (factory) {
11
+ if (typeof module === "object" && typeof module.exports === "object") {
12
+ var v = factory(require, exports);
13
+ if (v !== undefined) module.exports = v;
14
+ }
15
+ else if (typeof define === "function" && define.amd) {
16
+ define(["require", "exports", "node:os", "node:path", "../../core/utils/logger", "./preview-certificate", "./preview-headers", "./preview-proxy"], factory);
17
+ }
18
+ })(function (require, exports) {
19
+ "use strict";
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.SdkPreviewService = void 0;
22
+ const os = require("node:os");
23
+ const path = require("node:path");
24
+ const logger_1 = require("../../core/utils/logger");
25
+ const preview_certificate_1 = require("./preview-certificate");
26
+ const preview_headers_1 = require("./preview-headers");
27
+ const preview_proxy_1 = require("./preview-proxy");
28
+ class SdkPreviewService {
29
+ constructor(certificateDirectory = SdkPreviewService.CERTIFICATE_DIRECTORY) {
30
+ this.certificateDirectory = certificateDirectory;
31
+ }
32
+ preview(target, port, allowInlineScripts = false) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ const targetUrl = this.parseTarget(target);
35
+ const portNumber = this.parsePort(port);
36
+ const certificate = yield (0, preview_certificate_1.loadOrCreatePreviewCertificate)(this.certificateDirectory);
37
+ const proxy = yield (0, preview_proxy_1.startPreviewProxy)({
38
+ target: targetUrl,
39
+ port: portNumber,
40
+ certificate,
41
+ headers: preview_headers_1.LOCAL_PREVIEW_HEADERS,
42
+ allowInlineScripts,
43
+ });
44
+ logger_1.logger.info(`Local preview running on https://localhost:${portNumber}, forwarding to ${target}.`);
45
+ logger_1.logger.info(`Open the Local preview in your Codehub. If it stays blank, open https://localhost:${portNumber} in a browser tab and accept the certificate, then reload the preview.`);
46
+ logger_1.logger.info("Press Ctrl+C to stop.");
47
+ return proxy;
48
+ });
49
+ }
50
+ previewUntilStopped(target, port, allowInlineScripts = false) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ const proxy = yield this.preview(target, port, allowInlineScripts);
53
+ yield this.waitForShutdownSignal();
54
+ yield proxy.close();
55
+ });
56
+ }
57
+ waitForShutdownSignal() {
58
+ return new Promise(resolve => {
59
+ const stop = () => {
60
+ process.removeListener("SIGINT", stop);
61
+ process.removeListener("SIGTERM", stop);
62
+ resolve();
63
+ };
64
+ process.once("SIGINT", stop);
65
+ process.once("SIGTERM", stop);
66
+ });
67
+ }
68
+ parseTarget(target) {
69
+ const message = "--target must be an http or https origin, for example http://localhost:4200";
70
+ let url;
71
+ try {
72
+ url = new URL(target);
73
+ }
74
+ catch (_a) {
75
+ throw new logger_1.FatalError(message);
76
+ }
77
+ const isOrigin = (url.protocol === "http:" || url.protocol === "https:") &&
78
+ !url.username &&
79
+ !url.password &&
80
+ url.pathname === "/" &&
81
+ !url.search &&
82
+ !url.hash;
83
+ if (!isOrigin) {
84
+ throw new logger_1.FatalError(message);
85
+ }
86
+ return url;
87
+ }
88
+ parsePort(port) {
89
+ const value = typeof port === "number" || /^\d+$/.test(port) ? Number(port) : Number.NaN;
90
+ if (!Number.isInteger(value) || value < 1 || value > 65535) {
91
+ throw new logger_1.FatalError("--port must be a number between 1 and 65535");
92
+ }
93
+ return value;
94
+ }
95
+ }
96
+ exports.SdkPreviewService = SdkPreviewService;
97
+ SdkPreviewService.CERTIFICATE_DIRECTORY = path.resolve(os.homedir(), ".celonis-content-cli-preview");
98
+ });
@@ -13,7 +13,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  if (v !== undefined) module.exports = v;
14
14
  }
15
15
  else if (typeof define === "function" && define.amd) {
16
- define(["require", "exports", "node:fs", "node:path", "adm-zip", "./sdk-api", "../../core/utils/logger"], factory);
16
+ define(["require", "exports", "node:fs", "node:path", "adm-zip", "./sdk-api", "../../core/utils/logger", "../../core/profile/profile.service"], factory);
17
17
  }
18
18
  })(function (require, exports) {
19
19
  "use strict";
@@ -24,30 +24,34 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
24
24
  const AdmZip = require("adm-zip");
25
25
  const sdk_api_1 = require("./sdk-api");
26
26
  const logger_1 = require("../../core/utils/logger");
27
+ const profile_service_1 = require("../../core/profile/profile.service");
27
28
  class SdkService {
28
29
  constructor(context) {
30
+ this.context = context;
29
31
  this.sdkApi = new sdk_api_1.SdkApi(context);
30
32
  }
31
- downloadSdk(appId) {
32
- return __awaiter(this, void 0, void 0, function* () {
33
- const sdk = yield this.sdkApi.downloadSdk(appId);
34
- const sdkDirectory = path.resolve(process.cwd(), SdkService.SDK_DIRECTORY);
35
- fs.rmSync(sdkDirectory, { recursive: true, force: true });
36
- fs.mkdirSync(sdkDirectory, { recursive: true });
37
- const sdkFile = path.join(SdkService.SDK_DIRECTORY, `${appId}.tgz`);
38
- fs.writeFileSync(path.resolve(process.cwd(), sdkFile), sdk);
39
- logger_1.logger.info(`SDK downloaded successfully: ${sdkFile}`);
40
- });
41
- }
42
- publishApp(appId, dist) {
33
+ publishApp(appId, dist, buildVersion, message) {
43
34
  return __awaiter(this, void 0, void 0, function* () {
44
35
  const distDirectory = path.resolve(process.cwd(), dist);
45
36
  this.requireDirectory(distDirectory);
46
37
  const bundle = this.compressDirectory(distDirectory);
47
- const { version } = yield this.sdkApi.publishApp(appId, bundle);
38
+ const { version } = yield this.sdkApi.publishApp(appId, bundle, { version: buildVersion, message });
48
39
  logger_1.logger.info(`App published successfully as version '${version}'`);
49
40
  });
50
41
  }
42
+ printToken() {
43
+ return __awaiter(this, void 0, void 0, function* () {
44
+ const profile = this.requireProfile();
45
+ const token = yield new profile_service_1.ProfileService().acquireSdkToken(profile);
46
+ process.stdout.write(`${token}\n`);
47
+ });
48
+ }
49
+ requireProfile() {
50
+ if (!this.context.profile) {
51
+ throw new logger_1.FatalError("No profile provided. Please provide a profile to acquire a token for");
52
+ }
53
+ return this.context.profile;
54
+ }
51
55
  requireDirectory(directory) {
52
56
  if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {
53
57
  throw new logger_1.FatalError(`The dist directory ${directory} does not exist or is no directory`);
@@ -60,5 +64,4 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
60
64
  }
61
65
  }
62
66
  exports.SdkService = SdkService;
63
- SdkService.SDK_DIRECTORY = ".celonis-sdk";
64
67
  });
@@ -79,6 +79,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
79
79
  });
80
80
  });
81
81
  }
82
+ exportStagingPackages(packageKeys, withDependencies) {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const queryParams = new URLSearchParams();
85
+ packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey));
86
+ queryParams.set("withDependencies", withDependencies.toString());
87
+ return this.httpClient().getFile(`/package-manager/api/core/staging/packages/export/batch?${queryParams.toString()}`).catch(e => {
88
+ throw new logger_1.FatalError(`Problem exporting staging packages: ${e}`);
89
+ });
90
+ });
91
+ }
82
92
  exportPackagesByVersions(packageKeysWithVersion, withDependencies) {
83
93
  return __awaiter(this, void 0, void 0, function* () {
84
94
  const queryParams = new URLSearchParams();
@@ -21,5 +21,6 @@
21
21
  BatchExportImportConstants["JSON_EXTENSION"] = ".json";
22
22
  BatchExportImportConstants["NODES_FOLDER_NAME"] = "nodes/";
23
23
  BatchExportImportConstants["SCENARIO_NODE"] = "SCENARIO";
24
+ BatchExportImportConstants["STAGING_VERSION"] = "staging";
24
25
  })(BatchExportImportConstants || (exports.BatchExportImportConstants = BatchExportImportConstants = {}));
25
26
  });
@@ -45,6 +45,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
45
45
  .option("--unzip", "Unzip the exported file", "")
46
46
  .option("--gitProfile <gitProfile>", "Git profile which you want to use for the Git operations")
47
47
  .option("--gitBranch <gitBranch>", "Git branch in which you want to push the exported file")
48
+ .option("--staging", "Export the staging state of the given --packageKeys (including branch packages) instead of the latest deployed version", false)
48
49
  .action(this.batchExportPackages);
49
50
  t2tcPackageCommand.command("import")
50
51
  .description("Import packages from a Team-to-Team Copy archive produced by 't2tc package export'. To import a single standalone package, use 'config package import'.")
@@ -83,8 +84,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
83
84
  if (options.gitProfile && !options.gitBranch) {
84
85
  throw new Error("Please specify a branch using --gitBranch when using a Git profile.");
85
86
  }
87
+ if (options.staging && options.keysByVersion) {
88
+ throw new Error("Staging parameter is only supported with --packageKeys, not --keysByVersion.");
89
+ }
86
90
  options.withDependencies = (_a = options.withDependencies) !== null && _a !== void 0 ? _a : false;
87
- yield new t2tc_command_service_1.T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip);
91
+ yield new t2tc_command_service_1.T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip, options.staging);
88
92
  });
89
93
  }
90
94
  batchImportPackages(context, command, options) {
@@ -48,8 +48,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
48
48
  }
49
49
  });
50
50
  }
51
- batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip) {
52
- return this.t2tcPackageService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip);
51
+ batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip, staging = false) {
52
+ return this.t2tcPackageService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip, staging);
53
53
  }
54
54
  batchImportPackages(file, directory, overwrite, gitBranch, performValidation = false) {
55
55
  if ((directory || file) && gitBranch) {
@@ -13,7 +13,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  if (v !== undefined) module.exports = v;
14
14
  }
15
15
  else if (typeof define === "function" && define.amd) {
16
- define(["require", "exports", "uuid", "form-data", "stream", "adm-zip", "./batch-export-import.constants", "../../core/utils/file-service", "../../core/utils/cui-file-service", "../../core/utils/logger", "../../core/utils/json", "../studio/api/package-api", "./api/t2tc-package-api", "../configuration-management/api/variable-api", "../configuration-management/connection-variable.helper", "./studio.service", "../../core/git-profile/git/git.service", "fs", "../../core/utils/file.constants"], factory);
16
+ define(["require", "exports", "uuid", "form-data", "stream", "adm-zip", "./batch-export-import.constants", "../../core/utils/file-service", "../../core/utils/cui-file-service", "../../core/utils/logger", "../../core/utils/json", "../studio/api/package-api", "./api/t2tc-package-api", "../configuration-management/api/variable-api", "../configuration-management/api/staging-package-variables-api", "../configuration-management/connection-variable.helper", "./studio.service", "../../core/git-profile/git/git.service", "fs", "../../core/utils/file.constants"], factory);
17
17
  }
18
18
  })(function (require, exports) {
19
19
  "use strict";
@@ -31,6 +31,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
31
31
  const package_api_1 = require("../studio/api/package-api");
32
32
  const t2tc_package_api_1 = require("./api/t2tc-package-api");
33
33
  const variable_api_1 = require("../configuration-management/api/variable-api");
34
+ const staging_package_variables_api_1 = require("../configuration-management/api/staging-package-variables-api");
34
35
  const connection_variable_helper_1 = require("../configuration-management/connection-variable.helper");
35
36
  const studio_service_1 = require("./studio.service");
36
37
  const git_service_1 = require("../../core/git-profile/git/git.service");
@@ -40,6 +41,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
40
41
  constructor(context) {
41
42
  this.t2tcPackageApi = new t2tc_package_api_1.T2tcPackageApi(context);
42
43
  this.variableApi = new variable_api_1.VariableApi(context);
44
+ this.stagingPackageVariablesApi = new staging_package_variables_api_1.StagingPackageVariablesApi(context);
43
45
  this.studioPackageApi = new package_api_1.PackageApi(context);
44
46
  this.studioService = new studio_service_1.StudioService(context);
45
47
  this.gitService = new git_service_1.GitService(context);
@@ -77,10 +79,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
77
79
  });
78
80
  });
79
81
  }
80
- batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip) {
82
+ batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip, staging = false) {
81
83
  return __awaiter(this, void 0, void 0, function* () {
82
84
  let exportedPackagesData;
83
- if (packageKeys) {
85
+ if (staging) {
86
+ exportedPackagesData = yield this.t2tcPackageApi.exportStagingPackages(packageKeys, withDependencies);
87
+ }
88
+ else if (packageKeys) {
84
89
  exportedPackagesData = yield this.t2tcPackageApi.exportPackages(packageKeys, withDependencies);
85
90
  }
86
91
  else {
@@ -178,16 +183,40 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
178
183
  return versionsByPackageKey;
179
184
  }
180
185
  getVersionedVariablesForPackagesWithKeys(versionsByPackageKey) {
181
- const variableExportRequest = [];
182
- versionsByPackageKey === null || versionsByPackageKey === void 0 ? void 0 : versionsByPackageKey.forEach((versions, key) => {
183
- versions === null || versions === void 0 ? void 0 : versions.forEach(version => {
184
- variableExportRequest.push({
185
- packageKey: key,
186
- version: version,
186
+ return __awaiter(this, void 0, void 0, function* () {
187
+ const variableExportRequest = [];
188
+ const stagingPackageKeys = [];
189
+ versionsByPackageKey === null || versionsByPackageKey === void 0 ? void 0 : versionsByPackageKey.forEach((versions, key) => {
190
+ versions === null || versions === void 0 ? void 0 : versions.forEach(version => {
191
+ if (version === batch_export_import_constants_1.BatchExportImportConstants.STAGING_VERSION) {
192
+ stagingPackageKeys.push(key);
193
+ return;
194
+ }
195
+ variableExportRequest.push({
196
+ packageKey: key,
197
+ version: version,
198
+ });
199
+ });
200
+ });
201
+ const versionedVariables = yield this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest);
202
+ const stagingVariables = stagingPackageKeys.length
203
+ ? yield this.getStagingVariablesForPackagesWithKeys(stagingPackageKeys)
204
+ : [];
205
+ return [...versionedVariables, ...stagingVariables];
206
+ });
207
+ }
208
+ getStagingVariablesForPackagesWithKeys(packageKeys) {
209
+ return __awaiter(this, void 0, void 0, function* () {
210
+ const stagingVariables = yield this.stagingPackageVariablesApi.findAllByPackageKeys(packageKeys);
211
+ return stagingVariables.map(stagingVariable => {
212
+ var _a;
213
+ return ({
214
+ packageKey: stagingVariable.packageKey,
215
+ version: batch_export_import_constants_1.BatchExportImportConstants.STAGING_VERSION,
216
+ variables: (_a = stagingVariable.variables) !== null && _a !== void 0 ? _a : [],
187
217
  });
188
218
  });
189
219
  });
190
- return this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest);
191
220
  }
192
221
  buildBodyForImport(configs, sourcePath, variablesManifests) {
193
222
  const formData = new FormData();
package/content-cli.js CHANGED
@@ -102,8 +102,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
102
102
  bootstrapProgram.parseOptions(process.argv);
103
103
  const globalOpts = bootstrapProgram.opts();
104
104
  if (!globalOpts.quietmode) {
105
- console.log(`Content CLI - (C) Copyright 2025 - Celonis SE - Version ${version_1.VersionUtils.getCurrentCliVersion()}`);
106
- console.log();
105
+ console.error(`Content CLI - (C) Copyright 2025 - Celonis SE - Version ${version_1.VersionUtils.getCurrentCliVersion()}`);
106
+ console.error();
107
107
  }
108
108
  if (globalOpts.debug) {
109
109
  logger_1.logger.transports.forEach(t => {
@@ -236,7 +236,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
236
236
  }
237
237
  printBetaNoticeIfBetaCommand() {
238
238
  if (this.cmd.isBeta) {
239
- logger_1.logger.info(chalk.yellow("This command is in beta and may change in future releases."));
239
+ logger_1.logger.warn(chalk.yellow("This command is in beta and may change in future releases."));
240
240
  }
241
241
  }
242
242
  printBetaNoticeIfBetaOptions() {
@@ -244,12 +244,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
244
244
  return;
245
245
  }
246
246
  const providedOptions = this.cmd.optsWithGlobals();
247
- for (const option of this.cmd.options) {
248
- const optionName = option.attributeName();
249
- if (option.isBeta && Object.hasOwn(providedOptions, optionName)) {
250
- logger_1.logger.info(chalk.yellow(`The option '${option.long}' is in beta and may change in future releases.`));
251
- }
252
- }
247
+ this.cmd.options
248
+ .filter(option => option.isBeta && Object.hasOwn(providedOptions, option.attributeName()))
249
+ .forEach(option => logger_1.logger.warn(chalk.yellow(`The option '${option.long}' is in beta and may change in future releases.`)));
253
250
  }
254
251
  }
255
252
  exports.CommandConfig = CommandConfig;
@@ -133,11 +133,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
133
133
  });
134
134
  });
135
135
  }
136
- postBinary(url, body, contentType) {
136
+ postBinary(url, body, contentType, parameters) {
137
137
  return __awaiter(this, void 0, void 0, function* () {
138
138
  return new Promise((resolve, reject) => {
139
139
  this.axios.post(this.resolveUrl(url), body, {
140
- headers: this.buildHeaders(contentType)
140
+ headers: this.buildHeaders(contentType),
141
+ params: parameters
141
142
  }).then(response => {
142
143
  this.handleResponse(response, resolve, reject);
143
144
  }).catch(err => {
@@ -36,8 +36,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
36
36
  const OAUTH_SCOPES = ["studio", "package-manager", "integration.data-pools", "action-engine.projects"];
37
37
  /** Device code fallback: try without action-engine.projects if all 4 scopes fail. */
38
38
  const DEVICE_CODE_SCOPES_WITHOUT_ACTION_ENGINE = ["studio", "package-manager", "integration.data-pools"];
39
+ const SDK_SCOPE = "pro-code-app-manager.sdk";
39
40
  /** First attempt for both auth flows: all scopes plus the pro-code-app-manager scopes, which are not yet available on every environment. */
40
- const OAUTH_SCOPES_WITH_PRO_CODE = [...OAUTH_SCOPES, "pro-code-app-manager.sdk:read", "pro-code-app-manager.publish"];
41
+ const OAUTH_SCOPES_WITH_PRO_CODE = [...OAUTH_SCOPES, SDK_SCOPE, "pro-code-app-manager.publish"];
41
42
  class ProfileService {
42
43
  constructor() {
43
44
  this.profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles");
@@ -318,6 +319,49 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
318
319
  yield this.storeProfile(profile);
319
320
  });
320
321
  }
322
+ acquireSdkToken(profile) {
323
+ return __awaiter(this, void 0, void 0, function* () {
324
+ switch (profile.type) {
325
+ case profile_interface_1.ProfileType.DEVICE_CODE:
326
+ return this.acquireDeviceCodeSdkToken(profile);
327
+ case profile_interface_1.ProfileType.CLIENT_CREDENTIALS:
328
+ return this.acquireClientCredentialsSdkToken(profile);
329
+ default:
330
+ throw new logger_1.FatalError("An SDK token cannot be acquired for API key profiles. Use the API key directly for authentication.");
331
+ }
332
+ });
333
+ }
334
+ acquireDeviceCodeSdkToken(profile) {
335
+ return __awaiter(this, void 0, void 0, function* () {
336
+ const issuer = yield openid_client_1.Issuer.discover(profile.team);
337
+ const oauthClient = new issuer.Client({
338
+ client_id: "content-cli",
339
+ token_endpoint_auth_method: "none",
340
+ });
341
+ const tokenSet = yield oauthClient.refresh(profile.refreshToken, {
342
+ exchangeBody: { scope: SDK_SCOPE },
343
+ });
344
+ profile.refreshToken = tokenSet.refresh_token;
345
+ profile.expiresAt = 0;
346
+ yield this.storeProfile(profile);
347
+ return tokenSet.access_token;
348
+ });
349
+ }
350
+ acquireClientCredentialsSdkToken(profile) {
351
+ return __awaiter(this, void 0, void 0, function* () {
352
+ const issuer = yield openid_client_1.Issuer.discover(profile.team);
353
+ const oauthClient = new issuer.Client({
354
+ client_id: profile.clientId,
355
+ client_secret: profile.clientSecret,
356
+ token_endpoint_auth_method: profile.clientAuthenticationMethod,
357
+ });
358
+ const tokenSet = yield oauthClient.grant({
359
+ grant_type: "client_credentials",
360
+ scope: SDK_SCOPE,
361
+ });
362
+ return tokenSet.access_token;
363
+ });
364
+ }
321
365
  unrefreshableProfile(profileName, err) {
322
366
  const cause = err instanceof Error ? err.message : "an unrecognised error";
323
367
  logger_1.logger.debug(`Refreshing the profile ${profileName} failed: ${cause}`);
@@ -56,6 +56,7 @@
56
56
  transports: [
57
57
  new winston.transports.Console({
58
58
  level: "info",
59
+ stderrLevels: ["error", "warn"],
59
60
  format: winston.format.combine(winston.format.colorize()),
60
61
  }),
61
62
  new winston.transports.File({
@@ -3,6 +3,7 @@ import { PackageVersionCreatedTransport, PackageVersionTransport, SavePackageVer
3
3
  export declare class PackageVersionApi {
4
4
  private httpClient;
5
5
  constructor(context: Context);
6
+ findAll(packageKey: string): Promise<PackageVersionTransport[]>;
6
7
  findOne(packageKey: string, version: string): Promise<PackageVersionTransport>;
7
8
  createVersion(packageKey: string, request: SavePackageVersionTransport): Promise<PackageVersionCreatedTransport>;
8
9
  }
@@ -18,6 +18,7 @@ declare class Module extends IModule {
18
18
  private batchExportPackages;
19
19
  private exportPackagesMetadata;
20
20
  private getPackageVersion;
21
+ private listPackageVersions;
21
22
  private createPackageVersion;
22
23
  private batchImportPackages;
23
24
  private importSinglePackage;
@@ -3,5 +3,6 @@ export declare class PackageVersionCommandService {
3
3
  private packageVersionService;
4
4
  constructor(context: Context);
5
5
  getPackageVersion(packageKey: string, version: string, jsonResponse: boolean): Promise<void>;
6
+ listPackageVersions(packageKey: string, jsonResponse: boolean): Promise<void>;
6
7
  createPackageVersion(packageKey: string, version: string | undefined, versionBumpOption: string, summaryOfChanges: string | undefined, nodeFilterKeys: string[] | undefined, jsonResponse: boolean): Promise<void>;
7
8
  }
@@ -4,6 +4,7 @@ export declare class PackageVersionService {
4
4
  private readonly cuiFileService;
5
5
  constructor(context: Context);
6
6
  findPackageVersion(packageKey: string, version: string, jsonResponse: boolean): Promise<void>;
7
+ listPackageVersions(packageKey: string, jsonResponse: boolean): Promise<void>;
7
8
  createPackageVersion(packageKey: string, version: string | undefined, versionBumpOption: string, summaryOfChanges: string | undefined, nodeFilterKeys: string[] | undefined, jsonResponse: boolean): Promise<void>;
8
9
  private printPackageVersionTransport;
9
10
  private printPackageVersionCreatedTransport;
@@ -2,7 +2,8 @@ import { Configurator, IModule } from "../../core/command/module-handler";
2
2
  import { Context } from "../../core/command/cli-context";
3
3
  declare class Module extends IModule {
4
4
  register(context: Context, configurator: Configurator): void;
5
- private downloadSdk;
6
5
  private publishApp;
6
+ private previewApp;
7
+ private printToken;
7
8
  }
8
9
  export = Module;
@@ -0,0 +1,5 @@
1
+ export interface PreviewCertificate {
2
+ key: string;
3
+ cert: string;
4
+ }
5
+ export declare function loadOrCreatePreviewCertificate(directory: string, now?: Date): Promise<PreviewCertificate>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Copied from `localPreviewHeaders()` in ems-frontend `libs/common/fragments/sandbox/src/lib/policy.ts`,
3
+ * whose spec fails when the gateway policy drifts. Update both together.
4
+ */
5
+ export declare const LOCAL_PREVIEW_HEADERS: Readonly<Record<string, string>>;
@@ -0,0 +1,13 @@
1
+ export interface InlineScript {
2
+ /** `'sha256-<base64>'`, quoted and ready for a CSP source list. */
3
+ hash: string;
4
+ /** The first 60 characters of the script text, with whitespace collapsed. */
5
+ excerpt: string;
6
+ }
7
+ /**
8
+ * The inline scripts (`<script>` without `src`) of an HTML document, distinct by hash and in document order.
9
+ * Each hash covers the script text as the browser parses it for CSP (line endings normalized to LF).
10
+ */
11
+ export declare function inlineScriptHashes(html: string): InlineScript[];
12
+ /** Appends the hashes to the policy's `script-src` directive and leaves every other directive unchanged. */
13
+ export declare function allowInlineScripts(csp: string, hashes: readonly string[]): string;
@@ -0,0 +1,14 @@
1
+ import { PreviewCertificate } from "./preview-certificate";
2
+ export interface PreviewProxyOptions {
3
+ target: URL;
4
+ port: number;
5
+ certificate: PreviewCertificate;
6
+ headers: Readonly<Record<string, string>>;
7
+ /** Adds a hash of each inline script in proxied HTML to `script-src`. Published apps block inline scripts. */
8
+ allowInlineScripts: boolean;
9
+ }
10
+ export interface PreviewProxy {
11
+ url: string;
12
+ close(): Promise<void>;
13
+ }
14
+ export declare function startPreviewProxy(options: PreviewProxyOptions): Promise<PreviewProxy>;
@@ -1,11 +1,14 @@
1
1
  /// <reference types="node" />
2
2
  import { Context } from "../../core/command/cli-context";
3
+ export interface SdkPublishOptions {
4
+ version?: string;
5
+ message?: string;
6
+ }
3
7
  export interface SdkPublishResponse {
4
8
  version: string;
5
9
  }
6
10
  export declare class SdkApi {
7
11
  private readonly httpClient;
8
12
  constructor(context: Context);
9
- downloadSdk(appId: string): Promise<Buffer>;
10
- publishApp(appId: string, bundle: Buffer): Promise<SdkPublishResponse>;
13
+ publishApp(appId: string, bundle: Buffer, options: SdkPublishOptions): Promise<SdkPublishResponse>;
11
14
  }
@@ -0,0 +1,11 @@
1
+ import { PreviewProxy } from "./preview-proxy";
2
+ export declare class SdkPreviewService {
3
+ private readonly certificateDirectory;
4
+ static readonly CERTIFICATE_DIRECTORY: string;
5
+ constructor(certificateDirectory?: string);
6
+ preview(target: string, port: string | number, allowInlineScripts?: boolean): Promise<PreviewProxy>;
7
+ previewUntilStopped(target: string, port: string | number, allowInlineScripts?: boolean): Promise<void>;
8
+ private waitForShutdownSignal;
9
+ private parseTarget;
10
+ private parsePort;
11
+ }
@@ -1,10 +1,11 @@
1
1
  import { Context } from "../../core/command/cli-context";
2
2
  export declare class SdkService {
3
- static readonly SDK_DIRECTORY = ".celonis-sdk";
3
+ private readonly context;
4
4
  private readonly sdkApi;
5
5
  constructor(context: Context);
6
- downloadSdk(appId: string): Promise<void>;
7
- publishApp(appId: string, dist: string): Promise<void>;
6
+ publishApp(appId: string, dist: string, buildVersion?: string, message?: string): Promise<void>;
7
+ printToken(): Promise<void>;
8
+ private requireProfile;
8
9
  private requireDirectory;
9
10
  private compressDirectory;
10
11
  }
@@ -10,6 +10,7 @@ export declare class T2tcPackageApi {
10
10
  findActivePackagesByKeys(packageKeys: string[], withDependencies?: boolean): Promise<PackageExportTransport[]>;
11
11
  findPackagesByKeysAndVersion(packageKeysWithVersion: string[], withDependencies?: boolean): Promise<PackageExportTransport[]>;
12
12
  exportPackages(packageKeys: string[], withDependencies: boolean): Promise<Buffer>;
13
+ exportStagingPackages(packageKeys: string[], withDependencies: boolean): Promise<Buffer>;
13
14
  exportPackagesByVersions(packageKeysWithVersion: string[], withDependencies: boolean): Promise<Buffer>;
14
15
  importPackages(data: FormData, overwrite: boolean, performValidation: boolean): Promise<PostPackageImportData[]>;
15
16
  }
@@ -7,5 +7,6 @@ export declare enum BatchExportImportConstants {
7
7
  ZIP_EXTENSION = ".zip",
8
8
  JSON_EXTENSION = ".json",
9
9
  NODES_FOLDER_NAME = "nodes/",
10
- SCENARIO_NODE = "SCENARIO"
10
+ SCENARIO_NODE = "SCENARIO",
11
+ STAGING_VERSION = "staging"
11
12
  }
@@ -5,7 +5,7 @@ export declare class T2tcCommandService {
5
5
  private stagingPackageService;
6
6
  constructor(context: Context);
7
7
  listPackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys: string[], keysByVersion: string[], variableValue: string, variableType: string, includeBranches: boolean, staging: boolean): Promise<void>;
8
- batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise<void>;
8
+ batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean, staging?: boolean): Promise<void>;
9
9
  batchImportPackages(file: string, directory: string, overwrite: boolean, gitBranch: string, performValidation?: boolean): Promise<void>;
10
10
  diffPackages(file: string, hasChanges: boolean, baseVersion: string, jsonResponse: boolean): Promise<void>;
11
11
  private listPackagesByVariableValue;
@@ -2,6 +2,7 @@ import { Context } from "../../core/command/cli-context";
2
2
  export declare class T2tcPackageService {
3
3
  private t2tcPackageApi;
4
4
  private variableApi;
5
+ private stagingPackageVariablesApi;
5
6
  private studioPackageApi;
6
7
  private studioService;
7
8
  private gitService;
@@ -10,13 +11,14 @@ export declare class T2tcPackageService {
10
11
  listActivePackages(flavors: string[], includeBranches: boolean): Promise<void>;
11
12
  findAndExportListOfPackages(flavors: string[], packageKeys: string[], keysByVersion: string[], withDependencies: boolean, includeBranches: boolean): Promise<void>;
12
13
  listPackagesByKeysWithVersion(keysByVersion: string[], withDependencies: boolean): Promise<void>;
13
- batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise<void>;
14
+ batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean, staging?: boolean): Promise<void>;
14
15
  batchImportPackages(sourcePath: string, overwrite: boolean, gitBranch: string, performValidation?: boolean): Promise<void>;
15
16
  findAndExportListOfActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise<void>;
16
17
  listActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise<void>;
17
18
  private exportListOfPackages;
18
19
  private getVersionsByPackageKey;
19
20
  private getVersionedVariablesForPackagesWithKeys;
21
+ private getStagingVariablesForPackagesWithKeys;
20
22
  private buildBodyForImport;
21
23
  private assertUncompressedSizeWithinLimit;
22
24
  private getReadableStream;
@@ -17,7 +17,7 @@ export declare class HttpClient {
17
17
  getFile(url: string): Promise<any>;
18
18
  postFile(url: string, formData: FormData, parameters?: {}): Promise<any>;
19
19
  post(url: string, body: any): Promise<any>;
20
- postBinary(url: string, body: Buffer, contentType: string): Promise<any>;
20
+ postBinary(url: string, body: Buffer, contentType: string, parameters?: {}): Promise<any>;
21
21
  put(url: string, body: object): Promise<any>;
22
22
  delete(url: string): Promise<any>;
23
23
  downloadFile(url: string): Promise<any>;
@@ -19,6 +19,9 @@ export declare class ProfileService {
19
19
  getAllFilesInDirectory(): string[];
20
20
  authorizeProfile(profile: Profile): Promise<void>;
21
21
  refreshProfile(profile: Profile): Promise<void>;
22
+ acquireSdkToken(profile: Profile): Promise<string>;
23
+ private acquireDeviceCodeSdkToken;
24
+ private acquireClientCredentialsSdkToken;
22
25
  private unrefreshableProfile;
23
26
  private getProfileEnvVariables;
24
27
  private getBaseTeamUrl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celonis/content-cli",
3
- "version": "1.17.0",
3
+ "version": "1.18.1",
4
4
  "description": "CLI Tool to help manage content in Celonis Platform",
5
5
  "main": "content-cli.js",
6
6
  "bin": {
@@ -18,17 +18,18 @@
18
18
  "node": ">=18.20.5"
19
19
  },
20
20
  "dependencies": {
21
+ "@github/keytar": "7.10.6",
21
22
  "adm-zip": "0.6.0",
22
- "chalk": "4.1.2",
23
- "uuid": "9.0.1",
24
23
  "axios": "1.18.0",
24
+ "chalk": "4.1.2",
25
25
  "commander": "13.1.0",
26
26
  "form-data": "4.0.6",
27
- "openid-client": "5.6.1",
28
27
  "hpagent": "1.2.0",
29
- "@github/keytar": "7.10.6",
28
+ "openid-client": "5.6.1",
29
+ "selfsigned": "5.5.0",
30
30
  "semver": "7.6.3",
31
31
  "simple-git": "3.36.0",
32
+ "uuid": "9.0.1",
32
33
  "valid-url": "1.0.9",
33
34
  "winston": "3.17.0",
34
35
  "yaml": "2.8.3"