@scrymore/scry-deployer 0.7.0-next.20260926193255 → 0.7.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 CHANGED
@@ -2,7 +2,31 @@
2
2
 
3
3
  Deploy your Storybook to the cloud with one command. ⚡
4
4
 
5
- ## 🎯 Quick Start (5 seconds)
5
+ ## Set up with your AI assistant
6
+
7
+ From your application's repository, install the Scry setup skill:
8
+
9
+ ```bash
10
+ npx skills add epinnock/scry-node --skill scry-setup
11
+ ```
12
+
13
+ The current installer requires Node.js 22.20 or newer. Choose your assistant
14
+ (Claude Code, Codex, Cursor, or another compatible agent), then ask:
15
+ **“Set up Scry for this project and connect my assistant to its components.”**
16
+
17
+ The skill handles deployment configuration, GitHub Actions, component indexing,
18
+ MCP connections, and optional Figma linking. You can also ask for MCP alone.
19
+ Complete account sign-in in your browser and keep API keys in your environment
20
+ or CI secret store.
21
+
22
+ See the [setup guide](https://docs.scrymore.com/guide/skill) for installation
23
+ options, or inspect the [skill instructions](skills/scry-setup/SKILL.md).
24
+
25
+ ## Set up directly with the CLI
26
+
27
+ `init` writes configuration and workflows, configures GitHub secrets, then
28
+ commits and pushes. For local preparation before publishing, use the skill or
29
+ the manual deployment path below. `--skip-gh-setup` still commits and pushes.
6
30
 
7
31
  ### 1. Get your credentials
8
32
  Visit the [Scry Dashboard](https://dashboard.scrymore.com) and:
@@ -100,8 +124,8 @@ yarn add github:scryorg/scry-node --dev
100
124
  After installation, you can run commands using:
101
125
 
102
126
  ```bash
103
- # Using the storybook-deployer binary
104
- npx storybook-deployer init --projectId xxx --apiKey yyy
127
+ # Using the scry-deployer binary
128
+ npm exec -- scry-deployer init --projectId xxx --apiKey yyy
105
129
 
106
130
  # Using the scry alias
107
131
  npx scry init --projectId xxx --apiKey yyy
package/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { initTelemetry, captureCliError, flushTelemetry } = require('../lib/telemetry.js');
3
+ const { initTelemetry, captureCliError, flushTelemetry, redactArgv } = require('../lib/telemetry.js');
4
4
  const yargs = require('yargs/yargs');
5
5
  const { hideBin } = require('yargs/helpers');
6
6
  const fs = require('fs');
@@ -28,7 +28,8 @@ const { version: DEPLOYER_VERSION } = require('../package.json');
28
28
  async function runAnalysis(argv) {
29
29
  const logger = createLogger(argv);
30
30
  logger.info('📊 Starting Storybook analysis...');
31
- logger.debug(`Received arguments: ${JSON.stringify(argv)}`);
31
+ // Credentials masked: this line is also a Sentry breadcrumb.
32
+ logger.debug(`Received arguments: ${JSON.stringify(redactArgv(argv))}`);
32
33
 
33
34
  const outPath = path.join(os.tmpdir(), `storybook-analysis-${Date.now()}.zip`);
34
35
 
@@ -132,7 +133,8 @@ function describeCapture(report) {
132
133
  async function runDeployment(argv) {
133
134
  const logger = createLogger(argv);
134
135
  logger.info('🚀 Starting deployment...');
135
- logger.debug(`Received arguments: ${JSON.stringify(argv)}`);
136
+ // Credentials masked: this line is also a Sentry breadcrumb.
137
+ logger.debug(`Received arguments: ${JSON.stringify(redactArgv(argv))}`);
136
138
 
137
139
  const analysis = resolveAnalysis(argv);
138
140
  argv = { ...argv, withAnalysis: analysis.enabled };
package/lib/telemetry.js CHANGED
@@ -21,6 +21,8 @@ const Sentry = require('@sentry/node');
21
21
  * None of that is needed to know that an upload failed with EAI_AGAIN.
22
22
  */
23
23
 
24
+ const { version: PACKAGE_VERSION } = require('../package.json');
25
+
24
26
  const DSN =
25
27
  'https://c66ce229a1db2289f145eebd02436d9c@o4507889391828992.ingest.us.sentry.io/4510699330732032';
26
28
 
@@ -41,8 +43,58 @@ const SECRET_PATTERNS = [
41
43
  [/scry_proj_[A-Za-z0-9_\-]+/g, 'scry_proj_<redacted>'],
42
44
  [/(X-Amz-Signature=)[^&\s]+/gi, '$1<redacted>'],
43
45
  [/(Bearer\s+)[A-Za-z0-9._\-]+/gi, '$1<redacted>'],
46
+ // A credential flag echoed on a command line: --api-key X, --api-key=X,
47
+ // -k X, --token X, --openai-api-key X, ...
48
+ [/(--[\w-]*(?:api-key|apikey|token|secret|password)[\w-]*[=\s]+|(?:^|\s)-k[=\s]+)(?!<redacted>)[^\s"',]+/gi, '$1<redacted>'],
49
+ // A credential field inside serialized JSON: "apiKey":"X", "token": "X".
50
+ [/("[\w-]*(?:api[-_]?key|token|secret|password)[\w-]*"\s*:\s*)"[^"]*"/gi, '$1"<redacted>"'],
51
+ // An env echo: SCRY_API_KEY=X, STORYBOOK_DEPLOYER_API_KEY=X, JINA_API_KEY=X.
52
+ [/(\b[A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*=)(?!<redacted>)[^\s"',]+/g, '$1<redacted>'],
44
53
  ];
45
54
 
55
+ /** Field names whose values are credentials, in any spelling yargs produces. */
56
+ const SECRET_FIELD = /(^k$|api[-_]?key|token|secret|password)/i;
57
+
58
+ /**
59
+ * A copy of argv with every credential-named field masked, at any depth.
60
+ *
61
+ * For logging argv whole (the --verbose "Received arguments" line), which is
62
+ * also a Sentry breadcrumb. Unlike sanitizeArgv this keeps every field, so it
63
+ * must only be used for local diagnostics, never as event context.
64
+ */
65
+ function redactArgv(value, depth = 0) {
66
+ if (depth > 5 || value === null || typeof value !== 'object') return value;
67
+ if (Array.isArray(value)) return value.map((v) => redactArgv(v, depth + 1));
68
+ const out = {};
69
+ for (const [k, v] of Object.entries(value)) {
70
+ out[k] = SECRET_FIELD.test(k) && v !== undefined && v !== null && v !== '' ? '<redacted>' : redactArgv(v, depth + 1);
71
+ }
72
+ return out;
73
+ }
74
+
75
+ /** Scrub every string in a JSON-like value (breadcrumb data), in place-safe copy. */
76
+ function scrubDeep(value, depth = 0) {
77
+ if (typeof value === 'string') return scrub(value);
78
+ if (depth > 5 || value === null || typeof value !== 'object') return value;
79
+ if (Array.isArray(value)) return value.map((v) => scrubDeep(v, depth + 1));
80
+ const out = {};
81
+ for (const [k, v] of Object.entries(value)) {
82
+ out[k] = SECRET_FIELD.test(k) && typeof v === 'string' && v ? '<redacted>' : scrubDeep(v, depth + 1);
83
+ }
84
+ return out;
85
+ }
86
+
87
+ /**
88
+ * Breadcrumbs are sent with every event and beforeSend does not see them as
89
+ * separate items, so they get the same scrubbing on the way in.
90
+ */
91
+ function scrubBreadcrumb(breadcrumb) {
92
+ if (!breadcrumb) return breadcrumb;
93
+ if (breadcrumb.message) breadcrumb.message = scrub(breadcrumb.message);
94
+ if (breadcrumb.data) breadcrumb.data = scrubDeep(breadcrumb.data);
95
+ return breadcrumb;
96
+ }
97
+
46
98
  function scrub(value) {
47
99
  if (typeof value !== 'string') return value;
48
100
  return SECRET_PATTERNS.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), value);
@@ -77,6 +129,9 @@ function initTelemetry() {
77
129
  Sentry.init({
78
130
  dsn: DSN,
79
131
  environment: process.env.NODE_ENV || 'production',
132
+ // Which published deployer produced the event, so a fix can be matched to
133
+ // the versions still in the wild.
134
+ release: `@scrymore/scry-deployer@${PACKAGE_VERSION}`,
80
135
 
81
136
  // Errors only. Traces from a CLI describe the customer's build pipeline,
82
137
  // which is more than is needed to fix a crash.
@@ -86,8 +141,18 @@ function initTelemetry() {
86
141
  sendDefaultPii: false,
87
142
  serverName: false,
88
143
 
144
+ // The logger turns every line (including debug) into a breadcrumb, and
145
+ // Sentry's console integration does the same for console.*. None of that
146
+ // passed through beforeSend's scrubbing before.
147
+ beforeBreadcrumb(breadcrumb) {
148
+ return scrubBreadcrumb(breadcrumb);
149
+ },
150
+
89
151
  beforeSend(event) {
90
152
  if (event.message) event.message = scrub(event.message);
153
+ // Belt and braces for breadcrumbs added before init or by integrations
154
+ // that bypass beforeBreadcrumb.
155
+ if (Array.isArray(event.breadcrumbs)) event.breadcrumbs = event.breadcrumbs.map(scrubBreadcrumb);
91
156
 
92
157
  for (const entry of event.exception?.values ?? []) {
93
158
  if (entry.value) entry.value = scrub(entry.value);
@@ -135,6 +200,8 @@ module.exports = {
135
200
  flushTelemetry,
136
201
  telemetryDisabled,
137
202
  sanitizeArgv,
203
+ redactArgv,
138
204
  scrub,
205
+ scrubBreadcrumb,
139
206
  SAFE_ARGV_FIELDS,
140
207
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrymore/scry-deployer",
3
- "version": "0.7.0-next.20260926193255",
3
+ "version": "0.7.0",
4
4
  "description": "A CLI to automate the deployment of Storybook static builds.",
5
5
  "main": "index.js",
6
6
  "bin": {