@hwip/cli 0.0.1 → 0.1.1
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/LICENSE +190 -0
- package/NOTICE +11 -0
- package/README.md +1983 -0
- package/dist/agent-list.d.ts +76 -0
- package/dist/agent-list.js +102 -0
- package/dist/agent-read.d.ts +105 -0
- package/dist/agent-read.js +309 -0
- package/dist/agent-rules-fetch.d.ts +51 -0
- package/dist/agent-rules-fetch.js +275 -0
- package/dist/agent-rules.default.d.ts +30 -0
- package/dist/agent-rules.default.js +305 -0
- package/dist/agent-session.d.ts +23 -0
- package/dist/agent-session.js +44 -0
- package/dist/agent-state.d.ts +84 -0
- package/dist/agent-state.js +188 -0
- package/dist/agent-target.d.ts +60 -0
- package/dist/agent-target.js +81 -0
- package/dist/agents.d.ts +31 -0
- package/dist/agents.js +75 -0
- package/dist/api.d.ts +28 -0
- package/dist/api.js +76 -0
- package/dist/args.d.ts +74 -0
- package/dist/args.js +254 -0
- package/dist/ask-context-attach.d.ts +73 -0
- package/dist/ask-context-attach.js +151 -0
- package/dist/ask-context-screen.d.ts +21 -0
- package/dist/ask-context-screen.js +49 -0
- package/dist/ask-context-switch.d.ts +31 -0
- package/dist/ask-context-switch.js +74 -0
- package/dist/ask-context.d.ts +84 -0
- package/dist/ask-context.js +172 -0
- package/dist/ask-pending.d.ts +47 -0
- package/dist/ask-pending.js +221 -0
- package/dist/ask-seal.d.ts +120 -0
- package/dist/ask-seal.js +179 -0
- package/dist/ask.d.ts +107 -0
- package/dist/ask.js +551 -0
- package/dist/blocked-prompt-seal.d.ts +39 -0
- package/dist/blocked-prompt-seal.js +92 -0
- package/dist/blocked-prompt.d.ts +62 -0
- package/dist/blocked-prompt.js +518 -0
- package/dist/channel.d.ts +67 -0
- package/dist/channel.js +136 -0
- package/dist/check-update.d.ts +28 -0
- package/dist/check-update.js +153 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +813 -0
- package/dist/codex-config.d.ts +24 -0
- package/dist/codex-config.js +169 -0
- package/dist/codex-transcript.d.ts +59 -0
- package/dist/codex-transcript.js +373 -0
- package/dist/command-refusal.d.ts +56 -0
- package/dist/command-refusal.js +127 -0
- package/dist/command-scan.d.ts +46 -0
- package/dist/command-scan.js +154 -0
- package/dist/command-seal.d.ts +57 -0
- package/dist/command-seal.js +88 -0
- package/dist/command-seq.d.ts +30 -0
- package/dist/command-seq.js +150 -0
- package/dist/config.d.ts +156 -0
- package/dist/config.js +392 -0
- package/dist/core-sections.d.ts +18 -0
- package/dist/core-sections.js +28 -0
- package/dist/crypto.d.ts +278 -0
- package/dist/crypto.js +698 -0
- package/dist/desktop-notify.d.ts +33 -0
- package/dist/desktop-notify.js +49 -0
- package/dist/device-directory.d.ts +36 -0
- package/dist/device-directory.js +97 -0
- package/dist/device-presence.d.ts +17 -0
- package/dist/device-presence.js +62 -0
- package/dist/downloads.d.ts +29 -0
- package/dist/downloads.js +106 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +28 -0
- package/dist/file-crypto.d.ts +94 -0
- package/dist/file-crypto.js +278 -0
- package/dist/file-view-budget.d.ts +15 -0
- package/dist/file-view-budget.js +34 -0
- package/dist/file-view-switch.d.ts +28 -0
- package/dist/file-view-switch.js +122 -0
- package/dist/file-view.d.ts +108 -0
- package/dist/file-view.js +327 -0
- package/dist/gate.d.ts +141 -0
- package/dist/gate.js +336 -0
- package/dist/gemini-transcript.d.ts +32 -0
- package/dist/gemini-transcript.js +189 -0
- package/dist/http-failure.d.ts +98 -0
- package/dist/http-failure.js +349 -0
- package/dist/hwip-core.generated.d.ts +7 -0
- package/dist/hwip-core.generated.js +14 -0
- package/dist/hwip-hook.d.ts +77 -0
- package/dist/hwip-hook.js +340 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/input-sequencer.d.ts +65 -0
- package/dist/input-sequencer.js +142 -0
- package/dist/insecure-url.d.ts +16 -0
- package/dist/insecure-url.js +76 -0
- package/dist/installer.d.ts +96 -0
- package/dist/installer.js +850 -0
- package/dist/inventory-offload.d.ts +49 -0
- package/dist/inventory-offload.js +209 -0
- package/dist/inventory-worker.d.ts +2 -0
- package/dist/inventory-worker.js +27 -0
- package/dist/jsonc-doc.d.ts +106 -0
- package/dist/jsonc-doc.js +354 -0
- package/dist/keep-awake.d.ts +32 -0
- package/dist/keep-awake.js +46 -0
- package/dist/key-changes.d.ts +48 -0
- package/dist/key-changes.js +140 -0
- package/dist/lan-auth.d.ts +140 -0
- package/dist/lan-auth.js +217 -0
- package/dist/lan-endpoint.d.ts +41 -0
- package/dist/lan-endpoint.js +115 -0
- package/dist/lan-receiver.d.ts +122 -0
- package/dist/lan-receiver.js +510 -0
- package/dist/lan-sender.d.ts +76 -0
- package/dist/lan-sender.js +184 -0
- package/dist/lan-transfer.d.ts +36 -0
- package/dist/lan-transfer.js +54 -0
- package/dist/listener-device-id.d.ts +20 -0
- package/dist/listener-device-id.js +133 -0
- package/dist/listener-log.d.ts +4 -0
- package/dist/listener-log.js +23 -0
- package/dist/listener-process.d.ts +200 -0
- package/dist/listener-process.js +470 -0
- package/dist/listener-service.d.ts +225 -0
- package/dist/listener-service.js +682 -0
- package/dist/listener-worktree-cleanup.d.ts +27 -0
- package/dist/listener-worktree-cleanup.js +62 -0
- package/dist/listener.d.ts +1325 -0
- package/dist/listener.js +5681 -0
- package/dist/login-address.d.ts +25 -0
- package/dist/login-address.js +97 -0
- package/dist/login-page.d.ts +29 -0
- package/dist/login-page.js +93 -0
- package/dist/login.d.ts +98 -0
- package/dist/login.js +388 -0
- package/dist/mcp-command.d.ts +45 -0
- package/dist/mcp-command.js +50 -0
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.js +29 -0
- package/dist/node-support.d.ts +19 -0
- package/dist/node-support.js +38 -0
- package/dist/notify-failure.d.ts +57 -0
- package/dist/notify-failure.js +150 -0
- package/dist/opencode-transcript.d.ts +139 -0
- package/dist/opencode-transcript.js +390 -0
- package/dist/optional-features.d.ts +6 -0
- package/dist/optional-features.js +19 -0
- package/dist/pane-fit.d.ts +40 -0
- package/dist/pane-fit.js +119 -0
- package/dist/peer-key.d.ts +33 -0
- package/dist/peer-key.js +34 -0
- package/dist/pi-transcript.d.ts +26 -0
- package/dist/pi-transcript.js +255 -0
- package/dist/presence.d.ts +14 -0
- package/dist/presence.js +124 -0
- package/dist/private-files.d.ts +51 -0
- package/dist/private-files.js +218 -0
- package/dist/project-switch.d.ts +19 -0
- package/dist/project-switch.js +60 -0
- package/dist/redact.d.ts +10 -0
- package/dist/redact.js +21 -0
- package/dist/remote-agents.d.ts +410 -0
- package/dist/remote-agents.js +1218 -0
- package/dist/remote-hook.d.ts +12 -0
- package/dist/remote-hook.js +196 -0
- package/dist/replace-file.d.ts +50 -0
- package/dist/replace-file.js +328 -0
- package/dist/schedule-command.d.ts +35 -0
- package/dist/schedule-command.js +260 -0
- package/dist/schedule-launch.d.ts +15 -0
- package/dist/schedule-launch.js +24 -0
- package/dist/schedule-ready.d.ts +44 -0
- package/dist/schedule-ready.js +71 -0
- package/dist/schedule-remote.d.ts +77 -0
- package/dist/schedule-remote.js +167 -0
- package/dist/schedule-runner.d.ts +89 -0
- package/dist/schedule-runner.js +452 -0
- package/dist/schedule.d.ts +149 -0
- package/dist/schedule.js +428 -0
- package/dist/secret-mask.d.ts +29 -0
- package/dist/secret-mask.js +245 -0
- package/dist/self-register.d.ts +45 -0
- package/dist/self-register.js +67 -0
- package/dist/send.d.ts +56 -0
- package/dist/send.js +104 -0
- package/dist/session-diff.d.ts +161 -0
- package/dist/session-diff.js +445 -0
- package/dist/session-registry.d.ts +143 -0
- package/dist/session-registry.js +216 -0
- package/dist/sessions-report.d.ts +59 -0
- package/dist/sessions-report.js +99 -0
- package/dist/shell-audit.d.ts +34 -0
- package/dist/shell-audit.js +47 -0
- package/dist/subagent-transcripts.d.ts +167 -0
- package/dist/subagent-transcripts.js +437 -0
- package/dist/subscriber-trust.d.ts +128 -0
- package/dist/subscriber-trust.js +397 -0
- package/dist/templates.d.ts +105 -0
- package/dist/templates.js +871 -0
- package/dist/tmux-install.d.ts +44 -0
- package/dist/tmux-install.js +65 -0
- package/dist/tmux-liveness.d.ts +45 -0
- package/dist/tmux-liveness.js +71 -0
- package/dist/tmux-pane.d.ts +55 -0
- package/dist/tmux-pane.js +126 -0
- package/dist/tmux-socket.d.ts +20 -0
- package/dist/tmux-socket.js +292 -0
- package/dist/todo-fold.d.ts +31 -0
- package/dist/todo-fold.js +102 -0
- package/dist/transcript-tail.d.ts +312 -0
- package/dist/transcript-tail.js +718 -0
- package/dist/trust-command.d.ts +49 -0
- package/dist/trust-command.js +200 -0
- package/dist/trust-lock.d.ts +26 -0
- package/dist/trust-lock.js +94 -0
- package/dist/trust-pins.d.ts +37 -0
- package/dist/trust-pins.js +82 -0
- package/dist/turn-recorder-wire.d.ts +70 -0
- package/dist/turn-recorder-wire.js +165 -0
- package/dist/turn-recorder.d.ts +190 -0
- package/dist/turn-recorder.js +695 -0
- package/dist/turn-ring.d.ts +93 -0
- package/dist/turn-ring.js +418 -0
- package/dist/turn-watch.d.ts +177 -0
- package/dist/turn-watch.js +630 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js +2 -0
- package/dist/uninstall.d.ts +55 -0
- package/dist/uninstall.js +507 -0
- package/dist/usage.d.ts +17 -0
- package/dist/usage.js +421 -0
- package/dist/verify.d.ts +47 -0
- package/dist/verify.js +396 -0
- package/dist/worktree.d.ts +144 -0
- package/dist/worktree.js +310 -0
- package/dist/wrapper.d.ts +254 -0
- package/dist/wrapper.js +704 -0
- package/dist/ws-url.d.ts +109 -0
- package/dist/ws-url.js +202 -0
- package/package.json +78 -10
- package/bin/hwip.js +0 -3
package/dist/ws-url.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the listener's WebSocket URL comes from.
|
|
3
|
+
*
|
|
4
|
+
* The listener reads this once, at startup, and four things can supply it —
|
|
5
|
+
* which is exactly why a machine can sit on the wrong stage for hours with
|
|
6
|
+
* nothing to show for it but a line buried in listener.log. `hwip verify`
|
|
7
|
+
* reports the answer AND its source, so "config isn't being read" stops being
|
|
8
|
+
* a guess.
|
|
9
|
+
*
|
|
10
|
+
* Its own module rather than listener.ts so `hwip verify` can ask without
|
|
11
|
+
* loading the 187KB daemon.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_API_BASE = "https://api.hwip.app/v1";
|
|
14
|
+
export declare const DEFAULT_WS_URL = "wss://ws.hwip.app";
|
|
15
|
+
/** `scheme://host[:port]` of a URL, or null when it is not one (a `data:` URL has no origin either). Userinfo, path, query and fragment are not part of it. */
|
|
16
|
+
export declare const originOf: (url: string) => string | null;
|
|
17
|
+
/** Does the URL have a user name or a password in it (`https://user:password@host`)? Not a URL at all: no. */
|
|
18
|
+
export declare const carriesCredentials: (url: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the environment's `HWIP_WS_URL` may carry the key. The socket URL has the key in its query, so the
|
|
21
|
+
* config file's key (`keyFromFile`) goes only where the file says: with the default API, the default socket's
|
|
22
|
+
* origin. A key given by flag or `HWIP_API_KEY` goes with the environment as before.
|
|
23
|
+
*/
|
|
24
|
+
export declare const envWsAllowed: (envUrl: string, baseUrl: string, keyFromFile: boolean) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Why `url` cannot be the listener's socket, or null when it can: it has to be
|
|
27
|
+
* a `ws://` or `wss://` URL. A bare host (`ws.hwip.app`) used to pass
|
|
28
|
+
* unchecked and fail inside `new WebSocket`, whose error quotes the URL — API key
|
|
29
|
+
* included — into the log. The text shows the URL without its userinfo, query or
|
|
30
|
+
* fragment.
|
|
31
|
+
*/
|
|
32
|
+
export declare const wsUrlProblem: (url: unknown) => string | null;
|
|
33
|
+
/**
|
|
34
|
+
* The URL the listener connects to: `wsUrl` with the connection parameters in
|
|
35
|
+
* its query. Through `URL`, so a query the configured URL already has stays and
|
|
36
|
+
* each parameter (the API key among them) is its own value.
|
|
37
|
+
*/
|
|
38
|
+
export declare const socketUrlWith: (wsUrl: string, params: Record<string, string>) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Which input won. Reported to the user verbatim.
|
|
41
|
+
*
|
|
42
|
+
* `HWIP_WS_URL` used to sit ABOVE the config file, so an `export` in a shell
|
|
43
|
+
* profile silently outranked `~/.hwip/config.json` — invisible in the file,
|
|
44
|
+
* invisible in the app, indistinguishable from "the config is not being read".
|
|
45
|
+
* It now sits BELOW it, and warns whenever it is what answered.
|
|
46
|
+
*
|
|
47
|
+
* Not removed outright, because for one real configuration it was the only
|
|
48
|
+
* answer: a self-hosted deployment (custom API base) with the URL only in the
|
|
49
|
+
* environment. Dropping the lane made that listener refuse to start, and on the
|
|
50
|
+
* `hwip cc` autostart path its stderr goes to the log file, so the machine
|
|
51
|
+
* would just vanish from the app — the very failure this change exists to end.
|
|
52
|
+
*/
|
|
53
|
+
export type WsUrlSource = 'flag' | 'config' | 'env-deprecated' | 'default';
|
|
54
|
+
export interface ResolvedWsUrl {
|
|
55
|
+
readonly url: string;
|
|
56
|
+
readonly source: WsUrlSource;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A user who only set HWIP_API_KEY (no `hwip login`, no config file) gets the
|
|
60
|
+
* prod socket by default — mirroring the DEFAULT_API_BASE fallback for REST.
|
|
61
|
+
* The default only applies when the base URL is also the prod default: with a
|
|
62
|
+
* custom base URL, silently pointing the listener at the prod socket while
|
|
63
|
+
* REST talks to another stage would split the device across environments, so
|
|
64
|
+
* that combination still errors loudly.
|
|
65
|
+
*/
|
|
66
|
+
export declare const resolveWsUrlDetailed: (args: Record<string, string | boolean>, config: {
|
|
67
|
+
wsUrl?: string;
|
|
68
|
+
}, baseUrl: string,
|
|
69
|
+
/** The key about to be sent is the config file's: the environment's socket URL is not followed with it (C129). */
|
|
70
|
+
keyFromFile?: boolean) => ResolvedWsUrl | null;
|
|
71
|
+
/** The URL alone — the listener's own question. One precedence list, not two. */
|
|
72
|
+
export declare const resolveWsUrl: (args: Record<string, string | boolean>, config: {
|
|
73
|
+
wsUrl?: string;
|
|
74
|
+
}, baseUrl: string, keyFromFile?: boolean) => string | null;
|
|
75
|
+
export declare const endpointStage: (url: string) => string;
|
|
76
|
+
/**
|
|
77
|
+
* The URL the RUNNING listener actually connected to, read out of its own
|
|
78
|
+
* startup line. The daemon resolves this once and can outlive a config edit by
|
|
79
|
+
* hours, so "what the config says" and "what is connected" are different
|
|
80
|
+
* questions — and only the second one explains a machine missing from the app.
|
|
81
|
+
*
|
|
82
|
+
* Matches the line listener.ts writes: `[HH:MM:SS] hwip listener starting — vX — <url>`.
|
|
83
|
+
* Last one wins; the log accumulates every restart.
|
|
84
|
+
*/
|
|
85
|
+
export declare const parseListenerStartUrl: (logText: string) => string | null;
|
|
86
|
+
/**
|
|
87
|
+
* A one-line notice for a machine that still exports `HWIP_WS_URL`, or null.
|
|
88
|
+
*
|
|
89
|
+
* The variable is no longer read, and dropping it in silence would be the same
|
|
90
|
+
* failure it caused: a value that appears to be in effect while something else
|
|
91
|
+
* decides. Read only to say it is being ignored, never to use.
|
|
92
|
+
*/
|
|
93
|
+
export declare const legacyWsEnv: (env?: NodeJS.ProcessEnv) => string | null;
|
|
94
|
+
/**
|
|
95
|
+
* What to tell a machine that still exports `HWIP_WS_URL`, or null.
|
|
96
|
+
*
|
|
97
|
+
* Two different messages, because the two situations are not the same problem:
|
|
98
|
+
* the config file now wins, so an export alongside it is dead weight, while an
|
|
99
|
+
* export on its own is still holding the machine up and must not be deleted
|
|
100
|
+
* before the value is moved.
|
|
101
|
+
*/
|
|
102
|
+
export declare const legacyWsEnvNotice: (config?: {
|
|
103
|
+
wsUrl?: string;
|
|
104
|
+
}, env?: NodeJS.ProcessEnv,
|
|
105
|
+
/** Given when the key is the config file's: then an export that cannot carry it is said to be skipped (C129). */
|
|
106
|
+
fileKey?: {
|
|
107
|
+
baseUrl: string;
|
|
108
|
+
}) => string | null;
|
|
109
|
+
//# sourceMappingURL=ws-url.d.ts.map
|
package/dist/ws-url.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.legacyWsEnvNotice = exports.legacyWsEnv = exports.parseListenerStartUrl = exports.endpointStage = exports.resolveWsUrl = exports.resolveWsUrlDetailed = exports.socketUrlWith = exports.wsUrlProblem = exports.envWsAllowed = exports.carriesCredentials = exports.originOf = exports.DEFAULT_WS_URL = exports.DEFAULT_API_BASE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Where the listener's WebSocket URL comes from.
|
|
6
|
+
*
|
|
7
|
+
* The listener reads this once, at startup, and four things can supply it —
|
|
8
|
+
* which is exactly why a machine can sit on the wrong stage for hours with
|
|
9
|
+
* nothing to show for it but a line buried in listener.log. `hwip verify`
|
|
10
|
+
* reports the answer AND its source, so "config isn't being read" stops being
|
|
11
|
+
* a guess.
|
|
12
|
+
*
|
|
13
|
+
* Its own module rather than listener.ts so `hwip verify` can ask without
|
|
14
|
+
* loading the 187KB daemon.
|
|
15
|
+
*/
|
|
16
|
+
exports.DEFAULT_API_BASE = 'https://api.hwip.app/v1';
|
|
17
|
+
exports.DEFAULT_WS_URL = 'wss://ws.hwip.app';
|
|
18
|
+
/** `scheme://host[:port]` of a URL, or null when it is not one (a `data:` URL has no origin either). Userinfo, path, query and fragment are not part of it. */
|
|
19
|
+
const originOf = (url) => {
|
|
20
|
+
try {
|
|
21
|
+
const { origin } = new URL(url);
|
|
22
|
+
return origin === 'null' ? null : origin;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.originOf = originOf;
|
|
29
|
+
/** Does the URL have a user name or a password in it (`https://user:password@host`)? Not a URL at all: no. */
|
|
30
|
+
const carriesCredentials = (url) => {
|
|
31
|
+
try {
|
|
32
|
+
const { username, password } = new URL(url);
|
|
33
|
+
return username !== '' || password !== '';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
exports.carriesCredentials = carriesCredentials;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the environment's `HWIP_WS_URL` may carry the key. The socket URL has the key in its query, so the
|
|
42
|
+
* config file's key (`keyFromFile`) goes only where the file says: with the default API, the default socket's
|
|
43
|
+
* origin. A key given by flag or `HWIP_API_KEY` goes with the environment as before.
|
|
44
|
+
*/
|
|
45
|
+
const envWsAllowed = (envUrl, baseUrl, keyFromFile) => !keyFromFile || (baseUrl === exports.DEFAULT_API_BASE && (0, exports.originOf)(envUrl) !== null && (0, exports.originOf)(envUrl) === (0, exports.originOf)(exports.DEFAULT_WS_URL));
|
|
46
|
+
exports.envWsAllowed = envWsAllowed;
|
|
47
|
+
/**
|
|
48
|
+
* Why `url` cannot be the listener's socket, or null when it can: it has to be
|
|
49
|
+
* a `ws://` or `wss://` URL. A bare host (`ws.hwip.app`) used to pass
|
|
50
|
+
* unchecked and fail inside `new WebSocket`, whose error quotes the URL — API key
|
|
51
|
+
* included — into the log. The text shows the URL without its userinfo, query or
|
|
52
|
+
* fragment.
|
|
53
|
+
*/
|
|
54
|
+
const wsUrlProblem = (url) => {
|
|
55
|
+
// Whatever the config file holds: a number or a list is not a URL either.
|
|
56
|
+
if (typeof url !== 'string')
|
|
57
|
+
return 'the WebSocket URL has to be a string, like "wss://ws.hwip.app"';
|
|
58
|
+
const shown = url.replace(/\/\/[^/@]*@/, '//').replace(/[?#].*$/, '');
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = new URL(url);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return `the WebSocket URL "${shown}" is not a URL — it needs a scheme, like wss://${shown.replace(/^[a-z][a-z0-9+.-]*:\/*/i, '')}`;
|
|
65
|
+
}
|
|
66
|
+
if (parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') {
|
|
67
|
+
return `the WebSocket URL "${shown}" must start with ws:// or wss://`;
|
|
68
|
+
}
|
|
69
|
+
// The listener appends `?apiKey=…` to it: a space or a fragment would break the connection (or put the key in the fragment).
|
|
70
|
+
if (/\s/.test(url))
|
|
71
|
+
return `the WebSocket URL "${shown}" has a space in it`;
|
|
72
|
+
if (parsed.hash)
|
|
73
|
+
return `the WebSocket URL "${shown}" has a #fragment, which a socket URL cannot carry`;
|
|
74
|
+
// (A ws: or wss: URL cannot parse without a host, so there is none left to check.)
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
77
|
+
exports.wsUrlProblem = wsUrlProblem;
|
|
78
|
+
/**
|
|
79
|
+
* The URL the listener connects to: `wsUrl` with the connection parameters in
|
|
80
|
+
* its query. Through `URL`, so a query the configured URL already has stays and
|
|
81
|
+
* each parameter (the API key among them) is its own value.
|
|
82
|
+
*/
|
|
83
|
+
const socketUrlWith = (wsUrl, params) => {
|
|
84
|
+
const target = new URL(wsUrl);
|
|
85
|
+
for (const [name, value] of Object.entries(params))
|
|
86
|
+
target.searchParams.set(name, value);
|
|
87
|
+
return target.toString();
|
|
88
|
+
};
|
|
89
|
+
exports.socketUrlWith = socketUrlWith;
|
|
90
|
+
/**
|
|
91
|
+
* A user who only set HWIP_API_KEY (no `hwip login`, no config file) gets the
|
|
92
|
+
* prod socket by default — mirroring the DEFAULT_API_BASE fallback for REST.
|
|
93
|
+
* The default only applies when the base URL is also the prod default: with a
|
|
94
|
+
* custom base URL, silently pointing the listener at the prod socket while
|
|
95
|
+
* REST talks to another stage would split the device across environments, so
|
|
96
|
+
* that combination still errors loudly.
|
|
97
|
+
*/
|
|
98
|
+
const resolveWsUrlDetailed = (args, config, baseUrl,
|
|
99
|
+
/** The key about to be sent is the config file's: the environment's socket URL is not followed with it (C129). */
|
|
100
|
+
keyFromFile = false) => {
|
|
101
|
+
const fromArg = typeof args['ws-url'] === 'string' ? args['ws-url'] : null;
|
|
102
|
+
if (fromArg)
|
|
103
|
+
return { url: fromArg, source: 'flag' };
|
|
104
|
+
if (config.wsUrl)
|
|
105
|
+
return { url: config.wsUrl, source: 'config' };
|
|
106
|
+
// Below the config file, and above the built-in default: a machine that
|
|
107
|
+
// only ever had the environment variable keeps the stage it was on instead
|
|
108
|
+
// of being silently relocated to prod (or refusing to start).
|
|
109
|
+
const legacyEnv = (0, exports.legacyWsEnv)();
|
|
110
|
+
if (legacyEnv && (0, exports.envWsAllowed)(legacyEnv, baseUrl, keyFromFile))
|
|
111
|
+
return { url: legacyEnv, source: 'env-deprecated' };
|
|
112
|
+
return baseUrl === exports.DEFAULT_API_BASE ? { url: exports.DEFAULT_WS_URL, source: 'default' } : null;
|
|
113
|
+
};
|
|
114
|
+
exports.resolveWsUrlDetailed = resolveWsUrlDetailed;
|
|
115
|
+
/** The URL alone — the listener's own question. One precedence list, not two. */
|
|
116
|
+
const resolveWsUrl = (args, config, baseUrl, keyFromFile = false) => (0, exports.resolveWsUrlDetailed)(args, config, baseUrl, keyFromFile)?.url ?? null;
|
|
117
|
+
exports.resolveWsUrl = resolveWsUrl;
|
|
118
|
+
/**
|
|
119
|
+
* Stage label for a hwip endpoint. The two endpoints carry it differently, and
|
|
120
|
+
* both forms have to be read or a correctly paired dev setup reports as broken:
|
|
121
|
+
*
|
|
122
|
+
* - The REST base encodes it in the PATH PREFIX — `/v1` is prod, `/d1` is dev,
|
|
123
|
+
* on the same `api.hwip.app` host (see agent-rules-fetch.ts).
|
|
124
|
+
* - The socket encodes it in the HOSTNAME — `ws.hwip.app` vs `ws-dev.hwip.app`,
|
|
125
|
+
* with no path at all.
|
|
126
|
+
*
|
|
127
|
+
* Only used to notice that the two point at different stages — a split-brain
|
|
128
|
+
* device, pushes going one way and the agent list the other. Never blocks.
|
|
129
|
+
*/
|
|
130
|
+
const API_STAGE_PREFIX = { v1: 'prod', d1: 'dev' };
|
|
131
|
+
const endpointStage = (url) => {
|
|
132
|
+
let host = url;
|
|
133
|
+
let path = '';
|
|
134
|
+
try {
|
|
135
|
+
const parsed = new URL(url);
|
|
136
|
+
host = parsed.hostname;
|
|
137
|
+
path = parsed.pathname;
|
|
138
|
+
}
|
|
139
|
+
catch { /* not a URL — fall through and read it as a hostname */ }
|
|
140
|
+
// Host marker first: it is explicit, and it outranks the path. An
|
|
141
|
+
// `api-staging.hwip.app/v1` is staging — the `/v1` there says "the v1 API of
|
|
142
|
+
// the staging deployment", not "prod".
|
|
143
|
+
const marked = host.match(/(?:^|[.-])(dev|stage|staging|local|test)(?:[.-]|$)/);
|
|
144
|
+
if (marked)
|
|
145
|
+
return marked[1];
|
|
146
|
+
// Unmarked host: the path prefix is the only thing left that carries a
|
|
147
|
+
// stage, and on api.hwip.app it is the whole distinction.
|
|
148
|
+
const prefix = path.split('/').filter(Boolean)[0];
|
|
149
|
+
return (prefix && API_STAGE_PREFIX[prefix]) || 'prod';
|
|
150
|
+
};
|
|
151
|
+
exports.endpointStage = endpointStage;
|
|
152
|
+
/**
|
|
153
|
+
* The URL the RUNNING listener actually connected to, read out of its own
|
|
154
|
+
* startup line. The daemon resolves this once and can outlive a config edit by
|
|
155
|
+
* hours, so "what the config says" and "what is connected" are different
|
|
156
|
+
* questions — and only the second one explains a machine missing from the app.
|
|
157
|
+
*
|
|
158
|
+
* Matches the line listener.ts writes: `[HH:MM:SS] hwip listener starting — vX — <url>`.
|
|
159
|
+
* Last one wins; the log accumulates every restart.
|
|
160
|
+
*/
|
|
161
|
+
const parseListenerStartUrl = (logText) => {
|
|
162
|
+
const starts = [...logText.matchAll(/hwip listener starting\b.*?(\S+)\s*$/gm)];
|
|
163
|
+
const last = starts.at(-1);
|
|
164
|
+
return last ? last[1] : null;
|
|
165
|
+
};
|
|
166
|
+
exports.parseListenerStartUrl = parseListenerStartUrl;
|
|
167
|
+
/**
|
|
168
|
+
* A one-line notice for a machine that still exports `HWIP_WS_URL`, or null.
|
|
169
|
+
*
|
|
170
|
+
* The variable is no longer read, and dropping it in silence would be the same
|
|
171
|
+
* failure it caused: a value that appears to be in effect while something else
|
|
172
|
+
* decides. Read only to say it is being ignored, never to use.
|
|
173
|
+
*/
|
|
174
|
+
const legacyWsEnv = (env = process.env) => {
|
|
175
|
+
const value = env.HWIP_WS_URL;
|
|
176
|
+
// Same guard resolvedEnv applies: an unexpanded "${HWIP_WS_URL}" from an
|
|
177
|
+
// agent's env block is a placeholder, not a value.
|
|
178
|
+
return value && !value.startsWith('${') ? value : null;
|
|
179
|
+
};
|
|
180
|
+
exports.legacyWsEnv = legacyWsEnv;
|
|
181
|
+
/**
|
|
182
|
+
* What to tell a machine that still exports `HWIP_WS_URL`, or null.
|
|
183
|
+
*
|
|
184
|
+
* Two different messages, because the two situations are not the same problem:
|
|
185
|
+
* the config file now wins, so an export alongside it is dead weight, while an
|
|
186
|
+
* export on its own is still holding the machine up and must not be deleted
|
|
187
|
+
* before the value is moved.
|
|
188
|
+
*/
|
|
189
|
+
const legacyWsEnvNotice = (config = {}, env = process.env,
|
|
190
|
+
/** Given when the key is the config file's: then an export that cannot carry it is said to be skipped (C129). */
|
|
191
|
+
fileKey) => {
|
|
192
|
+
const exported = (0, exports.legacyWsEnv)(env);
|
|
193
|
+
if (!exported)
|
|
194
|
+
return null;
|
|
195
|
+
if (!config.wsUrl && fileKey && !(0, exports.envWsAllowed)(exported, fileKey.baseUrl, true)) {
|
|
196
|
+
return 'hwip: HWIP_WS_URL is not used: the API key in ~/.hwip/config.json is not sent to an address from the environment — set HWIP_API_KEY as well, or put "wsUrl" in ~/.hwip/config.json.';
|
|
197
|
+
}
|
|
198
|
+
return config.wsUrl
|
|
199
|
+
? 'hwip: HWIP_WS_URL is set but ~/.hwip/config.json wins now — the export does nothing and can go.'
|
|
200
|
+
: 'hwip: HWIP_WS_URL is deprecated and will stop being read — move the value to "wsUrl" in ~/.hwip/config.json.';
|
|
201
|
+
};
|
|
202
|
+
exports.legacyWsEnvNotice = legacyWsEnvNotice;
|
package/package.json
CHANGED
|
@@ -1,18 +1,86 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hwip/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Hwip CLI + push notification SDK for AI agents",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
8
12
|
},
|
|
9
|
-
"keywords": [],
|
|
10
|
-
"author": "",
|
|
11
|
-
"license": "MIT",
|
|
12
13
|
"bin": {
|
|
13
|
-
"hwip": "
|
|
14
|
+
"hwip": "./dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": "^20.17.0 || ^22.13.0 || >=23.5.0"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"!dist/**/*.map",
|
|
22
|
+
"!dist/**/*.tsbuildinfo",
|
|
23
|
+
"!dist/test-setup.*",
|
|
24
|
+
"!dist/test-state-dir.*",
|
|
25
|
+
"NOTICE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"sync:plugin": "node scripts/sync-from-plugin.mjs",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"@types/ws": "^8.18.1",
|
|
37
|
+
"esbuild": "^0.28.2",
|
|
38
|
+
"typescript": "^5.8.0",
|
|
39
|
+
"vitest": "^4.1.11"
|
|
14
40
|
},
|
|
41
|
+
"release": {
|
|
42
|
+
"branches": [
|
|
43
|
+
"main"
|
|
44
|
+
],
|
|
45
|
+
"plugins": [
|
|
46
|
+
"@semantic-release/commit-analyzer",
|
|
47
|
+
"@semantic-release/release-notes-generator",
|
|
48
|
+
"@semantic-release/npm",
|
|
49
|
+
"@semantic-release/github"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"author": "Hwip <dev@hwip.app>",
|
|
15
53
|
"repository": {
|
|
16
|
-
"
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/hwipit/cli"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/hwipit/cli",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/hwipit/cli/issues"
|
|
60
|
+
},
|
|
61
|
+
"keywords": [
|
|
62
|
+
"hwip",
|
|
63
|
+
"push",
|
|
64
|
+
"notification",
|
|
65
|
+
"cli",
|
|
66
|
+
"webhook",
|
|
67
|
+
"ai-agent",
|
|
68
|
+
"mcp",
|
|
69
|
+
"claude",
|
|
70
|
+
"devtools"
|
|
71
|
+
],
|
|
72
|
+
"license": "Apache-2.0",
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public",
|
|
75
|
+
"registry": "https://registry.npmjs.org/"
|
|
76
|
+
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"@hwip/mcp-server": "^0.1.0",
|
|
79
|
+
"@inquirer/prompts": "^8.4.3",
|
|
80
|
+
"https-proxy-agent": "^7.0.6",
|
|
81
|
+
"jsonc-parser": "^3.3.1",
|
|
82
|
+
"re2js": "^2.8.6",
|
|
83
|
+
"undici": "^6.29.0",
|
|
84
|
+
"ws": "^8.21.0"
|
|
17
85
|
}
|
|
18
86
|
}
|
package/bin/hwip.js
DELETED