@dashup/connector 0.1.3 → 0.1.4
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 +12 -19
- package/dist/index.js +110 -49
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,23 +8,16 @@ With Node 24:
|
|
|
8
8
|
npx @dashup/connector pair
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
To keep `dashup` as a command, install it with `npm install -g @dashup/connector`. The package bundles the workspace code; screen and input need the native helper from `packages/desktop`, which it does not include.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
pnpm install
|
|
15
|
-
pnpm exec turbo build --filter=@dashup/connector...
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The published package bundles the workspace code, and CI publishes it when the package version is new. Screen and input need the native helper from `packages/desktop`, which the package does not include.
|
|
19
|
-
|
|
20
|
-
`dashup pair` walks through pairing in the terminal: it shows a QR code that expires in five minutes, waits for a phone to scan it under Pair a computer, and offers to keep Dashup running in the background. The first phone adds the computer to its account, so the walkthrough names that account and shares nothing until you confirm it. Later codes pair more phones on the same account without asking, including through a connector that is already running. A phone that scans an expired code gets a pairing the computer revokes, because it never kept that key. Pairing needs a terminal: without one, an unpaired computer exits and says so. It uses `https://api.dashup.com`; for a local API, add `--api http://localhost:3000`. A physical phone needs a reachable HTTPS API. To run a paired connector in the foreground, use `dashup watch`.
|
|
13
|
+
`dashup pair` walks through pairing in the terminal: it shows a QR code that expires in five minutes, waits for a phone to scan it under Pair a computer, and offers to keep Dashup running in the background. The first phone adds the computer to its account, so the walkthrough names that account and shares nothing until you confirm it. Later codes pair more phones on the same account without asking, including through a connector that is already running. A phone that scans an expired code gets a pairing the computer revokes, because it never kept that key. To pair without the prompt, as an agent can, name the account with `--account you@example.com`; a phone signed in to any other account is refused. Without a terminal, the code opens in a browser on this computer instead of printing, because it carries the pairing's encryption key and an agent's output leaves the computer. Pairing then exits, and `dashup install` keeps Dashup running. Where no browser can open, pairing needs a terminal. It uses `https://api.dashup.com`; for a local API, add `--api http://localhost:3000`. A physical phone needs a reachable HTTPS API. To run a paired connector in the foreground, use `dashup watch`.
|
|
21
14
|
|
|
22
15
|
## Existing projects and alerts
|
|
23
16
|
|
|
24
17
|
```sh
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
dashup doctor
|
|
19
|
+
dashup projects
|
|
20
|
+
dashup alert --project PROJECT_ID --title "Dashup is connected"
|
|
28
21
|
```
|
|
29
22
|
|
|
30
23
|
The connector automatically reads existing Claude Code, Codex, and OpenCode conversations alongside Zed workspaces and VS Code chat history. It honors the harness's existing data directory, authentication, and native session IDs. Native stores are opened read-only; discovery never launches an agent, creates or resumes a conversation, or changes editor settings. Opening the same supported harness session through Zed and its native history uses one Dashup thread when their actual session identities match. Matching directory names alone never merges local and SSH projects.
|
|
@@ -63,10 +56,10 @@ Live discovery is bounded to 100 sessions per endpoint. OpenCode supplies the la
|
|
|
63
56
|
The default history and native-host path needs no wrapper or editor configuration. For an explicitly configured external ACP agent, use the connector as the editor's stdio wrapper; it uses the official ACP SDK's protocol contracts:
|
|
64
57
|
|
|
65
58
|
```sh
|
|
66
|
-
|
|
59
|
+
dashup acp -- /absolute/path/to/original-agent ORIGINAL_ARGUMENTS
|
|
67
60
|
```
|
|
68
61
|
|
|
69
|
-
|
|
62
|
+
Install `dashup` globally and set the editor's existing agent command to it, with the arguments above. Start the connector first and launch the agent through that editor configuration. The proxy starts the original command once and forwards the editor's own ACP stream. Phone messages and approval choices go to that same process and session. Only native approval choices are offered, including native file scope when the agent provides it. The proxy does not create another session when opening a phone thread.
|
|
70
63
|
|
|
71
64
|
The wrapper and connector must run on the same execution host. For SSH projects, confirm where that particular external agent runs before configuring its command; [Zed treats external-agent runtime and authentication as agent-owned](https://zed.dev/docs/ai/external-agents#remote-projects). Pair the development machine separately if it owns the wrapper process. Reading the PC's saved SSH project does not attach to a remote agent process.
|
|
72
65
|
|
|
@@ -78,8 +71,8 @@ Build the native WebRTC helper on the computer, following [desktop build require
|
|
|
78
71
|
|
|
79
72
|
```sh
|
|
80
73
|
pnpm --filter @dashup/desktop build
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
dashup permissions --screen
|
|
75
|
+
dashup permissions --screen --control
|
|
83
76
|
```
|
|
84
77
|
|
|
85
78
|
Run `permissions` without flags to disable access. Video and input use a WebRTC peer connection; SDP, ICE candidates, and DTLS fingerprints travel inside paired-device encrypted envelopes. Only one screen session is active at a time. The relay does not carry decoded frames. Set `DASHUP_DESKTOP_BIN` if the helper lives outside its workspace build path.
|
|
@@ -88,15 +81,15 @@ macOS requires Screen Recording and Accessibility permission. Windows uses the s
|
|
|
88
81
|
|
|
89
82
|
## Start at login
|
|
90
83
|
|
|
91
|
-
Answering yes to the walkthrough's background question, or running `dashup install` on a paired computer, registers a macOS LaunchAgent, Windows Startup entry, or Linux user systemd service and starts it, taking over from any connector already running for that state file. The service runs `watch` with the state file it was paired with (`DASHUP_CONFIG`). Linux requires a user systemd session. Under npx or pnpm dlx, whose caches can be cleared, the service runs a copy of that install kept in `app/` beside the state file; a checkout or global install runs in place. The Node executable must stay at its installed location. Signed desktop installers are not included.
|
|
84
|
+
Answering yes to the walkthrough's background question, or running `dashup install` on a paired computer, registers a macOS LaunchAgent, Windows Startup entry, or Linux user systemd service and starts it, taking over from any connector already running for that state file. The service runs `watch` with the state file it was paired with (`DASHUP_CONFIG`). Linux requires a user systemd session; without a desktop session, as over SSH, install also turns on lingering so the service keeps running after you log out. Under npx or pnpm dlx, whose caches can be cleared, the service runs a copy of that install kept in `app/` beside the state file; a checkout or global install runs in place. The Node executable must stay at its installed location. Signed desktop installers are not included.
|
|
92
85
|
|
|
93
|
-
|
|
86
|
+
`dashup uninstall` stops Dashup and removes the startup entry and its `app/` copy; the computer stays paired. `dashup stop` leaves the service stopped until the next sign-in; a crash restarts it.
|
|
94
87
|
|
|
95
88
|
## Local state and checks
|
|
96
89
|
|
|
97
90
|
`~/.dashup/connector.json` holds this computer's secret, pairing keys, encrypted outbox, and replay counters. It is written atomically with private Unix permissions; it is not currently protected by an OS credential vault. Do not share or copy it to another connector. `DASHUP_CONFIG` selects a separate state file. `DASHUP_ZED_DATA` and `DASHUP_VSCODE_DATA` select custom editor data directories. Native history discovery honors `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, and XDG paths. The selected conversation is parsed locally on demand; Dashup does not create another plaintext transcript database.
|
|
98
91
|
|
|
99
|
-
For isolated development fixtures, `DASHUP_LIVE_DISCOVERY=0` disables automatic process discovery. Explicit `DASHUP_OPENCODE_ENDPOINT`, `DASHUP_CODEX_SOCKET`, and `DASHUP_AHP_DATA` overrides allow local test endpoints without modifying editor configuration. They are not required for ordinary discovery.
|
|
92
|
+
From a checkout, run `pnpm install` and `pnpm exec turbo build --filter=@dashup/connector...` with pnpm 10.34.5, then use `pnpm --filter @dashup/connector dashup` in place of `dashup`. For isolated development fixtures, `DASHUP_LIVE_DISCOVERY=0` disables automatic process discovery. Explicit `DASHUP_OPENCODE_ENDPOINT`, `DASHUP_CODEX_SOCKET`, and `DASHUP_AHP_DATA` overrides allow local test endpoints without modifying editor configuration. They are not required for ordinary discovery.
|
|
100
93
|
|
|
101
94
|
```sh
|
|
102
95
|
pnpm --filter @dashup/connector test
|
package/dist/index.js
CHANGED
|
@@ -2706,7 +2706,7 @@ var require_pluralize = __commonJS({
|
|
|
2706
2706
|
|
|
2707
2707
|
// src/index.ts
|
|
2708
2708
|
import { randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
|
|
2709
|
-
import { createServer } from "node:http";
|
|
2709
|
+
import { createServer as createServer2 } from "node:http";
|
|
2710
2710
|
import * as ui2 from "@clack/prompts";
|
|
2711
2711
|
import { z as z3 } from "zod";
|
|
2712
2712
|
|
|
@@ -4004,13 +4004,16 @@ async function proxyAcp(config2, executable, args2) {
|
|
|
4004
4004
|
|
|
4005
4005
|
// src/pair.ts
|
|
4006
4006
|
import { hostname } from "node:os";
|
|
4007
|
+
import { promisify } from "node:util";
|
|
4008
|
+
import { createServer } from "node:http";
|
|
4009
|
+
import { execFile } from "node:child_process";
|
|
4007
4010
|
import { randomBytes as randomBytes2, createHash as createHash2 } from "node:crypto";
|
|
4008
4011
|
import QRCode from "qrcode";
|
|
4009
4012
|
import * as ui from "@clack/prompts";
|
|
4010
4013
|
import { z } from "zod";
|
|
4011
4014
|
|
|
4012
4015
|
// package.json
|
|
4013
|
-
var version = "0.1.
|
|
4016
|
+
var version = "0.1.4";
|
|
4014
4017
|
|
|
4015
4018
|
// src/pair.ts
|
|
4016
4019
|
var Invitation = z.object({
|
|
@@ -4026,29 +4029,55 @@ function invitation(config2, secret = config2.secret) {
|
|
|
4026
4029
|
const computer = createHash2("sha256").update(secret).digest("hex");
|
|
4027
4030
|
return { code, key, text: formatPairingCode({ api: config2.api, computer, code, key }) };
|
|
4028
4031
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4032
|
+
var SCAN = "Scan this in the Dashup app under Pair a computer. It expires in 5 minutes.";
|
|
4033
|
+
var BROWSER = {
|
|
4034
|
+
darwin: ["open"],
|
|
4035
|
+
win32: ["rundll32", "url.dll,FileProtocolHandler"]
|
|
4036
|
+
};
|
|
4037
|
+
var NO_BROWSER = "No browser here can show the pairing code. Run dashup pair in a terminal on this computer.";
|
|
4038
|
+
async function show(text3) {
|
|
4039
|
+
if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY)
|
|
4040
|
+
throw new Error(NO_BROWSER);
|
|
4041
|
+
const path = `/${randomBytes2(16).toString("hex")}`;
|
|
4042
|
+
const qr = await QRCode.toString(text3, { type: "svg", margin: 2, errorCorrectionLevel: "L" });
|
|
4043
|
+
const page = `<!doctype html><meta charset="utf-8"><title>Pair with Dashup</title><style>body{margin:0;min-height:100vh;display:grid;place-items:center;background:#fff;color:#16181d;font:16px/1.5 system-ui,sans-serif}main{max-width:22rem;padding:2rem;text-align:center}svg{width:100%;margin:1rem 0}code{font-size:11px;word-break:break-all;color:#5d6470}</style><main><p>${SCAN}</p>${qr}<p>Can't scan? Paste this code instead:</p><code>${text3}</code></main>`;
|
|
4044
|
+
const server = createServer((request2, response) => {
|
|
4045
|
+
if (request2.url !== path) return void response.writeHead(404).end();
|
|
4046
|
+
response.writeHead(200, {
|
|
4047
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
4048
|
+
"Cache-Control": "no-store",
|
|
4049
|
+
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'",
|
|
4050
|
+
"Referrer-Policy": "no-referrer"
|
|
4051
|
+
});
|
|
4052
|
+
response.end(page);
|
|
4034
4053
|
});
|
|
4035
|
-
|
|
4054
|
+
await new Promise((resolve3) => server.listen(0, "127.0.0.1", resolve3));
|
|
4055
|
+
const [command2, ...args2] = BROWSER[process.platform] ?? ["xdg-open"];
|
|
4056
|
+
const url2 = `http://127.0.0.1:${server.address().port}${path}`;
|
|
4057
|
+
try {
|
|
4058
|
+
await promisify(execFile)(command2, [...args2, url2]);
|
|
4059
|
+
} catch {
|
|
4060
|
+
server.close();
|
|
4061
|
+
throw new Error(NO_BROWSER);
|
|
4062
|
+
}
|
|
4063
|
+
process.stdout.write(`Opened the pairing code in a browser on this computer. ${SCAN}
|
|
4064
|
+
`);
|
|
4065
|
+
return () => server.close();
|
|
4066
|
+
}
|
|
4067
|
+
async function scanned(config2, { code, text: text3 }, done, secret = config2.secret) {
|
|
4036
4068
|
if (interactive) {
|
|
4037
|
-
|
|
4069
|
+
const qr = await QRCode.toString(text3, {
|
|
4070
|
+
type: "terminal",
|
|
4071
|
+
small: true,
|
|
4072
|
+
errorCorrectionLevel: "L"
|
|
4073
|
+
});
|
|
4074
|
+
ui.log.step(SCAN);
|
|
4038
4075
|
ui.log.message("Can't scan? Paste this code instead:", { spacing: 0 });
|
|
4039
4076
|
process.stdout.write(` ${text3}
|
|
4040
4077
|
`);
|
|
4041
|
-
ui.log.message(
|
|
4042
|
-
} else {
|
|
4043
|
-
process.stdout.write(
|
|
4044
|
-
`
|
|
4045
|
-
Pairing code: ${text3}
|
|
4046
|
-
|
|
4047
|
-
${rows.join("\n")}
|
|
4048
|
-
Scan this in the Dashup app (Pair a computer). It expires in 5 minutes.
|
|
4049
|
-
`
|
|
4050
|
-
);
|
|
4078
|
+
ui.log.message(qr.split("\n").filter((row) => row.replace(/\x1b\[\d+m/g, "")));
|
|
4051
4079
|
}
|
|
4080
|
+
const close = interactive ? void 0 : await show(text3);
|
|
4052
4081
|
const waiting = interactive ? ui.spinner() : void 0;
|
|
4053
4082
|
waiting?.start("Waiting for your phone");
|
|
4054
4083
|
try {
|
|
@@ -4071,16 +4100,18 @@ Scan this in the Dashup app (Pair a computer). It expires in 5 minutes.
|
|
|
4071
4100
|
if (!waiting) throw error;
|
|
4072
4101
|
waiting.error(error instanceof Error ? error.message : "Pairing failed");
|
|
4073
4102
|
throw Object.assign(error, { reported: true });
|
|
4103
|
+
} finally {
|
|
4104
|
+
close?.();
|
|
4074
4105
|
}
|
|
4075
4106
|
}
|
|
4076
4107
|
var reported = (error) => error?.reported === true;
|
|
4077
|
-
async function link(config2) {
|
|
4108
|
+
async function link(config2, account) {
|
|
4078
4109
|
const secret = newSecret(randomBytes2);
|
|
4079
4110
|
const invite = invitation(config2, secret);
|
|
4080
4111
|
const { pairing, email } = await scanned(
|
|
4081
4112
|
config2,
|
|
4082
4113
|
invite,
|
|
4083
|
-
(
|
|
4114
|
+
(account2) => `Scanned by ${account2}`,
|
|
4084
4115
|
secret
|
|
4085
4116
|
);
|
|
4086
4117
|
const linked = { ...config2, secret };
|
|
@@ -4091,14 +4122,13 @@ async function link(config2) {
|
|
|
4091
4122
|
kind: "desktop",
|
|
4092
4123
|
platform
|
|
4093
4124
|
});
|
|
4094
|
-
const confirmed = await ui.confirm({
|
|
4095
|
-
message: `Pair this computer with ${email}?`,
|
|
4096
|
-
initialValue: false
|
|
4097
|
-
});
|
|
4125
|
+
const confirmed = account ? email.toLowerCase() === account.toLowerCase() : await ui.confirm({ message: `Pair this computer with ${email}?`, initialValue: false });
|
|
4098
4126
|
if (confirmed !== true) {
|
|
4099
4127
|
await api(linked, `/dashup/pairings/${pairing.id}`, void 0, "DELETE").catch(() => {
|
|
4100
4128
|
});
|
|
4101
|
-
ui.cancel(
|
|
4129
|
+
ui.cancel(
|
|
4130
|
+
account ? `Pairing cancelled: that phone uses ${email}, not ${account}. Nothing was shared.` : "Pairing cancelled. Nothing was shared."
|
|
4131
|
+
);
|
|
4102
4132
|
return false;
|
|
4103
4133
|
}
|
|
4104
4134
|
Object.assign(config2, {
|
|
@@ -4120,9 +4150,9 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
4120
4150
|
import { spawn as spawn2 } from "node:child_process";
|
|
4121
4151
|
|
|
4122
4152
|
// src/screen.ts
|
|
4123
|
-
import { promisify } from "node:util";
|
|
4124
|
-
import { execFile, spawnSync } from "node:child_process";
|
|
4125
|
-
var run =
|
|
4153
|
+
import { promisify as promisify2 } from "node:util";
|
|
4154
|
+
import { execFile as execFile2, spawnSync } from "node:child_process";
|
|
4155
|
+
var run = promisify2(execFile2);
|
|
4126
4156
|
var powershell = (source) => run("powershell.exe", [
|
|
4127
4157
|
"-NoProfile",
|
|
4128
4158
|
"-NonInteractive",
|
|
@@ -16704,9 +16734,9 @@ var Codex = class {
|
|
|
16704
16734
|
import Socket3 from "ws";
|
|
16705
16735
|
import { homedir as homedir5 } from "node:os";
|
|
16706
16736
|
import { connect as connect2 } from "node:net";
|
|
16707
|
-
import { promisify as
|
|
16737
|
+
import { promisify as promisify3 } from "node:util";
|
|
16708
16738
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
16709
|
-
import { execFile as
|
|
16739
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
16710
16740
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
16711
16741
|
import { stat as stat2, readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
|
|
16712
16742
|
import { join as join5, dirname as dirname3, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
|
|
@@ -16719,7 +16749,7 @@ var MAX_METADATA = 64 * 1024;
|
|
|
16719
16749
|
var MAX_HOSTS = 16;
|
|
16720
16750
|
var MAX_SESSIONS2 = 100;
|
|
16721
16751
|
var ROOT_CHANNEL = "ahp-root://";
|
|
16722
|
-
var run2 =
|
|
16752
|
+
var run2 = promisify3(execFile3);
|
|
16723
16753
|
var CONFIRMATION_CHOICES = [
|
|
16724
16754
|
{ id: "approve", label: "Allow once", kind: "approve" },
|
|
16725
16755
|
{ id: "deny", label: "Reject", kind: "deny" }
|
|
@@ -17201,11 +17231,11 @@ var VSCode = class {
|
|
|
17201
17231
|
|
|
17202
17232
|
// src/harness/opencode.ts
|
|
17203
17233
|
import { join as join6 } from "node:path";
|
|
17204
|
-
import { promisify as
|
|
17234
|
+
import { promisify as promisify4 } from "node:util";
|
|
17205
17235
|
import { createHash as createHash4 } from "node:crypto";
|
|
17206
|
-
import { execFile as
|
|
17236
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
17207
17237
|
import { stat as stat3, readlink, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
17208
|
-
var run3 =
|
|
17238
|
+
var run3 = promisify4(execFile4);
|
|
17209
17239
|
var REQUEST_MS3 = 4e3;
|
|
17210
17240
|
var MAX_BYTES3 = 8 * 1024 * 1024;
|
|
17211
17241
|
var MAX_SESSIONS3 = 100;
|
|
@@ -18790,13 +18820,13 @@ var Connector = class {
|
|
|
18790
18820
|
|
|
18791
18821
|
// src/install.ts
|
|
18792
18822
|
import { existsSync as existsSync6 } from "node:fs";
|
|
18793
|
-
import { homedir as homedir6 } from "node:os";
|
|
18794
|
-
import { promisify as
|
|
18823
|
+
import { homedir as homedir6, userInfo } from "node:os";
|
|
18824
|
+
import { promisify as promisify5 } from "node:util";
|
|
18795
18825
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
18796
|
-
import { execFile as
|
|
18826
|
+
import { execFile as execFile5, spawn as spawn4 } from "node:child_process";
|
|
18797
18827
|
import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
18798
18828
|
import { dirname as dirname5, join as join8, relative, sep } from "node:path";
|
|
18799
|
-
var run4 =
|
|
18829
|
+
var run4 = promisify5(execFile5);
|
|
18800
18830
|
var xml = (value2) => value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
18801
18831
|
var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
18802
18832
|
var serviceFile = () => process.platform === "darwin" ? join8(homedir6(), "Library/LaunchAgents/com.dashup.connector.plist") : process.platform === "win32" ? join8(
|
|
@@ -18863,8 +18893,23 @@ async function installStartup() {
|
|
|
18863
18893
|
await run4("systemctl", ["--user", "daemon-reload"]);
|
|
18864
18894
|
await run4("systemctl", ["--user", "enable", "dashup.service"]);
|
|
18865
18895
|
await run4("systemctl", ["--user", "restart", "dashup.service"]);
|
|
18896
|
+
if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY)
|
|
18897
|
+
return run4("loginctl", ["enable-linger"]).then(
|
|
18898
|
+
() => void 0,
|
|
18899
|
+
() => `It stops when you log out. To keep it running, run sudo loginctl enable-linger ${userInfo().username}.`
|
|
18900
|
+
);
|
|
18866
18901
|
}
|
|
18867
18902
|
}
|
|
18903
|
+
async function uninstallStartup() {
|
|
18904
|
+
const path = serviceFile();
|
|
18905
|
+
if (process.platform === "darwin") await run4("launchctl", ["unload", path]).catch(() => {
|
|
18906
|
+
});
|
|
18907
|
+
else if (process.platform !== "win32")
|
|
18908
|
+
await run4("systemctl", ["--user", "disable", "dashup.service"]).catch(() => {
|
|
18909
|
+
});
|
|
18910
|
+
await rm(path, { force: true });
|
|
18911
|
+
await rm(join8(dirname5(configPath()), "app"), { recursive: true, force: true });
|
|
18912
|
+
}
|
|
18868
18913
|
|
|
18869
18914
|
// src/index.ts
|
|
18870
18915
|
var config = loadConfig();
|
|
@@ -18902,9 +18947,9 @@ async function background() {
|
|
|
18902
18947
|
try {
|
|
18903
18948
|
await running("/stop", { method: "POST" }).catch(() => null);
|
|
18904
18949
|
await answering(false);
|
|
18905
|
-
await installStartup();
|
|
18950
|
+
const note = await installStartup();
|
|
18906
18951
|
if (await answering(true)) {
|
|
18907
|
-
const message =
|
|
18952
|
+
const message = `Dashup runs in the background and starts when you sign in.${note ? ` ${note}` : ""}`;
|
|
18908
18953
|
if (waiting) waiting.stop(message);
|
|
18909
18954
|
else process.stdout.write(`${message}
|
|
18910
18955
|
`);
|
|
@@ -18941,11 +18986,21 @@ async function main() {
|
|
|
18941
18986
|
throw new Error("Use a separate DASHUP_CONFIG file to connect a different server");
|
|
18942
18987
|
config.api = url2.origin;
|
|
18943
18988
|
}
|
|
18989
|
+
const account = option("--account");
|
|
18990
|
+
if (account !== void 0 && !/^[^\s@]+@[^\s@]+$/.test(account))
|
|
18991
|
+
throw new Error("Use --account with the email you sign in to the Dashup app with");
|
|
18944
18992
|
if (command === "install") {
|
|
18945
18993
|
if (!config.secret) throw new Error("Pair this computer first: run dashup pair.");
|
|
18946
18994
|
if (!await background()) process.exitCode = 1;
|
|
18947
18995
|
return;
|
|
18948
18996
|
}
|
|
18997
|
+
if (command === "uninstall") {
|
|
18998
|
+
await running("/stop", { method: "POST" }).catch(() => null);
|
|
18999
|
+
await answering(false);
|
|
19000
|
+
await uninstallStartup();
|
|
19001
|
+
process.stdout.write("Dashup no longer starts when you sign in. This computer stays paired.\n");
|
|
19002
|
+
return;
|
|
19003
|
+
}
|
|
18949
19004
|
if (command === "stop") {
|
|
18950
19005
|
const response = await running("/stop", { method: "POST" }).catch(refused);
|
|
18951
19006
|
if (!response) throw new Error("Dashup is not running on this computer");
|
|
@@ -19036,20 +19091,24 @@ async function main() {
|
|
|
19036
19091
|
}
|
|
19037
19092
|
if (!["pair", "watch"].includes(command)) {
|
|
19038
19093
|
process.stdout.write(
|
|
19039
|
-
"Dashup: pair | watch | projects | alert --project <id> | doctor | permissions --screen --control | stop | acp -- <original-agent-command> | install\n"
|
|
19094
|
+
"Dashup: pair [--account <email>] | watch | projects | alert --project <id> | doctor | permissions --screen --control | stop | acp -- <original-agent-command> | install | uninstall\n"
|
|
19040
19095
|
);
|
|
19041
19096
|
return;
|
|
19042
19097
|
}
|
|
19043
19098
|
const pairPhone = (invite) => scanned(config, invite, (email) => `Paired a phone signed in as ${email}`);
|
|
19044
19099
|
const keepRunning = async (stopHere) => {
|
|
19045
|
-
|
|
19046
|
-
|
|
19047
|
-
if (answer === true) {
|
|
19100
|
+
const answer = startupInstalled() || interactive && await ui2.confirm({ message: BACKGROUND }) === true;
|
|
19101
|
+
if (answer) {
|
|
19048
19102
|
stopHere();
|
|
19049
19103
|
const started = await background();
|
|
19050
19104
|
ui2.outro(started ? DONE2 : "Run dashup to keep Dashup running in this terminal.");
|
|
19051
19105
|
process.exit(started ? 0 : 1);
|
|
19052
19106
|
}
|
|
19107
|
+
if (!interactive) {
|
|
19108
|
+
stopHere();
|
|
19109
|
+
process.stdout.write("Paired. Run dashup install to keep Dashup running in the background.\n");
|
|
19110
|
+
process.exit(0);
|
|
19111
|
+
}
|
|
19053
19112
|
ui2.log.info("Dashup runs in this terminal. Keep it open; Ctrl+C stops it.");
|
|
19054
19113
|
ui2.outro(DONE2);
|
|
19055
19114
|
};
|
|
@@ -19075,10 +19134,12 @@ async function main() {
|
|
|
19075
19134
|
if (interactive) ui2.outro(DONE2);
|
|
19076
19135
|
process.exit();
|
|
19077
19136
|
}
|
|
19078
|
-
if (!config.secret && !interactive)
|
|
19079
|
-
throw new Error(
|
|
19137
|
+
if (!config.secret && !interactive && !account)
|
|
19138
|
+
throw new Error(
|
|
19139
|
+
"To pair this computer, run dashup pair in a terminal, or add --account with your Dashup email."
|
|
19140
|
+
);
|
|
19080
19141
|
let connector;
|
|
19081
|
-
const server =
|
|
19142
|
+
const server = createServer2(async (request2, response) => {
|
|
19082
19143
|
if (!authorized(request2)) {
|
|
19083
19144
|
response.writeHead(403).end();
|
|
19084
19145
|
return;
|
|
@@ -19157,7 +19218,7 @@ async function main() {
|
|
|
19157
19218
|
const linking = !config.secret;
|
|
19158
19219
|
if (linking) {
|
|
19159
19220
|
intro2();
|
|
19160
|
-
if (!await link(config)) process.exit(1);
|
|
19221
|
+
if (!await link(config, account)) process.exit(1);
|
|
19161
19222
|
await keepRunning(() => server.close());
|
|
19162
19223
|
}
|
|
19163
19224
|
connector = new Connector(config);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dashup/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dashup": "dist/index.js"
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
30
|
"tsx": "^4.22.4",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
|
-
"@dashup/models": "0.1.0",
|
|
33
|
-
"@dashup/protocol": "0.1.0",
|
|
34
32
|
"@dashup/typescript-config": "0.0.0",
|
|
35
33
|
"@parcae/lynx": "0.1.0",
|
|
34
|
+
"@dashup/models": "0.1.0",
|
|
35
|
+
"@dashup/protocol": "0.1.0",
|
|
36
36
|
"@parcae/sdk": "0.8.1"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|