@kolatts/pncli 5.4.0 → 6.1.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 +2 -0
- package/dist/{chunk-L6OY2546.js → chunk-GHZMFNSX.js} +31 -3
- package/dist/chunk-GHZMFNSX.js.map +1 -0
- package/dist/{chunk-2J2HAFKO.js → chunk-Y3PO2RTO.js} +134 -13
- package/dist/chunk-Y3PO2RTO.js.map +1 -0
- package/dist/cli.js +913 -146
- package/dist/cli.js.map +1 -1
- package/dist/{config-OH2O5E3P.js → config-DYIXSPLA.js} +2 -2
- package/dist/{http-V7PZD7LE.js → http-3FAKF3X4.js} +2 -2
- package/package.json +1 -1
- package/skills/pncli/SKILL.md +19 -4
- package/skills/pncli/elasticsearch.md +53 -0
- package/skills/pncli/marketplace.md +1 -1
- package/skills/pncli/openshift.md +17 -0
- package/skills/pncli/saucelabs.md +175 -0
- package/dist/chunk-2J2HAFKO.js.map +0 -1
- package/dist/chunk-L6OY2546.js.map +0 -1
- /package/dist/{config-OH2O5E3P.js.map → config-DYIXSPLA.js.map} +0 -0
- /package/dist/{http-V7PZD7LE.js.map → http-3FAKF3X4.js.map} +0 -0
package/README.md
CHANGED
|
@@ -140,9 +140,11 @@ This project uses Conventional Commits for automatic versioning:
|
|
|
140
140
|
| Sonatype IQ | ⚪ Untested | Dependency policy enforcement |
|
|
141
141
|
| OpenShift | ⚪ Untested | Pods, events, logs, metrics |
|
|
142
142
|
| LogScale | ⚪ Untested | Log queries, repositories |
|
|
143
|
+
| Elasticsearch | ⚪ Untested | Search, count, indices, cluster health |
|
|
143
144
|
| Figma | ⚪ Untested | Design files, comments, history |
|
|
144
145
|
| Split.IO | ⚪ Untested | Feature flags, change requests |
|
|
145
146
|
| Alation | ⚪ Untested | Catalog metadata, search, documents |
|
|
147
|
+
| Sauce Labs | ⚪ Untested | Test jobs, builds, real devices |
|
|
146
148
|
|
|
147
149
|
Status reflects validation against a **real instance**, not code maturity: **Live** — used routinely day-to-day · **Beta** — exercised across several commands and instances · **Basic** — smoke-tested against one instance · **Untested** — shipped, not yet run against a live server. The [homepage](https://kolatts.github.io/pncli/) shows the same grid.
|
|
148
150
|
|
|
@@ -374,7 +374,8 @@ var SECRET_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
|
374
374
|
"adminApiKey",
|
|
375
375
|
"platformToken",
|
|
376
376
|
"password",
|
|
377
|
-
"connection"
|
|
377
|
+
"connection",
|
|
378
|
+
"accessKey"
|
|
378
379
|
]);
|
|
379
380
|
function findConfigStrings(root, predicate, prefix = []) {
|
|
380
381
|
const found = [];
|
|
@@ -577,6 +578,8 @@ var ENV_KEYS = {
|
|
|
577
578
|
DYNATRACE_PLATFORM_TOKEN: "PNCLI_DYNATRACE_PLATFORM_TOKEN",
|
|
578
579
|
LOGSCALE_BASE_URL: "PNCLI_LOGSCALE_BASE_URL",
|
|
579
580
|
LOGSCALE_TOKEN: "PNCLI_LOGSCALE_TOKEN",
|
|
581
|
+
ELASTICSEARCH_BASE_URL: "PNCLI_ELASTICSEARCH_BASE_URL",
|
|
582
|
+
ELASTICSEARCH_API_KEY: "PNCLI_ELASTICSEARCH_API_KEY",
|
|
580
583
|
SPLITIO_BASE_URL: "PNCLI_SPLITIO_BASE_URL",
|
|
581
584
|
SPLITIO_ADMIN_API_KEY: "PNCLI_SPLITIO_ADMIN_API_KEY",
|
|
582
585
|
FIGMA_BASE_URL: "PNCLI_FIGMA_BASE_URL",
|
|
@@ -584,6 +587,12 @@ var ENV_KEYS = {
|
|
|
584
587
|
ALATION_BASE_URL: "PNCLI_ALATION_BASE_URL",
|
|
585
588
|
ALATION_REFRESH_TOKEN: "PNCLI_ALATION_REFRESH_TOKEN",
|
|
586
589
|
ALATION_USER_ID: "PNCLI_ALATION_USER_ID",
|
|
590
|
+
SAUCELABS_BASE_URL: "PNCLI_SAUCELABS_BASE_URL",
|
|
591
|
+
SAUCELABS_USERNAME: "PNCLI_SAUCELABS_USERNAME",
|
|
592
|
+
SAUCELABS_ACCESS_KEY: "PNCLI_SAUCELABS_ACCESS_KEY",
|
|
593
|
+
// The names saucectl, Sauce Connect and Sauce's own CI docs read — see the comment above GITHUB_TOKEN_FALLBACK.
|
|
594
|
+
SAUCELABS_USERNAME_FALLBACK: "SAUCE_USERNAME",
|
|
595
|
+
SAUCELABS_ACCESS_KEY_FALLBACK: "SAUCE_ACCESS_KEY",
|
|
587
596
|
CONFIG_PATH: "PNCLI_CONFIG_PATH"
|
|
588
597
|
};
|
|
589
598
|
function getGlobalConfigPath(overridePath) {
|
|
@@ -656,9 +665,11 @@ var SECRET_ENV_OVERRIDES = [
|
|
|
656
665
|
["dynatrace.apiToken", [ENV_KEYS.DYNATRACE_API_TOKEN]],
|
|
657
666
|
["dynatrace.platformToken", [ENV_KEYS.DYNATRACE_PLATFORM_TOKEN]],
|
|
658
667
|
["logscale.token", [ENV_KEYS.LOGSCALE_TOKEN]],
|
|
668
|
+
["elasticsearch.apiKey", [ENV_KEYS.ELASTICSEARCH_API_KEY]],
|
|
659
669
|
["splitio.adminApiKey", [ENV_KEYS.SPLITIO_ADMIN_API_KEY]],
|
|
660
670
|
["figma.token", [ENV_KEYS.FIGMA_TOKEN]],
|
|
661
|
-
["alation.refreshToken", [ENV_KEYS.ALATION_REFRESH_TOKEN]]
|
|
671
|
+
["alation.refreshToken", [ENV_KEYS.ALATION_REFRESH_TOKEN]],
|
|
672
|
+
["saucelabs.accessKey", [ENV_KEYS.SAUCELABS_ACCESS_KEY, ENV_KEYS.SAUCELABS_ACCESS_KEY_FALLBACK]]
|
|
662
673
|
];
|
|
663
674
|
function envOverriddenSecretPaths(env = process.env) {
|
|
664
675
|
return new Set(SECRET_ENV_OVERRIDES.filter(([, keys]) => keys.some((k) => env[k] !== void 0)).map(([p]) => p));
|
|
@@ -765,6 +776,10 @@ function loadConfig(opts = {}) {
|
|
|
765
776
|
baseUrl: process.env[ENV_KEYS.LOGSCALE_BASE_URL] ?? globalConfig.logscale?.baseUrl,
|
|
766
777
|
token: process.env[ENV_KEYS.LOGSCALE_TOKEN] ?? globalConfig.logscale?.token
|
|
767
778
|
},
|
|
779
|
+
elasticsearch: {
|
|
780
|
+
baseUrl: process.env[ENV_KEYS.ELASTICSEARCH_BASE_URL] ?? globalConfig.elasticsearch?.baseUrl,
|
|
781
|
+
apiKey: process.env[ENV_KEYS.ELASTICSEARCH_API_KEY] ?? globalConfig.elasticsearch?.apiKey
|
|
782
|
+
},
|
|
768
783
|
splitio: {
|
|
769
784
|
baseUrl: process.env[ENV_KEYS.SPLITIO_BASE_URL] ?? globalConfig.splitio?.baseUrl,
|
|
770
785
|
adminApiKey: process.env[ENV_KEYS.SPLITIO_ADMIN_API_KEY] ?? globalConfig.splitio?.adminApiKey
|
|
@@ -778,6 +793,11 @@ function loadConfig(opts = {}) {
|
|
|
778
793
|
refreshToken: process.env[ENV_KEYS.ALATION_REFRESH_TOKEN] ?? globalConfig.alation?.refreshToken,
|
|
779
794
|
userId: process.env[ENV_KEYS.ALATION_USER_ID] ?? globalConfig.alation?.userId
|
|
780
795
|
},
|
|
796
|
+
saucelabs: {
|
|
797
|
+
baseUrl: process.env[ENV_KEYS.SAUCELABS_BASE_URL] ?? globalConfig.saucelabs?.baseUrl,
|
|
798
|
+
username: process.env[ENV_KEYS.SAUCELABS_USERNAME] ?? process.env[ENV_KEYS.SAUCELABS_USERNAME_FALLBACK] ?? globalConfig.saucelabs?.username,
|
|
799
|
+
accessKey: process.env[ENV_KEYS.SAUCELABS_ACCESS_KEY] ?? process.env[ENV_KEYS.SAUCELABS_ACCESS_KEY_FALLBACK] ?? globalConfig.saucelabs?.accessKey
|
|
800
|
+
},
|
|
781
801
|
defaults: mergedDefaults
|
|
782
802
|
};
|
|
783
803
|
}
|
|
@@ -935,6 +955,10 @@ function maskConfig(config) {
|
|
|
935
955
|
...config.logscale,
|
|
936
956
|
token: config.logscale.token ? "***" : void 0
|
|
937
957
|
},
|
|
958
|
+
elasticsearch: {
|
|
959
|
+
...config.elasticsearch,
|
|
960
|
+
apiKey: config.elasticsearch.apiKey ? "***" : void 0
|
|
961
|
+
},
|
|
938
962
|
splitio: {
|
|
939
963
|
...config.splitio,
|
|
940
964
|
adminApiKey: config.splitio.adminApiKey ? "***" : void 0
|
|
@@ -946,6 +970,10 @@ function maskConfig(config) {
|
|
|
946
970
|
alation: {
|
|
947
971
|
...config.alation,
|
|
948
972
|
refreshToken: config.alation.refreshToken ? "***" : void 0
|
|
973
|
+
},
|
|
974
|
+
saucelabs: {
|
|
975
|
+
...config.saucelabs,
|
|
976
|
+
accessKey: config.saucelabs.accessKey ? "***" : void 0
|
|
949
977
|
}
|
|
950
978
|
};
|
|
951
979
|
}
|
|
@@ -981,4 +1009,4 @@ export {
|
|
|
981
1009
|
setRepoConfigValue,
|
|
982
1010
|
maskConfig
|
|
983
1011
|
};
|
|
984
|
-
//# sourceMappingURL=chunk-
|
|
1012
|
+
//# sourceMappingURL=chunk-GHZMFNSX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/config.ts","../src/types/jira.ts","../src/lib/validate-custom-fields.ts","../src/lib/keychain.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { execSync } from 'child_process';\nimport type { GlobalConfig, RepoConfig, ResolvedConfig, JiraDefaults, BitbucketDefaults, GitHubDefaults, SonarDefaults, SdeDefaults, AdoDefaults, JenkinsDefaults, JenkinsInstanceConfig } from '../types/config.js';\nimport type { CustomFieldDefinition } from '../types/jira.js';\nimport { assertValidCustomFields } from './validate-custom-fields.js';\nimport { resolveKeychainRefs } from './keychain.js';\n\nconst ENV_KEYS = {\n EMAIL: 'PNCLI_EMAIL',\n USERID: 'PNCLI_USERID',\n JIRA_BASE_URL: 'PNCLI_JIRA_BASE_URL',\n JIRA_API_TOKEN: 'PNCLI_JIRA_API_TOKEN',\n BITBUCKET_BASE_URL: 'PNCLI_BITBUCKET_BASE_URL',\n BITBUCKET_PAT: 'PNCLI_BITBUCKET_PAT',\n GITHUB_BASE_URL: 'PNCLI_GITHUB_BASE_URL',\n GITHUB_TOKEN: 'PNCLI_GITHUB_TOKEN',\n // Well-known vars CI platforms provide without any pncli-specific setup.\n GITHUB_TOKEN_FALLBACK: 'GITHUB_TOKEN',\n GITHUB_API_URL_FALLBACK: 'GITHUB_API_URL',\n CONFLUENCE_BASE_URL: 'PNCLI_CONFLUENCE_BASE_URL',\n CONFLUENCE_API_TOKEN: 'PNCLI_CONFLUENCE_API_TOKEN',\n ARTIFACTORY_BASE_URL: 'PNCLI_ARTIFACTORY_BASE_URL',\n ARTIFACTORY_TOKEN: 'PNCLI_ARTIFACTORY_TOKEN',\n ARTIFACTORY_REPO_NPM: 'PNCLI_ARTIFACTORY_REPO_NPM',\n ARTIFACTORY_REPO_NUGET: 'PNCLI_ARTIFACTORY_REPO_NUGET',\n ARTIFACTORY_REPO_MAVEN: 'PNCLI_ARTIFACTORY_REPO_MAVEN',\n SONAR_BASE_URL: 'PNCLI_SONAR_BASE_URL',\n SONAR_TOKEN: 'PNCLI_SONAR_TOKEN',\n // SonarSource's own scanner CLI standard var name — see the comment above GITHUB_TOKEN_FALLBACK.\n SONAR_TOKEN_FALLBACK: 'SONAR_TOKEN',\n SDE_CONNECTION: 'PNCLI_SDE_CONNECTION',\n ADO_BASE_URL: 'PNCLI_ADO_BASE_URL',\n ADO_PAT: 'PNCLI_ADO_PAT',\n // Azure Pipelines' conventional name for $(System.AccessToken) — see the comment above GITHUB_TOKEN_FALLBACK.\n ADO_PAT_FALLBACK: 'SYSTEM_ACCESSTOKEN',\n JENKINS_BASE_URL: 'PNCLI_JENKINS_BASE_URL',\n JENKINS_USERNAME: 'PNCLI_JENKINS_USERNAME',\n JENKINS_API_TOKEN: 'PNCLI_JENKINS_API_TOKEN',\n CHECKMARX_BASE_URL: 'PNCLI_CHECKMARX_BASE_URL',\n CHECKMARX_TENANT_NAME: 'PNCLI_CHECKMARX_TENANT_NAME',\n CHECKMARX_API_KEY: 'PNCLI_CHECKMARX_API_KEY',\n CHECKMARX_CLIENT_ID: 'PNCLI_CHECKMARX_CLIENT_ID',\n CHECKMARX_CLIENT_SECRET: 'PNCLI_CHECKMARX_CLIENT_SECRET',\n CONTRAST_BASE_URL: 'PNCLI_CONTRAST_BASE_URL',\n CONTRAST_ORG_UUID: 'PNCLI_CONTRAST_ORG_UUID',\n CONTRAST_API_KEY: 'PNCLI_CONTRAST_API_KEY',\n CONTRAST_SERVICE_KEY: 'PNCLI_CONTRAST_SERVICE_KEY',\n CONTRAST_USERNAME: 'PNCLI_CONTRAST_USERNAME',\n SONATYPEIQ_BASE_URL: 'PNCLI_SONATYPEIQ_BASE_URL',\n SONATYPEIQ_USER_CODE: 'PNCLI_SONATYPEIQ_USER_CODE',\n SONATYPEIQ_PASSCODE: 'PNCLI_SONATYPEIQ_PASSCODE',\n OPENSHIFT_BASE_URL: 'PNCLI_OPENSHIFT_BASE_URL',\n OPENSHIFT_TOKEN: 'PNCLI_OPENSHIFT_TOKEN',\n DYNATRACE_BASE_URL: 'PNCLI_DYNATRACE_BASE_URL',\n DYNATRACE_API_TOKEN: 'PNCLI_DYNATRACE_API_TOKEN',\n DYNATRACE_PLATFORM_URL: 'PNCLI_DYNATRACE_PLATFORM_URL',\n DYNATRACE_PLATFORM_TOKEN: 'PNCLI_DYNATRACE_PLATFORM_TOKEN',\n LOGSCALE_BASE_URL: 'PNCLI_LOGSCALE_BASE_URL',\n LOGSCALE_TOKEN: 'PNCLI_LOGSCALE_TOKEN',\n ELASTICSEARCH_BASE_URL: 'PNCLI_ELASTICSEARCH_BASE_URL',\n ELASTICSEARCH_API_KEY: 'PNCLI_ELASTICSEARCH_API_KEY',\n SPLITIO_BASE_URL: 'PNCLI_SPLITIO_BASE_URL',\n SPLITIO_ADMIN_API_KEY: 'PNCLI_SPLITIO_ADMIN_API_KEY',\n FIGMA_BASE_URL: 'PNCLI_FIGMA_BASE_URL',\n FIGMA_TOKEN: 'PNCLI_FIGMA_TOKEN',\n ALATION_BASE_URL: 'PNCLI_ALATION_BASE_URL',\n ALATION_REFRESH_TOKEN: 'PNCLI_ALATION_REFRESH_TOKEN',\n ALATION_USER_ID: 'PNCLI_ALATION_USER_ID',\n SAUCELABS_BASE_URL: 'PNCLI_SAUCELABS_BASE_URL',\n SAUCELABS_USERNAME: 'PNCLI_SAUCELABS_USERNAME',\n SAUCELABS_ACCESS_KEY: 'PNCLI_SAUCELABS_ACCESS_KEY',\n // The names saucectl, Sauce Connect and Sauce's own CI docs read — see the comment above GITHUB_TOKEN_FALLBACK.\n SAUCELABS_USERNAME_FALLBACK: 'SAUCE_USERNAME',\n SAUCELABS_ACCESS_KEY_FALLBACK: 'SAUCE_ACCESS_KEY',\n CONFIG_PATH: 'PNCLI_CONFIG_PATH'\n} as const;\n\nfunction getGlobalConfigPath(overridePath?: string): string {\n if (overridePath) return overridePath;\n const envPath = process.env[ENV_KEYS.CONFIG_PATH];\n if (envPath) return envPath;\n return path.join(os.homedir(), '.pncli', 'config.json');\n}\n\nfunction loadJsonFile<T>(filePath: string): T | null {\n try {\n const content = fs.readFileSync(filePath, 'utf8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nfunction getRepoRoot(): string | null {\n try {\n return execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();\n } catch {\n return null;\n }\n}\n\nfunction parseSdeConnection(connection: string): { token: string; baseUrl: string } | null {\n const idx = connection.indexOf('@');\n if (idx <= 0) return null;\n const token = connection.slice(0, idx);\n const host = connection.slice(idx + 1);\n const baseUrl = /^https?:\\/\\//i.test(host) ? host : `https://${host}`;\n return { token, baseUrl };\n}\n\nfunction mergeCustomFields(\n global: CustomFieldDefinition[] | undefined,\n repo: CustomFieldDefinition[] | undefined\n): CustomFieldDefinition[] {\n const map = new Map<string, CustomFieldDefinition>();\n if (global != null) {\n assertValidCustomFields(global, { source: 'global config' });\n for (const f of global) map.set(f.id, f);\n }\n if (repo != null) {\n assertValidCustomFields(repo, { source: 'repo config (.pncli.json)', repo: true });\n for (const f of repo) map.set(f.id, f); // repo wins\n }\n return Array.from(map.values());\n}\n\nfunction mergeDefaults(\n global: GlobalConfig['defaults'],\n repo: RepoConfig['defaults']\n): { jira: JiraDefaults; bitbucket: BitbucketDefaults; github: GitHubDefaults; sonar: SonarDefaults; sde: SdeDefaults; ado: AdoDefaults; jenkins: JenkinsDefaults } {\n return {\n jira: { ...global?.jira, ...repo?.jira },\n bitbucket: { ...global?.bitbucket, ...repo?.bitbucket },\n github: { ...global?.github, ...repo?.github },\n sonar: { ...global?.sonar, ...repo?.sonar },\n sde: { ...global?.sde, ...repo?.sde },\n ado: { ...global?.ado, ...repo?.ado },\n jenkins: { ...global?.jenkins, ...repo?.jenkins }\n };\n}\n\n/** Secret fields and the env vars that override them (PNCLI_* first, then the CI fallback). */\nconst SECRET_ENV_OVERRIDES: [string, string[]][] = [\n ['jira.apiToken', [ENV_KEYS.JIRA_API_TOKEN]],\n ['bitbucket.pat', [ENV_KEYS.BITBUCKET_PAT]],\n ['github.token', [ENV_KEYS.GITHUB_TOKEN, ENV_KEYS.GITHUB_TOKEN_FALLBACK]],\n ['confluence.apiToken', [ENV_KEYS.CONFLUENCE_API_TOKEN]],\n ['artifactory.token', [ENV_KEYS.ARTIFACTORY_TOKEN]],\n ['sonar.token', [ENV_KEYS.SONAR_TOKEN, ENV_KEYS.SONAR_TOKEN_FALLBACK]],\n ['ado.pat', [ENV_KEYS.ADO_PAT, ENV_KEYS.ADO_PAT_FALLBACK]],\n ['jenkins.apiToken', [ENV_KEYS.JENKINS_API_TOKEN]],\n ['checkmarx.apiKey', [ENV_KEYS.CHECKMARX_API_KEY]],\n ['checkmarx.clientSecret', [ENV_KEYS.CHECKMARX_CLIENT_SECRET]],\n ['contrast.apiKey', [ENV_KEYS.CONTRAST_API_KEY]],\n ['contrast.serviceKey', [ENV_KEYS.CONTRAST_SERVICE_KEY]],\n ['sonatypeiq.passcode', [ENV_KEYS.SONATYPEIQ_PASSCODE]],\n ['openshift.token', [ENV_KEYS.OPENSHIFT_TOKEN]],\n ['dynatrace.apiToken', [ENV_KEYS.DYNATRACE_API_TOKEN]],\n ['dynatrace.platformToken', [ENV_KEYS.DYNATRACE_PLATFORM_TOKEN]],\n ['logscale.token', [ENV_KEYS.LOGSCALE_TOKEN]],\n ['elasticsearch.apiKey', [ENV_KEYS.ELASTICSEARCH_API_KEY]],\n ['splitio.adminApiKey', [ENV_KEYS.SPLITIO_ADMIN_API_KEY]],\n ['figma.token', [ENV_KEYS.FIGMA_TOKEN]],\n ['alation.refreshToken', [ENV_KEYS.ALATION_REFRESH_TOKEN]],\n ['saucelabs.accessKey', [ENV_KEYS.SAUCELABS_ACCESS_KEY, ENV_KEYS.SAUCELABS_ACCESS_KEY_FALLBACK]],\n];\n\n/** Config paths whose value an environment variable is currently overriding. */\nexport function envOverriddenSecretPaths(env: NodeJS.ProcessEnv = process.env): Set<string> {\n return new Set(SECRET_ENV_OVERRIDES.filter(([, keys]) => keys.some(k => env[k] !== undefined)).map(([p]) => p));\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n /** Resolve only these `keychain:` config paths (e.g. `github.token`); every other one stays unread. */\n keychainPaths?: string[];\n}\n\nexport function loadConfig(opts: LoadConfigOptions = {}): ResolvedConfig {\n const globalConfigPath = getGlobalConfigPath(opts.configPath);\n // `keychain:<account>` values are swapped for the OS-keychain secret here, below every env var\n // in precedence — env vars are read directly from process.env further down and still win, and a\n // field an env var overrides is not looked up at all.\n const globalConfig = resolveKeychainRefs(loadJsonFile<GlobalConfig>(globalConfigPath) ?? {}, undefined, {\n skipPaths: envOverriddenSecretPaths(),\n ...(opts.keychainPaths ? { onlyPaths: new Set(opts.keychainPaths) } : {})\n });\n\n const repoRoot = getRepoRoot();\n let repoConfig: RepoConfig = {};\n if (repoRoot) {\n repoConfig = loadJsonFile<RepoConfig>(path.join(repoRoot, '.pncli.json')) ?? {};\n }\n\n const mergedDefaults = mergeDefaults(globalConfig.defaults, repoConfig.defaults);\n\n return {\n user: {\n email: process.env[ENV_KEYS.EMAIL] ?? globalConfig.user?.email,\n userId: process.env[ENV_KEYS.USERID] ?? globalConfig.user?.userId\n },\n jira: {\n baseUrl: process.env[ENV_KEYS.JIRA_BASE_URL] ?? globalConfig.jira?.baseUrl,\n apiToken: process.env[ENV_KEYS.JIRA_API_TOKEN] ?? globalConfig.jira?.apiToken,\n customFields: mergeCustomFields(globalConfig.jira?.customFields, repoConfig.jira?.customFields)\n },\n bitbucket: {\n baseUrl: process.env[ENV_KEYS.BITBUCKET_BASE_URL] ?? globalConfig.bitbucket?.baseUrl,\n pat: process.env[ENV_KEYS.BITBUCKET_PAT] ?? globalConfig.bitbucket?.pat\n },\n github: {\n baseUrl: process.env[ENV_KEYS.GITHUB_BASE_URL] ?? process.env[ENV_KEYS.GITHUB_API_URL_FALLBACK] ?? globalConfig.github?.baseUrl,\n token: process.env[ENV_KEYS.GITHUB_TOKEN] ?? process.env[ENV_KEYS.GITHUB_TOKEN_FALLBACK] ?? globalConfig.github?.token\n },\n confluence: {\n baseUrl: process.env[ENV_KEYS.CONFLUENCE_BASE_URL] ?? globalConfig.confluence?.baseUrl,\n apiToken: process.env[ENV_KEYS.CONFLUENCE_API_TOKEN] ?? globalConfig.confluence?.apiToken\n ?? process.env[ENV_KEYS.JIRA_API_TOKEN] ?? globalConfig.jira?.apiToken,\n apiTokenExplicit: !!(\n process.env[ENV_KEYS.CONFLUENCE_API_TOKEN] ?? globalConfig.confluence?.apiToken\n )\n },\n artifactory: {\n baseUrl: process.env[ENV_KEYS.ARTIFACTORY_BASE_URL] ?? globalConfig.artifactory?.baseUrl,\n token: process.env[ENV_KEYS.ARTIFACTORY_TOKEN] ?? globalConfig.artifactory?.token,\n npmRepo: process.env[ENV_KEYS.ARTIFACTORY_REPO_NPM] ?? globalConfig.artifactory?.npmRepo,\n nugetRepo: process.env[ENV_KEYS.ARTIFACTORY_REPO_NUGET] ?? globalConfig.artifactory?.nugetRepo,\n mavenRepo: process.env[ENV_KEYS.ARTIFACTORY_REPO_MAVEN] ?? globalConfig.artifactory?.mavenRepo\n },\n sonar: {\n baseUrl: process.env[ENV_KEYS.SONAR_BASE_URL] ?? globalConfig.sonar?.baseUrl,\n token: process.env[ENV_KEYS.SONAR_TOKEN] ?? process.env[ENV_KEYS.SONAR_TOKEN_FALLBACK] ?? globalConfig.sonar?.token\n },\n sde: (() => {\n const raw = process.env[ENV_KEYS.SDE_CONNECTION] ?? globalConfig.sde?.connection;\n const parsed = raw ? parseSdeConnection(raw) : null;\n return { baseUrl: parsed?.baseUrl, token: parsed?.token };\n })(),\n ado: {\n baseUrl: process.env[ENV_KEYS.ADO_BASE_URL] ?? globalConfig.ado?.baseUrl,\n pat: process.env[ENV_KEYS.ADO_PAT] ?? process.env[ENV_KEYS.ADO_PAT_FALLBACK] ?? globalConfig.ado?.pat,\n fieldAliases: globalConfig.ado?.fieldAliases ?? {},\n discoveredFields: globalConfig.ado?.discoveredFields ?? [],\n discoveredTypes: globalConfig.ado?.discoveredTypes ?? []\n },\n jenkins: {\n baseUrl: repoConfig.defaults?.jenkins?.baseUrl ?? process.env[ENV_KEYS.JENKINS_BASE_URL] ?? globalConfig.jenkins?.baseUrl,\n username: process.env[ENV_KEYS.JENKINS_USERNAME] ?? globalConfig.jenkins?.username,\n apiToken: process.env[ENV_KEYS.JENKINS_API_TOKEN] ?? globalConfig.jenkins?.apiToken\n },\n jenkinsInstances: (Array.isArray(globalConfig.jenkinsInstances) ? globalConfig.jenkinsInstances : []) as JenkinsInstanceConfig[],\n checkmarx: {\n baseUrl: process.env[ENV_KEYS.CHECKMARX_BASE_URL] ?? globalConfig.checkmarx?.baseUrl,\n tenantName: process.env[ENV_KEYS.CHECKMARX_TENANT_NAME] ?? globalConfig.checkmarx?.tenantName,\n apiKey: process.env[ENV_KEYS.CHECKMARX_API_KEY] ?? globalConfig.checkmarx?.apiKey,\n clientId: process.env[ENV_KEYS.CHECKMARX_CLIENT_ID] ?? globalConfig.checkmarx?.clientId,\n clientSecret: process.env[ENV_KEYS.CHECKMARX_CLIENT_SECRET] ?? globalConfig.checkmarx?.clientSecret,\n },\n contrast: {\n baseUrl: process.env[ENV_KEYS.CONTRAST_BASE_URL] ?? globalConfig.contrast?.baseUrl,\n orgUuid: process.env[ENV_KEYS.CONTRAST_ORG_UUID] ?? globalConfig.contrast?.orgUuid,\n apiKey: process.env[ENV_KEYS.CONTRAST_API_KEY] ?? globalConfig.contrast?.apiKey,\n serviceKey: process.env[ENV_KEYS.CONTRAST_SERVICE_KEY] ?? globalConfig.contrast?.serviceKey,\n username: process.env[ENV_KEYS.CONTRAST_USERNAME] ?? globalConfig.contrast?.username,\n },\n sonatypeiq: {\n baseUrl: process.env[ENV_KEYS.SONATYPEIQ_BASE_URL] ?? globalConfig.sonatypeiq?.baseUrl,\n userCode: process.env[ENV_KEYS.SONATYPEIQ_USER_CODE] ?? globalConfig.sonatypeiq?.userCode,\n passcode: process.env[ENV_KEYS.SONATYPEIQ_PASSCODE] ?? globalConfig.sonatypeiq?.passcode,\n },\n openshift: {\n baseUrl: process.env[ENV_KEYS.OPENSHIFT_BASE_URL] ?? globalConfig.openshift?.baseUrl,\n token: process.env[ENV_KEYS.OPENSHIFT_TOKEN] ?? globalConfig.openshift?.token,\n defaultEnvironment: globalConfig.openshift?.defaultEnvironment,\n defaultInstance: globalConfig.openshift?.defaultInstance,\n environments: (typeof globalConfig.openshift?.environments === 'object' && globalConfig.openshift.environments !== null)\n ? globalConfig.openshift.environments\n : {},\n },\n dynatrace: {\n baseUrl: process.env[ENV_KEYS.DYNATRACE_BASE_URL] ?? globalConfig.dynatrace?.baseUrl,\n apiToken: process.env[ENV_KEYS.DYNATRACE_API_TOKEN] ?? globalConfig.dynatrace?.apiToken,\n platformUrl: process.env[ENV_KEYS.DYNATRACE_PLATFORM_URL] ?? globalConfig.dynatrace?.platformUrl,\n platformToken: process.env[ENV_KEYS.DYNATRACE_PLATFORM_TOKEN] ?? globalConfig.dynatrace?.platformToken,\n defaultEnvironment: globalConfig.dynatrace?.defaultEnvironment,\n environments: (typeof globalConfig.dynatrace?.environments === 'object' && globalConfig.dynatrace.environments !== null)\n ? globalConfig.dynatrace.environments\n : {},\n },\n logscale: {\n baseUrl: process.env[ENV_KEYS.LOGSCALE_BASE_URL] ?? globalConfig.logscale?.baseUrl,\n token: process.env[ENV_KEYS.LOGSCALE_TOKEN] ?? globalConfig.logscale?.token,\n },\n elasticsearch: {\n baseUrl: process.env[ENV_KEYS.ELASTICSEARCH_BASE_URL] ?? globalConfig.elasticsearch?.baseUrl,\n apiKey: process.env[ENV_KEYS.ELASTICSEARCH_API_KEY] ?? globalConfig.elasticsearch?.apiKey,\n },\n splitio: {\n baseUrl: process.env[ENV_KEYS.SPLITIO_BASE_URL] ?? globalConfig.splitio?.baseUrl,\n adminApiKey: process.env[ENV_KEYS.SPLITIO_ADMIN_API_KEY] ?? globalConfig.splitio?.adminApiKey,\n },\n figma: {\n baseUrl: process.env[ENV_KEYS.FIGMA_BASE_URL] ?? globalConfig.figma?.baseUrl,\n token: process.env[ENV_KEYS.FIGMA_TOKEN] ?? globalConfig.figma?.token,\n },\n alation: {\n baseUrl: process.env[ENV_KEYS.ALATION_BASE_URL] ?? globalConfig.alation?.baseUrl,\n refreshToken: process.env[ENV_KEYS.ALATION_REFRESH_TOKEN] ?? globalConfig.alation?.refreshToken,\n userId: process.env[ENV_KEYS.ALATION_USER_ID] ?? globalConfig.alation?.userId,\n },\n saucelabs: {\n baseUrl: process.env[ENV_KEYS.SAUCELABS_BASE_URL] ?? globalConfig.saucelabs?.baseUrl,\n username: process.env[ENV_KEYS.SAUCELABS_USERNAME]\n ?? process.env[ENV_KEYS.SAUCELABS_USERNAME_FALLBACK]\n ?? globalConfig.saucelabs?.username,\n accessKey: process.env[ENV_KEYS.SAUCELABS_ACCESS_KEY]\n ?? process.env[ENV_KEYS.SAUCELABS_ACCESS_KEY_FALLBACK]\n ?? globalConfig.saucelabs?.accessKey,\n },\n defaults: mergedDefaults\n };\n}\n\nexport function normalizeBaseUrl(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) return trimmed;\n return /^https?:\\/\\//i.test(trimmed) ? trimmed : `https://${trimmed}`;\n}\n\nexport function writeGlobalConfig(config: GlobalConfig, configPath?: string): void {\n const filePath = getGlobalConfigPath(configPath);\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\\n', 'utf8');\n}\n\nexport function writeRepoConfig(config: RepoConfig): void {\n const repoRoot = getRepoRoot();\n const targetDir = repoRoot ?? process.cwd();\n fs.writeFileSync(path.join(targetDir, '.pncli.json'), JSON.stringify(config, null, 2) + '\\n', 'utf8');\n}\n\nfunction parseConfigValue(value: string): unknown {\n try {\n const parsed = JSON.parse(value);\n if (typeof parsed === 'object' && parsed !== null) {\n return parsed;\n }\n } catch {\n // not valid JSON — fall through to raw string\n }\n return value;\n}\n\nexport function setConfigValue(key: string, value: string, configPath?: string): void {\n const filePath = getGlobalConfigPath(configPath);\n const existing = loadJsonFile<GlobalConfig>(filePath) ?? {};\n\n const parts = key.split('.');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let current: any = existing;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {};\n }\n current = current[part];\n }\n current[parts[parts.length - 1]!] = parseConfigValue(value);\n\n writeGlobalConfig(existing, configPath);\n}\n\nexport function setRepoConfigValue(key: string, value: string): void {\n const repoRoot = getRepoRoot();\n const targetDir = repoRoot ?? process.cwd();\n const filePath = path.join(targetDir, '.pncli.json');\n const existing = loadJsonFile<RepoConfig>(filePath) ?? {};\n\n const parts = key.split('.');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let current: any = existing;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {};\n }\n current = current[part];\n }\n current[parts[parts.length - 1]!] = parseConfigValue(value);\n\n writeRepoConfig(existing);\n}\n\nexport function maskConfig(config: ResolvedConfig): unknown {\n return {\n ...config,\n jira: {\n ...config.jira,\n apiToken: config.jira.apiToken ? '***' : undefined,\n customFields: config.jira.customFields\n },\n bitbucket: {\n ...config.bitbucket,\n pat: config.bitbucket.pat ? '***' : undefined\n },\n github: {\n ...config.github,\n token: config.github.token ? '***' : undefined\n },\n confluence: {\n ...config.confluence,\n apiToken: config.confluence.apiToken ? '***' : undefined\n },\n artifactory: {\n ...config.artifactory,\n token: config.artifactory.token ? '***' : undefined\n },\n sonar: {\n ...config.sonar,\n token: config.sonar.token ? '***' : undefined\n },\n sde: {\n baseUrl: config.sde.baseUrl,\n token: config.sde.token ? '***' : undefined\n },\n ado: {\n ...config.ado,\n pat: config.ado.pat ? '***' : undefined\n },\n jenkins: {\n ...config.jenkins,\n apiToken: config.jenkins.apiToken ? '***' : undefined\n },\n jenkinsInstances: config.jenkinsInstances.map(inst => ({\n ...inst,\n apiToken: inst.apiToken ? '***' : undefined\n })),\n checkmarx: {\n ...config.checkmarx,\n apiKey: config.checkmarx.apiKey ? '***' : undefined,\n clientSecret: config.checkmarx.clientSecret ? '***' : undefined\n },\n contrast: {\n ...config.contrast,\n apiKey: config.contrast.apiKey ? '***' : undefined,\n serviceKey: config.contrast.serviceKey ? '***' : undefined\n },\n sonatypeiq: {\n ...config.sonatypeiq,\n passcode: config.sonatypeiq.passcode ? '***' : undefined\n },\n openshift: {\n ...config.openshift,\n token: config.openshift.token ? '***' : undefined,\n environments: Object.fromEntries(\n Object.entries(config.openshift.environments).map(([envName, envCfg]) => [\n envName,\n {\n instances: Object.fromEntries(\n Object.entries(envCfg.instances ?? {}).map(([instName, instCfg]) => [\n instName,\n { ...instCfg, token: instCfg.token ? '***' : undefined }\n ])\n )\n }\n ])\n )\n },\n dynatrace: {\n ...config.dynatrace,\n apiToken: config.dynatrace.apiToken ? '***' : undefined,\n platformToken: config.dynatrace.platformToken ? '***' : undefined,\n environments: Object.fromEntries(\n Object.entries(config.dynatrace.environments).map(([name, env]) => [\n name,\n {\n ...env,\n apiToken: env.apiToken ? '***' : undefined,\n platformToken: env.platformToken ? '***' : undefined\n }\n ])\n )\n },\n logscale: {\n ...config.logscale,\n token: config.logscale.token ? '***' : undefined\n },\n elasticsearch: {\n ...config.elasticsearch,\n apiKey: config.elasticsearch.apiKey ? '***' : undefined\n },\n splitio: {\n ...config.splitio,\n adminApiKey: config.splitio.adminApiKey ? '***' : undefined\n },\n figma: {\n ...config.figma,\n token: config.figma.token ? '***' : undefined\n },\n alation: {\n ...config.alation,\n refreshToken: config.alation.refreshToken ? '***' : undefined\n },\n saucelabs: {\n ...config.saucelabs,\n accessKey: config.saucelabs.accessKey ? '***' : undefined\n }\n };\n}\n\nexport { getGlobalConfigPath, loadJsonFile };\n","// Phase 3 — Jira Data Cloud types\n\nexport const CUSTOM_FIELD_TYPES = [\n 'string',\n 'number',\n 'date',\n 'datetime',\n 'select',\n 'multi-select',\n 'option-id',\n 'cascading-select',\n 'user',\n 'labels',\n 'url',\n 'textarea'\n] as const;\n\nexport type CustomFieldType = (typeof CUSTOM_FIELD_TYPES)[number];\n\nexport interface CustomFieldDefinition {\n id: string;\n name: string;\n /** Drives how a `--field` value is shaped for Jira's API. Omitted = raw string. */\n type?: CustomFieldType;\n description?: string;\n examples?: string[];\n}\n\nexport interface CustomFieldMap {\n byName: Map<string, CustomFieldDefinition>;\n byId: Map<string, CustomFieldDefinition>;\n}\n\nexport interface JiraUser {\n name: string;\n displayName: string;\n}\n\nexport interface JiraFieldInfo {\n id: string;\n name: string;\n custom: boolean;\n schema?: { type: string; custom?: string };\n allowedValues?: Array<{ id: string; value?: string; name?: string; children?: Array<{ id: string; value: string }> }>;\n /** Suggested pncli config type inferred from the Jira field schema. */\n pncliType?: CustomFieldType;\n}\n\nexport interface JiraIssue {\n id: string;\n key: string;\n fields: {\n summary: string;\n description?: unknown;\n status: { name: string };\n priority?: { name: string };\n assignee?: JiraUser | null;\n reporter?: JiraUser;\n labels?: string[];\n issuetype: { name: string };\n project: { key: string; name: string };\n created: string;\n updated: string;\n [key: string]: unknown;\n };\n}\n\nexport interface JiraTransition {\n id: string;\n name: string;\n to: { name: string };\n}\n\nexport interface JiraComment {\n id: string;\n author: JiraUser;\n body: unknown;\n created: string;\n updated: string;\n}\n\nexport interface JiraSearchResult {\n issues: JiraIssue[];\n total: number;\n startAt: number;\n maxResults: number;\n}\n\nexport interface JiraAttachment {\n id: string;\n filename: string;\n author: JiraUser;\n created: string;\n size: number;\n mimeType: string;\n content: string;\n}\n\nexport interface JiraBoard {\n id: number;\n name: string;\n type: string;\n}\n\nexport interface JiraSprint {\n id: number;\n name: string;\n state: string;\n startDate?: string;\n endDate?: string;\n goal?: string;\n}\n\nexport interface JiraWorklog {\n id: string;\n author: JiraUser;\n comment?: unknown;\n started: string;\n timeSpent: string;\n timeSpentSeconds: number;\n created: string;\n updated: string;\n}\n","import { PncliError } from './errors.js';\nimport { CUSTOM_FIELD_TYPES } from '../types/jira.js';\nimport type { CustomFieldDefinition } from '../types/jira.js';\n\nexport interface ValidateCustomFieldsOptions {\n /** Where the value came from, for the error message — e.g. `global config`. */\n source?: string;\n /** Append `--repo` to the suggested fix, for values read from `.pncli.json`. */\n repo?: boolean;\n}\n\nconst EXAMPLE = '[{\"id\":\"customfield_10032\",\"name\":\"Epic Link\",\"type\":\"select\"}]';\n\n/**\n * Validates `jira.customFields` before it's indexed or merged. The value comes straight\n * off disk (or a `config set` value that fell back to a raw string when JSON.parse failed —\n * e.g. mangled shell quoting), so it isn't guaranteed to match CustomFieldDefinition at\n * runtime even though the type says it does. Shared by `loadConfig` (which names the\n * offending config file) and `buildFieldMap` (which doesn't know where the value came from).\n */\nexport function assertValidCustomFields(\n fields: unknown,\n opts: ValidateCustomFieldsOptions = {}\n): asserts fields is CustomFieldDefinition[] {\n const where = opts.source ? `in ${opts.source}` : 'config';\n const fix = (value: string) => `Fix with: pncli config set jira.customFields '${value}'${opts.repo ? ' --repo' : ''}`;\n\n if (!Array.isArray(fields)) {\n throw new PncliError(\n `Invalid jira.customFields ${where}: expected an array, got ${JSON.stringify(fields)}. ${fix('[]')}`,\n 1\n );\n }\n for (const f of fields) {\n const entry = f as { id?: unknown; name?: unknown; type?: unknown } | null;\n if (typeof entry !== 'object' || entry === null || typeof entry.id !== 'string' || typeof entry.name !== 'string') {\n throw new PncliError(\n `Invalid jira.customFields ${where}: each entry must be an object with string \"id\" and \"name\" fields. Got: ${JSON.stringify(f)}. ${fix(EXAMPLE)}`,\n 1\n );\n }\n // `type` is optional (omitted = raw string, per skills/pncli/jira.md), but when present it\n // must be a known CustomFieldType — otherwise formatFieldValue silently falls through.\n if (entry.type !== undefined && (typeof entry.type !== 'string' || !(CUSTOM_FIELD_TYPES as readonly string[]).includes(entry.type))) {\n throw new PncliError(\n `Invalid jira.customFields ${where}: entry \"${entry.name}\" has unknown \"type\" ${JSON.stringify(entry.type)}; expected one of ${CUSTOM_FIELD_TYPES.join(', ')}. ${fix(EXAMPLE)}`,\n 1\n );\n }\n }\n}\n","import { spawnSync } from 'child_process';\n\n/**\n * OS keychain storage for pncli secrets.\n *\n * Any string value in the global config can be a reference of the form `keychain:<account>`\n * instead of the secret itself. `loadConfig` resolves those references through the platform's\n * own credential store, so a token never has to sit in plaintext in `~/.pncli/config.json`:\n *\n * macOS → login Keychain, via the built-in `security` tool\n * Windows → Credential Manager, via the built-in Windows PowerShell and advapi32\n * Linux → Secret Service (GNOME Keyring / KWallet), via `secret-tool` (libsecret-tools)\n *\n * This is opt-in storage, never a requirement: plaintext config and `PNCLI_*` env vars keep\n * working exactly as before, and env vars still win over anything stored here. Secrets always\n * travel to the helper process over stdin, never argv, because argv is visible to every other\n * process on the machine.\n */\n\nexport const KEYCHAIN_PREFIX = 'keychain:';\nexport const KEYCHAIN_SERVICE = 'pncli';\n\n/** Account names are config-path-like: `github.token`, `marketplaces.internal-ai.token`. */\nconst ACCOUNT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nexport type KeychainBackendName = 'macos' | 'windows' | 'linux' | 'none';\n\nexport interface RunResult {\n status: number | null;\n stdout: string;\n stderr: string;\n error?: Error;\n}\n\n/** Spawns a helper process. Injected so tests can assert the exact invocation without a real keychain. */\nexport type Runner = (command: string, args: string[], input?: string) => RunResult;\n\nexport const defaultRunner: Runner = (command, args, input) => {\n const r = spawnSync(command, args, { input, encoding: 'utf8', windowsHide: true, timeout: 30_000 });\n return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? '', error: r.error };\n};\n\nexport interface KeychainBackend {\n name: KeychainBackendName;\n /** Human-readable name of the store, for status output. */\n store: string;\n available(): boolean;\n /** Returns a map of account → secret (null when the account has no entry). */\n getMany(accounts: string[]): Record<string, string | null>;\n set(account: string, secret: string): void;\n /** Returns false when there was nothing to delete. */\n delete(account: string): boolean;\n}\n\nexport class KeychainError extends Error {}\n\n/**\n * Some accounts in a batch lookup failed (locked item, access denied) while others were read. The\n * readable ones are in `partial`, so one bad entry never takes every other reference down with it.\n */\nexport class KeychainBatchError extends KeychainError {\n constructor(public partial: Record<string, string | null>, public errors: Record<string, string>) {\n super(Object.entries(errors).map(([a, e]) => `${a}: ${e}`).join('; '));\n }\n}\n\n/** Runs a per-account lookup, collecting failures instead of stopping at the first one. */\nfunction lookupEach(accounts: string[], lookup: (account: string) => string | null): Record<string, string | null> {\n const out: Record<string, string | null> = {};\n const errors: Record<string, string> = {};\n for (const account of accounts) {\n try {\n out[account] = lookup(account);\n } catch (err) {\n errors[account] = err instanceof Error ? err.message : String(err);\n }\n }\n if (Object.keys(errors).length > 0) throw new KeychainBatchError(out, errors);\n return out;\n}\n\nexport function isKeychainRef(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith(KEYCHAIN_PREFIX);\n}\n\nexport function keychainRef(account: string): string {\n assertValidAccount(account);\n return `${KEYCHAIN_PREFIX}${account}`;\n}\n\nexport function accountFromRef(ref: string): string {\n const account = ref.slice(KEYCHAIN_PREFIX.length);\n assertValidAccount(account);\n return account;\n}\n\nexport function assertValidAccount(account: string): void {\n if (!ACCOUNT_PATTERN.test(account)) {\n throw new KeychainError(`Invalid keychain account name \"${account}\" — use letters, digits, '.', '_' or '-' (max 128 chars).`);\n }\n}\n\nfunction assertStorableSecret(secret: string): void {\n if (!secret) throw new KeychainError('Refusing to store an empty secret.');\n if (/[\\r\\n]/.test(secret)) throw new KeychainError('Secrets containing newlines cannot be stored in the keychain.');\n}\n\nfunction failure(backend: string, action: string, r: RunResult): KeychainError {\n const detail = r.error?.message ?? (r.stderr.trim() || `exit code ${r.status}`);\n return new KeychainError(`${backend}: could not ${action}: ${detail}`);\n}\n\n// ─── macOS ──────────────────────────────────────────────────────────────────\n\nexport function macosBackend(run: Runner = defaultRunner): KeychainBackend {\n return {\n name: 'macos',\n store: 'macOS login Keychain',\n available() {\n const r = run('security', ['help']);\n return !r.error;\n },\n getMany(accounts) {\n return lookupEach(accounts, account => {\n assertValidAccount(account);\n const r = run('security', ['find-generic-password', '-s', KEYCHAIN_SERVICE, '-a', account, '-w']);\n // 44 = errSecItemNotFound\n if (r.status === 44) return null;\n if (r.status !== 0) throw failure('macOS Keychain', `read \"${account}\"`, r);\n return r.stdout.replace(/\\r?\\n$/, '');\n });\n },\n set(account, secret) {\n assertValidAccount(account);\n assertStorableSecret(secret);\n // `security -i` reads commands from stdin, so the secret never appears in argv. Hex (-X)\n // sidesteps security's quoting rules for secrets containing quotes or backslashes.\n const hex = Buffer.from(secret, 'utf8').toString('hex');\n const r = run('security', ['-i'], `add-generic-password -U -s ${KEYCHAIN_SERVICE} -a ${account} -l \"pncli ${account}\" -X ${hex}\\n`);\n if (r.status !== 0 || /error/i.test(r.stderr)) throw failure('macOS Keychain', `store \"${account}\"`, r);\n },\n delete(account) {\n assertValidAccount(account);\n const r = run('security', ['delete-generic-password', '-s', KEYCHAIN_SERVICE, '-a', account]);\n if (r.status === 44) return false;\n if (r.status !== 0) throw failure('macOS Keychain', `delete \"${account}\"`, r);\n return true;\n },\n };\n}\n\n// ─── Windows ────────────────────────────────────────────────────────────────\n\n/**\n * Windows Credential Manager has no built-in CLI that can read a secret back (`cmdkey` only\n * writes and lists), so this goes through advapi32 from the Windows PowerShell that ships with\n * every Windows install. All data — targets and secrets — is passed as JSON on stdin; the script\n * text is constant, so nothing user-controlled is ever interpolated into PowerShell.\n */\nexport const WINDOWS_CREDMAN_SCRIPT = String.raw`\n$ErrorActionPreference = 'Stop'\n# Redirected console streams default to the OEM code page; secrets are UTF-8 JSON both ways.\n[Console]::InputEncoding = New-Object System.Text.UTF8Encoding $false\n[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding $false\nAdd-Type -TypeDefinition @'\nusing System;\nusing System.Runtime.InteropServices;\nusing System.Text;\npublic static class PncliCredMan {\n [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\n private struct CREDENTIAL {\n public int Flags; public int Type; public string TargetName; public string Comment;\n public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;\n public int CredentialBlobSize; public IntPtr CredentialBlob; public int Persist;\n public int AttributeCount; public IntPtr Attributes; public string TargetAlias; public string UserName;\n }\n [DllImport(\"advapi32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\n private static extern bool CredReadW(string target, int type, int flags, out IntPtr cred);\n [DllImport(\"advapi32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\n private static extern bool CredWriteW(ref CREDENTIAL cred, int flags);\n [DllImport(\"advapi32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\n private static extern bool CredDeleteW(string target, int type, int flags);\n [DllImport(\"advapi32.dll\")]\n private static extern void CredFree(IntPtr cred);\n [DllImport(\"advapi32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\n private static extern bool CredEnumerateW(string filter, int flags, out int count, out IntPtr creds);\n private const int NotFound = 1168;\n public static string Read(string target) {\n IntPtr p;\n if (!CredReadW(target, 1, 0, out p)) {\n int err = Marshal.GetLastWin32Error();\n if (err == NotFound) return null;\n throw new System.ComponentModel.Win32Exception(err);\n }\n try {\n CREDENTIAL c = (CREDENTIAL)Marshal.PtrToStructure(p, typeof(CREDENTIAL));\n if (c.CredentialBlobSize == 0) return \"\";\n byte[] bytes = new byte[c.CredentialBlobSize];\n Marshal.Copy(c.CredentialBlob, bytes, 0, c.CredentialBlobSize);\n return Encoding.UTF8.GetString(bytes);\n } finally { CredFree(p); }\n }\n public static void Write(string target, string user, string secret) {\n byte[] bytes = Encoding.UTF8.GetBytes(secret);\n CREDENTIAL c = new CREDENTIAL();\n c.Type = 1; c.TargetName = target; c.UserName = user; c.Persist = 2;\n c.CredentialBlobSize = bytes.Length;\n c.CredentialBlob = Marshal.AllocHGlobal(bytes.Length);\n try {\n Marshal.Copy(bytes, 0, c.CredentialBlob, bytes.Length);\n if (!CredWriteW(ref c, 0)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\n } finally { Marshal.FreeHGlobal(c.CredentialBlob); }\n }\n // Git Credential Manager writes UTF-16LE blobs; pncli and git-credential-wincred write UTF-8.\n // Tokens are ASCII, so \"every odd byte is zero\" reliably identifies UTF-16.\n private static string Decode(byte[] bytes) {\n bool utf16 = bytes.Length % 2 == 0 && bytes.Length > 0;\n for (int i = 1; utf16 && i < bytes.Length; i += 2) if (bytes[i] != 0) utf16 = false;\n return utf16 ? Encoding.Unicode.GetString(bytes) : Encoding.UTF8.GetString(bytes);\n }\n // Flattened [target, user, secret] triples for every credential matching the filter (e.g. \"git:*\").\n public static string[] List(string filter) {\n int count; IntPtr list;\n if (!CredEnumerateW(filter, 0, out count, out list)) {\n int err = Marshal.GetLastWin32Error();\n if (err == NotFound) return new string[0];\n throw new System.ComponentModel.Win32Exception(err);\n }\n try {\n string[] outp = new string[count * 3];\n for (int i = 0; i < count; i++) {\n IntPtr item = Marshal.ReadIntPtr(list, i * IntPtr.Size);\n CREDENTIAL c = (CREDENTIAL)Marshal.PtrToStructure(item, typeof(CREDENTIAL));\n string secret = \"\";\n if (c.CredentialBlobSize > 0) {\n byte[] bytes = new byte[c.CredentialBlobSize];\n Marshal.Copy(c.CredentialBlob, bytes, 0, c.CredentialBlobSize);\n secret = Decode(bytes);\n }\n outp[i * 3] = c.TargetName; outp[i * 3 + 1] = c.UserName; outp[i * 3 + 2] = secret;\n }\n return outp;\n } finally { CredFree(list); }\n }\n public static bool Delete(string target) {\n if (CredDeleteW(target, 1, 0)) return true;\n int err = Marshal.GetLastWin32Error();\n if (err == NotFound) return false;\n throw new System.ComponentModel.Win32Exception(err);\n }\n}\n'@\n$req = [Console]::In.ReadToEnd() | ConvertFrom-Json\nswitch ($req.op) {\n 'get' {\n $out = @{}\n foreach ($t in $req.targets) { $out[$t] = [PncliCredMan]::Read($t) }\n [Console]::Out.Write((ConvertTo-Json -InputObject $out -Compress))\n }\n 'set' { [PncliCredMan]::Write($req.target, $req.user, $req.secret); [Console]::Out.Write('{\"ok\":true}') }\n 'delete' { $d = [PncliCredMan]::Delete($req.target); [Console]::Out.Write((ConvertTo-Json -InputObject @{ deleted = $d } -Compress)) }\n 'list' {\n $flat = [PncliCredMan]::List($req.filter)\n $items = @()\n for ($i = 0; $i -lt $flat.Length; $i += 3) { $items += @{ target = $flat[$i]; user = $flat[$i + 1]; secret = $flat[$i + 2] } }\n [Console]::Out.Write((ConvertTo-Json -InputObject @($items) -Compress -Depth 3))\n }\n 'ping' { [Console]::Out.Write('{\"ok\":true}') }\n}\n`;\n\nexport interface StoredCredential {\n target: string;\n user: string;\n /** Returned to the caller for fingerprinting and validation only — never printed. */\n secret: string;\n}\n\n/**\n * Every Windows Credential Manager entry whose target matches `filter` (e.g. `git:*`, which covers\n * what Git Credential Manager and git-credential-wincred store). Read-only.\n */\nexport function windowsListCredentials(filter: string, run: Runner = defaultRunner): StoredCredential[] {\n const encoded = Buffer.from(WINDOWS_CREDMAN_SCRIPT, 'utf16le').toString('base64');\n const r = run('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded], JSON.stringify({ op: 'list', filter }));\n if (r.status !== 0) throw failure('Windows Credential Manager', 'list credentials', r);\n const parsed: unknown = JSON.parse(r.stdout.trim() || '[]');\n const items = Array.isArray(parsed) ? parsed : [parsed];\n return items\n .filter((x): x is { target?: unknown; user?: unknown; secret?: unknown } => !!x && typeof x === 'object')\n .map(x => ({ target: String(x.target ?? ''), user: String(x.user ?? ''), secret: String(x.secret ?? '') }));\n}\n\nexport function windowsTarget(account: string): string {\n return `${KEYCHAIN_SERVICE}:${account}`;\n}\n\nexport function windowsBackend(run: Runner = defaultRunner): KeychainBackend {\n const encoded = Buffer.from(WINDOWS_CREDMAN_SCRIPT, 'utf16le').toString('base64');\n const invoke = (payload: object, action: string): unknown => {\n const r = run('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded], JSON.stringify(payload));\n if (r.status !== 0) throw failure('Windows Credential Manager', action, r);\n try {\n return JSON.parse(r.stdout.trim() || '{}');\n } catch {\n throw new KeychainError(`Windows Credential Manager: unexpected output while trying to ${action}.`);\n }\n };\n return {\n name: 'windows',\n store: 'Windows Credential Manager',\n available() {\n try { invoke({ op: 'ping' }, 'start PowerShell'); return true; } catch { return false; }\n },\n getMany(accounts) {\n accounts.forEach(assertValidAccount);\n if (accounts.length === 0) return {};\n const raw = invoke({ op: 'get', targets: accounts.map(windowsTarget) }, 'read credentials') as Record<string, string | null>;\n return Object.fromEntries(accounts.map(a => [a, raw[windowsTarget(a)] ?? null]));\n },\n set(account, secret) {\n assertValidAccount(account);\n assertStorableSecret(secret);\n invoke({ op: 'set', target: windowsTarget(account), user: account, secret }, `store \"${account}\"`);\n },\n delete(account) {\n assertValidAccount(account);\n const r = invoke({ op: 'delete', target: windowsTarget(account) }, `delete \"${account}\"`) as { deleted?: boolean };\n return !!r.deleted;\n },\n };\n}\n\n// ─── Linux ──────────────────────────────────────────────────────────────────\n\nexport function linuxBackend(run: Runner = defaultRunner): KeychainBackend {\n return {\n name: 'linux',\n store: 'Secret Service (secret-tool)',\n available() {\n const r = run('secret-tool', ['--version']);\n return !r.error;\n },\n getMany(accounts) {\n return lookupEach(accounts, account => {\n assertValidAccount(account);\n const r = run('secret-tool', ['lookup', 'service', KEYCHAIN_SERVICE, 'account', account]);\n if (r.error) throw failure('Secret Service', `read \"${account}\"`, r);\n // secret-tool exits 1 with empty output when nothing matches.\n if (r.status !== 0) return null;\n return r.stdout.replace(/\\r?\\n$/, '');\n });\n },\n set(account, secret) {\n assertValidAccount(account);\n assertStorableSecret(secret);\n const r = run('secret-tool', ['store', `--label=pncli ${account}`, 'service', KEYCHAIN_SERVICE, 'account', account], secret);\n if (r.status !== 0) throw failure('Secret Service', `store \"${account}\"`, r);\n },\n delete(account) {\n assertValidAccount(account);\n const existing = this.getMany([account])[account];\n if (existing === null) return false;\n const r = run('secret-tool', ['clear', 'service', KEYCHAIN_SERVICE, 'account', account]);\n if (r.status !== 0) throw failure('Secret Service', `delete \"${account}\"`, r);\n return true;\n },\n };\n}\n\n// ─── Selection ──────────────────────────────────────────────────────────────\n\nexport function noneBackend(): KeychainBackend {\n const unsupported = (): never => {\n throw new KeychainError(`No OS keychain backend is available on ${process.platform}. Set PNCLI_KEYCHAIN_BACKEND to macos, windows, or linux if one should be.`);\n };\n return { name: 'none', store: 'none', available: () => false, getMany: unsupported, set: unsupported, delete: unsupported };\n}\n\n/**\n * Picks the backend for this platform. `PNCLI_KEYCHAIN_BACKEND` overrides it — `none` disables\n * keychain resolution entirely (useful on a CI runner that inherits a developer config).\n */\nexport function getKeychainBackend(run: Runner = defaultRunner, platform: NodeJS.Platform = process.platform): KeychainBackend {\n const override = (process.env.PNCLI_KEYCHAIN_BACKEND || undefined) as KeychainBackendName | undefined;\n const name: KeychainBackendName = override\n ?? (platform === 'darwin' ? 'macos' : platform === 'win32' ? 'windows' : platform === 'linux' ? 'linux' : 'none');\n switch (name) {\n case 'macos': return macosBackend(run);\n case 'windows': return windowsBackend(run);\n case 'linux': return linuxBackend(run);\n case 'none': return noneBackend();\n default: throw new KeychainError(`Unknown PNCLI_KEYCHAIN_BACKEND \"${String(override)}\" — use macos, windows, linux, or none.`);\n }\n}\n\n// ─── Config integration ─────────────────────────────────────────────────────\n\n/** Leaf field names in the config that hold secrets. Used by `config keychain migrate` and doctor. */\nexport const SECRET_FIELD_NAMES = new Set([\n 'token', 'apiToken', 'pat', 'apiKey', 'clientSecret', 'serviceKey', 'passcode',\n 'refreshToken', 'adminApiKey', 'platformToken', 'password', 'connection', 'accessKey',\n]);\n\nexport interface ConfigSecretLocation {\n /** Path segments from the config root, e.g. ['marketplaces', 0, 'token']. */\n path: (string | number)[];\n value: string;\n}\n\n/**\n * Walks a parsed config object and returns every string leaf matching `predicate`. Arrays are\n * walked by index so `marketplaces[]` and `jenkinsInstances[]` entries are covered.\n */\nexport function findConfigStrings(\n root: unknown,\n predicate: (key: string | number, value: string) => boolean,\n prefix: (string | number)[] = []\n): ConfigSecretLocation[] {\n const found: ConfigSecretLocation[] = [];\n if (Array.isArray(root)) {\n root.forEach((item, i) => found.push(...findConfigStrings(item, predicate, [...prefix, i])));\n } else if (root && typeof root === 'object') {\n for (const [key, value] of Object.entries(root as Record<string, unknown>)) {\n if (typeof value === 'string') {\n if (predicate(key, value)) found.push({ path: [...prefix, key], value });\n } else {\n found.push(...findConfigStrings(value, predicate, [...prefix, key]));\n }\n }\n }\n return found;\n}\n\n/** Deletes keychain entries that no remaining reference in `config` points at. Returns the accounts deleted. */\nexport function purgeEntries(config: unknown, backend: KeychainBackend, accounts: string[]): string[] {\n const stillReferenced = new Set(findKeychainRefs(config).map(r => r.value.slice(KEYCHAIN_PREFIX.length)));\n const purged: string[] = [];\n for (const account of new Set(accounts)) {\n if (stillReferenced.has(account)) continue;\n if (backend.delete(account)) purged.push(account);\n }\n return purged;\n}\n\n/** Every `keychain:` reference in a config object. */\nexport function findKeychainRefs(root: unknown): ConfigSecretLocation[] {\n return findConfigStrings(root, (_k, v) => isKeychainRef(v));\n}\n\n/** Every plaintext secret (a secret-named field that is not already a keychain reference). */\nexport function findPlaintextSecrets(root: unknown): ConfigSecretLocation[] {\n return findConfigStrings(root, (k, v) => typeof k === 'string' && SECRET_FIELD_NAMES.has(k) && v.length > 0 && !isKeychainRef(v));\n}\n\n/**\n * Suggested keychain account for a config path. Array entries use a `name` field when the entry\n * has one (`marketplaces.internal-ai.token`), so the account survives reordering the array.\n */\nexport function accountForPath(root: unknown, segments: (string | number)[]): string {\n const parts: string[] = [];\n let node: unknown = root;\n for (const seg of segments) {\n if (typeof seg === 'number') {\n const entry = Array.isArray(node) ? node[seg] : undefined;\n const name = entry && typeof entry === 'object' ? (entry as { name?: unknown }).name : undefined;\n parts.push(typeof name === 'string' && /^[A-Za-z0-9._-]+$/.test(name) ? name : String(seg));\n } else {\n parts.push(seg);\n }\n node = node && typeof node === 'object' ? (node as Record<string | number, unknown>)[seg] : undefined;\n }\n return parts.join('.');\n}\n\nexport function setAtPath(root: Record<string, unknown>, segments: (string | number)[], value: unknown): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let node: any = root;\n for (const seg of segments.slice(0, -1)) node = node[seg];\n node[segments[segments.length - 1]!] = value;\n}\n\nexport interface UnresolvedRef {\n path: string;\n account: string;\n reason: string;\n}\n\nconst resolvedCache = new Map<string, string | null>();\nlet lastUnresolved: UnresolvedRef[] = [];\nlet warnedUnresolved = false;\n\n/** Refs the most recent resolution pass could not satisfy. Read by doctor. */\nexport function getUnresolvedKeychainRefs(): UnresolvedRef[] {\n return lastUnresolved;\n}\n\n/** Test hook: forget cached lookups between tests. */\nexport function resetKeychainCache(): void {\n resolvedCache.clear();\n lastUnresolved = [];\n warnedUnresolved = false;\n}\n\n/**\n * Returns a deep copy of `config` with every `keychain:` reference replaced by the stored secret.\n * One backend call covers all refs (a single PowerShell start on Windows), and results are cached\n * for the life of the process because `loadConfig` runs many times per command.\n *\n * A reference that cannot be resolved becomes `undefined` — the same as an unset key — so the\n * service that needs it reports \"not configured\" instead of sending the literal `keychain:...`\n * string as a credential. A single stderr warning names the refs; doctor reports them in detail.\n */\nexport function resolveKeychainRefs<T>(config: T, backend?: KeychainBackend, opts: { skipPaths?: Set<string>; onlyPaths?: Set<string> } = {}): T {\n const all = findKeychainRefs(config);\n if (all.length === 0) return config;\n\n const copy = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;\n // A field an env var overrides is never used, so never ask the OS store for it — no process spawn\n // on a CI runner that inherited a developer's config, and no warning about a value nobody reads.\n const refs = all.filter(r => {\n const key = r.path.join('.');\n // onlyPaths: the caller reads just these fields, so no other reference is looked up.\n if (!opts.skipPaths?.has(key) && (!opts.onlyPaths || opts.onlyPaths.has(key))) return true;\n setAtPath(copy, r.path, undefined);\n return false;\n });\n if (refs.length === 0) return copy as T;\n const unresolved: UnresolvedRef[] = [];\n const accounts = new Map<string, (string | number)[][]>();\n for (const ref of refs) {\n try {\n const account = accountFromRef(ref.value);\n accounts.set(account, [...(accounts.get(account) ?? []), ref.path]);\n } catch (err) {\n unresolved.push({ path: ref.path.join('.'), account: ref.value.slice(KEYCHAIN_PREFIX.length), reason: (err as Error).message });\n setAtPath(copy, ref.path, undefined);\n }\n }\n\n const activeBackend = backend ?? getKeychainBackend();\n if (activeBackend.name === 'none') {\n // PNCLI_KEYCHAIN_BACKEND=none (or an unsupported platform): references are ignored on purpose,\n // so no warning — doctor still lists them.\n for (const [account, paths] of accounts) {\n for (const p of paths) {\n setAtPath(copy, p, undefined);\n unresolved.push({ path: p.join('.'), account, reason: 'keychain disabled (PNCLI_KEYCHAIN_BACKEND=none or unsupported platform)' });\n }\n }\n lastUnresolved = unresolved;\n return copy as T;\n }\n\n const missing = [...accounts.keys()].filter(a => !resolvedCache.has(a));\n if (missing.length > 0) {\n let failed: Record<string, string> = {};\n try {\n const found = activeBackend.getMany(missing);\n for (const a of missing) resolvedCache.set(a, found[a] ?? null);\n } catch (err) {\n if (err instanceof KeychainBatchError) {\n // Keep what was readable; only the accounts that actually failed are unresolved.\n for (const a of missing) if (!(a in err.errors)) resolvedCache.set(a, err.partial[a] ?? null);\n failed = err.errors;\n } else {\n failed = Object.fromEntries(missing.map(a => [a, (err as Error).message]));\n }\n }\n for (const [account, paths] of accounts) {\n if (resolvedCache.has(account) || !(account in failed)) continue;\n for (const p of paths) unresolved.push({ path: p.join('.'), account, reason: failed[account]! });\n }\n }\n\n for (const [account, paths] of accounts) {\n const secret = resolvedCache.get(account);\n for (const p of paths) {\n if (secret != null) {\n setAtPath(copy, p, secret);\n } else {\n setAtPath(copy, p, undefined);\n if (resolvedCache.has(account)) unresolved.push({ path: p.join('.'), account, reason: 'no entry in the keychain' });\n }\n }\n }\n\n lastUnresolved = unresolved;\n if (unresolved.length > 0 && !warnedUnresolved) {\n warnedUnresolved = true;\n process.stderr.write(`Warning: ${unresolved.length} keychain reference(s) could not be resolved (${unresolved.map(u => u.path).join(', ')}). Run: pncli doctor\\n`);\n }\n return copy as T;\n}\n\n/**\n * Stores a secret and reads it back before returning — the caller only then points config at it,\n * so a backend that silently drops writes can never leave a reference that resolves to nothing.\n */\nexport function storeVerified(backend: KeychainBackend, account: string, secret: string): void {\n backend.set(account, secret);\n let readBack: string | null | undefined;\n try {\n readBack = backend.getMany([account])[account];\n } catch (err) {\n throw new KeychainError(`Stored \"${account}\" but could not read it back to verify: ${err instanceof Error ? err.message : String(err)}`);\n }\n if (readBack !== secret) throw new KeychainError(`Stored \"${account}\" but reading it back did not return the same secret — config was not changed.`);\n}\n\n/** Resolves a single value that may be a keychain reference (e.g. a marketplace token read from raw config). */\nexport function resolveSecretValue(value: string | undefined, backend?: KeychainBackend): string | undefined {\n if (!isKeychainRef(value)) return value;\n const resolved = resolveKeychainRefs({ v: value }, backend);\n return resolved.v;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -37,6 +37,44 @@ async function buildAdoFetcher(config) {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// src/lib/tls.ts
|
|
41
|
+
import tls from "tls";
|
|
42
|
+
var INSECURE_TLS_WARNING = "warning: NODE_TLS_REJECT_UNAUTHORIZED=0 is set, so pncli will NOT verify server certificates. Anyone on the network path can read or alter requests and responses, including your credentials and any writes. Unset it and trust the proxy or internal CA instead: install its root certificate in the OS store or set NODE_EXTRA_CA_CERTS=/path/to/ca.pem.\n";
|
|
43
|
+
function configureTls(env = process.env, api = tls) {
|
|
44
|
+
if (env.NODE_TLS_REJECT_UNAUTHORIZED === "0") process.stderr.write(INSECURE_TLS_WARNING);
|
|
45
|
+
if (env.NODE_USE_SYSTEM_CA !== void 0) return;
|
|
46
|
+
try {
|
|
47
|
+
const merged = /* @__PURE__ */ new Set([...api.getCACertificates("default"), ...api.getCACertificates("system")]);
|
|
48
|
+
api.setDefaultCACertificates([...merged]);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
process.stderr.write(`warning: could not load the OS certificate store (${msg}); only Node's bundled CAs are trusted.
|
|
52
|
+
`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var UNTRUSTED_HINT = "The certificate is not trusted by Node's bundled CAs or the OS certificate store. If your network uses an SSL-inspecting proxy or an internal CA, install its root certificate in the OS store or set NODE_EXTRA_CA_CERTS=/path/to/ca.pem. For a self-signed server, export its certificate from the browser and point NODE_EXTRA_CA_CERTS at that file.";
|
|
56
|
+
var TLS_ERROR_HINTS = {
|
|
57
|
+
SELF_SIGNED_CERT_IN_CHAIN: UNTRUSTED_HINT,
|
|
58
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: UNTRUSTED_HINT,
|
|
59
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: UNTRUSTED_HINT,
|
|
60
|
+
UNABLE_TO_GET_ISSUER_CERT: UNTRUSTED_HINT,
|
|
61
|
+
UNABLE_TO_GET_ISSUER_CERT_LOCALLY: UNTRUSTED_HINT,
|
|
62
|
+
CERT_UNTRUSTED: UNTRUSTED_HINT,
|
|
63
|
+
ERR_TLS_CERT_ALTNAME_INVALID: "The certificate does not match the host in the URL. Use the hostname the certificate was issued for (usually the fully qualified name, not a short alias or IP address) in baseUrl.",
|
|
64
|
+
CERT_HAS_EXPIRED: "The server's certificate has expired. Check the system clock; otherwise the server owner needs to renew it.",
|
|
65
|
+
CERT_NOT_YET_VALID: "The server's certificate is not valid yet. Check the system clock."
|
|
66
|
+
};
|
|
67
|
+
var NODE_SYSTEM_CA_ADVICE = /;? ?if the root CA is installed locally, try running Node\.js with --use-system-ca\.?/;
|
|
68
|
+
function describeFetchError(err) {
|
|
69
|
+
const topMsg = err instanceof Error ? err.message : String(err);
|
|
70
|
+
const cause = err instanceof Error ? err.cause : void 0;
|
|
71
|
+
const causeMsg = cause instanceof Error ? cause.message : void 0;
|
|
72
|
+
const causeCode = cause && typeof cause === "object" ? cause.code : void 0;
|
|
73
|
+
const hint = typeof causeCode === "string" ? TLS_ERROR_HINTS[causeCode] : void 0;
|
|
74
|
+
const detail = hint && causeMsg ? causeMsg.replace(NODE_SYSTEM_CA_ADVICE, "") : causeMsg;
|
|
75
|
+
return (detail ? `${topMsg}: ${detail}` : topMsg) + (hint ? `. ${hint}` : "");
|
|
76
|
+
}
|
|
77
|
+
|
|
40
78
|
// src/lib/checkmarxFetch.ts
|
|
41
79
|
function buildCheckmarxFetcher(config) {
|
|
42
80
|
const { baseUrl, tenantName, apiKey, clientId, clientSecret } = config.checkmarx;
|
|
@@ -66,11 +104,16 @@ function buildCheckmarxFetcher(config) {
|
|
|
66
104
|
client_id: clientId,
|
|
67
105
|
client_secret: clientSecret
|
|
68
106
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
107
|
+
let response;
|
|
108
|
+
try {
|
|
109
|
+
response = await fetch(tokenUrl, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
112
|
+
body: body.toString()
|
|
113
|
+
});
|
|
114
|
+
} catch (err) {
|
|
115
|
+
throw new PncliError(`Checkmarx token exchange failed: ${describeFetchError(err)}`, 0, tokenUrl);
|
|
116
|
+
}
|
|
74
117
|
if (!response.ok) {
|
|
75
118
|
const text = await response.text().catch(() => "");
|
|
76
119
|
throw new PncliError(
|
|
@@ -149,7 +192,7 @@ async function createAlationAccessToken(config) {
|
|
|
149
192
|
});
|
|
150
193
|
} catch (err) {
|
|
151
194
|
throw new PncliError(
|
|
152
|
-
`Alation token exchange failed: ${
|
|
195
|
+
`Alation token exchange failed: ${describeFetchError(err)}`,
|
|
153
196
|
0,
|
|
154
197
|
tokenUrl
|
|
155
198
|
);
|
|
@@ -181,7 +224,7 @@ async function validateAlationAccessToken(config) {
|
|
|
181
224
|
});
|
|
182
225
|
} catch (err) {
|
|
183
226
|
throw new PncliError(
|
|
184
|
-
`Alation token validation failed: ${
|
|
227
|
+
`Alation token validation failed: ${describeFetchError(err)}`,
|
|
185
228
|
0,
|
|
186
229
|
validateUrl
|
|
187
230
|
);
|
|
@@ -271,6 +314,9 @@ async function fetchWithTimeout(url, init, timeoutMs, fetcher = fetch) {
|
|
|
271
314
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
272
315
|
try {
|
|
273
316
|
return await fetcher(url, { ...init, signal: controller.signal });
|
|
317
|
+
} catch (err) {
|
|
318
|
+
if (err instanceof PncliError) throw err;
|
|
319
|
+
throw new PncliError(`Request failed: ${describeFetchError(err)}`, 0, url);
|
|
274
320
|
} finally {
|
|
275
321
|
clearTimeout(timer);
|
|
276
322
|
}
|
|
@@ -289,11 +335,8 @@ async function requestRaw(url, init, timeoutMs, fetcher = fetch) {
|
|
|
289
335
|
try {
|
|
290
336
|
response = await fetchWithTimeout(url, init, timeoutMs, fetcher);
|
|
291
337
|
} catch (err) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const fullMsg = causeMsg ? `${topMsg}: ${causeMsg}` : topMsg;
|
|
295
|
-
debug(` Error: ${fullMsg}`);
|
|
296
|
-
throw new PncliError(`Request failed: ${fullMsg}`, 0, url);
|
|
338
|
+
debug(` Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
339
|
+
throw err;
|
|
297
340
|
}
|
|
298
341
|
debug(`\u2190 ${response.status} ${response.statusText} (${Date.now() - reqStart}ms)`);
|
|
299
342
|
if (response.status === 429) {
|
|
@@ -336,6 +379,9 @@ async function requestRaw(url, init, timeoutMs, fetcher = fetch) {
|
|
|
336
379
|
if (parts.length === 0 && parsed.message) {
|
|
337
380
|
parts.push(String(parsed.message));
|
|
338
381
|
}
|
|
382
|
+
if (parts.length === 0 && typeof parsed.detail === "string") {
|
|
383
|
+
parts.push(parsed.detail);
|
|
384
|
+
}
|
|
339
385
|
if (parts.length > 0) message = parts.join("; ");
|
|
340
386
|
} catch {
|
|
341
387
|
}
|
|
@@ -1269,6 +1315,38 @@ Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
|
1269
1315
|
const msg = `DRY RUN: ${init.method} ${url}
|
|
1270
1316
|
Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
1271
1317
|
` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
|
|
1318
|
+
` : "");
|
|
1319
|
+
fs.writeSync(process.stderr.fd, msg);
|
|
1320
|
+
process.exitCode = ExitCode.SUCCESS;
|
|
1321
|
+
throw new PncliError("dry-run", 0);
|
|
1322
|
+
}
|
|
1323
|
+
return request(url, init, opts.timeoutMs ?? 3e4);
|
|
1324
|
+
}
|
|
1325
|
+
elasticsearchHeaders() {
|
|
1326
|
+
const { apiKey } = this.config.elasticsearch;
|
|
1327
|
+
if (!apiKey) throw new PncliError("Elasticsearch credentials not configured. Run: pncli config init");
|
|
1328
|
+
return {
|
|
1329
|
+
"Authorization": `ApiKey ${apiKey}`,
|
|
1330
|
+
"Content-Type": "application/json",
|
|
1331
|
+
"Accept": "application/json",
|
|
1332
|
+
"Connection": "close"
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
async elasticsearch(path, opts = {}) {
|
|
1336
|
+
const baseUrl = this.config.elasticsearch.baseUrl;
|
|
1337
|
+
if (!baseUrl) throw new PncliError("Elasticsearch baseUrl not configured. Run: pncli config init");
|
|
1338
|
+
const url = buildUrl(baseUrl, path, opts.params);
|
|
1339
|
+
const headers = this.elasticsearchHeaders();
|
|
1340
|
+
const init = {
|
|
1341
|
+
method: opts.method ?? "GET",
|
|
1342
|
+
headers,
|
|
1343
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
1344
|
+
};
|
|
1345
|
+
if (this.dryRun) {
|
|
1346
|
+
const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
|
|
1347
|
+
const msg = `DRY RUN: ${init.method} ${url}
|
|
1348
|
+
Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
1349
|
+
` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
|
|
1272
1350
|
` : "");
|
|
1273
1351
|
fs.writeSync(process.stderr.fd, msg);
|
|
1274
1352
|
process.exitCode = ExitCode.SUCCESS;
|
|
@@ -1384,6 +1462,47 @@ Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
|
1384
1462
|
const msg = `DRY RUN: ${init.method} ${url}
|
|
1385
1463
|
Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
1386
1464
|
` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
|
|
1465
|
+
` : "");
|
|
1466
|
+
fs.writeSync(process.stderr.fd, msg);
|
|
1467
|
+
process.exitCode = ExitCode.SUCCESS;
|
|
1468
|
+
throw new PncliError("dry-run", 0);
|
|
1469
|
+
}
|
|
1470
|
+
return request(url, init, opts.timeoutMs ?? 3e4);
|
|
1471
|
+
}
|
|
1472
|
+
saucelabsHeaders() {
|
|
1473
|
+
const { username, accessKey } = this.config.saucelabs;
|
|
1474
|
+
if (!username || !accessKey) {
|
|
1475
|
+
throw new PncliError("Sauce Labs credentials not configured. Run: pncli config set saucelabs.username <username> && pncli config set saucelabs.accessKey <access-key>");
|
|
1476
|
+
}
|
|
1477
|
+
const creds = Buffer.from(`${username}:${accessKey}`).toString("base64");
|
|
1478
|
+
return {
|
|
1479
|
+
"Authorization": `Basic ${creds}`,
|
|
1480
|
+
"Content-Type": "application/json",
|
|
1481
|
+
"Accept": "application/json",
|
|
1482
|
+
"Connection": "close"
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
/** The configured Sauce Labs username; most v1 paths are scoped to it. */
|
|
1486
|
+
saucelabsUsername() {
|
|
1487
|
+
const { username } = this.config.saucelabs;
|
|
1488
|
+
if (!username) throw new PncliError("Sauce Labs username not configured. Run: pncli config set saucelabs.username <username>");
|
|
1489
|
+
return username;
|
|
1490
|
+
}
|
|
1491
|
+
async saucelabs(path, opts = {}) {
|
|
1492
|
+
const baseUrl = this.config.saucelabs.baseUrl;
|
|
1493
|
+
if (!baseUrl) throw new PncliError("Sauce Labs baseUrl not configured. Run: pncli config set saucelabs.baseUrl https://api.us-west-1.saucelabs.com");
|
|
1494
|
+
const url = buildUrl(baseUrl, path, opts.params);
|
|
1495
|
+
const headers = { ...this.saucelabsHeaders(), ...opts.headers };
|
|
1496
|
+
const init = {
|
|
1497
|
+
method: opts.method ?? "GET",
|
|
1498
|
+
headers,
|
|
1499
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
1500
|
+
};
|
|
1501
|
+
if (this.dryRun) {
|
|
1502
|
+
const safeHeaders = { ...headers, Authorization: "[REDACTED]" };
|
|
1503
|
+
const msg = `DRY RUN: ${init.method} ${url}
|
|
1504
|
+
Headers: ${JSON.stringify(safeHeaders, null, 2)}
|
|
1505
|
+
` + (opts.body ? `Body: ${JSON.stringify(opts.body, null, 2)}
|
|
1387
1506
|
` : "");
|
|
1388
1507
|
fs.writeSync(process.stderr.fd, msg);
|
|
1389
1508
|
process.exitCode = ExitCode.SUCCESS;
|
|
@@ -1423,8 +1542,10 @@ function createHttpClient(config, dryRun = false) {
|
|
|
1423
1542
|
}
|
|
1424
1543
|
|
|
1425
1544
|
export {
|
|
1545
|
+
configureTls,
|
|
1546
|
+
describeFetchError,
|
|
1426
1547
|
validateAlationAccessToken,
|
|
1427
1548
|
HttpClient,
|
|
1428
1549
|
createHttpClient
|
|
1429
1550
|
};
|
|
1430
|
-
//# sourceMappingURL=chunk-
|
|
1551
|
+
//# sourceMappingURL=chunk-Y3PO2RTO.js.map
|