@chainpatrol/cli 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/{breakdown-BECXDJIS.js → breakdown-FFNYSXRQ.js} +2 -2
  3. package/dist/chunk-EBJMOX3Q.js +83 -0
  4. package/dist/{chunk-T4DYUWUD.js → chunk-R7BPW32M.js} +68 -4
  5. package/dist/{chunk-B3CCMSG2.js → chunk-WBKCXGLV.js} +1 -1
  6. package/dist/cli.js +24 -21
  7. package/dist/{configs-update-AHI7T6S4.js → configs-update-VOWH674W.js} +2 -2
  8. package/dist/{create-RS37JAMM.js → create-ASGUBKZ7.js} +2 -2
  9. package/dist/{drift-2JRZK2MC.js → drift-PASGDEEX.js} +2 -2
  10. package/dist/{found-YYYFF4FD.js → found-TIW7T4VX.js} +2 -2
  11. package/dist/{healthcheck-6RHZBEVE.js → healthcheck-SIE5EXN3.js} +2 -2
  12. package/dist/{list-MHEIY7LQ.js → list-CEWBMKJ3.js} +2 -2
  13. package/dist/{list-M2UQCXIO.js → list-KTIZ3UHA.js} +3 -3
  14. package/dist/{list-56BS25X4.js → list-TMFLCS5U.js} +4 -4
  15. package/dist/{list-json-GANUT7SB.js → list-json-SUZL2GBJ.js} +2 -2
  16. package/dist/{login-G7LPHKDR.js → login-C66GRR3Y.js} +30 -8
  17. package/dist/login-json-XGMXT5VJ.js +50 -0
  18. package/dist/login-plain-CWKOUZKE.js +60 -0
  19. package/dist/{run-7NTCD7JI.js → run-3TTLZ6HA.js} +3 -3
  20. package/dist/{run-JZNZSCDJ.js → run-MGOASUON.js} +2 -2
  21. package/dist/{setup-skill-ZUZ5MYLI.js → setup-skill-KKWETU4A.js} +1 -1
  22. package/dist/{snapshot-KGMO44YB.js → snapshot-MQ6DWDFG.js} +2 -2
  23. package/dist/{summary-PA2CCZVO.js → summary-QORQFCMW.js} +2 -2
  24. package/dist/{validate-NIBVL7X5.js → validate-II6GH6H6.js} +2 -2
  25. package/package.json +1 -1
  26. package/dist/login-json-LKB72OFY.js +0 -71
  27. package/dist/{chunk-DSOM6TZX.js → chunk-44FSS3CZ.js} +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 415dbcd: `chainpatrol login`:
8
+
9
+ - Auto-detect non-TTY stdout (Claude Code on the web, CI runners, piped
10
+ invocations) and fall back to a plain-text login flow that prints the
11
+ device code and verification URL immediately. Previously the
12
+ interactive Ink-based UI rendered nothing in those environments,
13
+ making the command appear to hang with no output until the device
14
+ code expired.
15
+ - `chainpatrol --json login` now emits the device-code line on **stdout**
16
+ instead of stderr, matching the `{"status":"success",...}` line. This
17
+ makes the URL visible without callers needing to remember `2>&1`.
18
+ Consumers should treat the output as JSON-lines and read the last
19
+ line for the terminal result.
20
+
21
+ Claude Code skill bundled with the CLI:
22
+
23
+ - New "Running `login` from an agent (headless / non-TTY)" section
24
+ documenting the background+tail pattern so agents surface the
25
+ verification URL to the user immediately instead of blocking on the
26
+ polling step.
27
+
28
+ ## 0.3.2
29
+
30
+ ### Patch Changes
31
+
32
+ - 06ff227: `chainpatrol login`:
33
+
34
+ - Skip the browser auto-open when running in a headless / cloud context
35
+ (Claude Code on the web, GitHub Codespaces, Gitpod, Replit, CI, plain
36
+ SSH sessions, or Linux without a display server). Detection is opt-in
37
+ overridable with `CHAINPATROL_HEADLESS=1` / `CHAINPATROL_HEADLESS=0`.
38
+ - Print `verification_uri_complete` as the primary, one-step link when
39
+ the server returns it, so a copy-paste to another device (e.g. a
40
+ phone) works without separately typing the user code. The bare
41
+ `verification_uri` + code is shown as a fallback.
42
+ - `login --json` output now includes a `headless` boolean so automation
43
+ can adapt the way it presents the URL.
44
+
45
+ Claude Code skill bundled with the CLI:
46
+
47
+ - Stop hard-coding `/usr/local/bin/chainpatrol`. The "Running the CLI"
48
+ section now documents typical local vs cloud install locations
49
+ (e.g. `/opt/node*/bin/chainpatrol`), includes a command to discover
50
+ the binary path, and tells the assistant to substitute the resolved
51
+ full path in all invocations.
52
+
3
53
  ## 0.3.1
4
54
 
5
55
  ### Patch Changes
@@ -4,9 +4,9 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-DSOM6TZX.js";
8
- import "./chunk-TFCNKBRC.js";
7
+ } from "./chunk-44FSS3CZ.js";
9
8
  import "./chunk-EEG7T6WT.js";
9
+ import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
12
12
  // src/commands/metrics/breakdown.ts
@@ -0,0 +1,83 @@
1
+ import {
2
+ fetchUserEmail,
3
+ getCredentials,
4
+ isLoggedIn,
5
+ pollForToken,
6
+ requestDeviceCode,
7
+ storeCredentials
8
+ } from "./chunk-EEG7T6WT.js";
9
+ import {
10
+ DateTime
11
+ } from "./chunk-TFCNKBRC.js";
12
+
13
+ // src/lib/login-flow.ts
14
+ async function runLoginFlow(emit) {
15
+ if (isLoggedIn()) {
16
+ const creds = getCredentials();
17
+ emit({ type: "already_logged_in", email: creds.email ?? null });
18
+ return true;
19
+ }
20
+ const deviceCode = await requestDeviceCode();
21
+ emit({ type: "device_code", code: deviceCode });
22
+ let interval = deviceCode.interval * 1e3;
23
+ while (true) {
24
+ await new Promise((resolve) => setTimeout(resolve, interval));
25
+ const result = await pollForToken(deviceCode.device_code);
26
+ switch (result.status) {
27
+ case "success": {
28
+ const expiresAt = DateTime.now().toUTC().plus({ seconds: result.expiresIn }).toISO();
29
+ if (!expiresAt) {
30
+ emit({ type: "error", message: "Failed to compute credential expiry." });
31
+ return false;
32
+ }
33
+ const email = await fetchUserEmail(result.accessToken);
34
+ storeCredentials({
35
+ accessToken: result.accessToken,
36
+ expiresAt,
37
+ ...email ? { email } : {}
38
+ });
39
+ emit({ type: "success", email: email ?? null });
40
+ return true;
41
+ }
42
+ case "pending":
43
+ continue;
44
+ case "slow_down":
45
+ interval += result.addSeconds * 1e3;
46
+ continue;
47
+ case "expired":
48
+ emit({ type: "expired" });
49
+ return false;
50
+ case "denied":
51
+ emit({ type: "denied" });
52
+ return false;
53
+ case "error":
54
+ emit({ type: "error", message: result.message });
55
+ return false;
56
+ }
57
+ }
58
+ }
59
+ function formatUserCode(code) {
60
+ return code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
61
+ }
62
+
63
+ // src/lib/headless.ts
64
+ function isHeadlessEnv(env = process.env, platform = process.platform) {
65
+ if (env.CHAINPATROL_HEADLESS === "1") return true;
66
+ if (env.CHAINPATROL_HEADLESS === "0") return false;
67
+ if (env.CLAUDE_CODE_REMOTE === "true") return true;
68
+ if (env.CODESPACES === "true") return true;
69
+ if (env.GITPOD_WORKSPACE_ID) return true;
70
+ if (env.REPL_ID) return true;
71
+ if (env.CI === "true") return true;
72
+ if (env.SSH_CONNECTION || env.SSH_TTY) return true;
73
+ if (platform === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY && !env.MIR_SOCKET) {
74
+ return true;
75
+ }
76
+ return false;
77
+ }
78
+
79
+ export {
80
+ runLoginFlow,
81
+ formatUserCode,
82
+ isHeadlessEnv
83
+ };
@@ -97,15 +97,39 @@ platform using the CLI tool.
97
97
 
98
98
  ## Running the CLI
99
99
 
100
- IMPORTANT: Claude Code's sandbox shell has a minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin)
101
- that does NOT include /usr/local/bin. Always use the full path to the binary:
100
+ IMPORTANT: Claude Code's sandbox shell often has a minimal PATH
101
+ (\`/usr/bin:/bin:/usr/sbin:/sbin\`) that may not include the directory where
102
+ \`chainpatrol\` is installed, so bare \`chainpatrol\` calls may fail with
103
+ "command not found". Always invoke the CLI by its full path.
104
+
105
+ The install location depends on the environment:
106
+
107
+ - **Local installs** typically land at \`/usr/local/bin/chainpatrol\` (when
108
+ installed globally via \`npm install -g @chainpatrol/cli\`).
109
+ - **Cloud / sandboxed environments** (e.g. Claude Code on the web, Cursor
110
+ Cloud) often install Node into \`/opt\` and the binary ends up under a
111
+ Node-version-specific path like \`/opt/node22/bin/chainpatrol\`. Variants
112
+ such as \`/opt/node20/bin/chainpatrol\` or \`/opt/node21/bin/chainpatrol\`
113
+ are also possible depending on which Node version is active.
114
+
115
+ To find the binary, try (in order):
102
116
 
103
117
  \`\`\`bash
118
+ command -v chainpatrol \\
119
+ || ls /usr/local/bin/chainpatrol /opt/node*/bin/chainpatrol 2>/dev/null \\
120
+ | head -n 1
121
+ \`\`\`
122
+
123
+ Then use that full path for every subsequent command, e.g.:
124
+
125
+ \`\`\`bash
126
+ /opt/node22/bin/chainpatrol <command> [options]
127
+ # or
104
128
  /usr/local/bin/chainpatrol <command> [options]
105
129
  \`\`\`
106
130
 
107
- All examples below use the short name for readability, but you MUST use
108
- \`/usr/local/bin/chainpatrol\` in all Bash commands.
131
+ All examples below use the short name \`chainpatrol\` for readability, but you
132
+ MUST substitute the full resolved path in your Bash commands.
109
133
 
110
134
  ## Available Commands
111
135
 
@@ -126,6 +150,46 @@ JSON mode (for automation):
126
150
  chainpatrol --json login
127
151
  \`\`\`
128
152
 
153
+ #### Running \`login\` from an agent (headless / non-TTY)
154
+
155
+ The login flow blocks for up to 30 minutes while polling for the user to
156
+ authorize in their browser. If you (the agent) run it as a foreground
157
+ command and wait for it to exit before reading output, you will appear
158
+ stuck \u2014 the verification URL will never be shown because the process is
159
+ still polling.
160
+
161
+ **Always run \`login\` in the background and stream its output**, then
162
+ surface the verification URL to the user as soon as it appears:
163
+
164
+ \`\`\`bash
165
+ # 1. Kick off login in the background (do NOT wait for it to exit)
166
+ chainpatrol --json login > /tmp/cp-login.out 2>&1 &
167
+
168
+ # 2. Wait briefly for the first line, which contains the URL
169
+ for _ in 1 2 3 4 5 6 7 8 9 10; do
170
+ test -s /tmp/cp-login.out && break
171
+ sleep 1
172
+ done
173
+ cat /tmp/cp-login.out
174
+ \`\`\`
175
+
176
+ The first line emitted is JSON describing the device code, e.g.:
177
+
178
+ \`\`\`json
179
+ {"action":"open_url","user_code":"ABCD-1234","verification_uri":"https://app.chainpatrol.io/auth/verify-device","verification_uri_complete":"https://app.chainpatrol.io/auth/verify-device?user_code=ABCD-1234","expires_in":1800,"headless":true}
180
+ \`\`\`
181
+
182
+ Show the user the \`verification_uri_complete\` link (or
183
+ \`verification_uri\` + \`user_code\` as a fallback) and explain that the
184
+ CLI will pick up the token automatically once they authorize. Then keep
185
+ the background process running and tail it for the final
186
+ \`{"status":"success",...}\` or \`{"error":...}\` line.
187
+
188
+ CLI v0.3.3+ also auto-detects non-TTY stdout and prints the URL as plain
189
+ text immediately when you run \`chainpatrol login\` without \`--json\`,
190
+ so the same background+tail pattern works without \`--json\`. Prefer
191
+ \`--json\` so the output is structured and machine-parseable.
192
+
129
193
  ### \`logout\` \u2014 Clear stored credentials
130
194
 
131
195
  \`\`\`bash
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
11
+ } from "./chunk-44FSS3CZ.js";
12
12
  import {
13
13
  DateTime
14
14
  } from "./chunk-TFCNKBRC.js";
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  getCliVersion,
14
14
  isSkillInstalled,
15
15
  readInstalledSkillVersion
16
- } from "./chunk-T4DYUWUD.js";
16
+ } from "./chunk-R7BPW32M.js";
17
17
  import "./chunk-IUZB3DQW.js";
18
18
  import {
19
19
  DateTime
@@ -686,12 +686,12 @@ function parseAttachmentUrls() {
686
686
  }
687
687
  async function handleConfigsList(org) {
688
688
  if (jsonMode) {
689
- const { listConfigsJson } = await import("./list-json-GANUT7SB.js");
689
+ const { listConfigsJson } = await import("./list-json-SUZL2GBJ.js");
690
690
  await listConfigsJson({ org });
691
691
  return;
692
692
  }
693
693
  const { render } = await import("ink");
694
- const { default: ConfigsList } = await import("./list-56BS25X4.js");
694
+ const { default: ConfigsList } = await import("./list-TMFLCS5U.js");
695
695
  const { default: React } = await import("react");
696
696
  render(React.createElement(ConfigsList, { org }));
697
697
  }
@@ -746,11 +746,14 @@ async function main() {
746
746
  );
747
747
  }
748
748
  if (jsonMode) {
749
- const { loginJson } = await import("./login-json-LKB72OFY.js");
749
+ const { loginJson } = await import("./login-json-XGMXT5VJ.js");
750
750
  await loginJson();
751
+ } else if (!process.stdout.isTTY) {
752
+ const { loginPlain } = await import("./login-plain-CWKOUZKE.js");
753
+ await loginPlain();
751
754
  } else {
752
755
  const { render } = await import("ink");
753
- const { default: Login } = await import("./login-G7LPHKDR.js");
756
+ const { default: Login } = await import("./login-C66GRR3Y.js");
754
757
  const { default: React } = await import("react");
755
758
  render(React.createElement(Login));
756
759
  }
@@ -786,7 +789,7 @@ async function main() {
786
789
  case "detections": {
787
790
  const org = await resolveOrg();
788
791
  if (subcommand === "healthcheck") {
789
- const { runDetectionsHealthcheck } = await import("./healthcheck-6RHZBEVE.js");
792
+ const { runDetectionsHealthcheck } = await import("./healthcheck-SIE5EXN3.js");
790
793
  await runDetectionsHealthcheck({
791
794
  org,
792
795
  source: cli.flags.source,
@@ -801,7 +804,7 @@ async function main() {
801
804
  break;
802
805
  }
803
806
  if (subcommand === "validate") {
804
- const { runDetectionsValidate } = await import("./validate-NIBVL7X5.js");
807
+ const { runDetectionsValidate } = await import("./validate-II6GH6H6.js");
805
808
  await runDetectionsValidate({
806
809
  org,
807
810
  source: cli.flags.source,
@@ -816,7 +819,7 @@ async function main() {
816
819
  break;
817
820
  }
818
821
  if (subcommand === "drift") {
819
- const { runDetectionsDrift } = await import("./drift-2JRZK2MC.js");
822
+ const { runDetectionsDrift } = await import("./drift-PASGDEEX.js");
820
823
  await runDetectionsDrift({
821
824
  org,
822
825
  source: cli.flags.source,
@@ -830,7 +833,7 @@ async function main() {
830
833
  break;
831
834
  }
832
835
  if (subcommand === "run") {
833
- const { runDetectionsRun } = await import("./run-JZNZSCDJ.js");
836
+ const { runDetectionsRun } = await import("./run-MGOASUON.js");
834
837
  await runDetectionsRun({
835
838
  org,
836
839
  configId: cli.flags.configId,
@@ -849,7 +852,7 @@ async function main() {
849
852
  break;
850
853
  }
851
854
  if (action === "run") {
852
- const { runDetectionsRun } = await import("./run-JZNZSCDJ.js");
855
+ const { runDetectionsRun } = await import("./run-MGOASUON.js");
853
856
  await runDetectionsRun({
854
857
  org,
855
858
  configId: cli.flags.configId,
@@ -867,7 +870,7 @@ async function main() {
867
870
  throw new Error("detections configs update requires --config-id");
868
871
  }
869
872
  const configPatch = getConfigPatchFromSetFlags();
870
- const { runDetectionsConfigsUpdate } = await import("./configs-update-AHI7T6S4.js");
873
+ const { runDetectionsConfigsUpdate } = await import("./configs-update-VOWH674W.js");
871
874
  await runDetectionsConfigsUpdate({
872
875
  org,
873
876
  configId: cli.flags.configId,
@@ -894,7 +897,7 @@ async function main() {
894
897
  case "metrics": {
895
898
  const org = await resolveOrg();
896
899
  if (subcommand === "summary") {
897
- const { runMetricsSummary } = await import("./summary-PA2CCZVO.js");
900
+ const { runMetricsSummary } = await import("./summary-QORQFCMW.js");
898
901
  await runMetricsSummary({
899
902
  org,
900
903
  from: cli.flags.from,
@@ -906,7 +909,7 @@ async function main() {
906
909
  break;
907
910
  }
908
911
  if (subcommand === "found") {
909
- const { runMetricsFound } = await import("./found-YYYFF4FD.js");
912
+ const { runMetricsFound } = await import("./found-TIW7T4VX.js");
910
913
  await runMetricsFound({
911
914
  org,
912
915
  from: cli.flags.from,
@@ -923,7 +926,7 @@ async function main() {
923
926
  if (!by || !["day", "type", "brand"].includes(by)) {
924
927
  throw new Error("metrics breakdown requires --by <day|type|brand>");
925
928
  }
926
- const { runMetricsBreakdown } = await import("./breakdown-BECXDJIS.js");
929
+ const { runMetricsBreakdown } = await import("./breakdown-FFNYSXRQ.js");
927
930
  await runMetricsBreakdown({
928
931
  org,
929
932
  by,
@@ -943,7 +946,7 @@ async function main() {
943
946
  case "reports": {
944
947
  if (subcommand === "list") {
945
948
  const org = await resolveOrg();
946
- const { runReportsList } = await import("./list-MHEIY7LQ.js");
949
+ const { runReportsList } = await import("./list-CEWBMKJ3.js");
947
950
  await runReportsList({
948
951
  org,
949
952
  limit: cli.flags.limit,
@@ -959,7 +962,7 @@ async function main() {
959
962
  }
960
963
  if (subcommand === "create") {
961
964
  const org = await tryResolveOrg();
962
- const { runReportsCreate } = await import("./create-RS37JAMM.js");
965
+ const { runReportsCreate } = await import("./create-ASGUBKZ7.js");
963
966
  await runReportsCreate({
964
967
  org,
965
968
  title: cli.flags.title,
@@ -983,7 +986,7 @@ async function main() {
983
986
  }
984
987
  case "queues": {
985
988
  if (subcommand === "snapshot") {
986
- const { runQueuesSnapshot } = await import("./snapshot-KGMO44YB.js");
989
+ const { runQueuesSnapshot } = await import("./snapshot-MQ6DWDFG.js");
987
990
  await runQueuesSnapshot({
988
991
  org: cli.flags.org,
989
992
  all: cli.flags.all,
@@ -1001,7 +1004,7 @@ async function main() {
1001
1004
  }
1002
1005
  case "presets": {
1003
1006
  if (subcommand === "list") {
1004
- const { runPresetsList } = await import("./list-M2UQCXIO.js");
1007
+ const { runPresetsList } = await import("./list-KTIZ3UHA.js");
1005
1008
  await runPresetsList({ outputFormat: cliContext.outputFormat });
1006
1009
  break;
1007
1010
  }
@@ -1012,7 +1015,7 @@ async function main() {
1012
1015
  );
1013
1016
  }
1014
1017
  const org = await resolveOrg();
1015
- const { runPresetsRun } = await import("./run-7NTCD7JI.js");
1018
+ const { runPresetsRun } = await import("./run-3TTLZ6HA.js");
1016
1019
  await runPresetsRun({
1017
1020
  presetId: action,
1018
1021
  org,
@@ -1029,12 +1032,12 @@ async function main() {
1029
1032
  case "setup":
1030
1033
  case "install":
1031
1034
  case "i": {
1032
- const { setupSkill } = await import("./setup-skill-ZUZ5MYLI.js");
1035
+ const { setupSkill } = await import("./setup-skill-KKWETU4A.js");
1033
1036
  setupSkill({ json: jsonMode });
1034
1037
  break;
1035
1038
  }
1036
1039
  case "uninstall": {
1037
- const { uninstallSkill } = await import("./setup-skill-ZUZ5MYLI.js");
1040
+ const { uninstallSkill } = await import("./setup-skill-KKWETU4A.js");
1038
1041
  uninstallSkill({ json: jsonMode });
1039
1042
  break;
1040
1043
  }
@@ -7,9 +7,9 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-DSOM6TZX.js";
11
- import "./chunk-TFCNKBRC.js";
10
+ } from "./chunk-44FSS3CZ.js";
12
11
  import "./chunk-EEG7T6WT.js";
12
+ import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
15
15
  // src/commands/detections/configs-update.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/reports/create.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/detections/drift.ts
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-DSOM6TZX.js";
7
+ } from "./chunk-44FSS3CZ.js";
8
+ import "./chunk-EEG7T6WT.js";
8
9
  import {
9
10
  DateTime
10
11
  } from "./chunk-TFCNKBRC.js";
11
- import "./chunk-EEG7T6WT.js";
12
12
  import "./chunk-U73SABXK.js";
13
13
 
14
14
  // src/lib/date-range.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/detections/healthcheck.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/reports/list.ts
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  PRESETS
3
- } from "./chunk-B3CCMSG2.js";
3
+ } from "./chunk-WBKCXGLV.js";
4
4
  import "./chunk-E2LAMILJ.js";
5
5
  import {
6
6
  printOutput,
7
7
  toCsvRows
8
8
  } from "./chunk-VFT3TD3E.js";
9
- import "./chunk-DSOM6TZX.js";
10
- import "./chunk-TFCNKBRC.js";
9
+ import "./chunk-44FSS3CZ.js";
11
10
  import "./chunk-EEG7T6WT.js";
11
+ import "./chunk-TFCNKBRC.js";
12
12
  import "./chunk-U73SABXK.js";
13
13
 
14
14
  // src/commands/presets/list.ts
@@ -1,16 +1,16 @@
1
- import {
2
- createApiClient
3
- } from "./chunk-DSOM6TZX.js";
4
- import "./chunk-TFCNKBRC.js";
5
1
  import {
6
2
  ErrorDisplay,
7
3
  Spinner
8
4
  } from "./chunk-JCMWDZYY.js";
5
+ import {
6
+ createApiClient
7
+ } from "./chunk-44FSS3CZ.js";
9
8
  import {
10
9
  AuthCorruptedError,
11
10
  AuthExpiredError,
12
11
  AuthNotLoggedInError
13
12
  } from "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/configs/list.tsx
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-DSOM6TZX.js";
4
- import "./chunk-TFCNKBRC.js";
3
+ } from "./chunk-44FSS3CZ.js";
5
4
  import "./chunk-EEG7T6WT.js";
5
+ import "./chunk-TFCNKBRC.js";
6
6
  import "./chunk-U73SABXK.js";
7
7
 
8
8
  // src/commands/configs/list-json.ts
@@ -2,6 +2,10 @@ import {
2
2
  ErrorDisplay,
3
3
  Spinner
4
4
  } from "./chunk-JCMWDZYY.js";
5
+ import {
6
+ formatUserCode,
7
+ isHeadlessEnv
8
+ } from "./chunk-EBJMOX3Q.js";
5
9
  import {
6
10
  fetchUserEmail,
7
11
  getCredentials,
@@ -10,6 +14,7 @@ import {
10
14
  requestDeviceCode,
11
15
  storeCredentials
12
16
  } from "./chunk-EEG7T6WT.js";
17
+ import "./chunk-TFCNKBRC.js";
13
18
  import "./chunk-U73SABXK.js";
14
19
 
15
20
  // src/commands/login.tsx
@@ -43,9 +48,11 @@ function Login() {
43
48
  setState({ phase: "requesting-code" });
44
49
  requestDeviceCode().then((deviceCode) => {
45
50
  setState({ phase: "waiting-for-approval", deviceCode });
46
- const uri = deviceCode.verification_uri_complete ?? deviceCode.verification_uri;
47
- open(uri).catch(() => {
48
- });
51
+ if (!isHeadlessEnv()) {
52
+ const uri = deviceCode.verification_uri_complete ?? deviceCode.verification_uri;
53
+ open(uri).catch(() => {
54
+ });
55
+ }
49
56
  }).catch((err) => {
50
57
  setState({
51
58
  phase: "error",
@@ -130,19 +137,34 @@ function Login() {
130
137
  ] });
131
138
  case "requesting-code":
132
139
  return /* @__PURE__ */ jsx(Spinner, { label: "Requesting device code..." });
133
- case "waiting-for-approval":
140
+ case "waiting-for-approval": {
141
+ const completeUri = state.deviceCode.verification_uri_complete;
134
142
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
135
143
  /* @__PURE__ */ jsxs(Box, { children: [
136
- /* @__PURE__ */ jsx(Text, { children: "Your code is: " }),
137
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: state.deviceCode.user_code.length === 8 ? `${state.deviceCode.user_code.slice(0, 4)}-${state.deviceCode.user_code.slice(4)}` : state.deviceCode.user_code })
144
+ /* @__PURE__ */ jsx(Text, { children: "Your code: " }),
145
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: formatUserCode(state.deviceCode.user_code) })
138
146
  ] }),
139
- /* @__PURE__ */ jsxs(Text, { children: [
147
+ completeUri ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
148
+ /* @__PURE__ */ jsx(Text, { children: "Open this URL on any device to approve in one step:" }),
149
+ /* @__PURE__ */ jsxs(Text, { color: "blue", underline: true, children: [
150
+ " ",
151
+ completeUri
152
+ ] }),
153
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
154
+ "Or visit ",
155
+ state.deviceCode.verification_uri,
156
+ " and enter the code above."
157
+ ] })
158
+ ] }) : /* @__PURE__ */ jsxs(Text, { children: [
140
159
  "Open this URL in your browser:",
141
160
  " ",
142
- /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: state.deviceCode.verification_uri })
161
+ /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: state.deviceCode.verification_uri }),
162
+ " ",
163
+ "and enter the code above."
143
164
  ] }),
144
165
  /* @__PURE__ */ jsx(Spinner, { label: "Waiting for approval..." })
145
166
  ] });
167
+ }
146
168
  case "success":
147
169
  return /* @__PURE__ */ jsxs(Text, { children: [
148
170
  /* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "\u2713" }),
@@ -0,0 +1,50 @@
1
+ import {
2
+ isHeadlessEnv,
3
+ runLoginFlow
4
+ } from "./chunk-EBJMOX3Q.js";
5
+ import "./chunk-EEG7T6WT.js";
6
+ import "./chunk-TFCNKBRC.js";
7
+ import "./chunk-U73SABXK.js";
8
+
9
+ // src/commands/login-json.ts
10
+ async function loginJson() {
11
+ const ok = await runLoginFlow((event) => {
12
+ switch (event.type) {
13
+ case "already_logged_in":
14
+ console.log(JSON.stringify({ status: "already_logged_in", email: event.email }));
15
+ return;
16
+ case "device_code":
17
+ console.log(
18
+ JSON.stringify({
19
+ action: "open_url",
20
+ user_code: event.code.user_code,
21
+ verification_uri: event.code.verification_uri,
22
+ verification_uri_complete: event.code.verification_uri_complete ?? null,
23
+ expires_in: event.code.expires_in,
24
+ headless: isHeadlessEnv()
25
+ })
26
+ );
27
+ return;
28
+ case "success":
29
+ console.log(JSON.stringify({ status: "success", email: event.email }));
30
+ return;
31
+ case "expired":
32
+ console.error(
33
+ JSON.stringify({ error: "Code expired. Run `chainpatrol login` again." })
34
+ );
35
+ return;
36
+ case "denied":
37
+ console.error(JSON.stringify({ error: "Authorization denied." }));
38
+ return;
39
+ case "error":
40
+ console.error(JSON.stringify({ error: event.message }));
41
+ return;
42
+ }
43
+ });
44
+ if (!ok) {
45
+ process.exit(1);
46
+ }
47
+ }
48
+ export {
49
+ loginJson
50
+ };
@@ -0,0 +1,60 @@
1
+ import {
2
+ formatUserCode,
3
+ isHeadlessEnv,
4
+ runLoginFlow
5
+ } from "./chunk-EBJMOX3Q.js";
6
+ import "./chunk-EEG7T6WT.js";
7
+ import "./chunk-TFCNKBRC.js";
8
+ import "./chunk-U73SABXK.js";
9
+
10
+ // src/commands/login-plain.ts
11
+ async function loginPlain() {
12
+ const ok = await runLoginFlow((event) => {
13
+ switch (event.type) {
14
+ case "already_logged_in":
15
+ console.log(
16
+ event.email ? `Already logged in as ${event.email}` : "Already logged in"
17
+ );
18
+ return;
19
+ case "device_code": {
20
+ const completeUri = event.code.verification_uri_complete;
21
+ const userCode = formatUserCode(event.code.user_code);
22
+ if (completeUri) {
23
+ console.log("Open this URL on any device to approve in one step:");
24
+ console.log(` ${completeUri}`);
25
+ console.log(
26
+ `Or visit ${event.code.verification_uri} and enter the code: ${userCode}`
27
+ );
28
+ } else {
29
+ console.log(`Open this URL in your browser: ${event.code.verification_uri}`);
30
+ console.log(`Enter the code: ${userCode}`);
31
+ }
32
+ if (isHeadlessEnv()) {
33
+ console.log("(headless environment detected \u2014 browser will not auto-open)");
34
+ }
35
+ console.log("Waiting for approval...");
36
+ return;
37
+ }
38
+ case "success":
39
+ console.log(
40
+ event.email ? `Logged in successfully as ${event.email}` : "Logged in successfully"
41
+ );
42
+ return;
43
+ case "expired":
44
+ console.error("Code expired. Run `chainpatrol login` again.");
45
+ return;
46
+ case "denied":
47
+ console.error("Authorization denied.");
48
+ return;
49
+ case "error":
50
+ console.error(event.message);
51
+ return;
52
+ }
53
+ });
54
+ if (!ok) {
55
+ process.exit(1);
56
+ }
57
+ }
58
+ export {
59
+ loginPlain
60
+ };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  getPresetDefinition,
3
3
  runPreset
4
- } from "./chunk-B3CCMSG2.js";
4
+ } from "./chunk-WBKCXGLV.js";
5
5
  import {
6
6
  CliExitError,
7
7
  ExitCode
8
8
  } from "./chunk-E2LAMILJ.js";
9
9
  import "./chunk-VFT3TD3E.js";
10
- import "./chunk-DSOM6TZX.js";
11
- import "./chunk-TFCNKBRC.js";
10
+ import "./chunk-44FSS3CZ.js";
12
11
  import "./chunk-EEG7T6WT.js";
12
+ import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
15
15
  // src/commands/presets/run.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/detections/run.ts
@@ -6,7 +6,7 @@ import {
6
6
  readInstalledSkillVersion,
7
7
  setupSkill,
8
8
  uninstallSkill
9
- } from "./chunk-T4DYUWUD.js";
9
+ } from "./chunk-R7BPW32M.js";
10
10
  import "./chunk-IUZB3DQW.js";
11
11
  export {
12
12
  getBundledSkillContent,
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/queues/snapshot.ts
@@ -4,9 +4,9 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-DSOM6TZX.js";
8
- import "./chunk-TFCNKBRC.js";
7
+ } from "./chunk-44FSS3CZ.js";
9
8
  import "./chunk-EEG7T6WT.js";
9
+ import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
12
12
  // src/commands/metrics/summary.ts
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-DSOM6TZX.js";
12
- import "./chunk-TFCNKBRC.js";
11
+ } from "./chunk-44FSS3CZ.js";
13
12
  import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
16
16
  // src/commands/detections/validate.ts
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@chainpatrol/cli",
3
3
  "description": "The official ChainPatrol CLI — terminal interface for threat detection",
4
4
  "author": "Umar Ahmed <umar@chainpatrol.io>",
5
- "version": "0.3.1",
5
+ "version": "0.3.3",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [
@@ -1,71 +0,0 @@
1
- import {
2
- fetchUserEmail,
3
- getCredentials,
4
- isLoggedIn,
5
- pollForToken,
6
- requestDeviceCode,
7
- storeCredentials
8
- } from "./chunk-EEG7T6WT.js";
9
- import "./chunk-U73SABXK.js";
10
-
11
- // src/commands/login-json.ts
12
- async function loginJson() {
13
- if (isLoggedIn()) {
14
- const creds = getCredentials();
15
- console.log(
16
- JSON.stringify({ status: "already_logged_in", email: creds.email ?? null })
17
- );
18
- return;
19
- }
20
- const deviceCode = await requestDeviceCode();
21
- console.error(
22
- JSON.stringify({
23
- action: "open_url",
24
- user_code: deviceCode.user_code,
25
- verification_uri: deviceCode.verification_uri,
26
- verification_uri_complete: deviceCode.verification_uri_complete ?? null,
27
- expires_in: deviceCode.expires_in
28
- })
29
- );
30
- let interval = deviceCode.interval * 1e3;
31
- while (true) {
32
- await new Promise((r) => setTimeout(r, interval));
33
- const result = await pollForToken(deviceCode.device_code);
34
- switch (result.status) {
35
- case "success": {
36
- const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
37
- const email = await fetchUserEmail(result.accessToken);
38
- const creds = {
39
- accessToken: result.accessToken,
40
- expiresAt,
41
- ...email ? { email } : {}
42
- };
43
- storeCredentials(creds);
44
- console.log(JSON.stringify({ status: "success", email: email ?? null }));
45
- return;
46
- }
47
- case "pending":
48
- continue;
49
- case "slow_down":
50
- interval += result.addSeconds * 1e3;
51
- continue;
52
- case "expired":
53
- console.error(
54
- JSON.stringify({ error: "Code expired. Run `chainpatrol login` again." })
55
- );
56
- process.exit(1);
57
- break;
58
- case "denied":
59
- console.error(JSON.stringify({ error: "Authorization denied." }));
60
- process.exit(1);
61
- break;
62
- case "error":
63
- console.error(JSON.stringify({ error: result.message }));
64
- process.exit(1);
65
- break;
66
- }
67
- }
68
- }
69
- export {
70
- loginJson
71
- };
@@ -1,9 +1,9 @@
1
- import {
2
- DateTime
3
- } from "./chunk-TFCNKBRC.js";
4
1
  import {
5
2
  getValidCredentials
6
3
  } from "./chunk-EEG7T6WT.js";
4
+ import {
5
+ DateTime
6
+ } from "./chunk-TFCNKBRC.js";
7
7
  import {
8
8
  getConfig
9
9
  } from "./chunk-U73SABXK.js";