@patchstack/connect 0.5.25 → 0.5.27

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
@@ -11,12 +11,40 @@ Connect a JavaScript / Node.js application to [Patchstack](https://patchstack.co
11
11
 
12
12
  ## Agent-assisted setup
13
13
 
14
- For an existing JS/Node project, copy this request into a coding assistant, or run the same command yourself. For a standalone HTML/CSS/JavaScript site without a package-managed app, use the [plain HTML widget instructions](AGENT-INSTALL.md#plain-html-sites); do not add Node tooling just for the widget.
14
+ For an existing JS/Node project in a hosted workspace, copy this request into a coding assistant, or run the same command yourself. For Gemini CLI, OpenCode, Codex CLI, or Claude Code on your own machine, use [Local coding CLIs](#local-coding-clis) below. For a standalone HTML/CSS/JavaScript site without a package-managed app, use the [plain HTML widget instructions](AGENT-INSTALL.md#plain-html-sites); do not add Node tooling just for the widget.
15
15
 
16
16
  > I have vetted the official [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect) package and authorize its Patchstack Connector, production build hooks, and runtime protection source edits. Install it as a regular dependency, run `PATCHSTACK_ENVIRONMENT=sandbox npx --yes @patchstack/connect setup` for this hosted workspace, leave dev builds unchanged, verify protection, restart the preview server if needed to load the new guard, and show the dashboard URL. The Patchstack Connector widget is part of this install and on by default: add it, and do not ask me whether to. Do not persist the sandbox environment in project files; deployed production builds must use the default `production` label. When setup finishes, tell me to refresh the preview if the Patchstack widget is not showing yet, and remind me to deploy when I am ready — do not deploy anything yourself.
17
17
 
18
18
  `setup` is state-aware and idempotent: it scans dependencies, provisions or reuses the site, manages the Patchstack Connector, installs and verifies the runtime guard, adds a dependency-install scan, wires the existing build command without replacing it, and prints the remaining setup status. It never runs the project build. `guide` provides the same project-specific status without changing files.
19
19
 
20
+ ### Local coding CLIs
21
+
22
+ Gemini CLI, OpenCode, Codex CLI, and Claude Code can use the same installed Connect commands. The tool does not determine the deployment tier: use the environment of the process running Connect. A tool's permission sandbox is separate from Connect's `sandbox` manifest label. On an ordinary laptop, leave `PATCHSTACK_ENVIRONMENT` unset so Connect reports `local`. An exported Lovable or Replit project retains its hosted-builder dependencies; when working on that export locally, set `PATCHSTACK_ENVIRONMENT=local` in the local process only to override the builder assumption.
23
+
24
+ Have the assistant work in the application's package directory, reuse any existing site UUID, and install Connect in `dependencies` with the package manager that owns the project. Then run the installed CLI:
25
+
26
+ | Package manager | Install if absent | Set up | Check source wiring |
27
+ |---|---|---|---|
28
+ | npm | `npm install --save @patchstack/connect` | `npx --no-install patchstack-connect setup` | `npx --no-install patchstack-connect protect --check` |
29
+ | pnpm | `pnpm add @patchstack/connect` | `pnpm exec patchstack-connect setup` | `pnpm exec patchstack-connect protect --check` |
30
+ | Yarn | `yarn add @patchstack/connect` | `yarn exec patchstack-connect setup` | `yarn exec patchstack-connect protect --check` |
31
+ | Bun | `bun add @patchstack/connect` | `bun run patchstack-connect setup` | `bun run patchstack-connect protect --check` |
32
+
33
+ Use the same invocation with `guide --verbose` to inspect the environment, build hooks, widget, and remaining setup work, and with `status` to recover the dashboard URL. Read the actual command results before reporting completion. A declared dependency, generated scaffold, or proposed command alone does not establish a completed install. Resolve source-check failures on server applications; for a client-only site, state the protection limitation. Restart an already-running app to load the new guard and refresh the page to check the widget.
34
+
35
+ Before an authorized deployment, preserve `.patchstackrc.json`, the dependency and lockfile changes, the install/build hooks, and the generated guard and layout edits. Keep `.patchstackrc.local.json` out of Git and configure `PATCHSTACK_API_KEY` through the deployment host's secret settings. Remove any local or workspace-only environment override from the deployment process; use the host's tier signals or the explicit [DigitalOcean build settings](#sandbox-and-production-manifests). Run the project's existing build command so both `scan` and `mark-build` execute. A successful source check establishes wiring, not that the deployed server loaded it; verify the deployed widget and server protection separately. Setup itself does not deploy the application.
36
+
37
+ To keep future agent sessions aware of the setup, add a short note to the application's existing project instructions. Preserve the instructions already there. Record the package manager, the installed Connect invocation, the existing site configuration, the verification command, and the deployment environment requirements. Refer to `node_modules/@patchstack/connect/AGENT-INSTALL.md` for the installed version's reference. Keep credentials and a fixed workspace tier out of these notes.
38
+
39
+ | Coding tool | Project instructions |
40
+ |---|---|
41
+ | Codex CLI | [AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md) |
42
+ | OpenCode | [AGENTS.md](https://opencode.ai/docs/rules/) |
43
+ | Claude Code | [CLAUDE.md, or a shared AGENTS.md via the documented configuration/import](https://code.claude.com/docs/en/memory) |
44
+ | Gemini CLI | [GEMINI.md, or configure it to load the shared AGENTS.md](https://geminicli.com/docs/cli/gemini-md/) |
45
+
46
+ Project instructions provide context; command permissions still belong to the coding tool. If execution is declined, use the handoff below and report which setup steps remain unverified.
47
+
20
48
  ### If your coding tool blocks the command
21
49
 
22
50
  Some tools will not run a third-party command until you approve it. Claude Code's auto mode, for example,
@@ -308,7 +336,7 @@ Environment variables:
308
336
  - `PATCHSTACK_SITE_UUID` — the site UUID from your Patchstack dashboard
309
337
  - `PATCHSTACK_ENDPOINT` — override the API endpoint (default `https://api.patchstack.com/monitor/pulse/manifest`)
310
338
  - `PATCHSTACK_TIMEOUT_MS` — request timeout in milliseconds (default `30000`)
311
- - `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the build platform's own variables. Where the platform names the tier (Vercel, Netlify, Render, Railway, GitLab CI), production reports `production` and a preview `sandbox`. Where it names only the branch (Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier), a branch named `main`, `master`, `production`, `prod`, `release` or `live` reports `production` and any other branch or a pull request `sandbox`. A Replit Deployment reports `production`, the Replit workspace `sandbox`. Failing all of that, a project a hosted builder generated and builds for itself (Lovable, Replit) reports `production`, because on those platforms the edit preview is a dev server and a build only happens when the owner publishes. Anything else — a developer machine, a CI runner this does not know (`CI=true` alone), a platform this does not know — reports `local`
339
+ - `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the build platform's own variables. Where the platform names the tier (Vercel, Netlify, Render, Railway, GitLab CI), production reports `production` and a preview `sandbox`. Vercel reads `VERCEL_TARGET_ENV` before `VERCEL_ENV`; custom targets report `sandbox`. Netlify Preview Servers report `sandbox`; Netlify Dev reports `local`, even with production settings. Vercel or Netlify with a platform marker but no tier reports `local`, without falling back to a CI branch or hosted builder. Where it names only the branch (Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier), a branch named `main`, `master`, `production`, `prod`, `release` or `live` reports `production` and any other branch or a pull request `sandbox`. A Replit Deployment reports `production`, the Replit workspace `sandbox`. Failing all of that, a project a hosted builder generated and builds for itself (Lovable, Replit) reports `production`, because on those platforms the edit preview is a dev server and a build only happens when the owner publishes. Anything else — a developer machine, a CI runner this does not know (`CI=true` alone), a platform this does not know — reports `local`
312
340
  - `PATCHSTACK_CLAIM_TOKEN` — connect the site straight to your account (see *Connecting straight to your account*)
313
341
 
314
342
  Two files, because one value is public and the other is not.
@@ -357,7 +385,21 @@ The token names your account, not the project: it is never written to `.patchsta
357
385
 
358
386
  ### Sandbox and production manifests
359
387
 
360
- Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the build platform's own variables. Platforms that name the tier answer directly: Vercel's `VERCEL_ENV`, Netlify's `CONTEXT`, Render's pull-request flag, Railway's environment name, GitLab's `CI_ENVIRONMENT_TIER`. Production reports `production`; a preview those platforms name as such reports `sandbox`. Platforms that name only the branch — Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier — are decided by the branch name: `main`, `master`, `production`, `prod`, `release` and `live` report `production`, any other branch reports `sandbox`, and a pull-request build reports `sandbox` whatever the branch. That is an assumption about naming, so the line `scan` prints says the decision rests on the branch name; set `PATCHSTACK_ENVIRONMENT` where the live branch is called something else. A Replit Deployment reports `production` and the Replit workspace `sandbox`. Where no platform answers, the project itself gets the last word: one a hosted builder generated and builds for itself — Lovable, Replit — reports `production`, because the edit preview there is a dev server, so a build running at all is the owner publishing. Everything else reports `local` — a developer's machine, a CI runner this does not know (`CI=true` alone proves automation, not deployment), and a platform whose build environment carries no such signal. A local manifest is inventory — it tells Patchstack what the app is built from — and never counts as contact with a live site, so an app that has only been set up on a laptop shows in the dashboard as **Configured locally**, not as connected or deployed. The label also decides whether `mark-build` stamps the live-site marker, so a deployment that reads `local` ships its pages without one: set `PATCHSTACK_ENVIRONMENT=production` where builds run on a platform this list does not know. Sandboxed builders should set `PATCHSTACK_ENVIRONMENT=sandbox` in the sandbox process only. Patchstack stores and deduplicates manifests per environment, so an iterative workspace scan does not replace the last production manifest.
388
+ Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the build platform's own variables. Platforms that name the tier answer directly: Vercel's `VERCEL_TARGET_ENV` (falling back to `VERCEL_ENV`), Netlify's `CONTEXT`, Render's pull-request flag, Railway's environment name, GitLab's `CI_ENVIRONMENT_TIER`. Production reports `production`; a preview those platforms name as such reports `sandbox`. Platforms that name only the branch — Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier — are decided by the branch name: `main`, `master`, `production`, `prod`, `release` and `live` report `production`, any other branch reports `sandbox`, and a pull-request build reports `sandbox` whatever the branch. That is an assumption about naming, so the line `scan` prints says the decision rests on the branch name; set `PATCHSTACK_ENVIRONMENT` where the live branch is called something else. A Replit Deployment reports `production` and the Replit workspace `sandbox`. Where no platform answers, the project itself gets the last word: one a hosted builder generated and builds for itself — Lovable, Replit — reports `production`, because the edit preview there is a dev server, so a build running at all is the owner publishing. Everything else reports `local` — a developer's machine, a CI runner this does not know (`CI=true` alone proves automation, not deployment), and a platform whose build environment carries no such signal. A local manifest is inventory — it tells Patchstack what the app is built from — and never counts as contact with a live site, so an app that has only been set up on a laptop shows in the dashboard as **Configured locally**, not as connected or deployed. The label also decides whether `mark-build` stamps the live-site marker, so a deployment that reads `local` ships its pages without one: set `PATCHSTACK_ENVIRONMENT=production` where builds run on a platform this list does not know. Sandboxed builders should set `PATCHSTACK_ENVIRONMENT=sandbox` in the sandbox process only. Patchstack stores and deduplicates manifests per environment, so an iterative workspace scan does not replace the last production manifest.
389
+
390
+ The environment is re-evaluated on every scan and `mark-build` run; setup does not persist an inferred tier. Hosting signals take precedence over CI branch guesses and hosted-builder dependencies. A Vercel or Netlify marker without its tier stays `local`; `scan --verbose` explains the missing signal. Vercel's target variable also works without the separate `VERCEL` marker, and custom targets report `sandbox`. Netlify's `NETLIFY_PREVIEW_SERVER=true` reports `sandbox` even if `CONTEXT` says production. Outside a hosted Preview Server, `NETLIFY_DEV=true` reports `local` even when Netlify Dev loads production settings. See the [Vercel system variables](https://vercel.com/docs/environment-variables/system-environment-variables) and [Netlify build variables](https://docs.netlify.com/build/configure-builds/environment-variables/), and [Netlify Dev implementation](https://github.com/netlify/cli/blob/main/src/commands/dev/dev.ts).
391
+
392
+ For DigitalOcean App Platform or Droplets, set the tier explicitly in the environment of the process that builds the app:
393
+
394
+ | Deployment | Build setting |
395
+ |---|---|
396
+ | Production | `PATCHSTACK_ENVIRONMENT=production` |
397
+ | Staging, preview, or sandbox | `PATCHSTACK_ENVIRONMENT=sandbox` |
398
+ | Local development | Leave unset, or set `PATCHSTACK_ENVIRONMENT=local` |
399
+
400
+ In App Platform, make the variable available at build time (`BUILD_TIME` or `RUN_AND_BUILD_TIME`); a runtime-only setting cannot label the prebuild scan or stamp built HTML. Docker builds must pass it into the build steps that run Connect. DigitalOcean's documented app URL and ID variables identify an app, not its deployment tier; `NODE_ENV=production` also does not distinguish a local optimized build from a deployment. See [DigitalOcean environment configuration](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/).
401
+
402
+ An explicit `PATCHSTACK_ENVIRONMENT` overrides `.patchstackrc.json`, and the file's `environment` overrides automatic detection. When production keeps reporting sandbox after deployment, remove a workspace-only override from the committed config and from the production build environment, then rebuild and deploy. Apply sandbox overrides only to preview processes or preview deployment settings.
361
403
 
362
404
  Do not commit `"environment": "sandbox"` to `.patchstackrc.json` when the same files are deployed to production. Scope the variable to the sandbox command/process instead:
363
405
 
package/dist/cli.js CHANGED
@@ -1343,8 +1343,8 @@ function computeManifestChecksum(packages) {
1343
1343
 
1344
1344
  // src/hosting.ts
1345
1345
  var RULES = [
1346
- { platform: "netlify", any: ["NETLIFY", "NETLIFY_BUILD_BASE", "DEPLOY_PRIME_URL"] },
1347
- { platform: "vercel", any: ["VERCEL", "VERCEL_ENV", "VERCEL_URL"] },
1346
+ { platform: "netlify", any: ["NETLIFY", "NETLIFY_PREVIEW_SERVER", "NETLIFY_DEV", "NETLIFY_BUILD_BASE", "DEPLOY_PRIME_URL"] },
1347
+ { platform: "vercel", any: ["VERCEL", "VERCEL_TARGET_ENV", "VERCEL_ENV", "VERCEL_URL"] },
1348
1348
  { platform: "cloudflare", any: ["CF_PAGES", "CF_PAGES_URL", "WORKERS_CI", "WORKERS_CI_BRANCH", "CLOUDFLARE_ACCOUNT_ID"] },
1349
1349
  { platform: "aws", any: ["AWS_APP_ID", "AWS_BRANCH", "AWS_LAMBDA_FUNCTION_NAME", "AWS_EXECUTION_ENV"] },
1350
1350
  { platform: "render", any: ["RENDER", "RENDER_SERVICE_ID", "RENDER_EXTERNAL_URL"] },
@@ -2237,7 +2237,7 @@ import { randomUUID as randomUUID2 } from "crypto";
2237
2237
  // src/environment.ts
2238
2238
  import { readFileSync as readFileSync2 } from "fs";
2239
2239
  import path6 from "path";
2240
- var set = (value) => value !== void 0 && value !== "";
2240
+ var set = (value) => value !== void 0 && value.trim() !== "";
2241
2241
  var PRODUCTION_BRANCHES = /* @__PURE__ */ new Set([
2242
2242
  "main",
2243
2243
  "master",
@@ -2253,14 +2253,31 @@ var DISCRIMINATORS = [
2253
2253
  {
2254
2254
  platform: "vercel",
2255
2255
  read: (env) => {
2256
- if (!set(env.VERCEL) || !set(env.VERCEL_ENV)) return null;
2257
- return env.VERCEL_ENV === "production" ? { environment: "production", evidence: "VERCEL_ENV=production" } : { environment: "sandbox", evidence: `VERCEL_ENV=${env.VERCEL_ENV}` };
2256
+ const variable = set(env.VERCEL_TARGET_ENV) ? "VERCEL_TARGET_ENV" : "VERCEL_ENV";
2257
+ const tier = env[variable];
2258
+ if (set(tier)) {
2259
+ return {
2260
+ environment: tier === "production" ? "production" : "sandbox",
2261
+ evidence: `${variable}=${tier}`
2262
+ };
2263
+ }
2264
+ if (env.VERCEL !== "1") return null;
2265
+ return { environment: "local", evidence: "VERCEL=1 without a deployment tier; set PATCHSTACK_ENVIRONMENT" };
2258
2266
  }
2259
2267
  },
2260
2268
  {
2261
2269
  platform: "netlify",
2262
2270
  read: (env) => {
2263
- if (env.NETLIFY !== "true" || !set(env.CONTEXT)) return null;
2271
+ if (env.NETLIFY_PREVIEW_SERVER === "true") {
2272
+ return { environment: "sandbox", evidence: "NETLIFY_PREVIEW_SERVER=true" };
2273
+ }
2274
+ if (env.NETLIFY_DEV === "true") {
2275
+ return { environment: "local", evidence: "NETLIFY_DEV=true (the local development server)" };
2276
+ }
2277
+ if (env.NETLIFY !== "true") return null;
2278
+ if (!set(env.CONTEXT)) {
2279
+ return { environment: "local", evidence: "NETLIFY=true without CONTEXT; set PATCHSTACK_ENVIRONMENT" };
2280
+ }
2264
2281
  return env.CONTEXT === "production" ? { environment: "production", evidence: "CONTEXT=production" } : { environment: "sandbox", evidence: `CONTEXT=${env.CONTEXT}` };
2265
2282
  }
2266
2283
  },
@@ -10851,7 +10868,8 @@ async function runScan(args, options = {}) {
10851
10868
  );
10852
10869
  }
10853
10870
  if (config.environment === "local") {
10854
- say("Environment: local (this machine). Set PATCHSTACK_ENVIRONMENT=production on a live build your host does not identify.");
10871
+ const because = (config.environmentEvidence ?? []).join("; ") || "this machine";
10872
+ say(`Environment: local (${because}). Set PATCHSTACK_ENVIRONMENT=production on a live build your host does not identify.`);
10855
10873
  } else {
10856
10874
  const because = (config.environmentEvidence ?? []).length > 0 ? ` (${config.environmentEvidence.join("; ")})` : "";
10857
10875
  say(`Environment: ${config.environment}${because}.`);