@pendral/connect 1.0.1 → 1.0.2

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
@@ -1,22 +1,84 @@
1
- # @pendral/connect
1
+ <p align="center">
2
+ <a href="https://evryai.app/download">
3
+ <img src="https://cdn.jsdelivr.net/npm/@pendral/connect@1.0.2/assets/banner.png" alt="EvryAI Connect" width="100%" />
4
+ </a>
5
+ </p>
2
6
 
3
- Connects a machine to EvryAI without SSH. It dials out, so no open ports are needed.
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@pendral/connect"><img src="https://img.shields.io/npm/v/@pendral/connect?color=02334e&label=npm" alt="npm version" /></a>
9
+ <img src="https://img.shields.io/node/v/@pendral/connect?color=02334e" alt="Node version" />
10
+ <img src="https://img.shields.io/badge/platforms-Linux%20%C2%B7%20macOS%20%C2%B7%20Windows-02334e" alt="Linux, macOS and Windows" />
11
+ <img src="https://img.shields.io/badge/license-commercial-02334e" alt="Commercial license" />
12
+ </p>
4
13
 
5
- Make the command in EvryAI (Add machine, then Command, then npx) and paste it into the machine's terminal:
14
+ <p align="center">
15
+ <a href="https://evryai.app/download"><strong>Get EvryAI Desktop</strong></a> ·
16
+ <a href="https://evryai.app">EvryAI</a>
17
+ </p>
18
+
19
+ # EvryAI Connect
20
+
21
+ Connect any Linux, macOS or Windows machine to **EvryAI Desktop** with one command. The machine dials out to EvryAI, so
22
+ there are no SSH keys to set up, no open ports and no VPN. Once it's connected, your coding agents run on it just like
23
+ on your own computer: Claude Code, Codex and other agents, git, files, terminals, dev servers and live machine stats.
24
+
25
+ - **One command.** Paste it into the machine's terminal and choose Connect in the app.
26
+ - **No inbound access.** The machine connects out over HTTPS, so it works behind NAT and firewalls.
27
+ - **End-to-end encrypted.** Traffic between the machine and your computer is encrypted with keys only the two of them
28
+ hold, and every command is signed and checked before it runs.
29
+ - **Yours to revoke.** Remove the machine in the app and its access ends at once.
30
+
31
+ ## Connect a machine
32
+
33
+ 1. In EvryAI Desktop, open **Coding settings › Machines › Add machine**, then **Command**, then **npx**.
34
+ 2. Paste the command it shows into the machine's terminal. It looks like this:
6
35
 
7
36
  ```sh
8
- EVRYAI_CODE=<code> npx -y @pendral/connect@1.0.1 --origin <your EvryAI address> [--service] [--name NAME]
37
+ EVRYAI_CODE=<code> npx -y @pendral/connect@1.0.2 --origin <your EvryAI address> --service
9
38
  ```
10
39
 
11
40
  On Windows, in PowerShell:
12
41
 
13
42
  ```powershell
14
- $env:EVRYAI_CODE='<code>'; npx.cmd -y @pendral/connect@1.0.1 --origin <your EvryAI address> [--service] [--name NAME]
43
+ $env:EVRYAI_CODE='<code>'; npx.cmd -y @pendral/connect@1.0.2 --origin <your EvryAI address> --service
44
+ ```
45
+
46
+ 3. Choose **Connect** in the app when it asks. The machine shows up in your machine list, ready to use.
47
+
48
+ The one-time code is read from the environment, never from the command line, so it doesn't show up in process lists.
49
+
50
+ ## Options
51
+
52
+ | Option | What it does |
53
+ | --- | --- |
54
+ | `--service` | Keeps the connector running after restarts (systemd on Linux, launchd on macOS, a scheduled task at sign-in on Windows). |
55
+ | `--name NAME` | The name the machine has in the app. Defaults to its host name. |
56
+ | `--origin URL` | Your EvryAI address. The app fills this in. |
57
+
58
+ ## Requirements
59
+
60
+ - Node.js 20.10 or newer. On a machine without Node, use the **curl** (or PowerShell) command from the same dialog
61
+ instead; it brings its own Node.
62
+ - EvryAI Desktop on your computer, signed in to an account with machines turned on.
63
+
64
+ ## After it's connected
65
+
66
+ The connector installs to `~/.evryai/connector` (Windows: `%LOCALAPPDATA%\EvryAI\connector`), the same place the curl
67
+ command uses.
68
+
69
+ ```sh
70
+ ~/.evryai/connector/bin/evryai-connect status # is it connected, and to which computer
71
+ ~/.evryai/connector/bin/evryai-connect update # move to the connector version the app expects
72
+ ~/.evryai/connector/bin/evryai-connect remove [--all] # disconnect from your computers
15
73
  ```
16
74
 
17
- - Needs Node 20.10 or newer. Without Node, use the curl (or PowerShell) command from the same dialog.
18
- - The one-time code is read from the environment, never the command line.
19
- - It installs the connector to `~/.evryai/connector` (Windows: `%LOCALAPPDATA%\EvryAI\connector`), the same place the curl command does, then pairs. `--service` keeps it running after restarts.
20
- - Afterwards: `~/.evryai/connector/bin/evryai-connect status | update | remove [--all]`.
75
+ ## Security
76
+
77
+ This package carries the exact connector the EvryAI app expects: the same version and sha256, checked again before it's
78
+ installed. The app only ever asks a machine to run that build. The server relays traffic but can't read it or run
79
+ anything on the machine.
80
+
81
+ ## License
21
82
 
22
- This package holds the exact connector bundle the EvryAI app pins (same version and sha256), checked again before it's installed.
83
+ Commercial. See [LICENSE](./LICENSE). Use of EvryAI is governed by your agreement with Pendral, Inc. Third-party
84
+ notices are at the end of the bundled files.
Binary file
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // EvryAI connector. Copyright (c) 2026 Pendral, Inc. All rights reserved.
3
- import Y from"fs";import y from"path";import{fileURLToPath as M}from"url";import{spawn as E}from"child_process";import{createHash as T}from"crypto";import i from"fs";import x from"os";import p from"path";var w=n=>`EvryAI connector ${n}
3
+ import Y from"fs";import v from"path";import{fileURLToPath as M}from"url";import{spawn as E}from"child_process";import{createHash as T}from"crypto";import i from"fs";import x from"os";import p from"path";var S=n=>`EvryAI connector ${n}
4
4
 
5
5
  EVRYAI_CODE=CODE npx -y @pendral/connect@${n} --origin URL [--service] [--name NAME]
6
6
 
@@ -12,8 +12,8 @@ rem Written by EvryAI. Runs the connector with the Node it was installed with.\r
12
12
  [ -n "\${HTTPS_PROXY:-}\${https_proxy:-}" ] && export NODE_USE_ENV_PROXY=1
13
13
  exec ${N(n)}${e} ${N(r)} "$@"
14
14
  `}var I=(n,r=process.platform)=>p.join(n,"bin",r==="win32"?"evryai-connect.cmd":"evryai-connect"),A=n=>T("sha256").update(n).digest("hex"),f=class extends Error{constructor(e,s){super(s);this.code=e}};function L(n){let r=p.join(n,"install.lock");try{i.mkdirSync(r)}catch{let e=!1;try{let s=Number(i.readFileSync(p.join(r,"pid"),"utf8").trim());s>1&&(process.kill(s,0),e=Date.now()-i.statSync(r).mtimeMs<10*6e4)}catch{e=!1}if(e)throw new f(1,"Another install is running on this machine. Try again in a minute.");i.rmSync(r,{recursive:!0,force:!0}),i.mkdirSync(r)}return i.writeFileSync(p.join(r,"pid"),`${process.pid}
15
- `),()=>i.rmSync(r,{recursive:!0,force:!0})}function F(n,r,e,s=process.execPath,t=process.versions.node,c=process.platform){var a;if(A(r)!==n.sha256)throw new f(3,"This package's connector didn't match its checksum. Nothing was installed.");for(let m of["bin","lib","tmp"])i.mkdirSync(p.join(e,m),{recursive:!0,mode:448});if(c!=="win32")try{p.basename(p.dirname(e))===".evryai"&&i.chmodSync(p.dirname(e),448),i.chmodSync(e,448)}catch{}let o=L(e);try{let m=p.join(e,"lib",`connector-${n.version}.mjs`),l=i.existsSync(m)?i.readFileSync(m):null;if(!l||A(l)!==n.sha256){let h=p.join(e,"tmp",`connector.${process.pid}.mjs`);i.writeFileSync(h,r,{mode:384}),i.renameSync(h,m)}let u=I(e,c),d=i.existsSync(u)?i.readFileSync(u,"utf8"):"",g=c==="win32"?null:((a=/ '([^']*\/lib\/connector-[^']*\.mjs)' /.exec(d))==null?void 0:a[1])??null;for(let h of i.readdirSync(p.join(e,"lib"))){let v=p.join(e,"lib",h);/^connector-.*\.mjs$/.test(h)&&v!==m&&v!==g&&i.rmSync(v,{force:!0})}let j=P(t,"22.15")?" --use-system-ca":"";return i.writeFileSync(`${u}.tmp`,D(s,m,j,c),{mode:493}),i.chmodSync(`${u}.tmp`,493),i.renameSync(`${u}.tmp`,u),{wrapper:u,lib:m}}finally{o()}}function U(n,r=process.execPath){let e={};for(let[t,c]of Object.entries(n))/^npm_/i.test(t)||t==="INIT_CWD"||t==="NODE"&&c===r||(e[t]=c);let s=Object.keys(e).find(t=>t.toUpperCase()==="PATH");if(s&&e[s]){let t=e[s].split(p.delimiter),c=a=>/[\\/]node_modules[\\/]\.bin$|[\\/]node-gyp-bin$/.test(a),o=0;for(;o<t.length&&c(t[o]);)o++;e[s]=t.slice(o).join(p.delimiter)}return e}function J(n){return["pair",...n.service?["--service"]:[],...n.name?["--name",n.name]:[],"--origin",n.origin]}function $(n,r,e,s){let t=process.platform==="win32",c=t?/ (--use-system-ca) /.test(i.readFileSync(n,"utf8"))?["--use-system-ca"]:[]:[],o=t?E(process.execPath,[...c,r,...e],{stdio:["ignore","inherit","inherit"],env:s}):E(n,e,{stdio:["ignore","inherit","inherit"],env:s}),a=()=>{};for(let l of["SIGINT","SIGHUP","SIGQUIT"])(!t||l==="SIGINT")&&process.on(l,a);let m=()=>o.kill("SIGTERM");return process.on("SIGTERM",m),new Promise(l=>{o.once("error",u=>{process.stderr.write(`Couldn't start the connector: ${u.message}
15
+ `),()=>i.rmSync(r,{recursive:!0,force:!0})}function F(n,r,e,s=process.execPath,t=process.versions.node,c=process.platform){var a;if(A(r)!==n.sha256)throw new f(3,"This package's connector didn't match its checksum. Nothing was installed.");for(let m of["bin","lib","tmp"])i.mkdirSync(p.join(e,m),{recursive:!0,mode:448});if(c!=="win32")try{p.basename(p.dirname(e))===".evryai"&&i.chmodSync(p.dirname(e),448),i.chmodSync(e,448)}catch{}let o=L(e);try{let m=p.join(e,"lib",`connector-${n.version}.mjs`),l=i.existsSync(m)?i.readFileSync(m):null;if(!l||A(l)!==n.sha256){let h=p.join(e,"tmp",`connector.${process.pid}.mjs`);i.writeFileSync(h,r,{mode:384}),i.renameSync(h,m)}let u=I(e,c),d=i.existsSync(u)?i.readFileSync(u,"utf8"):"",g=c==="win32"?null:((a=/ '([^']*\/lib\/connector-[^']*\.mjs)' /.exec(d))==null?void 0:a[1])??null;for(let h of i.readdirSync(p.join(e,"lib"))){let w=p.join(e,"lib",h);/^connector-.*\.mjs$/.test(h)&&w!==m&&w!==g&&i.rmSync(w,{force:!0})}let j=P(t,"22.15")?" --use-system-ca":"";return i.writeFileSync(`${u}.tmp`,D(s,m,j,c),{mode:493}),i.chmodSync(`${u}.tmp`,493),i.renameSync(`${u}.tmp`,u),{wrapper:u,lib:m}}finally{o()}}function U(n,r=process.execPath){let e={};for(let[t,c]of Object.entries(n))/^npm_/i.test(t)||t==="INIT_CWD"||t==="NODE"&&c===r||(e[t]=c);let s=Object.keys(e).find(t=>t.toUpperCase()==="PATH");if(s&&e[s]){let t=e[s].split(p.delimiter),c=a=>/[\\/]node_modules[\\/]\.bin$|[\\/]node-gyp-bin$/.test(a),o=0;for(;o<t.length&&c(t[o]);)o++;e[s]=t.slice(o).join(p.delimiter)}return e}function J(n){return["pair",...n.service?["--service"]:[],...n.name?["--name",n.name]:[],"--origin",n.origin]}function $(n,r,e,s){let t=process.platform==="win32",c=t?/ (--use-system-ca) /.test(i.readFileSync(n,"utf8"))?["--use-system-ca"]:[]:[],o=t?E(process.execPath,[...c,r,...e],{stdio:["ignore","inherit","inherit"],env:s}):E(n,e,{stdio:["ignore","inherit","inherit"],env:s}),a=()=>{};for(let l of["SIGINT","SIGHUP","SIGQUIT"])(!t||l==="SIGINT")&&process.on(l,a);let m=()=>o.kill("SIGTERM");return process.on("SIGTERM",m),new Promise(l=>{o.once("error",u=>{process.stderr.write(`Couldn't start the connector: ${u.message}
16
16
  `),l(1)}),o.once("exit",(u,d)=>l(u??(d?128+(x.constants.signals[d]??0):1)))})}function W(n){var r;try{let e=i.readFileSync(n,"utf8");return((r=/"([^"]*connector-[^"]*\.mjs)"|'([^']*connector-[^']*\.mjs)'/.exec(e))==null?void 0:r.slice(1).find(Boolean))??null}catch{return null}}async function b(n,r,e=process.argv.slice(2)){var u;let s=d=>process.stdout.write(`${d}
17
17
  `),t=d=>process.stderr.write(`${d}
18
- `);if(!P(process.versions.node,n.minNode))return t(`This needs Node ${n.minNode} or newer (this is ${process.versions.node}). Update Node, or use the curl command instead.`),2;let c=process.env.EVRYAI_CODE??"";delete process.env.EVRYAI_CODE;let o=R(e);if(o.kind==="help")return s(w(n.version)),0;if(o.kind==="version")return s(n.version),0;if(o.kind==="error")return t(o.message),t(w(n.version)),2;let a=_();if(!a)return t(process.platform==="win32"?"This needs LOCALAPPDATA to be set.":"This needs a home folder (HOME isn't set)."),2;let m=U(process.env);if(o.kind==="passthrough"){let d=I(a),g=W(d);return g?$(d,g,o.args,c?{...m,EVRYAI_CODE:c}:m):(t("The connector isn't installed on this machine. Make a command in EvryAI: Add machine, then Command."),2)}if(!c)return t(w(n.version)),2;if(process.platform!=="linux"&&process.platform!=="darwin"&&process.platform!=="win32")return t("This works on Linux, macOS and Windows."),2;s(`EvryAI connector ${n.version}`),s(`Installing from npm for ${o.opts.origin}`),process.platform!=="win32"&&((u=process.getuid)==null?void 0:u.call(process))===0&&s("Running as root. Coding agents on this machine will run as root."),s(`Using Node ${process.versions.node} (found on this machine)`);let l;try{l=F(n,i.readFileSync(r),a)}catch(d){return t(d instanceof f?d.message:`Couldn't install the connector: ${d.message}`),d instanceof f?d.code:1}return $(l.wrapper,l.lib,J(o.opts),{...m,EVRYAI_CODE:c})}var O=y.resolve(y.dirname(M(import.meta.url)),".."),S=JSON.parse(Y.readFileSync(y.join(O,"package.json"),"utf8")),k={version:S.version,sha256:S.evryai.sha256,minNode:S.evryai.minNode};b(k,y.join(O,"lib",`connector-${k.version}.mjs`)).then(n=>process.exit(n),n=>{process.stderr.write(`Something went wrong: ${n.message}
18
+ `);if(!P(process.versions.node,n.minNode))return t(`This needs Node ${n.minNode} or newer (this is ${process.versions.node}). Update Node, or use the curl command instead.`),2;let c=process.env.EVRYAI_CODE??"";delete process.env.EVRYAI_CODE;let o=R(e);if(o.kind==="help")return s(S(n.version)),0;if(o.kind==="version")return s(n.version),0;if(o.kind==="error")return t(o.message),t(S(n.version)),2;let a=_();if(!a)return t(process.platform==="win32"?"This needs LOCALAPPDATA to be set.":"This needs a home folder (HOME isn't set)."),2;let m=U(process.env);if(o.kind==="passthrough"){let d=I(a),g=W(d);return g?$(d,g,o.args,c?{...m,EVRYAI_CODE:c}:m):(t("The connector isn't installed on this machine. Make a command in EvryAI: Add machine, then Command."),2)}if(!c)return t(S(n.version)),2;if(process.platform!=="linux"&&process.platform!=="darwin"&&process.platform!=="win32")return t("This works on Linux, macOS and Windows."),2;s(`EvryAI connector ${n.version}`),s(`Installing from npm for ${o.opts.origin}`),process.platform!=="win32"&&((u=process.getuid)==null?void 0:u.call(process))===0&&s("Running as root. Coding agents on this machine will run as root."),s(`Using Node ${process.versions.node} (found on this machine)`);let l;try{l=F(n,i.readFileSync(r),a)}catch(d){return t(d instanceof f?d.message:`Couldn't install the connector: ${d.message}`),d instanceof f?d.code:1}return $(l.wrapper,l.lib,J(o.opts),{...m,EVRYAI_CODE:c})}var O=v.resolve(v.dirname(M(import.meta.url)),".."),y=JSON.parse(Y.readFileSync(v.join(O,"package.json"),"utf8")),k={version:y.evryai.connector??y.version,sha256:y.evryai.sha256,minNode:y.evryai.minNode};b(k,v.join(O,"lib",`connector-${k.version}.mjs`)).then(n=>process.exit(n),n=>{process.stderr.write(`Something went wrong: ${n.message}
19
19
  `),process.exit(1)});
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "@pendral/connect",
3
- "version": "1.0.1",
4
- "description": "Connect a machine to EvryAI without SSH. Run the command EvryAI gives you.",
3
+ "version": "1.0.2",
4
+ "description": "Connect any Linux, macOS or Windows machine to EvryAI without SSH: one command, no open ports, end-to-end encrypted.",
5
5
  "keywords": [
6
6
  "evryai",
7
7
  "pendral",
8
- "remote",
9
- "connector"
8
+ "remote development",
9
+ "coding agents",
10
+ "connector",
11
+ "ssh alternative",
12
+ "claude code",
13
+ "codex"
10
14
  ],
11
- "homepage": "https://evryai.app",
15
+ "homepage": "https://evryai.app/download",
12
16
  "license": "SEE LICENSE IN LICENSE",
13
17
  "author": "Pendral, Inc.",
14
18
  "type": "module",
@@ -18,6 +22,7 @@
18
22
  "files": [
19
23
  "bin",
20
24
  "lib",
25
+ "assets",
21
26
  "README.md",
22
27
  "LICENSE"
23
28
  ],
@@ -25,6 +30,7 @@
25
30
  "node": ">=20.10.0"
26
31
  },
27
32
  "evryai": {
33
+ "connector": "1.0.1",
28
34
  "sha256": "0a8982f0eac947e74599f98195d9bbde431a19aa43ceaf29005f9c86de9598d0",
29
35
  "minNode": "20.10"
30
36
  }