@kolatts/pncli 1.26.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/copilot-instructions.md +1 -36
- package/dist/chunk-HZF6WQPU.js +152 -0
- package/dist/chunk-HZF6WQPU.js.map +1 -0
- package/dist/{chunk-JOAWUILY.js → chunk-PXKUQPPF.js} +32 -17
- package/dist/chunk-PXKUQPPF.js.map +1 -0
- package/dist/{chunk-AGELHQKO.js → chunk-ZDJYOI3W.js} +103 -216
- package/dist/chunk-ZDJYOI3W.js.map +1 -0
- package/dist/cli.js +815 -357
- package/dist/cli.js.map +1 -1
- package/dist/{config-SQ3YHJHI.js → config-YHYQTGKT.js} +4 -2
- package/dist/{http-WCDXXCKQ.js → http-6QJU2SWP.js} +3 -2
- package/dist/output-XT3WT5L5.js +24 -0
- package/dist/output-XT3WT5L5.js.map +1 -0
- package/package.json +1 -1
- package/skills/pncli/SKILL.md +5 -2
- package/skills/pncli/figma.md +62 -0
- package/skills/pncli/jenkins.md +47 -0
- package/skills/pncli/openshift.md +23 -1
- package/skills/pncli/splitio.md +178 -0
- package/dist/chunk-AGELHQKO.js.map +0 -1
- package/dist/chunk-JOAWUILY.js.map +0 -1
- package/skills/pncli/udeploy.md +0 -48
- /package/dist/{config-SQ3YHJHI.js.map → config-YHYQTGKT.js.map} +0 -0
- /package/dist/{http-WCDXXCKQ.js.map → http-6QJU2SWP.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -4,15 +4,18 @@ import {
|
|
|
4
4
|
loadConfig,
|
|
5
5
|
loadJsonFile,
|
|
6
6
|
maskConfig,
|
|
7
|
+
normalizeBaseUrl,
|
|
7
8
|
setConfigValue,
|
|
8
9
|
setRepoConfigValue,
|
|
9
10
|
writeGlobalConfig,
|
|
10
11
|
writeRepoConfig
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-PXKUQPPF.js";
|
|
13
|
+
import {
|
|
14
|
+
createHttpClient
|
|
15
|
+
} from "./chunk-ZDJYOI3W.js";
|
|
12
16
|
import {
|
|
13
17
|
ExitCode,
|
|
14
18
|
PncliError,
|
|
15
|
-
createHttpClient,
|
|
16
19
|
fail,
|
|
17
20
|
log,
|
|
18
21
|
setGlobalOptions,
|
|
@@ -20,7 +23,7 @@ import {
|
|
|
20
23
|
success,
|
|
21
24
|
warn,
|
|
22
25
|
writeRawOutput
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-HZF6WQPU.js";
|
|
24
27
|
import {
|
|
25
28
|
AdoCoreClient
|
|
26
29
|
} from "./chunk-VDL32PSG.js";
|
|
@@ -6311,16 +6314,6 @@ function registerConfigCommands(program2) {
|
|
|
6311
6314
|
} else {
|
|
6312
6315
|
results.artifactory = { ok: null, message: "not configured" };
|
|
6313
6316
|
}
|
|
6314
|
-
if (cfg.udeploy.baseUrl && (cfg.udeploy.pat || cfg.udeploy.username && cfg.udeploy.password)) {
|
|
6315
|
-
try {
|
|
6316
|
-
await http.udeploy("/cli/application");
|
|
6317
|
-
results.udeploy = { ok: true, message: "connected" };
|
|
6318
|
-
} catch (err) {
|
|
6319
|
-
results.udeploy = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
6320
|
-
}
|
|
6321
|
-
} else {
|
|
6322
|
-
results.udeploy = { ok: null, message: "not configured" };
|
|
6323
|
-
}
|
|
6324
6317
|
if (cfg.checkmarx.baseUrl && cfg.checkmarx.tenantName && (cfg.checkmarx.apiKey || cfg.checkmarx.clientId && cfg.checkmarx.clientSecret)) {
|
|
6325
6318
|
try {
|
|
6326
6319
|
await http.checkmarx("projects", { params: { limit: 1 } });
|
|
@@ -6413,6 +6406,26 @@ function registerConfigCommands(program2) {
|
|
|
6413
6406
|
} else {
|
|
6414
6407
|
results.logscale = { ok: null, message: "not configured" };
|
|
6415
6408
|
}
|
|
6409
|
+
if (cfg.splitio.baseUrl && cfg.splitio.adminApiKey) {
|
|
6410
|
+
try {
|
|
6411
|
+
await http.splitio("/internal/api/v2/workspaces");
|
|
6412
|
+
results.splitio = { ok: true, message: "connected" };
|
|
6413
|
+
} catch (err) {
|
|
6414
|
+
results.splitio = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
6415
|
+
}
|
|
6416
|
+
} else {
|
|
6417
|
+
results.splitio = { ok: null, message: "not configured" };
|
|
6418
|
+
}
|
|
6419
|
+
if (cfg.figma.baseUrl && cfg.figma.token) {
|
|
6420
|
+
try {
|
|
6421
|
+
await http.figma("/v1/me");
|
|
6422
|
+
results.figma = { ok: true, message: "connected" };
|
|
6423
|
+
} catch (err) {
|
|
6424
|
+
results.figma = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
6425
|
+
}
|
|
6426
|
+
} else {
|
|
6427
|
+
results.figma = { ok: null, message: "not configured" };
|
|
6428
|
+
}
|
|
6416
6429
|
success(results, "config", "test", start);
|
|
6417
6430
|
} catch (err) {
|
|
6418
6431
|
fail(err, "config", "test", start);
|
|
@@ -6537,18 +6550,6 @@ function registerConfigCommands(program2) {
|
|
|
6537
6550
|
results.jenkins = categorize2(err);
|
|
6538
6551
|
}
|
|
6539
6552
|
}
|
|
6540
|
-
if (!cfg.udeploy.pat && !(cfg.udeploy.username && cfg.udeploy.password)) {
|
|
6541
|
-
results.udeploy = { status: "blank", message: "not configured" };
|
|
6542
|
-
} else if (!cfg.udeploy.baseUrl) {
|
|
6543
|
-
results.udeploy = { status: "error", message: "baseUrl not configured" };
|
|
6544
|
-
} else {
|
|
6545
|
-
try {
|
|
6546
|
-
await http.udeploy("/cli/application", { timeoutMs: 1e4 });
|
|
6547
|
-
results.udeploy = { status: "valid", message: "ok" };
|
|
6548
|
-
} catch (err) {
|
|
6549
|
-
results.udeploy = categorize2(err);
|
|
6550
|
-
}
|
|
6551
|
-
}
|
|
6552
6553
|
const artResult = await checkArtifactoryConnectivity(cfg.artifactory);
|
|
6553
6554
|
if (!artResult.configured) {
|
|
6554
6555
|
results.artifactory = { status: "blank", message: artResult.error ?? "not configured" };
|
|
@@ -6669,11 +6670,35 @@ function registerConfigCommands(program2) {
|
|
|
6669
6670
|
results.logscale = categorize2(err);
|
|
6670
6671
|
}
|
|
6671
6672
|
}
|
|
6673
|
+
if (!cfg.splitio.adminApiKey) {
|
|
6674
|
+
results.splitio = { status: "blank", message: "not configured" };
|
|
6675
|
+
} else if (!cfg.splitio.baseUrl) {
|
|
6676
|
+
results.splitio = { status: "error", message: "baseUrl not configured" };
|
|
6677
|
+
} else {
|
|
6678
|
+
try {
|
|
6679
|
+
await http.splitio("/internal/api/v2/workspaces", { timeoutMs: 1e4 });
|
|
6680
|
+
results.splitio = { status: "valid", message: "ok" };
|
|
6681
|
+
} catch (err) {
|
|
6682
|
+
results.splitio = categorize2(err);
|
|
6683
|
+
}
|
|
6684
|
+
}
|
|
6685
|
+
if (!cfg.figma.token) {
|
|
6686
|
+
results.figma = { status: "blank", message: "not configured" };
|
|
6687
|
+
} else if (!cfg.figma.baseUrl) {
|
|
6688
|
+
results.figma = { status: "error", message: "baseUrl not configured" };
|
|
6689
|
+
} else {
|
|
6690
|
+
try {
|
|
6691
|
+
await http.figma("/v1/me", { timeoutMs: 1e4 });
|
|
6692
|
+
results.figma = { status: "valid", message: "ok" };
|
|
6693
|
+
} catch (err) {
|
|
6694
|
+
results.figma = categorize2(err);
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6672
6697
|
const statuses = Object.values(results).map((r) => r.status);
|
|
6673
6698
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
6674
6699
|
else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
|
|
6675
6700
|
if (cmdOpts.output === "table") {
|
|
6676
|
-
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "
|
|
6701
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale", "splitio", "figma"];
|
|
6677
6702
|
const labelWidth = 14;
|
|
6678
6703
|
const statusWidth = 9;
|
|
6679
6704
|
for (const svc of services) {
|
|
@@ -6687,7 +6712,7 @@ function registerConfigCommands(program2) {
|
|
|
6687
6712
|
process.stdout.write("\n");
|
|
6688
6713
|
} else {
|
|
6689
6714
|
if (opts.pretty) {
|
|
6690
|
-
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "
|
|
6715
|
+
const services = ["jira", "bitbucket", "github", "confluence", "sonar", "sde", "ado", "jenkins", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq", "openshift", "dynatrace", "dynatrace_platform", "logscale", "splitio", "figma"];
|
|
6691
6716
|
const labelWidth = 14;
|
|
6692
6717
|
const statusWidth = 9;
|
|
6693
6718
|
for (const svc of services) {
|
|
@@ -6707,11 +6732,6 @@ function registerConfigCommands(program2) {
|
|
|
6707
6732
|
}
|
|
6708
6733
|
});
|
|
6709
6734
|
}
|
|
6710
|
-
function normalizeBaseUrl(raw) {
|
|
6711
|
-
const trimmed = raw.trim();
|
|
6712
|
-
if (!trimmed) return trimmed;
|
|
6713
|
-
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
6714
|
-
}
|
|
6715
6735
|
async function initGlobalConfig(start) {
|
|
6716
6736
|
process.stderr.write("pncli config init \u2014 Global configuration\n\n");
|
|
6717
6737
|
process.stderr.write("\u2500\u2500 Identity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
@@ -6939,7 +6959,7 @@ async function initGlobalConfig(start) {
|
|
|
6939
6959
|
}
|
|
6940
6960
|
process.stderr.write("\n\u2500\u2500 Jenkins \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
6941
6961
|
const useJenkins = await confirm({
|
|
6942
|
-
message: "Configure Jenkins
|
|
6962
|
+
message: "Configure Jenkins for pipeline operations?",
|
|
6943
6963
|
default: false
|
|
6944
6964
|
});
|
|
6945
6965
|
let jenkinsBaseUrl = "";
|
|
@@ -6958,66 +6978,6 @@ async function initGlobalConfig(start) {
|
|
|
6958
6978
|
message: "Jenkins API token:"
|
|
6959
6979
|
});
|
|
6960
6980
|
}
|
|
6961
|
-
process.stderr.write("\n\u2500\u2500 IBM UrbanCode Deploy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
6962
|
-
const useUdeploy = await confirm({
|
|
6963
|
-
message: "Configure IBM UrbanCode Deploy for component imports and deployment processes?",
|
|
6964
|
-
default: false
|
|
6965
|
-
});
|
|
6966
|
-
let udeployBaseUrl = "";
|
|
6967
|
-
let udeployPat = "";
|
|
6968
|
-
let udeployUsername = "";
|
|
6969
|
-
let udeployPassword = "";
|
|
6970
|
-
let udeployDefaultApp = "";
|
|
6971
|
-
let udeployDefaultEnv = "";
|
|
6972
|
-
if (useUdeploy) {
|
|
6973
|
-
udeployBaseUrl = await input({
|
|
6974
|
-
message: "UDeploy base URL (e.g. ucd.imagile.dev:8443):",
|
|
6975
|
-
default: ""
|
|
6976
|
-
});
|
|
6977
|
-
udeployUsername = await input({
|
|
6978
|
-
message: "UDeploy username (leave blank to use a PAT instead):",
|
|
6979
|
-
default: ""
|
|
6980
|
-
});
|
|
6981
|
-
if (udeployUsername) {
|
|
6982
|
-
udeployPassword = await password({
|
|
6983
|
-
message: "UDeploy password:",
|
|
6984
|
-
validate: (v) => v.length > 0 || "Password cannot be blank"
|
|
6985
|
-
});
|
|
6986
|
-
} else {
|
|
6987
|
-
udeployPat = await password({
|
|
6988
|
-
message: "UDeploy personal access token:"
|
|
6989
|
-
});
|
|
6990
|
-
}
|
|
6991
|
-
udeployDefaultApp = await input({
|
|
6992
|
-
message: "Default application name (optional):",
|
|
6993
|
-
default: ""
|
|
6994
|
-
});
|
|
6995
|
-
udeployDefaultEnv = await input({
|
|
6996
|
-
message: "Default environment name (optional):",
|
|
6997
|
-
default: ""
|
|
6998
|
-
});
|
|
6999
|
-
const hasCredentials = udeployBaseUrl && (udeployPat || udeployUsername && udeployPassword);
|
|
7000
|
-
if (hasCredentials) {
|
|
7001
|
-
process.stderr.write("\n Verifying connection...\n");
|
|
7002
|
-
try {
|
|
7003
|
-
const tempConfig = {
|
|
7004
|
-
...loadConfig(),
|
|
7005
|
-
udeploy: {
|
|
7006
|
-
baseUrl: normalizeBaseUrl(udeployBaseUrl),
|
|
7007
|
-
pat: udeployPat || void 0,
|
|
7008
|
-
username: udeployUsername || void 0,
|
|
7009
|
-
password: udeployPassword || void 0
|
|
7010
|
-
}
|
|
7011
|
-
};
|
|
7012
|
-
const tempHttp = createHttpClient(tempConfig);
|
|
7013
|
-
await tempHttp.udeploy("/cli/application");
|
|
7014
|
-
process.stderr.write(" Connected.\n");
|
|
7015
|
-
} catch (err) {
|
|
7016
|
-
warn(`Could not connect to UDeploy: ${err instanceof Error ? err.message : String(err)}`);
|
|
7017
|
-
warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
|
|
7018
|
-
}
|
|
7019
|
-
}
|
|
7020
|
-
}
|
|
7021
6981
|
process.stderr.write("\n\u2500\u2500 Checkmarx \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
7022
6982
|
const useCheckmarx = await confirm({
|
|
7023
6983
|
message: "Configure Checkmarx One for vulnerability scanning?",
|
|
@@ -7344,6 +7304,70 @@ async function initGlobalConfig(start) {
|
|
|
7344
7304
|
}
|
|
7345
7305
|
}
|
|
7346
7306
|
}
|
|
7307
|
+
process.stderr.write("\n\u2500\u2500 Split.IO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
7308
|
+
const useSplitio = await confirm({
|
|
7309
|
+
message: "Configure Split.IO for feature flag administration?",
|
|
7310
|
+
default: false
|
|
7311
|
+
});
|
|
7312
|
+
let splitioBaseUrl = "";
|
|
7313
|
+
let splitioAdminApiKey = "";
|
|
7314
|
+
if (useSplitio) {
|
|
7315
|
+
splitioBaseUrl = await input({
|
|
7316
|
+
message: "Split.IO Admin API base URL (e.g. https://api.split.io):",
|
|
7317
|
+
default: ""
|
|
7318
|
+
});
|
|
7319
|
+
splitioAdminApiKey = await password({
|
|
7320
|
+
message: "Split.IO Admin API key:"
|
|
7321
|
+
});
|
|
7322
|
+
if (splitioBaseUrl && splitioAdminApiKey) {
|
|
7323
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
7324
|
+
try {
|
|
7325
|
+
const tempConfig = {
|
|
7326
|
+
...loadConfig(),
|
|
7327
|
+
splitio: {
|
|
7328
|
+
baseUrl: normalizeBaseUrl(splitioBaseUrl),
|
|
7329
|
+
adminApiKey: splitioAdminApiKey
|
|
7330
|
+
}
|
|
7331
|
+
};
|
|
7332
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
7333
|
+
await tempHttp.splitio("/internal/api/v2/workspaces");
|
|
7334
|
+
process.stderr.write(" Connected.\n");
|
|
7335
|
+
} catch (err) {
|
|
7336
|
+
warn(`Could not connect to Split.IO: ${err instanceof Error ? err.message : String(err)}`);
|
|
7337
|
+
warn("Config will be saved anyway. Check your URL and API key and re-run pncli config init or pncli config test.");
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
process.stderr.write("\n\u2500\u2500 Figma \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
7342
|
+
const useFigma = await confirm({
|
|
7343
|
+
message: "Configure Figma for reading design files and comments?",
|
|
7344
|
+
default: false
|
|
7345
|
+
});
|
|
7346
|
+
let figmaToken = "";
|
|
7347
|
+
if (useFigma) {
|
|
7348
|
+
process.stderr.write(" Generate a personal access token in Figma under Account Settings \u2192 Personal access tokens.\n");
|
|
7349
|
+
figmaToken = await password({
|
|
7350
|
+
message: "Figma personal access token:"
|
|
7351
|
+
});
|
|
7352
|
+
if (figmaToken) {
|
|
7353
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
7354
|
+
try {
|
|
7355
|
+
const tempConfig = {
|
|
7356
|
+
...loadConfig(),
|
|
7357
|
+
figma: {
|
|
7358
|
+
baseUrl: "https://api.figma.com",
|
|
7359
|
+
token: figmaToken
|
|
7360
|
+
}
|
|
7361
|
+
};
|
|
7362
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
7363
|
+
await tempHttp.figma("/v1/me");
|
|
7364
|
+
process.stderr.write(" Connected.\n");
|
|
7365
|
+
} catch (err) {
|
|
7366
|
+
warn(`Could not connect to Figma: ${err instanceof Error ? err.message : String(err)}`);
|
|
7367
|
+
warn("Config will be saved anyway. Check your token and re-run pncli config init or pncli config test.");
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7347
7371
|
process.stderr.write("\n\u2500\u2500 Defaults \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
7348
7372
|
const jiraProject = await input({
|
|
7349
7373
|
message: "Default Jira project key (optional):",
|
|
@@ -7367,7 +7391,10 @@ async function initGlobalConfig(start) {
|
|
|
7367
7391
|
process.exitCode = ExitCode.SUCCESS;
|
|
7368
7392
|
return;
|
|
7369
7393
|
}
|
|
7394
|
+
const existingGlobal = loadJsonFile(getGlobalConfigPath());
|
|
7395
|
+
const existingInstances = Array.isArray(existingGlobal?.jenkinsInstances) ? existingGlobal.jenkinsInstances : [];
|
|
7370
7396
|
writeGlobalConfig({
|
|
7397
|
+
jenkinsInstances: existingInstances,
|
|
7371
7398
|
user: {
|
|
7372
7399
|
email: userEmail || void 0,
|
|
7373
7400
|
userId: userId || void 0
|
|
@@ -7428,14 +7455,6 @@ async function initGlobalConfig(start) {
|
|
|
7428
7455
|
...jenkinsApiToken ? { apiToken: jenkinsApiToken } : {}
|
|
7429
7456
|
}
|
|
7430
7457
|
} : {},
|
|
7431
|
-
...useUdeploy && udeployBaseUrl ? {
|
|
7432
|
-
udeploy: {
|
|
7433
|
-
baseUrl: normalizeBaseUrl(udeployBaseUrl),
|
|
7434
|
-
...udeployPat ? { pat: udeployPat } : {},
|
|
7435
|
-
...udeployUsername ? { username: udeployUsername } : {},
|
|
7436
|
-
...udeployPassword ? { password: udeployPassword } : {}
|
|
7437
|
-
}
|
|
7438
|
-
} : {},
|
|
7439
7458
|
...useCheckmarx && checkmarxBaseUrl ? {
|
|
7440
7459
|
checkmarx: {
|
|
7441
7460
|
baseUrl: normalizeBaseUrl(checkmarxBaseUrl),
|
|
@@ -7489,6 +7508,18 @@ async function initGlobalConfig(start) {
|
|
|
7489
7508
|
token: logscaleToken || void 0
|
|
7490
7509
|
}
|
|
7491
7510
|
} : {},
|
|
7511
|
+
...useSplitio && splitioBaseUrl ? {
|
|
7512
|
+
splitio: {
|
|
7513
|
+
baseUrl: normalizeBaseUrl(splitioBaseUrl),
|
|
7514
|
+
adminApiKey: splitioAdminApiKey || void 0
|
|
7515
|
+
}
|
|
7516
|
+
} : {},
|
|
7517
|
+
...useFigma && figmaToken ? {
|
|
7518
|
+
figma: {
|
|
7519
|
+
baseUrl: "https://api.figma.com",
|
|
7520
|
+
token: figmaToken
|
|
7521
|
+
}
|
|
7522
|
+
} : {},
|
|
7492
7523
|
defaults: {
|
|
7493
7524
|
jira: {
|
|
7494
7525
|
project: jiraProject || void 0
|
|
@@ -7508,12 +7539,6 @@ async function initGlobalConfig(start) {
|
|
|
7508
7539
|
collection: adoCollection || void 0,
|
|
7509
7540
|
project: adoProject || void 0
|
|
7510
7541
|
}
|
|
7511
|
-
} : {},
|
|
7512
|
-
...useUdeploy && (udeployDefaultApp || udeployDefaultEnv) ? {
|
|
7513
|
-
udeploy: {
|
|
7514
|
-
application: udeployDefaultApp || void 0,
|
|
7515
|
-
environment: udeployDefaultEnv || void 0
|
|
7516
|
-
}
|
|
7517
7542
|
} : {}
|
|
7518
7543
|
}
|
|
7519
7544
|
});
|
|
@@ -8245,10 +8270,10 @@ function registerAdoWorkCommands(ado) {
|
|
|
8245
8270
|
const types = await discoverTypes(workClient, collection, project);
|
|
8246
8271
|
if (opts.save) {
|
|
8247
8272
|
const globalConfigPath = getGlobalConfigPath(globalOpts.config);
|
|
8248
|
-
const
|
|
8249
|
-
const existing = JSON.parse(
|
|
8273
|
+
const fs6 = await import("fs");
|
|
8274
|
+
const existing = JSON.parse(fs6.readFileSync(globalConfigPath, "utf8") || "{}");
|
|
8250
8275
|
existing.ado = { ...existing.ado ?? {}, discoveredTypes: types };
|
|
8251
|
-
|
|
8276
|
+
fs6.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
8252
8277
|
warn(`Saved ${types.length} types to ${globalConfigPath}`);
|
|
8253
8278
|
}
|
|
8254
8279
|
success(types, "ado", "work-types", start);
|
|
@@ -8277,11 +8302,11 @@ function registerAdoWorkCommands(ado) {
|
|
|
8277
8302
|
) : allFields;
|
|
8278
8303
|
if (opts.save) {
|
|
8279
8304
|
const globalConfigPath = getGlobalConfigPath(globalOpts.config);
|
|
8280
|
-
const
|
|
8281
|
-
const existing = JSON.parse(
|
|
8305
|
+
const fs6 = await import("fs");
|
|
8306
|
+
const existing = JSON.parse(fs6.readFileSync(globalConfigPath, "utf8") || "{}");
|
|
8282
8307
|
const aliases = buildDefaultAliases(allFields);
|
|
8283
8308
|
existing.ado = { ...existing.ado ?? {}, discoveredFields: allFields, fieldAliases: aliases };
|
|
8284
|
-
|
|
8309
|
+
fs6.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
8285
8310
|
warn(`Saved ${allFields.length} fields and ${Object.keys(aliases).length} aliases to ${globalConfigPath}`);
|
|
8286
8311
|
}
|
|
8287
8312
|
success(fields, "ado", "work-fields", start);
|
|
@@ -8578,11 +8603,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
8578
8603
|
const start = Date.now();
|
|
8579
8604
|
try {
|
|
8580
8605
|
const globalOpts = ado.optsWithGlobals();
|
|
8581
|
-
const { loadConfig: lc } = await import("./config-
|
|
8606
|
+
const { loadConfig: lc } = await import("./config-YHYQTGKT.js");
|
|
8582
8607
|
const config = lc({ configPath: globalOpts.config });
|
|
8583
8608
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
8584
8609
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
8585
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
8610
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-6QJU2SWP.js");
|
|
8586
8611
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
8587
8612
|
const coreClient = new AdoCoreClient2(http);
|
|
8588
8613
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -8840,11 +8865,22 @@ var JenkinsClient = class {
|
|
|
8840
8865
|
};
|
|
8841
8866
|
|
|
8842
8867
|
// src/services/jenkins/commands.ts
|
|
8843
|
-
function getClient7(
|
|
8844
|
-
const opts =
|
|
8868
|
+
function getClient7(cmd) {
|
|
8869
|
+
const opts = cmd.optsWithGlobals();
|
|
8845
8870
|
const config = loadConfig({ configPath: opts.config });
|
|
8846
|
-
|
|
8847
|
-
|
|
8871
|
+
let jenkins = config.jenkins;
|
|
8872
|
+
if (opts.instance) {
|
|
8873
|
+
const inst = config.jenkinsInstances.find((i) => i.name === opts.instance);
|
|
8874
|
+
if (!inst) throw new PncliError(`Jenkins instance "${opts.instance}" not found. Add it to your global config using pncli config set jenkinsInstances.`);
|
|
8875
|
+
jenkins = { baseUrl: inst.baseUrl, username: inst.username, apiToken: inst.apiToken };
|
|
8876
|
+
}
|
|
8877
|
+
if (!jenkins.baseUrl) {
|
|
8878
|
+
if (opts.instance) {
|
|
8879
|
+
throw new PncliError(`Jenkins instance "${opts.instance}" has no baseUrl configured. Add a baseUrl to that entry in jenkinsInstances.`);
|
|
8880
|
+
}
|
|
8881
|
+
throw new PncliError("Jenkins not configured. Run: pncli config init");
|
|
8882
|
+
}
|
|
8883
|
+
const http = createHttpClient({ ...config, jenkins }, Boolean(opts.dryRun));
|
|
8848
8884
|
return new JenkinsClient(http);
|
|
8849
8885
|
}
|
|
8850
8886
|
async function pollQueueItem(client, queueItemId, pollMs, timeoutMs) {
|
|
@@ -8869,12 +8905,70 @@ async function pollBuildComplete(client, jobName, buildNumber, pollMs, timeoutMs
|
|
|
8869
8905
|
throw new PncliError(`Timed out waiting for build #${buildNumber} to complete after ${timeoutMs / 1e3}s`, 1);
|
|
8870
8906
|
}
|
|
8871
8907
|
function registerJenkinsCommands(program2) {
|
|
8872
|
-
const jenkins = program2.command("jenkins").description("Jenkins
|
|
8908
|
+
const jenkins = program2.command("jenkins").description("Jenkins operations").option("--instance <name>", "Named Jenkins instance from global config (overrides default jenkins config)");
|
|
8873
8909
|
const pipeline = jenkins.command("pipeline").description("Pipeline (job/build) operations");
|
|
8910
|
+
const instance = jenkins.command("instance").description("Manage named Jenkins instances in the global config");
|
|
8911
|
+
instance.command("list").description("List configured Jenkins instances (API tokens masked)").action(() => {
|
|
8912
|
+
const start = Date.now();
|
|
8913
|
+
try {
|
|
8914
|
+
const opts = jenkins.optsWithGlobals();
|
|
8915
|
+
const config = loadConfig({ configPath: opts.config });
|
|
8916
|
+
const instances = config.jenkinsInstances.map((i) => ({
|
|
8917
|
+
...i,
|
|
8918
|
+
apiToken: i.apiToken ? "***" : void 0
|
|
8919
|
+
}));
|
|
8920
|
+
success({ instances }, "jenkins", "instance-list", start);
|
|
8921
|
+
} catch (err) {
|
|
8922
|
+
fail(err, "jenkins", "instance-list", start);
|
|
8923
|
+
}
|
|
8924
|
+
});
|
|
8925
|
+
instance.command("add").description("Add a named Jenkins instance to the global config (appends \u2014 existing instances are preserved)").requiredOption("--name <name>", "Instance name used with --instance").requiredOption("--base-url <url>", "Jenkins base URL (e.g. jenkins.imagile.dev)").option("--username <user>", "Jenkins username").option("--api-token <token>", "Jenkins API token (prompted for when omitted on an interactive terminal, keeping it out of shell history)").option("--force", "Overwrite an instance that already uses this name").action(async (o) => {
|
|
8926
|
+
const start = Date.now();
|
|
8927
|
+
try {
|
|
8928
|
+
const opts = jenkins.optsWithGlobals();
|
|
8929
|
+
const configPath = opts.config;
|
|
8930
|
+
const existing = loadConfig({ configPath }).jenkinsInstances;
|
|
8931
|
+
const idx = existing.findIndex((i) => i.name === o.name);
|
|
8932
|
+
if (idx >= 0 && !o.force) {
|
|
8933
|
+
throw new PncliError(`Jenkins instance "${o.name}" already exists. Pass --force to overwrite it, or run: pncli jenkins instance remove --name ${o.name}`, 1);
|
|
8934
|
+
}
|
|
8935
|
+
let apiToken = o.apiToken;
|
|
8936
|
+
if (!apiToken && process.stdin.isTTY) {
|
|
8937
|
+
const { default: password2 } = await import("@inquirer/password");
|
|
8938
|
+
apiToken = await password2({ message: `API token for "${o.name}" (leave blank to skip):` });
|
|
8939
|
+
}
|
|
8940
|
+
const entry = {
|
|
8941
|
+
name: o.name,
|
|
8942
|
+
baseUrl: normalizeBaseUrl(o.baseUrl),
|
|
8943
|
+
...o.username ? { username: o.username } : {},
|
|
8944
|
+
...apiToken ? { apiToken } : {}
|
|
8945
|
+
};
|
|
8946
|
+
const next = idx >= 0 ? existing.map((i, n) => n === idx ? entry : i) : [...existing, entry];
|
|
8947
|
+
setConfigValue("jenkinsInstances", JSON.stringify(next), configPath);
|
|
8948
|
+
success({ name: entry.name, baseUrl: entry.baseUrl, overwritten: idx >= 0 }, "jenkins", "instance-add", start);
|
|
8949
|
+
} catch (err) {
|
|
8950
|
+
fail(err, "jenkins", "instance-add", start);
|
|
8951
|
+
}
|
|
8952
|
+
});
|
|
8953
|
+
instance.command("remove").description("Remove a named Jenkins instance from the global config").requiredOption("--name <name>", "Instance name to remove").action((o) => {
|
|
8954
|
+
const start = Date.now();
|
|
8955
|
+
try {
|
|
8956
|
+
const opts = jenkins.optsWithGlobals();
|
|
8957
|
+
const configPath = opts.config;
|
|
8958
|
+
const existing = loadConfig({ configPath }).jenkinsInstances;
|
|
8959
|
+
if (!existing.some((i) => i.name === o.name)) {
|
|
8960
|
+
throw new PncliError(`Jenkins instance "${o.name}" not found. Run: pncli jenkins instance list`, 1);
|
|
8961
|
+
}
|
|
8962
|
+
setConfigValue("jenkinsInstances", JSON.stringify(existing.filter((i) => i.name !== o.name)), configPath);
|
|
8963
|
+
success({ removed: o.name }, "jenkins", "instance-remove", start);
|
|
8964
|
+
} catch (err) {
|
|
8965
|
+
fail(err, "jenkins", "instance-remove", start);
|
|
8966
|
+
}
|
|
8967
|
+
});
|
|
8874
8968
|
pipeline.command("list").description("List all jobs on the Jenkins instance (or within a folder)").option("--folder <name>", "Folder name to enumerate (supports nested folders: parentFolder/childFolder)").action(async (opts) => {
|
|
8875
8969
|
const start = Date.now();
|
|
8876
8970
|
try {
|
|
8877
|
-
const client = getClient7(
|
|
8971
|
+
const client = getClient7(jenkins);
|
|
8878
8972
|
const data = await client.listJobs(opts.folder);
|
|
8879
8973
|
success(data, "jenkins", "pipeline-list", start);
|
|
8880
8974
|
} catch (err) {
|
|
@@ -8884,7 +8978,7 @@ function registerJenkinsCommands(program2) {
|
|
|
8884
8978
|
pipeline.command("get").description("Get details for a specific job").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").action(async (opts) => {
|
|
8885
8979
|
const start = Date.now();
|
|
8886
8980
|
try {
|
|
8887
|
-
const client = getClient7(
|
|
8981
|
+
const client = getClient7(jenkins);
|
|
8888
8982
|
const data = await client.getJob(opts.name);
|
|
8889
8983
|
success(data, "jenkins", "pipeline-get", start);
|
|
8890
8984
|
} catch (err) {
|
|
@@ -8897,7 +8991,7 @@ function registerJenkinsCommands(program2) {
|
|
|
8897
8991
|
}, []).option("--wait", "Wait for the build to complete before returning").option("--timeout <s>", "Max wait time in seconds per phase (queue-wait and build-wait each get this budget; default 600)", "600").option("--poll <s>", "Poll interval in seconds (default 10)", "10").action(async (opts) => {
|
|
8898
8992
|
const start = Date.now();
|
|
8899
8993
|
try {
|
|
8900
|
-
const client = getClient7(
|
|
8994
|
+
const client = getClient7(jenkins);
|
|
8901
8995
|
for (const p of opts.parameter) {
|
|
8902
8996
|
if (!p.includes("=")) throw new PncliError(`Invalid --parameter "${p}". Expected format: key=value`, 1);
|
|
8903
8997
|
}
|
|
@@ -8930,7 +9024,7 @@ function registerJenkinsCommands(program2) {
|
|
|
8930
9024
|
pipeline.command("list-runs").description("List recent builds for a job").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").option("--top <n>", "Maximum number of builds to return (default 25)", "25").action(async (opts) => {
|
|
8931
9025
|
const start = Date.now();
|
|
8932
9026
|
try {
|
|
8933
|
-
const client = getClient7(
|
|
9027
|
+
const client = getClient7(jenkins);
|
|
8934
9028
|
const top = parseInt(opts.top, 10);
|
|
8935
9029
|
if (isNaN(top) || top <= 0) throw new PncliError(`Invalid --top "${opts.top}". Expected a positive integer.`, 1);
|
|
8936
9030
|
const data = await client.listBuilds(opts.name, top);
|
|
@@ -8942,7 +9036,7 @@ function registerJenkinsCommands(program2) {
|
|
|
8942
9036
|
pipeline.command("get-run").description("Get details for a specific build").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").requiredOption("--number <n>", "Build number").action(async (opts) => {
|
|
8943
9037
|
const start = Date.now();
|
|
8944
9038
|
try {
|
|
8945
|
-
const client = getClient7(
|
|
9039
|
+
const client = getClient7(jenkins);
|
|
8946
9040
|
const buildNumber = parseInt(opts.number, 10);
|
|
8947
9041
|
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
8948
9042
|
const data = await client.getBuild(opts.name, buildNumber);
|
|
@@ -8954,7 +9048,7 @@ function registerJenkinsCommands(program2) {
|
|
|
8954
9048
|
pipeline.command("logs").description("Fetch console log for a build (consider --output-file for large logs)").requiredOption("--name <job>", "Job name (use folder/job for folder-scoped jobs)").requiredOption("--number <n>", "Build number").action(async (opts) => {
|
|
8955
9049
|
const start = Date.now();
|
|
8956
9050
|
try {
|
|
8957
|
-
const client = getClient7(
|
|
9051
|
+
const client = getClient7(jenkins);
|
|
8958
9052
|
const buildNumber = parseInt(opts.number, 10);
|
|
8959
9053
|
if (isNaN(buildNumber)) throw new PncliError(`Invalid --number "${opts.number}". Expected an integer.`, 1);
|
|
8960
9054
|
const text = await client.getConsoleLog(opts.name, buildNumber);
|
|
@@ -9031,7 +9125,7 @@ var ArtifactoryClient = class {
|
|
|
9031
9125
|
}));
|
|
9032
9126
|
const result = { builds, uri: raw.uri };
|
|
9033
9127
|
if (builds.length === 0) {
|
|
9034
|
-
result.hint = 'No builds are registered in Artifactory Build Integration. Build names may differ from ADO pipeline
|
|
9128
|
+
result.hint = 'No builds are registered in Artifactory Build Integration. Build names may differ from ADO pipeline names. Use "artifactory repos" to list repositories, then "artifactory search --repo <repo>" to locate artifacts directly.';
|
|
9035
9129
|
}
|
|
9036
9130
|
return result;
|
|
9037
9131
|
}
|
|
@@ -9134,7 +9228,7 @@ function registerArtifactoryCommands(program2) {
|
|
|
9134
9228
|
fail(err, "artifactory", "search", start);
|
|
9135
9229
|
}
|
|
9136
9230
|
});
|
|
9137
|
-
art.command("builds").description('List builds registered in Artifactory Build Integration (names may differ from ADO pipeline
|
|
9231
|
+
art.command("builds").description('List builds registered in Artifactory Build Integration (names may differ from ADO pipeline names; use "search --repo" when empty)').option("--timeout <s>", "Request timeout in seconds (default 60; increase for large Artifactory instances)", "60").action(async (opts) => {
|
|
9138
9232
|
const start = Date.now();
|
|
9139
9233
|
try {
|
|
9140
9234
|
const client = getClient8(program2);
|
|
@@ -9169,226 +9263,6 @@ function registerArtifactoryCommands(program2) {
|
|
|
9169
9263
|
});
|
|
9170
9264
|
}
|
|
9171
9265
|
|
|
9172
|
-
// src/services/udeploy/client.ts
|
|
9173
|
-
var UdeployClient = class {
|
|
9174
|
-
constructor(http) {
|
|
9175
|
-
this.http = http;
|
|
9176
|
-
}
|
|
9177
|
-
http;
|
|
9178
|
-
listApplications() {
|
|
9179
|
-
return this.http.udeploy("/cli/application");
|
|
9180
|
-
}
|
|
9181
|
-
listEnvironments(application) {
|
|
9182
|
-
return this.http.udeploy("/cli/application/environmentsInApplication", {
|
|
9183
|
-
params: { application }
|
|
9184
|
-
});
|
|
9185
|
-
}
|
|
9186
|
-
listComponents() {
|
|
9187
|
-
return this.http.udeploy("/cli/component");
|
|
9188
|
-
}
|
|
9189
|
-
listVersions(component) {
|
|
9190
|
-
return this.http.udeploy("/cli/component/versions", {
|
|
9191
|
-
params: { component }
|
|
9192
|
-
});
|
|
9193
|
-
}
|
|
9194
|
-
createVersion(component, name) {
|
|
9195
|
-
return this.http.udeploy("/cli/version/createVersion", {
|
|
9196
|
-
method: "POST",
|
|
9197
|
-
params: { component, name }
|
|
9198
|
-
});
|
|
9199
|
-
}
|
|
9200
|
-
finishImporting(component, version) {
|
|
9201
|
-
return this.http.udeploy("/cli/version/finishedImporting", {
|
|
9202
|
-
method: "POST",
|
|
9203
|
-
params: { component, version }
|
|
9204
|
-
});
|
|
9205
|
-
}
|
|
9206
|
-
runProcess(opts) {
|
|
9207
|
-
return this.http.udeploy("/cli/applicationProcessRequest/request", {
|
|
9208
|
-
method: "PUT",
|
|
9209
|
-
body: {
|
|
9210
|
-
application: opts.application,
|
|
9211
|
-
applicationProcess: opts.applicationProcess,
|
|
9212
|
-
environment: opts.environment,
|
|
9213
|
-
onlyChanged: opts.onlyChanged ?? false,
|
|
9214
|
-
...opts.versions ? { versions: opts.versions } : {},
|
|
9215
|
-
...opts.snapshot ? { snapshot: opts.snapshot } : {},
|
|
9216
|
-
...opts.properties ? { properties: opts.properties } : {}
|
|
9217
|
-
}
|
|
9218
|
-
});
|
|
9219
|
-
}
|
|
9220
|
-
getRequestStatus(requestId) {
|
|
9221
|
-
return this.http.udeploy("/cli/applicationProcessRequest/requestStatus", {
|
|
9222
|
-
params: { requestId }
|
|
9223
|
-
});
|
|
9224
|
-
}
|
|
9225
|
-
getRequestInfo(requestId) {
|
|
9226
|
-
return this.http.udeploy("/cli/applicationProcessRequest/requestInfo", {
|
|
9227
|
-
params: { requestId }
|
|
9228
|
-
});
|
|
9229
|
-
}
|
|
9230
|
-
};
|
|
9231
|
-
|
|
9232
|
-
// src/services/udeploy/commands.ts
|
|
9233
|
-
var POLL_INTERVAL_MS2 = 5e3;
|
|
9234
|
-
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
9235
|
-
function getClient9(program2) {
|
|
9236
|
-
const opts = program2.optsWithGlobals();
|
|
9237
|
-
const config = loadConfig({ configPath: opts.config });
|
|
9238
|
-
if (!config.udeploy.baseUrl) {
|
|
9239
|
-
throw new PncliError("UDeploy baseUrl not configured. Run: pncli config init", 78);
|
|
9240
|
-
}
|
|
9241
|
-
const http = createHttpClient(config, opts.dryRun);
|
|
9242
|
-
return { client: new UdeployClient(http), config };
|
|
9243
|
-
}
|
|
9244
|
-
function resolveApplication(config, cliValue) {
|
|
9245
|
-
const app = cliValue ?? config.defaults.udeploy.application;
|
|
9246
|
-
if (!app) throw new PncliError("--application is required (or set defaults.udeploy.application in config)", 2);
|
|
9247
|
-
return app;
|
|
9248
|
-
}
|
|
9249
|
-
function resolveEnvironment(config, cliValue) {
|
|
9250
|
-
const env = cliValue ?? config.defaults.udeploy.environment;
|
|
9251
|
-
if (!env) throw new PncliError("--environment is required (or set defaults.udeploy.environment in config)", 2);
|
|
9252
|
-
return env;
|
|
9253
|
-
}
|
|
9254
|
-
function registerUdeployCommands(program2) {
|
|
9255
|
-
const udeploy = program2.command("udeploy").description("IBM UrbanCode Deploy \u2014 component versions and deployment processes").option("--application <name>", "Application name or ID").option("--environment <name>", "Environment name or ID");
|
|
9256
|
-
udeploy.command("apps").description("List all applications").action(async () => {
|
|
9257
|
-
const start = Date.now();
|
|
9258
|
-
try {
|
|
9259
|
-
const { client } = getClient9(program2);
|
|
9260
|
-
const apps = await client.listApplications();
|
|
9261
|
-
success(apps, "udeploy", "apps", start);
|
|
9262
|
-
} catch (err) {
|
|
9263
|
-
fail(err, "udeploy", "apps", start);
|
|
9264
|
-
}
|
|
9265
|
-
});
|
|
9266
|
-
udeploy.command("environments").description("List environments for an application").action(async () => {
|
|
9267
|
-
const start = Date.now();
|
|
9268
|
-
try {
|
|
9269
|
-
const { client, config } = getClient9(program2);
|
|
9270
|
-
const parentOpts = udeploy.opts();
|
|
9271
|
-
const application = resolveApplication(config, parentOpts.application);
|
|
9272
|
-
const envs = await client.listEnvironments(application);
|
|
9273
|
-
success(envs, "udeploy", "environments", start);
|
|
9274
|
-
} catch (err) {
|
|
9275
|
-
fail(err, "udeploy", "environments", start);
|
|
9276
|
-
}
|
|
9277
|
-
});
|
|
9278
|
-
udeploy.command("components").description("List all components").action(async () => {
|
|
9279
|
-
const start = Date.now();
|
|
9280
|
-
try {
|
|
9281
|
-
const { client } = getClient9(program2);
|
|
9282
|
-
const components = await client.listComponents();
|
|
9283
|
-
success(components, "udeploy", "components", start);
|
|
9284
|
-
} catch (err) {
|
|
9285
|
-
fail(err, "udeploy", "components", start);
|
|
9286
|
-
}
|
|
9287
|
-
});
|
|
9288
|
-
udeploy.command("versions").description("List versions for a component").requiredOption("--component <name>", "Component name or ID").action(async (cmdOpts) => {
|
|
9289
|
-
const start = Date.now();
|
|
9290
|
-
try {
|
|
9291
|
-
const { client } = getClient9(program2);
|
|
9292
|
-
const versions = await client.listVersions(cmdOpts.component);
|
|
9293
|
-
success(versions, "udeploy", "versions", start);
|
|
9294
|
-
} catch (err) {
|
|
9295
|
-
fail(err, "udeploy", "versions", start);
|
|
9296
|
-
}
|
|
9297
|
-
});
|
|
9298
|
-
udeploy.command("import-version").description("Create a component version and mark it ready for deployment").requiredOption("--component <name>", "Component name or ID").requiredOption("--name <name>", "Version name").option("--no-finish", "Skip marking the version as finished importing").action(async (cmdOpts) => {
|
|
9299
|
-
const start = Date.now();
|
|
9300
|
-
try {
|
|
9301
|
-
const { client } = getClient9(program2);
|
|
9302
|
-
const created = await client.createVersion(cmdOpts.component, cmdOpts.name);
|
|
9303
|
-
if (cmdOpts.finish) {
|
|
9304
|
-
await client.finishImporting(cmdOpts.component, cmdOpts.name);
|
|
9305
|
-
}
|
|
9306
|
-
success({ ...created, finishedImporting: cmdOpts.finish }, "udeploy", "import-version", start);
|
|
9307
|
-
} catch (err) {
|
|
9308
|
-
fail(err, "udeploy", "import-version", start);
|
|
9309
|
-
}
|
|
9310
|
-
});
|
|
9311
|
-
udeploy.command("run").description("Run an application deployment process").requiredOption("--process <name>", "Application process name or ID").option("--component <name>", "Component name (repeatable)", collect, []).option("--component-version <name>", "Component version (repeatable; positionally paired with --component)", collect, []).option("--snapshot <name>", "Snapshot name or ID (alternative to specifying versions)").option("--only-changed", "Deploy only changed components", false).option("--wait", "Poll until the process completes", false).option("--timeout <ms>", "Max wait time in milliseconds", String(DEFAULT_TIMEOUT_MS)).action(async (cmdOpts) => {
|
|
9312
|
-
const start = Date.now();
|
|
9313
|
-
try {
|
|
9314
|
-
const { client, config } = getClient9(program2);
|
|
9315
|
-
const parentOpts = udeploy.opts();
|
|
9316
|
-
const application = resolveApplication(config, parentOpts.application);
|
|
9317
|
-
const environment = resolveEnvironment(config, parentOpts.environment);
|
|
9318
|
-
const components = cmdOpts.component;
|
|
9319
|
-
const versions = cmdOpts.componentVersion;
|
|
9320
|
-
const versionEntries = components.map((c, i) => ({
|
|
9321
|
-
component: c,
|
|
9322
|
-
version: versions[i] ?? "latest"
|
|
9323
|
-
}));
|
|
9324
|
-
const result = await client.runProcess({
|
|
9325
|
-
application,
|
|
9326
|
-
applicationProcess: cmdOpts.process,
|
|
9327
|
-
environment,
|
|
9328
|
-
onlyChanged: cmdOpts.onlyChanged,
|
|
9329
|
-
...versionEntries.length > 0 ? { versions: versionEntries } : {},
|
|
9330
|
-
...cmdOpts.snapshot ? { snapshot: cmdOpts.snapshot } : {}
|
|
9331
|
-
});
|
|
9332
|
-
if (!cmdOpts.wait) {
|
|
9333
|
-
success(result, "udeploy", "run", start);
|
|
9334
|
-
return;
|
|
9335
|
-
}
|
|
9336
|
-
const timeoutMs = parseInt(cmdOpts.timeout, 10);
|
|
9337
|
-
const requestId = result.requestId;
|
|
9338
|
-
log(`Process started. Request ID: ${requestId}. Polling for completion...`);
|
|
9339
|
-
while (true) {
|
|
9340
|
-
const elapsed = Date.now() - start;
|
|
9341
|
-
if (elapsed >= timeoutMs) {
|
|
9342
|
-
throw new PncliError(`Timed out after ${timeoutMs}ms waiting for request ${requestId}`, 1);
|
|
9343
|
-
}
|
|
9344
|
-
await sleep(POLL_INTERVAL_MS2);
|
|
9345
|
-
const status = await client.getRequestStatus(requestId);
|
|
9346
|
-
if (status.status === "CLOSED") {
|
|
9347
|
-
const outcome = { requestId, status: status.status, result: status.result, elapsed_ms: Date.now() - start };
|
|
9348
|
-
if (status.result === "SUCCEEDED") {
|
|
9349
|
-
success(outcome, "udeploy", "run", start);
|
|
9350
|
-
} else {
|
|
9351
|
-
throw new PncliError(
|
|
9352
|
-
`Deployment ${status.result ?? "failed"} (requestId: ${requestId})`,
|
|
9353
|
-
1
|
|
9354
|
-
);
|
|
9355
|
-
}
|
|
9356
|
-
return;
|
|
9357
|
-
}
|
|
9358
|
-
log(`Status: ${status.status} (${Math.round((Date.now() - start) / 1e3)}s elapsed)`);
|
|
9359
|
-
}
|
|
9360
|
-
} catch (err) {
|
|
9361
|
-
fail(err, "udeploy", "run", start);
|
|
9362
|
-
}
|
|
9363
|
-
});
|
|
9364
|
-
udeploy.command("request-status").description("Get the current status of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
|
|
9365
|
-
const start = Date.now();
|
|
9366
|
-
try {
|
|
9367
|
-
const { client } = getClient9(program2);
|
|
9368
|
-
const status = await client.getRequestStatus(cmdOpts.requestId);
|
|
9369
|
-
success(status, "udeploy", "request-status", start);
|
|
9370
|
-
} catch (err) {
|
|
9371
|
-
fail(err, "udeploy", "request-status", start);
|
|
9372
|
-
}
|
|
9373
|
-
});
|
|
9374
|
-
udeploy.command("request-info").description("Get full details of a deployment process request").requiredOption("--request-id <id>", "Request ID returned by udeploy run").action(async (cmdOpts) => {
|
|
9375
|
-
const start = Date.now();
|
|
9376
|
-
try {
|
|
9377
|
-
const { client } = getClient9(program2);
|
|
9378
|
-
const info = await client.getRequestInfo(cmdOpts.requestId);
|
|
9379
|
-
success(info, "udeploy", "request-info", start);
|
|
9380
|
-
} catch (err) {
|
|
9381
|
-
fail(err, "udeploy", "request-info", start);
|
|
9382
|
-
}
|
|
9383
|
-
});
|
|
9384
|
-
}
|
|
9385
|
-
function collect(val, prev) {
|
|
9386
|
-
return [...prev, val];
|
|
9387
|
-
}
|
|
9388
|
-
function sleep(ms) {
|
|
9389
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9390
|
-
}
|
|
9391
|
-
|
|
9392
9266
|
// src/services/checkmarx/client.ts
|
|
9393
9267
|
var CheckmarxClient = class {
|
|
9394
9268
|
constructor(http) {
|
|
@@ -9429,7 +9303,7 @@ var CheckmarxClient = class {
|
|
|
9429
9303
|
};
|
|
9430
9304
|
|
|
9431
9305
|
// src/services/checkmarx/commands.ts
|
|
9432
|
-
function
|
|
9306
|
+
function getClient9(program2) {
|
|
9433
9307
|
const opts = program2.optsWithGlobals();
|
|
9434
9308
|
const config = loadConfig({ configPath: opts.config });
|
|
9435
9309
|
if (!config.checkmarx.baseUrl) throw new PncliError("Checkmarx not configured. Run: pncli config init");
|
|
@@ -9443,7 +9317,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
9443
9317
|
project.command("list").description("List all projects").action(async () => {
|
|
9444
9318
|
const start = Date.now();
|
|
9445
9319
|
try {
|
|
9446
|
-
const data = await
|
|
9320
|
+
const data = await getClient9(program2).listProjects();
|
|
9447
9321
|
success(data, "checkmarx", "project-list", start);
|
|
9448
9322
|
} catch (err) {
|
|
9449
9323
|
fail(err, "checkmarx", "project-list", start);
|
|
@@ -9452,7 +9326,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
9452
9326
|
project.command("get").description("Get a project by ID").requiredOption("--id <id>", "Project UUID").action(async (opts) => {
|
|
9453
9327
|
const start = Date.now();
|
|
9454
9328
|
try {
|
|
9455
|
-
const data = await
|
|
9329
|
+
const data = await getClient9(program2).getProject(opts.id);
|
|
9456
9330
|
success(data, "checkmarx", "project-get", start);
|
|
9457
9331
|
} catch (err) {
|
|
9458
9332
|
fail(err, "checkmarx", "project-get", start);
|
|
@@ -9463,7 +9337,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
9463
9337
|
try {
|
|
9464
9338
|
const last = opts.last ? parseInt(opts.last, 10) : void 0;
|
|
9465
9339
|
if (opts.last && isNaN(last)) throw new PncliError(`Invalid --last "${opts.last}". Expected an integer.`, 1);
|
|
9466
|
-
const data = await
|
|
9340
|
+
const data = await getClient9(program2).listScans({ projectId: opts.project, last });
|
|
9467
9341
|
success(data, "checkmarx", "scan-list", start);
|
|
9468
9342
|
} catch (err) {
|
|
9469
9343
|
fail(err, "checkmarx", "scan-list", start);
|
|
@@ -9472,7 +9346,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
9472
9346
|
scan.command("get").description("Get a scan by ID").requiredOption("--id <id>", "Scan UUID").action(async (opts) => {
|
|
9473
9347
|
const start = Date.now();
|
|
9474
9348
|
try {
|
|
9475
|
-
const data = await
|
|
9349
|
+
const data = await getClient9(program2).getScan(opts.id);
|
|
9476
9350
|
success(data, "checkmarx", "scan-get", start);
|
|
9477
9351
|
} catch (err) {
|
|
9478
9352
|
fail(err, "checkmarx", "scan-get", start);
|
|
@@ -9481,7 +9355,7 @@ function registerCheckmarxCommands(program2) {
|
|
|
9481
9355
|
scan.command("stats").description("Get results statistics for a scan").requiredOption("--id <id>", "Scan UUID").action(async (opts) => {
|
|
9482
9356
|
const start = Date.now();
|
|
9483
9357
|
try {
|
|
9484
|
-
const data = await
|
|
9358
|
+
const data = await getClient9(program2).getScanResultsStatistics(opts.id);
|
|
9485
9359
|
success(data, "checkmarx", "scan-stats", start);
|
|
9486
9360
|
} catch (err) {
|
|
9487
9361
|
fail(err, "checkmarx", "scan-stats", start);
|
|
@@ -10934,6 +10808,29 @@ function podAge(creationTimestamp) {
|
|
|
10934
10808
|
if (ageHours < 24) return `${ageHours}h`;
|
|
10935
10809
|
return `${Math.floor(ageHours / 24)}d`;
|
|
10936
10810
|
}
|
|
10811
|
+
function parseCpuMillicores(cpu) {
|
|
10812
|
+
if (!cpu) return null;
|
|
10813
|
+
if (cpu.endsWith("n")) return Math.round(Number(cpu.slice(0, -1)) / 1e6);
|
|
10814
|
+
if (cpu.endsWith("u")) return Math.round(Number(cpu.slice(0, -1)) / 1e3);
|
|
10815
|
+
if (cpu.endsWith("m")) return Number(cpu.slice(0, -1));
|
|
10816
|
+
const whole = Number(cpu);
|
|
10817
|
+
if (!Number.isNaN(whole)) return Math.round(whole * 1e3);
|
|
10818
|
+
return null;
|
|
10819
|
+
}
|
|
10820
|
+
function parseMemMiB(mem) {
|
|
10821
|
+
if (!mem) return null;
|
|
10822
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
10823
|
+
if (mem.endsWith("Ki")) return round2(Number(mem.slice(0, -2)) / 1024);
|
|
10824
|
+
if (mem.endsWith("Mi")) return Number(mem.slice(0, -2));
|
|
10825
|
+
if (mem.endsWith("Gi")) return Math.round(Number(mem.slice(0, -2)) * 1024);
|
|
10826
|
+
if (mem.endsWith("Ti")) return Math.round(Number(mem.slice(0, -2)) * 1024 * 1024);
|
|
10827
|
+
if (mem.endsWith("K")) return round2(Number(mem.slice(0, -1)) * 1e3 / (1024 * 1024));
|
|
10828
|
+
if (mem.endsWith("M")) return round2(Number(mem.slice(0, -1)) * 1e3 * 1e3 / (1024 * 1024));
|
|
10829
|
+
if (mem.endsWith("G")) return Math.round(Number(mem.slice(0, -1)) * 1e3 * 1e3 * 1e3 / (1024 * 1024));
|
|
10830
|
+
const bytes = Number(mem);
|
|
10831
|
+
if (!Number.isNaN(bytes)) return round2(bytes / (1024 * 1024));
|
|
10832
|
+
return null;
|
|
10833
|
+
}
|
|
10937
10834
|
function registerOpenShiftCommands(program2) {
|
|
10938
10835
|
const oc = program2.command("openshift").description("OpenShift / Kubernetes operations");
|
|
10939
10836
|
oc.command("pods").description("List pod health summary for a namespace \u2014 pre-processed for LLM analysis. Use --raw to return the full unmodified Kubernetes pod objects.").requiredOption("--namespace <ns>", "Kubernetes namespace").option("--label-selector <selector>", "Label selector (e.g. app=my-app)").option("--raw", "Return full Kubernetes pod objects without summarization (includes env vars, images, node name, annotations, resources, etc.)", false).action(async (opts) => {
|
|
@@ -11131,6 +11028,71 @@ function registerOpenShiftCommands(program2) {
|
|
|
11131
11028
|
fail(err, "openshift", "pod-metrics", start);
|
|
11132
11029
|
}
|
|
11133
11030
|
});
|
|
11031
|
+
oc.command("resource-usage").description("Combined CPU/memory usage, limits, and requests for all pods in a namespace \u2014 all values normalized to millicores (m) and mebibytes (Mi). Requires metrics-server. Use --csv to emit a spreadsheet-friendly CSV.").requiredOption("--namespace <ns>", "Kubernetes namespace").option("--label-selector <selector>", "Label selector (e.g. app=my-app)").option("--csv", "Output as CSV instead of JSON (suitable for Excel)", false).action(async (opts) => {
|
|
11032
|
+
const start = Date.now();
|
|
11033
|
+
try {
|
|
11034
|
+
const http = getHttp2(program2);
|
|
11035
|
+
const nsEncoded = encodeURIComponent(opts.namespace);
|
|
11036
|
+
const podParams = {};
|
|
11037
|
+
if (opts.labelSelector) podParams["labelSelector"] = opts.labelSelector;
|
|
11038
|
+
const [podList, metricsList] = await Promise.all([
|
|
11039
|
+
http.openshift(`/api/v1/namespaces/${nsEncoded}/pods`, { params: podParams }),
|
|
11040
|
+
http.openshift(`/apis/metrics.k8s.io/v1beta1/namespaces/${nsEncoded}/pods`, { params: podParams })
|
|
11041
|
+
]);
|
|
11042
|
+
const metricsIndex = /* @__PURE__ */ new Map();
|
|
11043
|
+
for (const pm of metricsList.items) {
|
|
11044
|
+
const containerMap = /* @__PURE__ */ new Map();
|
|
11045
|
+
for (const c of pm.containers) {
|
|
11046
|
+
containerMap.set(c.name, c.usage);
|
|
11047
|
+
}
|
|
11048
|
+
metricsIndex.set(pm.metadata.name, containerMap);
|
|
11049
|
+
}
|
|
11050
|
+
const rows = [];
|
|
11051
|
+
for (const pod of podList.items) {
|
|
11052
|
+
const podName = pod.metadata.name;
|
|
11053
|
+
const podMetrics = metricsIndex.get(podName);
|
|
11054
|
+
const containers = pod.spec.containers ?? [];
|
|
11055
|
+
for (const container of containers) {
|
|
11056
|
+
const usage = podMetrics?.get(container.name);
|
|
11057
|
+
rows.push({
|
|
11058
|
+
pod: podName,
|
|
11059
|
+
container: container.name,
|
|
11060
|
+
cpuUsageMillicores: parseCpuMillicores(usage?.cpu),
|
|
11061
|
+
memUsageMiB: parseMemMiB(usage?.memory),
|
|
11062
|
+
cpuLimitsMillicores: parseCpuMillicores(container.resources?.limits?.cpu),
|
|
11063
|
+
memLimitsMiB: parseMemMiB(container.resources?.limits?.memory),
|
|
11064
|
+
cpuRequestsMillicores: parseCpuMillicores(container.resources?.requests?.cpu),
|
|
11065
|
+
memRequestsMiB: parseMemMiB(container.resources?.requests?.memory)
|
|
11066
|
+
});
|
|
11067
|
+
}
|
|
11068
|
+
}
|
|
11069
|
+
if (opts.csv) {
|
|
11070
|
+
const header = "Pod,Container,CPU Usage (m),Memory Usage (Mi),CPU Limits (m),Memory Limits (Mi),CPU Requests (m),Memory Requests (Mi)";
|
|
11071
|
+
const lines = rows.map(
|
|
11072
|
+
(r) => [
|
|
11073
|
+
r.pod,
|
|
11074
|
+
r.container,
|
|
11075
|
+
r.cpuUsageMillicores ?? "",
|
|
11076
|
+
r.memUsageMiB ?? "",
|
|
11077
|
+
r.cpuLimitsMillicores ?? "",
|
|
11078
|
+
r.memLimitsMiB ?? "",
|
|
11079
|
+
r.cpuRequestsMillicores ?? "",
|
|
11080
|
+
r.memRequestsMiB ?? ""
|
|
11081
|
+
].join(",")
|
|
11082
|
+
);
|
|
11083
|
+
writeRawOutput([header, ...lines].join("\n") + "\n");
|
|
11084
|
+
} else {
|
|
11085
|
+
success(
|
|
11086
|
+
{ namespace: opts.namespace, count: rows.length, units: { cpu: "m", memory: "Mi" }, rows },
|
|
11087
|
+
"openshift",
|
|
11088
|
+
"resource-usage",
|
|
11089
|
+
start
|
|
11090
|
+
);
|
|
11091
|
+
}
|
|
11092
|
+
} catch (err) {
|
|
11093
|
+
fail(err, "openshift", "resource-usage", start);
|
|
11094
|
+
}
|
|
11095
|
+
});
|
|
11134
11096
|
}
|
|
11135
11097
|
|
|
11136
11098
|
// src/services/dynatrace/commands.ts
|
|
@@ -11356,6 +11318,500 @@ function registerLogscaleCommands(program2) {
|
|
|
11356
11318
|
});
|
|
11357
11319
|
}
|
|
11358
11320
|
|
|
11321
|
+
// src/services/splitio/commands.ts
|
|
11322
|
+
import fs5 from "fs";
|
|
11323
|
+
import confirm2 from "@inquirer/confirm";
|
|
11324
|
+
function getHttp5(program2) {
|
|
11325
|
+
const opts = program2.optsWithGlobals();
|
|
11326
|
+
return createHttpClient(
|
|
11327
|
+
loadConfig({ configPath: opts.config }),
|
|
11328
|
+
Boolean(opts.dryRun)
|
|
11329
|
+
);
|
|
11330
|
+
}
|
|
11331
|
+
function loadInputFile(filePath) {
|
|
11332
|
+
let raw;
|
|
11333
|
+
if (filePath === "-") {
|
|
11334
|
+
raw = fs5.readFileSync(process.stdin.fd, "utf8");
|
|
11335
|
+
} else {
|
|
11336
|
+
raw = fs5.readFileSync(filePath, "utf8");
|
|
11337
|
+
}
|
|
11338
|
+
try {
|
|
11339
|
+
return JSON.parse(raw);
|
|
11340
|
+
} catch {
|
|
11341
|
+
throw new PncliError(`Could not parse JSON from ${filePath === "-" ? "stdin" : filePath}`);
|
|
11342
|
+
}
|
|
11343
|
+
}
|
|
11344
|
+
async function confirmWrite(opts, opLabel) {
|
|
11345
|
+
if (opts.yes) return;
|
|
11346
|
+
const proceed = await confirm2({
|
|
11347
|
+
message: `Submit Change Request "${opts.mnemonic}" (${opLabel})? This creates an approval request in Split.`,
|
|
11348
|
+
default: false
|
|
11349
|
+
});
|
|
11350
|
+
if (!proceed) throw new PncliError("Aborted by user.", 0);
|
|
11351
|
+
}
|
|
11352
|
+
function buildCrBase(opts) {
|
|
11353
|
+
const base = {
|
|
11354
|
+
title: opts.mnemonic,
|
|
11355
|
+
comment: opts.description,
|
|
11356
|
+
crNumber: opts.changeNumber
|
|
11357
|
+
};
|
|
11358
|
+
if (opts.approvers) {
|
|
11359
|
+
base["approvers"] = opts.approvers.split(",").map((s) => s.trim()).filter(Boolean).map((id) => ({ id }));
|
|
11360
|
+
}
|
|
11361
|
+
return base;
|
|
11362
|
+
}
|
|
11363
|
+
function registerSplitioCommands(program2) {
|
|
11364
|
+
const splitio = program2.command("splitio").description("Split.IO feature flag administration");
|
|
11365
|
+
const workspaces = splitio.command("workspaces").description("Workspace operations");
|
|
11366
|
+
workspaces.command("list").description("List all Split.IO workspaces").action(async () => {
|
|
11367
|
+
const start = Date.now();
|
|
11368
|
+
try {
|
|
11369
|
+
const http = getHttp5(program2);
|
|
11370
|
+
const data = await http.splitio("/internal/api/v2/workspaces");
|
|
11371
|
+
success(
|
|
11372
|
+
{ count: data.objects.length, workspaces: data.objects },
|
|
11373
|
+
"splitio",
|
|
11374
|
+
"workspaces list",
|
|
11375
|
+
start
|
|
11376
|
+
);
|
|
11377
|
+
} catch (err) {
|
|
11378
|
+
fail(err, "splitio", "workspaces list", start);
|
|
11379
|
+
}
|
|
11380
|
+
});
|
|
11381
|
+
const environments = splitio.command("environments").description("Environment operations");
|
|
11382
|
+
environments.command("list").description("List environments in a workspace").requiredOption("--workspace <id>", "Workspace ID").action(async (opts) => {
|
|
11383
|
+
const start = Date.now();
|
|
11384
|
+
try {
|
|
11385
|
+
const http = getHttp5(program2);
|
|
11386
|
+
const data = await http.splitio(
|
|
11387
|
+
`/internal/api/v2/workspaces/${encodeURIComponent(opts.workspace)}/environments`
|
|
11388
|
+
);
|
|
11389
|
+
success(
|
|
11390
|
+
{ count: data.objects.length, environments: data.objects },
|
|
11391
|
+
"splitio",
|
|
11392
|
+
"environments list",
|
|
11393
|
+
start
|
|
11394
|
+
);
|
|
11395
|
+
} catch (err) {
|
|
11396
|
+
fail(err, "splitio", "environments list", start);
|
|
11397
|
+
}
|
|
11398
|
+
});
|
|
11399
|
+
const flags = splitio.command("flags").description("Feature flag operations");
|
|
11400
|
+
flags.command("list").description("List feature flags in a workspace").requiredOption("--workspace <id>", "Workspace ID").option("--limit <n>", "Maximum number of flags to return", "50").option("--offset <n>", "Pagination offset", "0").option("--flag-set <name>", "Filter by flag set name").action(async (opts) => {
|
|
11401
|
+
const start = Date.now();
|
|
11402
|
+
try {
|
|
11403
|
+
const http = getHttp5(program2);
|
|
11404
|
+
const params = {
|
|
11405
|
+
wsId: opts.workspace,
|
|
11406
|
+
limit: parseInt(opts.limit, 10),
|
|
11407
|
+
offset: parseInt(opts.offset, 10)
|
|
11408
|
+
};
|
|
11409
|
+
if (opts.flagSet) params["flagSetName"] = opts.flagSet;
|
|
11410
|
+
const data = await http.splitio("/internal/api/v2/splits", { params });
|
|
11411
|
+
success(
|
|
11412
|
+
{ count: data.objects.length, totalCount: data.totalCount, flags: data.objects },
|
|
11413
|
+
"splitio",
|
|
11414
|
+
"flags list",
|
|
11415
|
+
start
|
|
11416
|
+
);
|
|
11417
|
+
} catch (err) {
|
|
11418
|
+
fail(err, "splitio", "flags list", start);
|
|
11419
|
+
}
|
|
11420
|
+
});
|
|
11421
|
+
flags.command("get").description("Get a flag definition").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").option("--environment <id>", "Environment ID \u2014 include targeting rules for this environment").action(async (opts) => {
|
|
11422
|
+
const start = Date.now();
|
|
11423
|
+
try {
|
|
11424
|
+
const http = getHttp5(program2);
|
|
11425
|
+
if (opts.environment) {
|
|
11426
|
+
const data = await http.splitio(
|
|
11427
|
+
`/internal/api/v2/splits/${encodeURIComponent(opts.flag)}/environments/${encodeURIComponent(opts.environment)}`,
|
|
11428
|
+
{ params: { wsId: opts.workspace } }
|
|
11429
|
+
);
|
|
11430
|
+
success(data, "splitio", "flags get", start);
|
|
11431
|
+
} else {
|
|
11432
|
+
const data = await http.splitio(
|
|
11433
|
+
`/internal/api/v2/splits/${encodeURIComponent(opts.flag)}`,
|
|
11434
|
+
{ params: { wsId: opts.workspace } }
|
|
11435
|
+
);
|
|
11436
|
+
success(data, "splitio", "flags get", start);
|
|
11437
|
+
}
|
|
11438
|
+
} catch (err) {
|
|
11439
|
+
fail(err, "splitio", "flags get", start);
|
|
11440
|
+
}
|
|
11441
|
+
});
|
|
11442
|
+
flags.command("update").description("Submit a Change Request to replace a flag's full definition in an environment").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--input-file <path>", "JSON file with the complete flag definition body (use - for stdin)").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11443
|
+
const start = Date.now();
|
|
11444
|
+
try {
|
|
11445
|
+
if (!opts.inputFile) throw new PncliError("--input-file is required for flags update");
|
|
11446
|
+
const definition = loadInputFile(opts.inputFile);
|
|
11447
|
+
await confirmWrite(opts, `UPDATE ${opts.flag} in ${opts.environment}`);
|
|
11448
|
+
const http = getHttp5(program2);
|
|
11449
|
+
const body = {
|
|
11450
|
+
...buildCrBase(opts),
|
|
11451
|
+
operationType: "UPDATE_SPLIT_DEFINITION",
|
|
11452
|
+
split: {
|
|
11453
|
+
name: opts.flag,
|
|
11454
|
+
environment: { id: opts.environment },
|
|
11455
|
+
definition
|
|
11456
|
+
},
|
|
11457
|
+
workspace: { id: opts.workspace }
|
|
11458
|
+
};
|
|
11459
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11460
|
+
method: "POST",
|
|
11461
|
+
body
|
|
11462
|
+
});
|
|
11463
|
+
success(data, "splitio", "flags update", start);
|
|
11464
|
+
} catch (err) {
|
|
11465
|
+
fail(err, "splitio", "flags update", start);
|
|
11466
|
+
}
|
|
11467
|
+
});
|
|
11468
|
+
flags.command("set-rule").description("Change one targeting rule without altering the rest of the flag definition").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--rule-index <n>", "Zero-based index of the targeting rule to update").option("--treatment <name>", "Treatment to assign to the entire rule bucket").option("--weights <json>", "JSON array of {treatment, size} objects for weighted distribution").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11469
|
+
const start = Date.now();
|
|
11470
|
+
try {
|
|
11471
|
+
if (!opts.treatment && !opts.weights) {
|
|
11472
|
+
throw new PncliError("Either --treatment or --weights must be specified");
|
|
11473
|
+
}
|
|
11474
|
+
const http = getHttp5(program2);
|
|
11475
|
+
const current = await http.splitio(
|
|
11476
|
+
`/internal/api/v2/splits/${encodeURIComponent(opts.flag)}/environments/${encodeURIComponent(opts.environment)}`,
|
|
11477
|
+
{ params: { wsId: opts.workspace } }
|
|
11478
|
+
);
|
|
11479
|
+
const rules = current.rules ?? [];
|
|
11480
|
+
const idx = parseInt(opts.ruleIndex, 10);
|
|
11481
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= rules.length) {
|
|
11482
|
+
throw new PncliError(`Rule index ${Number.isNaN(idx) ? JSON.stringify(opts.ruleIndex) : idx} is out of range (flag has ${rules.length} rules)`);
|
|
11483
|
+
}
|
|
11484
|
+
if (opts.treatment) {
|
|
11485
|
+
rules[idx] = { ...rules[idx], buckets: [{ treatment: opts.treatment, size: 1e5 }] };
|
|
11486
|
+
} else {
|
|
11487
|
+
const parsed = JSON.parse(opts.weights);
|
|
11488
|
+
rules[idx] = { ...rules[idx], buckets: parsed };
|
|
11489
|
+
}
|
|
11490
|
+
await confirmWrite(opts, `SET_RULE ${idx} on ${opts.flag} in ${opts.environment}`);
|
|
11491
|
+
const body = {
|
|
11492
|
+
...buildCrBase(opts),
|
|
11493
|
+
operationType: "UPDATE_SPLIT_DEFINITION",
|
|
11494
|
+
split: {
|
|
11495
|
+
name: opts.flag,
|
|
11496
|
+
environment: { id: opts.environment },
|
|
11497
|
+
definition: { ...current, rules }
|
|
11498
|
+
},
|
|
11499
|
+
workspace: { id: opts.workspace }
|
|
11500
|
+
};
|
|
11501
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11502
|
+
method: "POST",
|
|
11503
|
+
body
|
|
11504
|
+
});
|
|
11505
|
+
success(data, "splitio", "flags set-rule", start);
|
|
11506
|
+
} catch (err) {
|
|
11507
|
+
fail(err, "splitio", "flags set-rule", start);
|
|
11508
|
+
}
|
|
11509
|
+
});
|
|
11510
|
+
flags.command("set-default").description("Update the fallback default rule for traffic matching no targeting rule").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--treatment <name>", "Treatment to assign as the default").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11511
|
+
const start = Date.now();
|
|
11512
|
+
try {
|
|
11513
|
+
const http = getHttp5(program2);
|
|
11514
|
+
const current = await http.splitio(
|
|
11515
|
+
`/internal/api/v2/splits/${encodeURIComponent(opts.flag)}/environments/${encodeURIComponent(opts.environment)}`,
|
|
11516
|
+
{ params: { wsId: opts.workspace } }
|
|
11517
|
+
);
|
|
11518
|
+
await confirmWrite(opts, `SET_DEFAULT ${opts.treatment} on ${opts.flag} in ${opts.environment}`);
|
|
11519
|
+
const body = {
|
|
11520
|
+
...buildCrBase(opts),
|
|
11521
|
+
operationType: "UPDATE_SPLIT_DEFINITION",
|
|
11522
|
+
split: {
|
|
11523
|
+
name: opts.flag,
|
|
11524
|
+
environment: { id: opts.environment },
|
|
11525
|
+
definition: {
|
|
11526
|
+
...current,
|
|
11527
|
+
defaultRule: [{ treatment: opts.treatment, size: 1e5 }]
|
|
11528
|
+
}
|
|
11529
|
+
},
|
|
11530
|
+
workspace: { id: opts.workspace }
|
|
11531
|
+
};
|
|
11532
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11533
|
+
method: "POST",
|
|
11534
|
+
body
|
|
11535
|
+
});
|
|
11536
|
+
success(data, "splitio", "flags set-default", start);
|
|
11537
|
+
} catch (err) {
|
|
11538
|
+
fail(err, "splitio", "flags set-default", start);
|
|
11539
|
+
}
|
|
11540
|
+
});
|
|
11541
|
+
flags.command("toggle").description("Enable or disable a single flag via a Change Request").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--enabled <bool>", "true to restore / false to kill the flag").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11542
|
+
const start = Date.now();
|
|
11543
|
+
try {
|
|
11544
|
+
const enable = opts.enabled.toLowerCase() !== "false" && opts.enabled !== "0";
|
|
11545
|
+
const opType = enable ? "RESTORE_SPLIT" : "KILL_SPLIT";
|
|
11546
|
+
await confirmWrite(opts, `${opType} ${opts.flag} in ${opts.environment}`);
|
|
11547
|
+
const http = getHttp5(program2);
|
|
11548
|
+
const body = {
|
|
11549
|
+
...buildCrBase(opts),
|
|
11550
|
+
operationType: opType,
|
|
11551
|
+
split: { name: opts.flag, environment: { id: opts.environment } },
|
|
11552
|
+
workspace: { id: opts.workspace }
|
|
11553
|
+
};
|
|
11554
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11555
|
+
method: "POST",
|
|
11556
|
+
body
|
|
11557
|
+
});
|
|
11558
|
+
success(data, "splitio", "flags toggle", start);
|
|
11559
|
+
} catch (err) {
|
|
11560
|
+
fail(err, "splitio", "flags toggle", start);
|
|
11561
|
+
}
|
|
11562
|
+
});
|
|
11563
|
+
flags.command("batch-toggle").description("Enable or disable multiple flags from a JSON file (each creates its own Change Request)").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--environment <id>", "Environment ID").requiredOption("--mnemonic <text>", "Short Change Request title prefix (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve each CR").option("--input-file <path>", "JSON file: array of {flag, enabled} objects (use - for stdin)").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11564
|
+
const start = Date.now();
|
|
11565
|
+
try {
|
|
11566
|
+
if (!opts.inputFile) throw new PncliError("--input-file is required for batch-toggle");
|
|
11567
|
+
const entries = loadInputFile(opts.inputFile);
|
|
11568
|
+
if (!Array.isArray(entries)) throw new PncliError("Input file must be a JSON array of {flag, enabled} objects");
|
|
11569
|
+
await confirmWrite(opts, `BATCH_TOGGLE ${entries.length} flags in ${opts.environment}`);
|
|
11570
|
+
const http = getHttp5(program2);
|
|
11571
|
+
const results = [];
|
|
11572
|
+
for (const entry of entries) {
|
|
11573
|
+
try {
|
|
11574
|
+
const opType = entry.enabled ? "RESTORE_SPLIT" : "KILL_SPLIT";
|
|
11575
|
+
const body = {
|
|
11576
|
+
...buildCrBase(opts),
|
|
11577
|
+
title: `${opts.mnemonic} \u2014 ${entry.flag}`,
|
|
11578
|
+
operationType: opType,
|
|
11579
|
+
split: { name: entry.flag, environment: { id: opts.environment } },
|
|
11580
|
+
workspace: { id: opts.workspace }
|
|
11581
|
+
};
|
|
11582
|
+
const cr = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11583
|
+
method: "POST",
|
|
11584
|
+
body
|
|
11585
|
+
});
|
|
11586
|
+
results.push({ flag: entry.flag, ok: true, id: cr.id });
|
|
11587
|
+
} catch (err) {
|
|
11588
|
+
results.push({
|
|
11589
|
+
flag: entry.flag,
|
|
11590
|
+
ok: false,
|
|
11591
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11592
|
+
});
|
|
11593
|
+
}
|
|
11594
|
+
}
|
|
11595
|
+
success({ submitted: results.length, results }, "splitio", "flags batch-toggle", start);
|
|
11596
|
+
} catch (err) {
|
|
11597
|
+
fail(err, "splitio", "flags batch-toggle", start);
|
|
11598
|
+
}
|
|
11599
|
+
});
|
|
11600
|
+
flags.command("kill").description("Submit a Change Request to kill (disable) a flag").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11601
|
+
const start = Date.now();
|
|
11602
|
+
try {
|
|
11603
|
+
await confirmWrite(opts, `KILL ${opts.flag} in ${opts.environment}`);
|
|
11604
|
+
const http = getHttp5(program2);
|
|
11605
|
+
const body = {
|
|
11606
|
+
...buildCrBase(opts),
|
|
11607
|
+
operationType: "KILL_SPLIT",
|
|
11608
|
+
split: { name: opts.flag, environment: { id: opts.environment } },
|
|
11609
|
+
workspace: { id: opts.workspace }
|
|
11610
|
+
};
|
|
11611
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11612
|
+
method: "POST",
|
|
11613
|
+
body
|
|
11614
|
+
});
|
|
11615
|
+
success(data, "splitio", "flags kill", start);
|
|
11616
|
+
} catch (err) {
|
|
11617
|
+
fail(err, "splitio", "flags kill", start);
|
|
11618
|
+
}
|
|
11619
|
+
});
|
|
11620
|
+
flags.command("restore").description("Submit a Change Request to restore a killed flag").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--environment <id>", "Environment ID").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--approvers <ids>", "Comma-separated Split user IDs required to approve this CR").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11621
|
+
const start = Date.now();
|
|
11622
|
+
try {
|
|
11623
|
+
await confirmWrite(opts, `RESTORE ${opts.flag} in ${opts.environment}`);
|
|
11624
|
+
const http = getHttp5(program2);
|
|
11625
|
+
const body = {
|
|
11626
|
+
...buildCrBase(opts),
|
|
11627
|
+
operationType: "RESTORE_SPLIT",
|
|
11628
|
+
split: { name: opts.flag, environment: { id: opts.environment } },
|
|
11629
|
+
workspace: { id: opts.workspace }
|
|
11630
|
+
};
|
|
11631
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11632
|
+
method: "POST",
|
|
11633
|
+
body
|
|
11634
|
+
});
|
|
11635
|
+
success(data, "splitio", "flags restore", start);
|
|
11636
|
+
} catch (err) {
|
|
11637
|
+
fail(err, "splitio", "flags restore", start);
|
|
11638
|
+
}
|
|
11639
|
+
});
|
|
11640
|
+
flags.command("archive").description("Submit a Change Request to archive (delete definition of) a flag").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--flag <name>", "Feature flag name").requiredOption("--mnemonic <text>", "Short Change Request title (mnemonic)").requiredOption("--description <text>", "Change Request description").requiredOption("--change-number <CHG>", "ServiceNow change request number (e.g. CHG0001234)").option("--yes", "Skip interactive confirmation prompt").action(async (opts) => {
|
|
11641
|
+
const start = Date.now();
|
|
11642
|
+
try {
|
|
11643
|
+
await confirmWrite(opts, `ARCHIVE ${opts.flag}`);
|
|
11644
|
+
const http = getHttp5(program2);
|
|
11645
|
+
const body = {
|
|
11646
|
+
...buildCrBase(opts),
|
|
11647
|
+
operationType: "ARCHIVE_SPLIT",
|
|
11648
|
+
split: { name: opts.flag },
|
|
11649
|
+
workspace: { id: opts.workspace }
|
|
11650
|
+
};
|
|
11651
|
+
const data = await http.splitio("/internal/api/v2/changeRequests", {
|
|
11652
|
+
method: "POST",
|
|
11653
|
+
body
|
|
11654
|
+
});
|
|
11655
|
+
success(data, "splitio", "flags archive", start);
|
|
11656
|
+
} catch (err) {
|
|
11657
|
+
fail(err, "splitio", "flags archive", start);
|
|
11658
|
+
}
|
|
11659
|
+
});
|
|
11660
|
+
const changeRequests = splitio.command("change-requests").description("Change Request tracking");
|
|
11661
|
+
changeRequests.command("list").description("List Change Requests for a workspace").requiredOption("--workspace <id>", "Workspace ID").option("--environment <id>", "Filter by environment ID").option("--status <status>", "Filter by status (REQUESTED, APPROVED, REJECTED, PUBLISHED)").option("--limit <n>", "Maximum number of results", "25").option("--offset <n>", "Pagination offset", "0").action(async (opts) => {
|
|
11662
|
+
const start = Date.now();
|
|
11663
|
+
try {
|
|
11664
|
+
const http = getHttp5(program2);
|
|
11665
|
+
const params = {
|
|
11666
|
+
wsId: opts.workspace,
|
|
11667
|
+
limit: parseInt(opts.limit, 10),
|
|
11668
|
+
offset: parseInt(opts.offset, 10)
|
|
11669
|
+
};
|
|
11670
|
+
if (opts.environment) params["environmentId"] = opts.environment;
|
|
11671
|
+
if (opts.status) params["status"] = opts.status;
|
|
11672
|
+
const data = await http.splitio(
|
|
11673
|
+
"/internal/api/v2/changeRequests",
|
|
11674
|
+
{ params }
|
|
11675
|
+
);
|
|
11676
|
+
success(
|
|
11677
|
+
{ count: data.objects.length, totalCount: data.totalCount, changeRequests: data.objects },
|
|
11678
|
+
"splitio",
|
|
11679
|
+
"change-requests list",
|
|
11680
|
+
start
|
|
11681
|
+
);
|
|
11682
|
+
} catch (err) {
|
|
11683
|
+
fail(err, "splitio", "change-requests list", start);
|
|
11684
|
+
}
|
|
11685
|
+
});
|
|
11686
|
+
changeRequests.command("get").description("Get a Change Request by ID").requiredOption("--id <id>", "Change Request ID").action(async (opts) => {
|
|
11687
|
+
const start = Date.now();
|
|
11688
|
+
try {
|
|
11689
|
+
const http = getHttp5(program2);
|
|
11690
|
+
const data = await http.splitio(
|
|
11691
|
+
`/internal/api/v2/changeRequests/${encodeURIComponent(opts.id)}`
|
|
11692
|
+
);
|
|
11693
|
+
success(data, "splitio", "change-requests get", start);
|
|
11694
|
+
} catch (err) {
|
|
11695
|
+
fail(err, "splitio", "change-requests get", start);
|
|
11696
|
+
}
|
|
11697
|
+
});
|
|
11698
|
+
}
|
|
11699
|
+
|
|
11700
|
+
// src/services/figma/commands.ts
|
|
11701
|
+
function parseFileKey(input3) {
|
|
11702
|
+
if (input3.startsWith("http://") || input3.startsWith("https://")) {
|
|
11703
|
+
const match = /figma\.com\/(?:design|file)\/([A-Za-z0-9]+)/i.exec(input3);
|
|
11704
|
+
if (!match?.[1]) {
|
|
11705
|
+
throw new Error(`Could not extract Figma file key from URL: ${input3}
|
|
11706
|
+
Expected format: https://www.figma.com/design/<file-key>/...`);
|
|
11707
|
+
}
|
|
11708
|
+
return match[1];
|
|
11709
|
+
}
|
|
11710
|
+
return input3;
|
|
11711
|
+
}
|
|
11712
|
+
function getHttp6(program2) {
|
|
11713
|
+
const opts = program2.optsWithGlobals();
|
|
11714
|
+
return createHttpClient(
|
|
11715
|
+
loadConfig({ configPath: opts.config }),
|
|
11716
|
+
Boolean(opts.dryRun)
|
|
11717
|
+
);
|
|
11718
|
+
}
|
|
11719
|
+
function registerFigmaCommands(program2) {
|
|
11720
|
+
const figma = program2.command("figma").description("Figma design operations (files, comments, versions)");
|
|
11721
|
+
figma.command("file").description("Get a Figma file \u2014 metadata, structure, and component/style inventory").argument("<file-key-or-url>", "Figma file key or full Figma URL (https://www.figma.com/design/<key>/...)").option("--document", "Include the full document node tree (can be large; omitted by default)").action(async (fileKeyOrUrl, opts) => {
|
|
11722
|
+
const start = Date.now();
|
|
11723
|
+
const { success: success2, fail: fail2 } = await import("./output-XT3WT5L5.js");
|
|
11724
|
+
try {
|
|
11725
|
+
const fileKey = parseFileKey(fileKeyOrUrl);
|
|
11726
|
+
const params = {};
|
|
11727
|
+
if (!opts.document) {
|
|
11728
|
+
params["geometry"] = "paths";
|
|
11729
|
+
params["depth"] = 0;
|
|
11730
|
+
}
|
|
11731
|
+
const data = await getHttp6(program2).figma(`/v1/files/${encodeURIComponent(fileKey)}`, { params });
|
|
11732
|
+
success2(
|
|
11733
|
+
{
|
|
11734
|
+
fileKey,
|
|
11735
|
+
name: data.name,
|
|
11736
|
+
lastModified: data.lastModified,
|
|
11737
|
+
version: data.version,
|
|
11738
|
+
thumbnailUrl: data.thumbnailUrl,
|
|
11739
|
+
role: data.role,
|
|
11740
|
+
editorType: data.editorType,
|
|
11741
|
+
schemaVersion: data.schemaVersion,
|
|
11742
|
+
...opts.document ? { document: data.document } : {},
|
|
11743
|
+
componentCount: data.components ? Object.keys(data.components).length : 0,
|
|
11744
|
+
styleCount: data.styles ? Object.keys(data.styles).length : 0
|
|
11745
|
+
},
|
|
11746
|
+
"figma",
|
|
11747
|
+
"file",
|
|
11748
|
+
start
|
|
11749
|
+
);
|
|
11750
|
+
} catch (err) {
|
|
11751
|
+
fail2(err, "figma", "file", start);
|
|
11752
|
+
}
|
|
11753
|
+
});
|
|
11754
|
+
figma.command("comments").description("Get comments on a Figma file").argument("<file-key-or-url>", "Figma file key or full Figma URL").option("--as-of <date>", "ISO 8601 timestamp \u2014 return only comments created before this time").action(async (fileKeyOrUrl, opts) => {
|
|
11755
|
+
const start = Date.now();
|
|
11756
|
+
const { success: success2, fail: fail2 } = await import("./output-XT3WT5L5.js");
|
|
11757
|
+
try {
|
|
11758
|
+
const fileKey = parseFileKey(fileKeyOrUrl);
|
|
11759
|
+
const params = {};
|
|
11760
|
+
if (opts.asOf) params["as_of"] = opts.asOf;
|
|
11761
|
+
const data = await getHttp6(program2).figma(`/v1/files/${encodeURIComponent(fileKey)}/comments`, { params });
|
|
11762
|
+
success2(
|
|
11763
|
+
{ fileKey, count: data.comments.length, comments: data.comments },
|
|
11764
|
+
"figma",
|
|
11765
|
+
"comments",
|
|
11766
|
+
start
|
|
11767
|
+
);
|
|
11768
|
+
} catch (err) {
|
|
11769
|
+
fail2(err, "figma", "comments", start);
|
|
11770
|
+
}
|
|
11771
|
+
});
|
|
11772
|
+
figma.command("versions").description("Get the version history of a Figma file").argument("<file-key-or-url>", "Figma file key or full Figma URL").action(async (fileKeyOrUrl) => {
|
|
11773
|
+
const start = Date.now();
|
|
11774
|
+
const { success: success2, fail: fail2 } = await import("./output-XT3WT5L5.js");
|
|
11775
|
+
try {
|
|
11776
|
+
const fileKey = parseFileKey(fileKeyOrUrl);
|
|
11777
|
+
const data = await getHttp6(program2).figma(`/v1/files/${encodeURIComponent(fileKey)}/versions`);
|
|
11778
|
+
success2(
|
|
11779
|
+
{ fileKey, count: data.versions.length, versions: data.versions },
|
|
11780
|
+
"figma",
|
|
11781
|
+
"versions",
|
|
11782
|
+
start
|
|
11783
|
+
);
|
|
11784
|
+
} catch (err) {
|
|
11785
|
+
fail2(err, "figma", "versions", start);
|
|
11786
|
+
}
|
|
11787
|
+
});
|
|
11788
|
+
figma.command("project-files").description("List files in a Figma project").argument("<project-id>", "Figma project ID (visible in the project URL)").action(async (projectId) => {
|
|
11789
|
+
const start = Date.now();
|
|
11790
|
+
const { success: success2, fail: fail2 } = await import("./output-XT3WT5L5.js");
|
|
11791
|
+
try {
|
|
11792
|
+
const data = await getHttp6(program2).figma(`/v1/projects/${encodeURIComponent(projectId)}/files`);
|
|
11793
|
+
success2(
|
|
11794
|
+
{ projectId, count: data.files.length, files: data.files },
|
|
11795
|
+
"figma",
|
|
11796
|
+
"project-files",
|
|
11797
|
+
start
|
|
11798
|
+
);
|
|
11799
|
+
} catch (err) {
|
|
11800
|
+
fail2(err, "figma", "project-files", start);
|
|
11801
|
+
}
|
|
11802
|
+
});
|
|
11803
|
+
figma.command("me").description("Get the current Figma user (useful for verifying credentials)").action(async () => {
|
|
11804
|
+
const start = Date.now();
|
|
11805
|
+
const { success: success2, fail: fail2 } = await import("./output-XT3WT5L5.js");
|
|
11806
|
+
try {
|
|
11807
|
+
const data = await getHttp6(program2).figma("/v1/me");
|
|
11808
|
+
success2(data, "figma", "me", start);
|
|
11809
|
+
} catch (err) {
|
|
11810
|
+
fail2(err, "figma", "me", start);
|
|
11811
|
+
}
|
|
11812
|
+
});
|
|
11813
|
+
}
|
|
11814
|
+
|
|
11359
11815
|
// src/cli.ts
|
|
11360
11816
|
if (!process.env.PNCLI_VERIFY_TLS) {
|
|
11361
11817
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
@@ -11392,7 +11848,6 @@ registerConfigCommands(program);
|
|
|
11392
11848
|
registerAdoCommands(program);
|
|
11393
11849
|
registerJenkinsCommands(program);
|
|
11394
11850
|
registerArtifactoryCommands(program);
|
|
11395
|
-
registerUdeployCommands(program);
|
|
11396
11851
|
registerCheckmarxCommands(program);
|
|
11397
11852
|
registerServiceNowCommands(program);
|
|
11398
11853
|
registerContrastCommands(program);
|
|
@@ -11402,6 +11857,8 @@ registerJwtCommands(program);
|
|
|
11402
11857
|
registerOpenShiftCommands(program);
|
|
11403
11858
|
registerDynatraceCommands(program);
|
|
11404
11859
|
registerLogscaleCommands(program);
|
|
11860
|
+
registerSplitioCommands(program);
|
|
11861
|
+
registerFigmaCommands(program);
|
|
11405
11862
|
program.addHelpText("after", `
|
|
11406
11863
|
Services:
|
|
11407
11864
|
git Local git operations (status, diff, log, branch)
|
|
@@ -11413,9 +11870,8 @@ Services:
|
|
|
11413
11870
|
sonar SonarQube Server (quality gates, issues, metrics, hotspots)
|
|
11414
11871
|
sde SDElements (threat modeling, countermeasures, compliance)
|
|
11415
11872
|
ado Azure DevOps Server (work items, repos, PRs, pipelines)
|
|
11416
|
-
jenkins Jenkins
|
|
11873
|
+
jenkins Jenkins (jobs, builds, logs, named instances)
|
|
11417
11874
|
artifactory Artifactory (repos, artifact search, build info, properties)
|
|
11418
|
-
udeploy IBM UrbanCode Deploy (component versions, deployment processes)
|
|
11419
11875
|
checkmarx Checkmarx One (projects, scans, scan statistics)
|
|
11420
11876
|
servicenow ServiceNow (change requests)
|
|
11421
11877
|
contrast Contrast IAST (applications, vulnerability findings)
|
|
@@ -11423,6 +11879,8 @@ Services:
|
|
|
11423
11879
|
openshift OpenShift / Kubernetes (pods, events, logs, metrics)
|
|
11424
11880
|
dynatrace Dynatrace (services, entities, problems, traces, Kubernetes workloads)
|
|
11425
11881
|
logscale LogScale (log query, repositories)
|
|
11882
|
+
splitio Split.IO (feature flags, Change Requests)
|
|
11883
|
+
figma Figma (files, comments, version history)
|
|
11426
11884
|
config Manage pncli configuration
|
|
11427
11885
|
skills Download and manage Claude Code skills
|
|
11428
11886
|
jwt JWT token utilities (decode header and payload)
|