@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 +72 -10
- package/assets/banner.png +0 -0
- package/bin/evryai-connect.mjs +3 -3
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,22 +1,84 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
package/bin/evryai-connect.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
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.
|
|
4
|
-
"description": "Connect
|
|
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
|
-
"
|
|
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
|
}
|