@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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { NotifyPayload } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The part of a channel `GET /channels` returns that this CLI shows. The record
|
|
4
|
+
* also carries fields that are not for a terminal or a `--json` pipe (a
|
|
5
|
+
* webhook secret, the index tag), so the list is rebuilt from these and
|
|
6
|
+
* nothing else is passed along.
|
|
7
|
+
*/
|
|
8
|
+
export interface ChannelSummary {
|
|
9
|
+
channelId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
isPublic: boolean;
|
|
13
|
+
subscriberCount: number;
|
|
14
|
+
ownerId: string;
|
|
15
|
+
/** Whether the account owns it, as the server says. Absent from a server that does not send it yet. */
|
|
16
|
+
isOwner?: boolean;
|
|
17
|
+
createdAt?: string;
|
|
18
|
+
}
|
|
19
|
+
/** The channels in a `GET /channels` answer; a row without an id or a name is dropped, not guessed. */
|
|
20
|
+
export declare const parseChannels: (data: unknown) => ChannelSummary[];
|
|
21
|
+
/**
|
|
22
|
+
* What `hwip channel list` prints. The server says per channel whether the
|
|
23
|
+
* account owns it (`isOwner`), and answers with the owned ones first. A server
|
|
24
|
+
* that does not send the field yet gives the rows no way to say so — an API key
|
|
25
|
+
* cannot learn its own user id — so such a row shows its owner and leaves the
|
|
26
|
+
* comparison to the person (the server refuses `notify --channel` for a channel
|
|
27
|
+
* the account does not own).
|
|
28
|
+
*/
|
|
29
|
+
export declare const channelLines: (channels: readonly ChannelSummary[]) => string[];
|
|
30
|
+
export type ChannelRef = {
|
|
31
|
+
channelId: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
} | {
|
|
34
|
+
error: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Which channel `--channel <ref>` means. An id in the list wins; otherwise the
|
|
38
|
+
* name must match exactly one channel *the account owns* — a channel it only
|
|
39
|
+
* follows with the same name does not make the name ambiguous, and is never
|
|
40
|
+
* the target. Two owned channels with one name are refused rather than picked:
|
|
41
|
+
* a push to the wrong channel reaches strangers. An id the list does not hold is
|
|
42
|
+
* passed on as it is — the server decides whether it exists and is the account's.
|
|
43
|
+
*
|
|
44
|
+
* A server that does not say who owns what (`isOwner` absent on any row) leaves
|
|
45
|
+
* every name match a candidate, as before: ambiguity is then refused, and a
|
|
46
|
+
* channel the account does not own is refused by the server.
|
|
47
|
+
*/
|
|
48
|
+
export declare const resolveChannel: (channels: readonly ChannelSummary[], ref: string) => ChannelRef;
|
|
49
|
+
export interface ChannelNotify {
|
|
50
|
+
ref: string;
|
|
51
|
+
payload: Omit<NotifyPayload, 'channelId'>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The flags of `hwip notify --channel`, or what is wrong with them.
|
|
55
|
+
*
|
|
56
|
+
* A channel push is not the agent hook's push: the gate (`--auto`), the device
|
|
57
|
+
* and the session flags mean "this agent session", and none of them go to a
|
|
58
|
+
* channel — refusing them is better than dropping them silently, and the
|
|
59
|
+
* default title and body a hook gets from the branch name would publish a
|
|
60
|
+
* project name to subscribers.
|
|
61
|
+
*/
|
|
62
|
+
export declare const parseChannelNotify: (args: Readonly<Record<string, string | boolean>>) => ChannelNotify | {
|
|
63
|
+
error: string;
|
|
64
|
+
};
|
|
65
|
+
/** Printed to stderr on every channel push: whatever the account's end-to-end setting, this one is readable by the server. */
|
|
66
|
+
export declare const PLAINTEXT_NOTE = "Channel pushes are sent as plain text, not sealed \u2014 the server can read this one even with end-to-end encryption on.";
|
|
67
|
+
//# sourceMappingURL=channel.d.ts.map
|
package/dist/channel.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLAINTEXT_NOTE = exports.parseChannelNotify = exports.resolveChannel = exports.channelLines = exports.parseChannels = void 0;
|
|
4
|
+
const text = (value) => (typeof value === 'string' && value ? value : undefined);
|
|
5
|
+
/** The channels in a `GET /channels` answer; a row without an id or a name is dropped, not guessed. */
|
|
6
|
+
const parseChannels = (data) => {
|
|
7
|
+
if (!Array.isArray(data))
|
|
8
|
+
return [];
|
|
9
|
+
const channels = [];
|
|
10
|
+
for (const raw of data) {
|
|
11
|
+
if (!raw || typeof raw !== 'object')
|
|
12
|
+
continue;
|
|
13
|
+
const row = raw;
|
|
14
|
+
const channelId = text(row.channelId);
|
|
15
|
+
const name = text(row.name);
|
|
16
|
+
if (!channelId || !name)
|
|
17
|
+
continue;
|
|
18
|
+
const description = text(row.description);
|
|
19
|
+
const createdAt = text(row.createdAt);
|
|
20
|
+
channels.push({
|
|
21
|
+
channelId,
|
|
22
|
+
name,
|
|
23
|
+
...(description && { description }),
|
|
24
|
+
isPublic: row.isPublic === true,
|
|
25
|
+
subscriberCount: typeof row.subscriberCount === 'number' ? row.subscriberCount : 0,
|
|
26
|
+
ownerId: text(row.ownerId) ?? '',
|
|
27
|
+
...(typeof row.isOwner === 'boolean' && { isOwner: row.isOwner }),
|
|
28
|
+
...(createdAt && { createdAt }),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return channels;
|
|
32
|
+
};
|
|
33
|
+
exports.parseChannels = parseChannels;
|
|
34
|
+
/**
|
|
35
|
+
* What `hwip channel list` prints. The server says per channel whether the
|
|
36
|
+
* account owns it (`isOwner`), and answers with the owned ones first. A server
|
|
37
|
+
* that does not send the field yet gives the rows no way to say so — an API key
|
|
38
|
+
* cannot learn its own user id — so such a row shows its owner and leaves the
|
|
39
|
+
* comparison to the person (the server refuses `notify --channel` for a channel
|
|
40
|
+
* the account does not own).
|
|
41
|
+
*/
|
|
42
|
+
const channelLines = (channels) => {
|
|
43
|
+
if (channels.length === 0)
|
|
44
|
+
return ['No channels.'];
|
|
45
|
+
const idWidth = Math.max(...channels.map((c) => c.channelId.length));
|
|
46
|
+
const nameWidth = Math.max(...channels.map((c) => c.name.length));
|
|
47
|
+
const lines = channels.map((c) => {
|
|
48
|
+
const subs = `${c.subscriberCount} ${c.subscriberCount === 1 ? 'subscriber' : 'subscribers'}`;
|
|
49
|
+
const role = c.isOwner === true ? 'yours' : c.isOwner === false ? 'following' : c.ownerId ? `owner ${c.ownerId.slice(0, 8)}` : 'owner unknown';
|
|
50
|
+
return ` ${c.channelId.padEnd(idWidth)} ${c.name.padEnd(nameWidth)} ${c.isPublic ? 'public ' : 'private'} ${subs} ${role}`;
|
|
51
|
+
});
|
|
52
|
+
lines.push('', 'Only an owner can send to a channel.');
|
|
53
|
+
return lines;
|
|
54
|
+
};
|
|
55
|
+
exports.channelLines = channelLines;
|
|
56
|
+
/**
|
|
57
|
+
* Which channel `--channel <ref>` means. An id in the list wins; otherwise the
|
|
58
|
+
* name must match exactly one channel *the account owns* — a channel it only
|
|
59
|
+
* follows with the same name does not make the name ambiguous, and is never
|
|
60
|
+
* the target. Two owned channels with one name are refused rather than picked:
|
|
61
|
+
* a push to the wrong channel reaches strangers. An id the list does not hold is
|
|
62
|
+
* passed on as it is — the server decides whether it exists and is the account's.
|
|
63
|
+
*
|
|
64
|
+
* A server that does not say who owns what (`isOwner` absent on any row) leaves
|
|
65
|
+
* every name match a candidate, as before: ambiguity is then refused, and a
|
|
66
|
+
* channel the account does not own is refused by the server.
|
|
67
|
+
*/
|
|
68
|
+
const resolveChannel = (channels, ref) => {
|
|
69
|
+
const known = channels.every((c) => typeof c.isOwner === 'boolean');
|
|
70
|
+
const byId = channels.find((c) => c.channelId === ref);
|
|
71
|
+
if (byId) {
|
|
72
|
+
if (known && !byId.isOwner)
|
|
73
|
+
return { error: `you follow "${byId.name}" (${byId.channelId}) but do not own it — only the owner can send to a channel` };
|
|
74
|
+
return { channelId: byId.channelId, name: byId.name };
|
|
75
|
+
}
|
|
76
|
+
const named = channels.filter((c) => c.name === ref);
|
|
77
|
+
const byName = known ? named.filter((c) => c.isOwner) : named;
|
|
78
|
+
if (byName.length === 1)
|
|
79
|
+
return { channelId: byName[0].channelId, name: byName[0].name };
|
|
80
|
+
if (byName.length > 1) {
|
|
81
|
+
const ids = byName.map((c) => c.channelId).join(', ');
|
|
82
|
+
return { error: `${byName.length} channels are named "${ref}" (${ids}) — use the channelId` };
|
|
83
|
+
}
|
|
84
|
+
if (known && named.length > 0) {
|
|
85
|
+
return { error: `you follow "${ref}" (${named.map((c) => c.channelId).join(', ')}) but do not own a channel by that name — only the owner can send to a channel` };
|
|
86
|
+
}
|
|
87
|
+
if (/^ch_/.test(ref))
|
|
88
|
+
return { channelId: ref };
|
|
89
|
+
return { error: `no channel named "${ref}" — 'hwip channel list' shows the channels; use a channelId or an exact name` };
|
|
90
|
+
};
|
|
91
|
+
exports.resolveChannel = resolveChannel;
|
|
92
|
+
const PRIORITIES = ['low', 'normal', 'high', 'urgent'];
|
|
93
|
+
const TYPES = ['note', 'link', 'file', 'hook'];
|
|
94
|
+
/**
|
|
95
|
+
* The flags of `hwip notify --channel`, or what is wrong with them.
|
|
96
|
+
*
|
|
97
|
+
* A channel push is not the agent hook's push: the gate (`--auto`), the device
|
|
98
|
+
* and the session flags mean "this agent session", and none of them go to a
|
|
99
|
+
* channel — refusing them is better than dropping them silently, and the
|
|
100
|
+
* default title and body a hook gets from the branch name would publish a
|
|
101
|
+
* project name to subscribers.
|
|
102
|
+
*/
|
|
103
|
+
const parseChannelNotify = (args) => {
|
|
104
|
+
const ref = args.channel;
|
|
105
|
+
if (typeof ref !== 'string' || !ref.trim())
|
|
106
|
+
return { error: '--channel needs a channelId or a channel name' };
|
|
107
|
+
for (const flag of ['auto', 'device', 'session']) {
|
|
108
|
+
if (args[flag] !== undefined)
|
|
109
|
+
return { error: `--${flag} is for agent sessions and cannot be combined with --channel` };
|
|
110
|
+
}
|
|
111
|
+
const title = typeof args.title === 'string' ? args.title : undefined;
|
|
112
|
+
const body = typeof args.body === 'string' ? args.body : undefined;
|
|
113
|
+
if (!title && !body)
|
|
114
|
+
return { error: 'a channel push needs --title or --body' };
|
|
115
|
+
const type = typeof args.type === 'string' ? args.type : 'hook';
|
|
116
|
+
if (!TYPES.includes(type))
|
|
117
|
+
return { error: `--type must be one of ${TYPES.join(', ')}` };
|
|
118
|
+
const priority = typeof args.priority === 'string' ? args.priority : undefined;
|
|
119
|
+
if (priority && !PRIORITIES.includes(priority)) {
|
|
120
|
+
return { error: `--priority must be one of ${PRIORITIES.join(', ')}` };
|
|
121
|
+
}
|
|
122
|
+
const url = typeof args.url === 'string' ? args.url : undefined;
|
|
123
|
+
return {
|
|
124
|
+
ref: ref.trim(),
|
|
125
|
+
payload: {
|
|
126
|
+
...(title && { title }),
|
|
127
|
+
...(body && { body }),
|
|
128
|
+
...(url && { url }),
|
|
129
|
+
type: type,
|
|
130
|
+
...(priority && { priority: priority }),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
exports.parseChannelNotify = parseChannelNotify;
|
|
135
|
+
/** Printed to stderr on every channel push: whatever the account's end-to-end setting, this one is readable by the server. */
|
|
136
|
+
exports.PLAINTEXT_NOTE = 'Channel pushes are sent as plain text, not sealed — the server can read this one even with end-to-end encryption on.';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How to update. A global install is what `hwip` on PATH runs — `npx @hwip/cli
|
|
3
|
+
* install` only re-ran setup from a fresh download and left it as it was —
|
|
4
|
+
* and the daemon keeps the old build until it restarts.
|
|
5
|
+
*/
|
|
6
|
+
export declare const UPDATE_HINT = "npm i -g @hwip/cli \u2014 the next `hwip cc` restarts the listener on it (or run: hwip listener --restart)";
|
|
7
|
+
/** Semver-ish compare: returns true when `latest` is strictly newer than `current`. */
|
|
8
|
+
export declare const isNewer: (latest: string, current: string) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The mcp-server version this CLI will actually run. It ships as a dependency
|
|
11
|
+
* (see mcp.ts), so it is pinned at install time — no longer "whatever npx
|
|
12
|
+
* fetched last". Returns null on a broken install rather than throwing; the
|
|
13
|
+
* report degrades to "latest is vX" the way it always did for this package.
|
|
14
|
+
*/
|
|
15
|
+
export declare const installedMcpServerVersion: () => string | null;
|
|
16
|
+
/**
|
|
17
|
+
* One line for `hwip cc` when a newer CLI is out, or undefined. Asks npm at
|
|
18
|
+
* most once a day, briefly, and remembers the answer; an offline day keeps
|
|
19
|
+
* the last known version rather than asking again on every launch.
|
|
20
|
+
*/
|
|
21
|
+
export declare const cachedUpdateNotice: (deps?: {
|
|
22
|
+
now?: () => number;
|
|
23
|
+
latest?: () => Promise<string | null>;
|
|
24
|
+
file?: string;
|
|
25
|
+
env?: NodeJS.ProcessEnv;
|
|
26
|
+
}) => Promise<string | undefined>;
|
|
27
|
+
export declare const handleCheckUpdate: (args: Record<string, string | boolean>) => Promise<number>;
|
|
28
|
+
//# sourceMappingURL=check-update.d.ts.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleCheckUpdate = exports.cachedUpdateNotice = exports.installedMcpServerVersion = exports.isNewer = exports.UPDATE_HINT = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const os_1 = require("os");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const private_files_js_1 = require("./private-files.js");
|
|
8
|
+
const config_js_1 = require("./config.js");
|
|
9
|
+
const args_js_1 = require("./args.js");
|
|
10
|
+
// Compares installed versions against the npm registry. Pure read-only —
|
|
11
|
+
// never installs anything; just tells the user if a newer release exists.
|
|
12
|
+
const PACKAGES = ['@hwip/cli', '@hwip/mcp-server'];
|
|
13
|
+
/**
|
|
14
|
+
* How to update. A global install is what `hwip` on PATH runs — `npx @hwip/cli
|
|
15
|
+
* install` only re-ran setup from a fresh download and left it as it was —
|
|
16
|
+
* and the daemon keeps the old build until it restarts.
|
|
17
|
+
*/
|
|
18
|
+
exports.UPDATE_HINT = 'npm i -g @hwip/cli — the next `hwip cc` restarts the listener on it (or run: hwip listener --restart)';
|
|
19
|
+
/** Fetch the `latest` dist-tag version for a package from the npm registry. */
|
|
20
|
+
const fetchLatest = async (pkg, timeoutMs = 10_000) => {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
23
|
+
headers: { Accept: 'application/json' },
|
|
24
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok)
|
|
27
|
+
return null;
|
|
28
|
+
const json = await res.json();
|
|
29
|
+
return json.version ?? null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/** Semver-ish compare: returns true when `latest` is strictly newer than `current`. */
|
|
36
|
+
const isNewer = (latest, current) => {
|
|
37
|
+
const norm = (v) => v.replace(/^v/, '').split('-')[0].split('.').map((n) => parseInt(n, 10) || 0);
|
|
38
|
+
const [a, b] = [norm(latest), norm(current)];
|
|
39
|
+
for (let i = 0; i < 3; i++) {
|
|
40
|
+
if ((a[i] ?? 0) > (b[i] ?? 0))
|
|
41
|
+
return true;
|
|
42
|
+
if ((a[i] ?? 0) < (b[i] ?? 0))
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
exports.isNewer = isNewer;
|
|
48
|
+
/**
|
|
49
|
+
* The mcp-server version this CLI will actually run. It ships as a dependency
|
|
50
|
+
* (see mcp.ts), so it is pinned at install time — no longer "whatever npx
|
|
51
|
+
* fetched last". Returns null on a broken install rather than throwing; the
|
|
52
|
+
* report degrades to "latest is vX" the way it always did for this package.
|
|
53
|
+
*/
|
|
54
|
+
const installedMcpServerVersion = () => {
|
|
55
|
+
try {
|
|
56
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(require.resolve('@hwip/mcp-server/package.json'), 'utf-8'));
|
|
57
|
+
return pkg.version ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
exports.installedMcpServerVersion = installedMcpServerVersion;
|
|
64
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
65
|
+
const updateCacheFile = () => (0, path_1.join)((0, os_1.homedir)(), '.hwip', 'update-check.json');
|
|
66
|
+
const readUpdateCache = (file) => {
|
|
67
|
+
try {
|
|
68
|
+
const data = JSON.parse((0, fs_1.readFileSync)(file, 'utf-8'));
|
|
69
|
+
return typeof data.checkedAt === 'number' ? { checkedAt: data.checkedAt, latest: data.latest ?? null } : null;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const writeUpdateCache = (file, cache) => {
|
|
76
|
+
try {
|
|
77
|
+
(0, private_files_js_1.mkdirHandedBack)((0, path_1.dirname)(file));
|
|
78
|
+
(0, fs_1.writeFileSync)(file, JSON.stringify(cache) + '\n');
|
|
79
|
+
(0, private_files_js_1.handBackToSudoUser)(file);
|
|
80
|
+
}
|
|
81
|
+
catch { /* a notice is not worth failing over */ }
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* One line for `hwip cc` when a newer CLI is out, or undefined. Asks npm at
|
|
85
|
+
* most once a day, briefly, and remembers the answer; an offline day keeps
|
|
86
|
+
* the last known version rather than asking again on every launch.
|
|
87
|
+
*/
|
|
88
|
+
const cachedUpdateNotice = async (deps = {}) => {
|
|
89
|
+
const now = (deps.now ?? Date.now)();
|
|
90
|
+
const file = deps.file ?? updateCacheFile();
|
|
91
|
+
// CI and anyone who opts out never reach the registry from `hwip cc`.
|
|
92
|
+
const env = deps.env ?? process.env;
|
|
93
|
+
if (env.CI || env.HWIP_NO_UPDATE_CHECK)
|
|
94
|
+
return undefined;
|
|
95
|
+
let cache = readUpdateCache(file);
|
|
96
|
+
// A checkedAt in the future (clock skew, a hand-edited file) counts as stale.
|
|
97
|
+
if (!cache || now - cache.checkedAt >= DAY_MS || cache.checkedAt > now) {
|
|
98
|
+
const latest = await (deps.latest ?? (() => fetchLatest('@hwip/cli', 1_500)))();
|
|
99
|
+
cache = { checkedAt: now, latest: latest ?? cache?.latest ?? null };
|
|
100
|
+
writeUpdateCache(file, cache);
|
|
101
|
+
}
|
|
102
|
+
if (!cache.latest || !(0, exports.isNewer)(cache.latest, config_js_1.VERSION))
|
|
103
|
+
return undefined;
|
|
104
|
+
return `hwip: v${cache.latest} is out (this is v${config_js_1.VERSION}). Update: ${exports.UPDATE_HINT}`;
|
|
105
|
+
};
|
|
106
|
+
exports.cachedUpdateNotice = cachedUpdateNotice;
|
|
107
|
+
const handleCheckUpdate = async (args) => {
|
|
108
|
+
const isJson = (0, args_js_1.flagOn)(args, 'json');
|
|
109
|
+
// Both installed versions are knowable: the CLI's from its own
|
|
110
|
+
// package.json, and mcp-server's because `hwip mcp` runs it in-process,
|
|
111
|
+
// which makes it a dependency rather than an npx download of unknown age.
|
|
112
|
+
// Two unrelated registry round-trips — awaiting them one after the other
|
|
113
|
+
// doubled the command's wall time for nothing.
|
|
114
|
+
const latestVersions = await Promise.all(PACKAGES.map((pkg) => fetchLatest(pkg)));
|
|
115
|
+
// A fresh answer is as good as the daily one `hwip cc` would fetch.
|
|
116
|
+
if (latestVersions[0])
|
|
117
|
+
writeUpdateCache(updateCacheFile(), { checkedAt: Date.now(), latest: latestVersions[0] });
|
|
118
|
+
const results = PACKAGES.map((pkg, i) => {
|
|
119
|
+
const latest = latestVersions[i];
|
|
120
|
+
const current = pkg === '@hwip/cli' ? config_js_1.VERSION : (0, exports.installedMcpServerVersion)();
|
|
121
|
+
return { pkg, current, latest, outdated: !!(latest && current && (0, exports.isNewer)(latest, current)) };
|
|
122
|
+
});
|
|
123
|
+
if (isJson) {
|
|
124
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
console.log('\n Hwip — update check\n');
|
|
128
|
+
let anyOutdated = false;
|
|
129
|
+
for (const r of results) {
|
|
130
|
+
if (!r.latest) {
|
|
131
|
+
console.log(` ? ${r.pkg}: could not reach npm registry`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (r.current === null) {
|
|
135
|
+
console.log(` • ${r.pkg}: latest is v${r.latest}`);
|
|
136
|
+
}
|
|
137
|
+
else if (r.outdated) {
|
|
138
|
+
anyOutdated = true;
|
|
139
|
+
console.log(` ⬆ ${r.pkg}: v${r.current} → v${r.latest} (update available)`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(` ✓ ${r.pkg}: v${r.current} (up to date)`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (anyOutdated) {
|
|
146
|
+
console.log(`\n Update with: ${exports.UPDATE_HINT}\n`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.log('');
|
|
150
|
+
}
|
|
151
|
+
return 0;
|
|
152
|
+
};
|
|
153
|
+
exports.handleCheckUpdate = handleCheckUpdate;
|
package/dist/cli.d.ts
ADDED