@kolatts/pncli 5.3.0 → 6.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 +2 -1
- package/dist/{chunk-2J2HAFKO.js → chunk-4FR4WGNR.js} +100 -13
- package/dist/chunk-4FR4WGNR.js.map +1 -0
- package/dist/chunk-WDWAPTVC.js +1001 -0
- package/dist/chunk-WDWAPTVC.js.map +1 -0
- package/dist/cli.js +3000 -493
- package/dist/cli.js.map +1 -1
- package/dist/{config-Y6IE6GY7.js → config-5LC7ZTEP.js} +4 -2
- package/dist/{http-V7PZD7LE.js → http-DJSGEGD6.js} +2 -2
- package/package.json +1 -1
- package/skills/pncli/SKILL.md +25 -6
- package/skills/pncli/ado.md +4 -0
- package/skills/pncli/bitbucket.md +4 -0
- package/skills/pncli/github.md +37 -0
- package/skills/pncli/jira.md +45 -1
- package/skills/pncli/marketplace.md +53 -1
- package/skills/pncli/saucelabs.md +175 -0
- package/skills/pncli/skills-guide.md +169 -0
- package/dist/chunk-2J2HAFKO.js.map +0 -1
- package/dist/chunk-QHJ2HM5J.js +0 -466
- package/dist/chunk-QHJ2HM5J.js.map +0 -1
- /package/dist/{config-Y6IE6GY7.js.map → config-5LC7ZTEP.js.map} +0 -0
- /package/dist/{http-V7PZD7LE.js.map → http-DJSGEGD6.js.map} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
envOverriddenSecretPaths,
|
|
3
4
|
getGlobalConfigPath,
|
|
4
5
|
loadConfig,
|
|
5
6
|
loadJsonFile,
|
|
@@ -9,9 +10,10 @@ import {
|
|
|
9
10
|
setRepoConfigValue,
|
|
10
11
|
writeGlobalConfig,
|
|
11
12
|
writeRepoConfig
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WDWAPTVC.js";
|
|
13
14
|
import "./chunk-HRNOAQDN.js";
|
|
14
15
|
export {
|
|
16
|
+
envOverriddenSecretPaths,
|
|
15
17
|
getGlobalConfigPath,
|
|
16
18
|
loadConfig,
|
|
17
19
|
loadJsonFile,
|
|
@@ -22,4 +24,4 @@ export {
|
|
|
22
24
|
writeGlobalConfig,
|
|
23
25
|
writeRepoConfig
|
|
24
26
|
};
|
|
25
|
-
//# sourceMappingURL=config-
|
|
27
|
+
//# sourceMappingURL=config-5LC7ZTEP.js.map
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HttpClient,
|
|
4
4
|
createHttpClient
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4FR4WGNR.js";
|
|
6
6
|
import "./chunk-BOFSNQQ2.js";
|
|
7
7
|
import "./chunk-HRNOAQDN.js";
|
|
8
8
|
export {
|
|
9
9
|
HttpClient,
|
|
10
10
|
createHttpClient
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=http-
|
|
12
|
+
//# sourceMappingURL=http-DJSGEGD6.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolatts/pncli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "The Paperwork Nightmare CLI — structured JSON access to Jira, Bitbucket, GitHub, Confluence, Azure DevOps, SonarQube, Jenkins, and more, built for AI coding agents. Connectivity without MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/skills/pncli/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
services: config
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
pncli gives AI agents and humans unified CLI access to enterprise tools: Jira, Bitbucket, GitHub, Confluence, SonarQube, SDElements, Azure DevOps, Jenkins, Artifactory, Checkmarx, Contrast Security IAST, Sonatype IQ Server, OpenShift / Kubernetes, Dynatrace, LogScale, Split.IO, Figma, and
|
|
12
|
+
pncli gives AI agents and humans unified CLI access to enterprise tools: Jira, Bitbucket, GitHub, Confluence, SonarQube, SDElements, Azure DevOps, Jenkins, Artifactory, Checkmarx, Contrast Security IAST, Sonatype IQ Server, OpenShift / Kubernetes, Dynatrace, LogScale, Split.IO, Figma, Alation, and Sauce Labs.
|
|
13
13
|
|
|
14
14
|
Every service authenticates with a long-lived credential you generate once in that tool's own UI and put in an env var or the config file — for almost all of them a personal access token that goes straight into a header. Alation is the exception: you configure its refresh token, and pncli exchanges it for short-lived API tokens on every run without any interaction. If a tool you need is missing from the table below, it is not out of scope by default — pncli covers enterprise tooling broadly, and the only hard requirement is a credential you can generate once with no browser or interactive step at use time.
|
|
15
15
|
|
|
@@ -74,9 +74,22 @@ network stay direct while SaaS ones route out through the proxy. If a proxy
|
|
|
74
74
|
variable is set but the proxy cannot be configured, pncli warns on stderr rather
|
|
75
75
|
than silently bypassing it.
|
|
76
76
|
|
|
77
|
-
TLS verification is **
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
TLS certificate verification is always **on**, and pncli trusts the OS
|
|
78
|
+
certificate store alongside Node's bundled CAs. If your IT department has
|
|
79
|
+
installed the SSL-inspecting proxy's root certificate on your machine (the usual
|
|
80
|
+
case: it is what makes your browser work), pncli trusts it too, with nothing to
|
|
81
|
+
set. If a certificate still fails, pncli's error says why and how to fix it:
|
|
82
|
+
|
|
83
|
+
- **Untrusted certificate** — install the proxy's or internal CA's root in the
|
|
84
|
+
OS store, or point `NODE_EXTRA_CA_CERTS=/path/to/ca.pem` at it. For a
|
|
85
|
+
self-signed server, export its certificate from the browser and point
|
|
86
|
+
`NODE_EXTRA_CA_CERTS` at that file.
|
|
87
|
+
- **Hostname mismatch** — use the host name the certificate was issued for in
|
|
88
|
+
`baseUrl`, usually the fully qualified name rather than a short alias or IP.
|
|
89
|
+
|
|
90
|
+
Set `NODE_USE_SYSTEM_CA=0` to trust only Node's bundled CAs. (Earlier versions
|
|
91
|
+
disabled verification by default and used `PNCLI_VERIFY_TLS=1` to opt back in;
|
|
92
|
+
that variable is no longer read.)
|
|
80
93
|
|
|
81
94
|
## Large text fields (descriptions, acceptance criteria)
|
|
82
95
|
|
|
@@ -106,8 +119,10 @@ Each service has its own file in this skill with the config keys and example val
|
|
|
106
119
|
| LogScale | `logscale.md` | Log queries, repository listing |
|
|
107
120
|
| Split.IO | `splitio.md` | Feature flag discovery, targeting updates, Change Requests |
|
|
108
121
|
| Figma | `figma.md` | Design files, comments, version history |
|
|
122
|
+
| Sauce Labs | `saucelabs.md` | Test jobs and builds, real-device inventory and availability, device sessions, Sauce Connect tunnels |
|
|
109
123
|
| Alation | `alation.md` | Data catalog metadata (data sources, schemas, tables, columns), search, Document Hubs |
|
|
110
124
|
| Skills Marketplace | `marketplace.md` | Org plugins and shipped AGENTS.md / CLAUDE.md from git marketplaces |
|
|
125
|
+
| Skills guide | `skills-guide.md` | How skills management fits together: sources, agent hosts, scopes, sync, private-repo auth, OS keychain |
|
|
111
126
|
|
|
112
127
|
## Installing skills
|
|
113
128
|
|
|
@@ -115,7 +130,7 @@ The skills bundled with pncli install into a repo with `pncli skills install` (d
|
|
|
115
130
|
|
|
116
131
|
Installed skills are a copy — after upgrading pncli, re-run `pncli skills install` to refresh them. `skills list` and `skills status` warn when the installed copy came from a different pncli version.
|
|
117
132
|
|
|
118
|
-
Org-internal plugins come from a git-hosted marketplace: `pncli skills marketplace add <git-clone-url> --all-agents` registers one and installs every plugin into all three agent hosts, and `pncli skills marketplace sync --marketplace all --all-agents` keeps them current. A marketplace can also ship an `instructions/AGENTS.md` and `instructions/CLAUDE.md`; `add` and `sync` merge those into each agent's user-level instructions file (`~/.codex/AGENTS.md`, `~/.copilot/copilot-instructions.md`, `~/.claude/CLAUDE.md`) as a marked block that leaves your own content untouched — `pncli skills marketplace instructions list|install|remove` manages them. `pncli skills status`, `pncli skills locations`, and `pncli doctor` show what is installed and where. The full workflow is in the `marketplace.md` file that ships inside the installed skill, and `pncli skills marketplace --help` summarises it.
|
|
133
|
+
Org-internal plugins come from a git-hosted marketplace: `pncli skills marketplace add <git-clone-url> --all-agents` registers one and installs every plugin into all three agent hosts, and `pncli skills marketplace sync --marketplace all --all-agents` keeps them current. A marketplace can also ship an `instructions/AGENTS.md` and `instructions/CLAUDE.md`; `add` and `sync` merge those into each agent's user-level instructions file (`~/.codex/AGENTS.md`, `~/.copilot/copilot-instructions.md`, `~/.claude/CLAUDE.md`) as a marked block that leaves your own content untouched — `pncli skills marketplace instructions list|install|remove` manages them. `pncli skills status`, `pncli skills locations`, and `pncli doctor` show what is installed and where. The full workflow is in the `marketplace.md` file that ships inside the installed skill, and `pncli skills marketplace --help` summarises it. For the concepts behind all of this — where skills come from, how they stay current, and how private marketplaces authenticate — run `pncli skills guide` (or read `skills-guide.md`).
|
|
119
134
|
|
|
120
135
|
## Setup walkthrough
|
|
121
136
|
|
|
@@ -159,10 +174,14 @@ Review results. If any service shows `ok: false`, help troubleshoot the URL or c
|
|
|
159
174
|
pncli config show
|
|
160
175
|
```
|
|
161
176
|
|
|
177
|
+
**Keeping tokens out of plaintext** — `pncli config keychain migrate` moves every secret in `~/.pncli/config.json` into the OS keychain (macOS Keychain, Windows Credential Manager, Secret Service) and leaves `keychain:` references in their place; `--to config` moves them back. `PNCLI_*` environment variables still take precedence.
|
|
178
|
+
|
|
162
179
|
**Troubleshooting** — when any command fails unexpectedly, run:
|
|
163
180
|
|
|
164
181
|
```
|
|
165
182
|
pncli doctor
|
|
166
183
|
```
|
|
167
184
|
|
|
168
|
-
It reports config-file health, credential validity per service, and skill install state (including stale skills) in one JSON envelope, with a `problems` array listing suggested fixes. Add `--offline` to skip the network checks.
|
|
185
|
+
It reports config-file health, credential validity per service, keychain references that do not resolve, git authentication for marketplace hosts (GitHub token scope, expiry, and SSO authorization), and skill install state (including stale skills) in one JSON envelope, with a `problems` array listing suggested fixes. Add `--offline` to skip the network checks.
|
|
186
|
+
|
|
187
|
+
When a clone fails with "repository not found" or an auth error, `pncli git credentials inspect` shows which credential git actually uses for each remote (after `insteadOf` rewriting, or from Git Credential Manager / the `gh` CLI / the OS keychain), and whether it can read the repo; `--scan <dir>` checks every clone under a folder, and `pncli git credentials stored` lists what the credential stores hold. See `github.md`.
|
package/skills/pncli/ado.md
CHANGED
|
@@ -51,3 +51,7 @@ pncli ado work schema --example-only > wi.json
|
|
|
51
51
|
pncli ado work create --type Bug --input-file wi.json
|
|
52
52
|
pncli ado work update --id 123 --input-file wi.json --field Priority=1 # flag wins, and it's reported
|
|
53
53
|
```
|
|
54
|
+
|
|
55
|
+
## Skills marketplaces on this host
|
|
56
|
+
|
|
57
|
+
A skills marketplace repository on this host (see `marketplace.md`) with no `--token` of its own uses `ado.pat` (`PNCLI_ADO_PAT`) for clone and pull, and for `pncli skills git-auth`. The PAT needs the Code (Read) scope.
|
|
@@ -38,3 +38,7 @@ pncli bitbucket --project MYPROJ create-repo --name my-new-repo
|
|
|
38
38
|
# With a description and explicit project flag
|
|
39
39
|
pncli bitbucket create-repo --project MYPROJ --name my-new-repo --description "My project"
|
|
40
40
|
```
|
|
41
|
+
|
|
42
|
+
## Skills marketplaces on this host
|
|
43
|
+
|
|
44
|
+
A skills marketplace repository on this host (see `marketplace.md`) with no `--token` of its own uses `bitbucket.pat` (`PNCLI_BITBUCKET_PAT`) for clone and pull, and for `pncli skills git-auth`. Bitbucket Data Center personal access tokens are usually sent with your Bitbucket username: add the marketplace with `--username <you>`, or set it later with `pncli skills marketplace update <name> --username <you>`.
|
package/skills/pncli/github.md
CHANGED
|
@@ -65,3 +65,40 @@ pncli github resolve-thread --thread-id PRT_kwDOHfWCIM4APCA
|
|
|
65
65
|
`list-review-threads` returns the first 100 threads with their resolution status,
|
|
66
66
|
file path/line, and first 10 comments. If a PR has more than 100 threads, a warning
|
|
67
67
|
is written to stderr.
|
|
68
|
+
|
|
69
|
+
## Which token does a clone use? (`pncli git credentials`)
|
|
70
|
+
|
|
71
|
+
Classic-PAT setups usually put the token in a gitconfig rewrite such as
|
|
72
|
+
`url."https://<classic-PAT>@github.com/acme/".insteadOf "https://github.com/acme/"`, while Git Credential
|
|
73
|
+
Manager, the `gh` CLI, or the Windows Credential Manager may hold other tokens for the same host. A repo outside
|
|
74
|
+
every mapping silently clones anonymously and fails as "repository not found"; a mapped token may be valid but
|
|
75
|
+
unable to see a particular repo; a stored token may be shadowed by another helper. These commands work on any
|
|
76
|
+
clone, not just skills marketplaces, and never print a token — only its type and a fingerprint (`ghp_…a1b2`):
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
pncli git credentials inspect # every remote of the current clone
|
|
80
|
+
pncli git credentials inspect ~/src/tools --remote upstream
|
|
81
|
+
pncli git credentials inspect https://github.com/acme/tools.git
|
|
82
|
+
pncli git credentials inspect --scan ~/src --problems-only # clones that are unmapped or lack access
|
|
83
|
+
pncli git credentials mappings # every insteadOf rewrite and URL-scoped helper, tokens checked
|
|
84
|
+
pncli git credentials stored # what the credential stores hold, and what git actually sends
|
|
85
|
+
pncli git credentials forget --host github.com --username octo # drop a stale stored credential
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **`inspect`** — per remote: the URL as configured, the URL git uses after `insteadOf` rewriting, the mapping
|
|
89
|
+
that applied, and where the credential comes from (`insteadOf`, the `url` itself, a `helper`, or `none`). For a
|
|
90
|
+
helper it names the kind — Git Credential Manager, wincred, osxkeychain, libsecret, store, `gh`, pncli — and
|
|
91
|
+
GCM's backing store (`wincredman`, `dpapi`, `keychain`, `secretservice`, …). Online it runs an authenticated
|
|
92
|
+
`git ls-remote`, and for GitHub reports the account, a classic token missing `repo`, SAML SSO authorization,
|
|
93
|
+
expiry, and whether the token can see **this** repository — each problem with a concrete fix, such as the exact
|
|
94
|
+
`insteadOf` line to add for an unmapped org.
|
|
95
|
+
- **`stored`** — the Windows Credential Manager's `git:` entries (Git Credential Manager and wincred), the plaintext
|
|
96
|
+
`~/.git-credentials` file, and the macOS Keychain's git entries (metadata only), plus what git actually sends per
|
|
97
|
+
host and per stored path. Flags stored tokens that GitHub rejects or that expire soon, several accounts for one
|
|
98
|
+
host, plaintext storage, and **shadowed** entries — a stored credential git never sends because another helper
|
|
99
|
+
(typically `gh auth setup-git`) answers first.
|
|
100
|
+
- **`forget`** — `git credential reject` for one host (and optionally account or path), which every helper
|
|
101
|
+
implements; it reports the fingerprint before and after. A shadowed Windows entry cannot be reached this way —
|
|
102
|
+
`stored` gives the exact `cmdkey /delete:` command instead.
|
|
103
|
+
|
|
104
|
+
`--offline` skips every network check.
|
package/skills/pncli/jira.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Jira
|
|
2
2
|
|
|
3
|
-
Enables: `pncli jira get-issue`, `create-issue`, `update-issue`, `search`, `list-boards`, `list-sprints`, `set-sprint`, and more — get, create, and update issues, transitions, comments, attachments, custom fields, and
|
|
3
|
+
Enables: `pncli jira get-issue`, `create-issue`, `update-issue`, `search`, `list-boards`, `list-sprints`, `set-sprint`, `log-work`, and more — get, create, and update issues, transitions, comments, attachments, custom fields, sprints, and worklogs.
|
|
4
4
|
|
|
5
5
|
## Required config
|
|
6
6
|
|
|
@@ -42,6 +42,17 @@ pncli jira set-sprint --key ACME-123 --sprint <sprint-id>
|
|
|
42
42
|
|
|
43
43
|
`list-sprints` output includes `startDate`/`endDate`/`state`/`goal` for each sprint.
|
|
44
44
|
|
|
45
|
+
## Worklogs
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
pncli jira log-work --key ACME-123 --time-spent "2h 30m" --comment "Investigated the failing build"
|
|
49
|
+
pncli jira log-work --key ACME-123 --time-spent "1d" --started 2024-01-15T09:00:00.000+0000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--time-spent` uses Jira's own duration format (`1d`, `2h 30m`, `45m`, ...). `--started` takes
|
|
53
|
+
Jira's datetime format (`yyyy-MM-dd'T'HH:mm:ss.SSSZ`, e.g. `2024-01-15T09:00:00.000+0000`) and
|
|
54
|
+
defaults to now when omitted.
|
|
55
|
+
|
|
45
56
|
## Custom fields
|
|
46
57
|
|
|
47
58
|
Register a custom field once so `--field <Name>=value` and `--input-file` can address it by
|
|
@@ -60,6 +71,36 @@ mangle nested double quotes inside a single-quoted argument, which silently stor
|
|
|
60
71
|
value. `pncli jira fields` prints what's currently registered; `pncli jira fields --discover`
|
|
61
72
|
fetches field metadata straight from the Jira API instead.
|
|
62
73
|
|
|
74
|
+
Registration is only needed to address a field by **friendly name**, or to get automatic
|
|
75
|
+
value shaping from `type`. Both `--field` and `--fields-file` accept an unregistered raw
|
|
76
|
+
Jira field id or name directly (e.g. `--field fixVersions=@versions.json`,
|
|
77
|
+
`{"fixVersions": [...]}` in a `--fields-file` JSON file) — nothing needs to be pre-registered
|
|
78
|
+
just to use a standard field like `fixVersions` or a custom field you already know the
|
|
79
|
+
`customfield_NNNNN` id for. Registration only fails for a key that still has whitespace in
|
|
80
|
+
it and isn't a registered friendly name — that's almost always a typo.
|
|
81
|
+
|
|
82
|
+
Some select-type fields (a "Crew" picker, for example) reject the display text you see in
|
|
83
|
+
Jira's UI and only accept the option's numeric key. Run `pncli jira fields --discover
|
|
84
|
+
--project <key>` to see each field's `allowedValues`; if a field's values are `{id, value}`
|
|
85
|
+
pairs rather than plain strings, register it with `"type":"option-id"` and pass the numeric
|
|
86
|
+
`id`, not the display text. **Sprint** is not a custom field at all — don't try to set it
|
|
87
|
+
via `--field` or `--fields-file`; use `pncli jira set-sprint --key <key> --sprint <id>` after
|
|
88
|
+
the issue exists, with the id from `pncli jira list-sprints`.
|
|
89
|
+
|
|
90
|
+
### PowerShell-safe JSON values
|
|
91
|
+
|
|
92
|
+
Passing JSON inline in `--field Name={"a":1}` is unreliable in PowerShell — its argument
|
|
93
|
+
parser mangles embedded quotes before pncli ever sees them. Use the `@file` form instead,
|
|
94
|
+
which sidesteps shell quoting entirely:
|
|
95
|
+
|
|
96
|
+
```powershell
|
|
97
|
+
'{"steps":[{"action":"click"}]}' | Out-File -Encoding utf8 steps.json
|
|
98
|
+
pncli jira create-issue --project ACME --summary "..." --field "Test Steps=@steps.json"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This also applies to `--jql` and any other flag that would otherwise need inline JSON or
|
|
102
|
+
nested quotes on Windows.
|
|
103
|
+
|
|
63
104
|
## Large fields via --input-file
|
|
64
105
|
|
|
65
106
|
`create-issue` and `update-issue` accept `--input-file <path>` (`-` for stdin) instead of, or alongside, individual flags — useful for a long description or many custom fields at once. Run `pncli jira schema` to print the JSON Schema plus a runnable example. Any string value in `fields` may be `@path/to/file` to pull that field's content from a file (e.g. a big HTML description) instead of inlining it. Custom fields resolve by friendly name (if registered — see **Custom fields** above) or by raw id (`customfield_10032`) with no registration required. Individual flags (`--summary`, `--description`, `--field`, ...) override matching keys from the file; overridden keys are printed to stderr and included in the output's `meta.overrides`.
|
|
@@ -79,3 +120,6 @@ pncli jira create-issue --input-file issue.json --priority Low # --priority wi
|
|
|
79
120
|
- `--assignee` on `create-issue`, `update-issue`, and `assign` takes a **username**, as does
|
|
80
121
|
any `user`-typed custom field passed via `--field`
|
|
81
122
|
- Custom fields discovered with `pncli jira fields --discover`
|
|
123
|
+
- `create-issue` does not check for duplicates before submitting. If a request times out or
|
|
124
|
+
the response is otherwise ambiguous, run `pncli jira search` for the exact summary before
|
|
125
|
+
retrying — a timeout does not tell you whether the issue was actually created.
|
|
@@ -27,7 +27,59 @@ pncli skills marketplace add https://bitbucket.imagile.dev/scm/ai/skills.git --n
|
|
|
27
27
|
|
|
28
28
|
You can register as many marketplaces as you like — just run `add` again with a different URL. `marketplace setup` is kept as an alias of `add` for backward compatibility.
|
|
29
29
|
|
|
30
|
-
For a private repo, pass `--token <token>` (a Bitbucket or GitHub access token) — it's stored with that marketplace's entry and
|
|
30
|
+
For a private repo, pass `--token <token>` (a Bitbucket or GitHub access token) — it's stored with that marketplace's entry and supplied to git for each clone and pull; it is not left in the clone's `.git/config`. For a marketplace hosted on `github.com` (or the host configured as `github.baseUrl`), you can omit `--token` if you already have a working GitHub credential configured (`PNCLI_GITHUB_TOKEN`, `GITHUB_TOKEN`, or `pncli config set github.token`) — `marketplace add` and `marketplace sync` fall back to it automatically. An explicit `--token` on the marketplace always takes priority over that fallback. To rotate a marketplace's own token, re-run `marketplace add <url> --token <new-token>` (repeat `--keychain` if the old one was stored there, or use `pncli config keychain set marketplaces.<name>.token`); a rejected or expired token surfaces as a clear error naming the marketplace rather than raw git output.
|
|
31
|
+
|
|
32
|
+
Add `--keychain` to store the token in the OS keychain (macOS Keychain, Windows Credential Manager, Secret Service) instead of plaintext config; the marketplace entry then holds a `keychain:marketplaces.<name>.token` reference.
|
|
33
|
+
|
|
34
|
+
### Providers: GitHub, Bitbucket, Azure DevOps
|
|
35
|
+
|
|
36
|
+
A marketplace can live on any HTTPS git host. pncli detects the provider from your configured service hosts and the URL shape (`/_git/` is Azure DevOps, `/scm/` is Bitbucket Data Center), and uses it to pick a fallback token when the marketplace has none of its own:
|
|
37
|
+
|
|
38
|
+
| Provider | Example clone URL | Fallback token (when no `--token`) |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| GitHub / GitHub Enterprise | `https://ghe.imagile.dev/ai/skills.git` | `PNCLI_GITHUB_TOKEN` / `GITHUB_TOKEN` / `github.token` (github.com or the host of `github.baseUrl`) |
|
|
41
|
+
| Bitbucket Data Center | `https://bitbucket.imagile.dev/scm/ai/skills.git` | `PNCLI_BITBUCKET_PAT` / `bitbucket.pat` (the host of `bitbucket.baseUrl`) |
|
|
42
|
+
| Azure DevOps Server | `https://ado.imagile.dev/DefaultCollection/Platform/_git/skills` | `PNCLI_ADO_PAT` / `SYSTEM_ACCESSTOKEN` / `ado.pat` (the host of `ado.baseUrl`) |
|
|
43
|
+
| anything else | `https://git.imagile.dev/ai/skills.git` | none; pass `--token` |
|
|
44
|
+
|
|
45
|
+
Each marketplace keeps its own `--token`, so marketplaces on the same host (or on different providers) can use different secrets. Pass `--provider github|bitbucket|ado|git` if detection guesses wrong.
|
|
46
|
+
|
|
47
|
+
The token is sent with a username: `x-access-token` on github.com, `x-token-auth` everywhere else (unchanged from earlier versions; Azure DevOps accepts any username with a PAT). **Bitbucket Data Center personal access tokens are usually sent with your Bitbucket username** — set it per marketplace:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pncli skills marketplace add https://bitbucket.imagile.dev/scm/ai/skills.git --token <token> --username jdoe --all-agents
|
|
51
|
+
pncli skills marketplace update internal-ai --username jdoe # for one you already added
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Rotate a token or change credentials (`update`)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
pncli skills marketplace update internal-ai --token <new-token> [--keychain]
|
|
58
|
+
pncli skills marketplace update internal-ai --username jdoe
|
|
59
|
+
pncli skills marketplace update internal-ai --clear-token # use the provider fallback instead
|
|
60
|
+
pncli skills marketplace update internal-ai --provider bitbucket
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`update` changes the stored entry only — no re-clone, no reinstall. A keychain entry the marketplace no longer points at is deleted.
|
|
64
|
+
|
|
65
|
+
### Let git itself authenticate (`git-auth`)
|
|
66
|
+
|
|
67
|
+
pncli's own clone and pull are covered above, but an agent host that clones a marketplace itself (e.g. Claude Code's `/plugin marketplace add`) runs plain `git`, which has no credential for it. Give it one:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
pncli skills git-auth enable # every marketplace that has a credential
|
|
71
|
+
pncli skills git-auth enable --marketplace internal-ai --mode keychain
|
|
72
|
+
pncli skills git-auth status # per marketplace: mode, username, token source, match
|
|
73
|
+
pncli skills git-auth disable [--marketplace internal-ai] [--forget-keychain]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Entries are **scoped to each marketplace's repository URL** (with and without `.git`), for example `credential.https://bitbucket.imagile.dev/scm/ai/skills.git.helper`. git consults them only for that repository, so every other repo on the same host keeps using your own login, untouched.
|
|
77
|
+
- `--mode helper` (default) writes an empty reset entry, `!pncli skills git-credential --marketplace '<name>'`, then whatever helpers git used for that repo before (the host's `gh auth setup-git` entry, Git Credential Manager, osxkeychain). pncli answers with that marketplace's own credential — the same username and token its clone and pull use — resolved from env → config → keychain, so nothing is copied into gitconfig and rotation needs no re-run. If pncli cannot answer, git falls through to your helpers as before.
|
|
78
|
+
- `--mode keychain` stores the credential in git's own credential store via `git credential approve`, keyed to the repository (a repo-scoped `useHttpPath`). It works where pncli is not on git's `PATH`, but must be re-run after rotating the token; `pncli doctor` detects the stale copy.
|
|
79
|
+
- `--host <host>` writes a whole-host entry instead, answered with the provider token pncli has for that host. Use it deliberately: it puts that token in front of every repo on the host.
|
|
80
|
+
- `disable` restores exactly what `enable` replaced; pncli records it under `gitAuth.scopes` in its global config. `enable` also rewrites any clone whose `origin` still carries an embedded token.
|
|
81
|
+
|
|
82
|
+
`pncli doctor` reports a `gitAuth` entry per marketplace: provider, mode, username, token source, whether git's credential matches pncli's, and clones with embedded tokens. Online, it runs an authenticated `git ls-remote` with exactly that username and token — which catches a wrong Bitbucket username as reliably as an expired token — and, for GitHub, reports the token kind (classic `ghp_` vs fine-grained), a missing `repo` scope, SSO authorization, and expiry within 14 days.
|
|
31
83
|
|
|
32
84
|
If you upgrade pncli from a version that only supported a single marketplace, your existing config is migrated to the multi-marketplace format automatically the first time you run any `marketplace` command — no manual steps required.
|
|
33
85
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Sauce Labs
|
|
2
|
+
|
|
3
|
+
pncli calls the Sauce Labs REST API directly with your username and access key; no `saucectl`, Sauce Connect, or other CLI is required.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
| Key | Environment variable | CI fallback | Purpose |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| `saucelabs.baseUrl` | `PNCLI_SAUCELABS_BASE_URL` | — | API endpoint for your data center (see below) |
|
|
10
|
+
| `saucelabs.username` | `PNCLI_SAUCELABS_USERNAME` | `SAUCE_USERNAME` | Your Sauce Labs username |
|
|
11
|
+
| `saucelabs.accessKey` | `PNCLI_SAUCELABS_ACCESS_KEY` | `SAUCE_ACCESS_KEY` | Your access key |
|
|
12
|
+
|
|
13
|
+
Find both the username and the access key in Sauce Labs under **Account → User Settings**. The access key is long-lived; pncli sends it with the username as HTTP Basic auth.
|
|
14
|
+
|
|
15
|
+
`baseUrl` depends on the data center your account lives in:
|
|
16
|
+
|
|
17
|
+
| Data center | `baseUrl` |
|
|
18
|
+
|---|---|
|
|
19
|
+
| US West | `https://api.us-west-1.saucelabs.com` |
|
|
20
|
+
| US East | `https://api.us-east-4.saucelabs.com` (real devices only — no virtual-device jobs) |
|
|
21
|
+
| EU Central | `https://api.eu-central-1.saucelabs.com` |
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pncli config set saucelabs.baseUrl https://api.us-west-1.saucelabs.com
|
|
25
|
+
pncli config set saucelabs.username <your-username>
|
|
26
|
+
pncli config set saucelabs.accessKey <your-access-key>
|
|
27
|
+
pncli config test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or with environment variables:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
export PNCLI_SAUCELABS_BASE_URL=https://api.us-west-1.saucelabs.com
|
|
34
|
+
export PNCLI_SAUCELABS_USERNAME=<your-username>
|
|
35
|
+
export PNCLI_SAUCELABS_ACCESS_KEY=<your-access-key>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**CI:** `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` are the names `saucectl`, Sauce Connect, and Sauce's CI integrations already use, so a pipeline that sets them needs only `PNCLI_SAUCELABS_BASE_URL`. Precedence for the username and access key is `PNCLI_SAUCELABS_*` → `SAUCE_*` → `.pncli.json` / `~/.pncli/config.json`.
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
### Account and platform
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pncli saucelabs status # Is Sauce Labs operational? Current wait time
|
|
46
|
+
pncli saucelabs concurrency # Allowed vs. in-use VMs and real devices (also verifies credentials)
|
|
47
|
+
pncli saucelabs platforms --api appium # Supported platforms: all | appium | webdriver
|
|
48
|
+
pncli saucelabs appium-versions # Appium versions for real-device sessions, with EOL dates
|
|
49
|
+
pncli saucelabs users --phrase jane # Look up users in your organization
|
|
50
|
+
pncli saucelabs team list
|
|
51
|
+
pncli saucelabs team get <team-id>
|
|
52
|
+
pncli saucelabs team members <team-id>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Jobs (virtual devices and desktop browsers)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pncli saucelabs job list --limit 20 --from 2026-09-01T00:00:00Z
|
|
59
|
+
pncli saucelabs job get <job-id> # Status, platform, timings, log and video URLs
|
|
60
|
+
pncli saucelabs job assets <job-id> # Asset file names (logs, video, screenshots)
|
|
61
|
+
pncli saucelabs job update <job-id> --passed --build release-42 --tags smoke,email
|
|
62
|
+
pncli saucelabs job update <job-id> --public team # public | public restricted | share | team | private
|
|
63
|
+
pncli saucelabs job stop <job-id>
|
|
64
|
+
pncli saucelabs job delete <job-id>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`--from` / `--to` accept Unix seconds or an ISO 8601 date. `--tags` replaces the job's existing tags.
|
|
68
|
+
|
|
69
|
+
### Real-device jobs
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pncli saucelabs rdc-job list --limit 20
|
|
73
|
+
pncli saucelabs rdc-job list --live # Manual (live) tests only
|
|
74
|
+
pncli saucelabs rdc-job get <job-id> # Device, result, timings, device/network/crash log URLs
|
|
75
|
+
pncli saucelabs rdc-job update <job-id> --failed --name "Gmail rendering"
|
|
76
|
+
pncli saucelabs rdc-job stop <job-id>
|
|
77
|
+
pncli saucelabs rdc-job delete <job-id>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Builds
|
|
81
|
+
|
|
82
|
+
`--source` is `vdc` (virtual devices, the default) or `rdc` (real devices).
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pncli saucelabs build list --source rdc --status failed,error --limit 10
|
|
86
|
+
pncli saucelabs build get <build-id> --source rdc
|
|
87
|
+
pncli saucelabs build jobs <build-id> --failed # also --errored --passed --running --queued --completed --finished --faulty
|
|
88
|
+
pncli saucelabs build for-job <job-id> # Which build a job belongs to
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Real devices
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pncli saucelabs device list --os android --type phone
|
|
95
|
+
pncli saucelabs device list --name "iPhone 1[56].*" --os-version 17
|
|
96
|
+
pncli saucelabs device get iPhone_15_real # Full hardware and OS descriptor
|
|
97
|
+
pncli saucelabs device status --state available # Which devices are free right now
|
|
98
|
+
pncli saucelabs device status --private-only
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`--name` and `--os-version` are regular expressions on the Sauce side.
|
|
102
|
+
|
|
103
|
+
### Device sessions (Real Device Access API)
|
|
104
|
+
|
|
105
|
+
A session reserves a real device and lets you drive it through the API. Close it when you are done — an open session holds a device and counts against your concurrency.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Reserve a device; --wait polls until it is ACTIVE (default timeout 300s)
|
|
109
|
+
pncli saucelabs session create --device-name "Samsung Galaxy S2[34].*" --os android --wait
|
|
110
|
+
pncli saucelabs session create --os ios --duration PT30M --tunnel-name <tunnel-name> --wait
|
|
111
|
+
|
|
112
|
+
pncli saucelabs session list --state active
|
|
113
|
+
pncli saucelabs session get <session-id> # State, expiry, Appium URL, live-view link
|
|
114
|
+
|
|
115
|
+
# Drive the device
|
|
116
|
+
pncli saucelabs session open-url <session-id> https://preview.imagile.dev/welcome-email
|
|
117
|
+
pncli saucelabs session shell <session-id> "getprop ro.build.version.release" # Android only
|
|
118
|
+
pncli saucelabs session install-app <session-id> storage:filename=app.apk --launch
|
|
119
|
+
pncli saucelabs session installations <session-id>
|
|
120
|
+
pncli saucelabs session launch-app <session-id> --package-name com.google.android.gm
|
|
121
|
+
pncli saucelabs session launch-app <session-id> --bundle-id com.apple.mobilesafari
|
|
122
|
+
pncli saucelabs session uninstall-app <session-id> --package-name <package>
|
|
123
|
+
pncli saucelabs session settings <session-id> --orientation landscape --locale de_DE
|
|
124
|
+
|
|
125
|
+
# Record a test (a job) inside the session
|
|
126
|
+
pncli saucelabs session start-test <session-id> --name "Welcome email" --build release-42 --video --device-logs
|
|
127
|
+
pncli saucelabs session end-test <session-id> --passed
|
|
128
|
+
pncli saucelabs session tests <session-id>
|
|
129
|
+
|
|
130
|
+
# Network throttling
|
|
131
|
+
pncli saucelabs session network-profiles <session-id>
|
|
132
|
+
pncli saucelabs session network <session-id> --profile 4G-fast
|
|
133
|
+
pncli saucelabs session network <session-id> --download 1500 --upload 750 --latency 300 --loss 1
|
|
134
|
+
pncli saucelabs session network <session-id> --reset
|
|
135
|
+
|
|
136
|
+
# Appium
|
|
137
|
+
pncli saucelabs session appium <session-id>
|
|
138
|
+
pncli saucelabs session appium <session-id> --start --appium-version <version>
|
|
139
|
+
|
|
140
|
+
# Release the device
|
|
141
|
+
pncli saucelabs session delete <session-id>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Limits Sauce Labs enforces, not pncli:
|
|
145
|
+
|
|
146
|
+
- Sessions default to 6 hours. Public devices cap at 1 hour, private devices at 24 hours; longer `--duration` values are capped rather than rejected.
|
|
147
|
+
- `session shell` works on Android only, and public devices accept only an allowlisted set of commands.
|
|
148
|
+
- `session settings --locale` and `--animations` are Android only; `--orientation` works on both.
|
|
149
|
+
- A `409` on `session create` means your concurrency limit is reached — check `pncli saucelabs concurrency`.
|
|
150
|
+
|
|
151
|
+
### Sauce Connect tunnels
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pncli saucelabs tunnel list # Full details for your tunnels
|
|
155
|
+
pncli saucelabs tunnel list --all # Include tunnels shared with you
|
|
156
|
+
pncli saucelabs tunnel get <tunnel-id>
|
|
157
|
+
pncli saucelabs tunnel jobs <tunnel-id> # Jobs currently running through it
|
|
158
|
+
pncli saucelabs tunnel stop <tunnel-id>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### App storage
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pncli saucelabs storage files --kind android --query gmail
|
|
165
|
+
pncli saucelabs storage groups --kind ios
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Not supported
|
|
169
|
+
|
|
170
|
+
pncli returns JSON from API calls; it does not handle binary content or visual comparison.
|
|
171
|
+
|
|
172
|
+
- Downloading job assets (videos, logs, screenshots), device screenshots, and pulling or pushing files on a device. `job assets` and the `*_url` fields on jobs give you the links.
|
|
173
|
+
- Uploading apps to app storage.
|
|
174
|
+
- Judging how something renders. To check an email on real devices, reserve a session, `open-url` the email's web view or `launch-app` the mail client, record it with `start-test --video --screenshots`, and review the recording through the job's `video_url` or the session's `liveViewUrl`.
|
|
175
|
+
- Starting a Sauce Connect tunnel. Run Sauce Connect itself, then reference the tunnel with `--tunnel-name`.
|