@raidiant/notifai 7.0.2 → 9.0.0
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/CHANGELOG.md +75 -0
- package/README.md +20 -13
- package/dist/atomic-file.d.ts +14 -0
- package/dist/atomic-file.js +27 -2
- package/dist/atomic-file.js.map +1 -1
- package/dist/cli-bin.d.ts +4 -0
- package/dist/cli-bin.js +62 -0
- package/dist/cli-bin.js.map +1 -0
- package/dist/cli-release.d.ts +10 -0
- package/dist/cli-release.js +75 -0
- package/dist/cli-release.js.map +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +13 -3
- package/dist/client.js.map +1 -1
- package/dist/commands-auth.d.ts +6 -0
- package/dist/commands-auth.js +28 -3
- package/dist/commands-auth.js.map +1 -1
- package/dist/commands-config.js +22 -3
- package/dist/commands-config.js.map +1 -1
- package/dist/commands-core.d.ts +12 -0
- package/dist/commands-core.js +27 -3
- package/dist/commands-core.js.map +1 -1
- package/dist/commands-devices.d.ts +2 -1
- package/dist/commands-devices.js +20 -8
- package/dist/commands-devices.js.map +1 -1
- package/dist/commands-doctor.d.ts +1 -0
- package/dist/commands-doctor.js +85 -27
- package/dist/commands-doctor.js.map +1 -1
- package/dist/commands-guidance.d.ts +27 -0
- package/dist/commands-guidance.js +163 -0
- package/dist/commands-guidance.js.map +1 -0
- package/dist/commands-hooks.d.ts +16 -5
- package/dist/commands-hooks.js +433 -38
- package/dist/commands-hooks.js.map +1 -1
- package/dist/commands-init.d.ts +10 -18
- package/dist/commands-init.js +139 -54
- package/dist/commands-init.js.map +1 -1
- package/dist/commands-send-support.d.ts +17 -2
- package/dist/commands-send-support.js +66 -8
- package/dist/commands-send-support.js.map +1 -1
- package/dist/commands-send.d.ts +0 -1
- package/dist/commands-send.js +105 -39
- package/dist/commands-send.js.map +1 -1
- package/dist/commands-setup-proof.d.ts +8 -0
- package/dist/commands-setup-proof.js +31 -2
- package/dist/commands-setup-proof.js.map +1 -1
- package/dist/commands-skill.d.ts +11 -1
- package/dist/commands-skill.js +61 -11
- package/dist/commands-skill.js.map +1 -1
- package/dist/commands.d.ts +2 -1
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/config-schema.d.ts +1 -1
- package/dist/config-schema.js +30 -14
- package/dist/config-schema.js.map +1 -1
- package/dist/config.d.ts +24 -5
- package/dist/config.js +37 -8
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +10 -1
- package/dist/credentials.js +25 -12
- package/dist/credentials.js.map +1 -1
- package/dist/guidance-content.d.ts +50 -0
- package/dist/guidance-content.js +240 -0
- package/dist/guidance-content.js.map +1 -0
- package/dist/guidance.d.ts +69 -0
- package/dist/guidance.js +173 -0
- package/dist/guidance.js.map +1 -0
- package/dist/hook-adapter.js +37 -3
- package/dist/hook-adapter.js.map +1 -1
- package/dist/hooks.d.ts +68 -7
- package/dist/hooks.js +238 -92
- package/dist/hooks.js.map +1 -1
- package/dist/install-hooks.d.ts +10 -18
- package/dist/install-hooks.js +110 -23
- package/dist/install-hooks.js.map +1 -1
- package/dist/interactive.d.ts +1 -1
- package/dist/interactive.js +14 -9
- package/dist/interactive.js.map +1 -1
- package/dist/logging.d.ts +8 -1
- package/dist/logging.js +43 -2
- package/dist/logging.js.map +1 -1
- package/dist/main.js +5 -487
- package/dist/main.js.map +1 -1
- package/dist/native-skills.d.ts +19 -0
- package/dist/native-skills.js +30 -8
- package/dist/native-skills.js.map +1 -1
- package/dist/opencode-plugin.js +14 -1
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/program.d.ts +49 -0
- package/dist/program.js +576 -0
- package/dist/program.js.map +1 -0
- package/dist/send.d.ts +12 -10
- package/dist/send.js +65 -29
- package/dist/send.js.map +1 -1
- package/dist/session-activation.d.ts +15 -0
- package/dist/session-activation.js +35 -0
- package/dist/session-activation.js.map +1 -0
- package/dist/session-labels.d.ts +9 -3
- package/dist/session-labels.js +38 -7
- package/dist/session-labels.js.map +1 -1
- package/dist/url-policy.d.ts +85 -0
- package/dist/url-policy.js +369 -0
- package/dist/url-policy.js.map +1 -0
- package/package.json +6 -4
- package/src/atomic-file.ts +36 -2
- package/src/cli-bin.test.ts +38 -0
- package/src/cli-bin.ts +67 -0
- package/src/cli-release.test.ts +46 -0
- package/src/cli-release.ts +78 -0
- package/src/client.test.ts +95 -0
- package/src/client.ts +16 -3
- package/src/commands-auth.ts +32 -3
- package/src/commands-config.ts +25 -2
- package/src/commands-core.ts +38 -3
- package/src/commands-devices.ts +28 -9
- package/src/commands-doctor.ts +116 -25
- package/src/commands-guidance.ts +216 -0
- package/src/commands-hooks.ts +513 -42
- package/src/commands-init.ts +170 -60
- package/src/commands-send-support.test.ts +156 -0
- package/src/commands-send-support.ts +72 -8
- package/src/commands-send.ts +123 -38
- package/src/commands-setup-proof.ts +36 -2
- package/src/commands-skill.ts +78 -15
- package/src/commands.test.ts +1490 -197
- package/src/commands.ts +6 -0
- package/src/config-schema.ts +32 -16
- package/src/config.test.ts +57 -2
- package/src/config.ts +49 -10
- package/src/credentials.test.ts +71 -3
- package/src/credentials.ts +29 -15
- package/src/guidance-content.ts +256 -0
- package/src/guidance.test.ts +299 -0
- package/src/guidance.ts +233 -0
- package/src/hook-adapter.test.ts +113 -1
- package/src/hook-adapter.ts +37 -3
- package/src/hooks.test.ts +783 -170
- package/src/hooks.ts +306 -131
- package/src/install-hooks.test.ts +135 -16
- package/src/install-hooks.ts +113 -25
- package/src/interactive.ts +14 -9
- package/src/logging.test.ts +66 -3
- package/src/logging.ts +45 -2
- package/src/main.ts +6 -614
- package/src/native-skills.test.ts +41 -1
- package/src/native-skills.ts +40 -6
- package/src/opencode-plugin.ts +14 -1
- package/src/program.test.ts +237 -0
- package/src/program.ts +751 -0
- package/src/send.test.ts +28 -0
- package/src/send.ts +81 -34
- package/src/session-activation.ts +45 -0
- package/src/session-labels.test.ts +120 -3
- package/src/session-labels.ts +38 -7
- package/src/skill.test.ts +153 -12
- package/src/trust-policy.test.ts +701 -0
- package/src/url-policy.ts +399 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
9
9
|
and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
10
10
|
|
|
11
|
+
## [9.0.0](https://github.com/Raidiant-io/notifai/compare/v8.0.0...v9.0.0) (2026-08-25)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### ⚠ BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
* **cli:** publishing now requires a verified protocol release before the CLI is published, and npm >= 11.5.1.
|
|
17
|
+
* **cli:** `notifai send --event` is removed.
|
|
18
|
+
* remote media and approval URLs must satisfy the stated trust policy; hosts outside it require an explicit User-owned origins exception.
|
|
19
|
+
* **protocol:** begin-pairing requests must include confirmation_hash, and dashboard pairing actions require the full code and confirmation secret.
|
|
20
|
+
* notification drafts and evidence snapshots no longer carry an `event` field.
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* **cli:** ask one project-or-machine setup scope ([#66](https://github.com/Raidiant-io/notifai/issues/66)) ([bedc0ea](https://github.com/Raidiant-io/notifai/commit/bedc0ea82aa428b2fe63dd03cf8092d0d813bc6d))
|
|
25
|
+
* hold hostile repository input to a stated trust policy ([#70](https://github.com/Raidiant-io/notifai/issues/70)) ([1568768](https://github.com/Raidiant-io/notifai/commit/15687686eed3f74f5f0f6d4d8a9e49b50de4bebc))
|
|
26
|
+
* **protocol:** require proof for pairing actions ([#69](https://github.com/Raidiant-io/notifai/issues/69)) ([ad3d5dc](https://github.com/Raidiant-io/notifai/commit/ad3d5dc593ae7248abb487a0574b5af9ada99506))
|
|
27
|
+
* remove send --event and the protocol event field ([#67](https://github.com/Raidiant-io/notifai/issues/67)) ([7dde05f](https://github.com/Raidiant-io/notifai/commit/7dde05f6161fc3474a637c71abe47d54aacf50a9))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* **cli:** default the service origin to https://api.notifai.sh ([#60](https://github.com/Raidiant-io/notifai/issues/60)) ([8063d20](https://github.com/Raidiant-io/notifai/commit/8063d2040caaa1c09fe7cdb2f5f7302f86221bad))
|
|
33
|
+
* **cli:** drop the terminal-timing rule from when-to-notify guidance ([0c1ed8e](https://github.com/Raidiant-io/notifai/commit/0c1ed8e40fe4470ebcab49d13663c704cf6056ed))
|
|
34
|
+
* **cli:** keep machine credentials out of argv and harden trusted publishing ([#71](https://github.com/Raidiant-io/notifai/issues/71)) ([a03d85c](https://github.com/Raidiant-io/notifai/commit/a03d85ccba6fc36d378168feeb5297103065e62e))
|
|
35
|
+
* **cli:** keep the bin runnable and harden doctor, send, and Codex hooks ([#64](https://github.com/Raidiant-io/notifai/issues/64)) ([d7f2d7b](https://github.com/Raidiant-io/notifai/commit/d7f2d7b612d27e426c328284e2f72d0723b0ae43))
|
|
36
|
+
* **cli:** pin signed-in origin and redact sensitive local logs ([#59](https://github.com/Raidiant-io/notifai/issues/59)) ([7dffafb](https://github.com/Raidiant-io/notifai/commit/7dffafb1e2ebbc307adb99ab9638e8ad3787e776))
|
|
37
|
+
* **cli:** restore question pushes after a spent Stop waiter ([89f6d76](https://github.com/Raidiant-io/notifai/commit/89f6d76e6569190cbd04120f922ecf284a89798c))
|
|
38
|
+
* harden proactive Notifai activation ([#63](https://github.com/Raidiant-io/notifai/issues/63)) ([e486d22](https://github.com/Raidiant-io/notifai/commit/e486d224a5261ba039ba26494a9fdf8b74711817))
|
|
39
|
+
* **hooks:** retire questions without reserved client events ([#65](https://github.com/Raidiant-io/notifai/issues/65)) ([154fd11](https://github.com/Raidiant-io/notifai/commit/154fd1116d7a5b79aeabc8cb1b897262e6953d9c))
|
|
40
|
+
* **hooks:** survive a node upgrade that moves the pinned runtime ([#61](https://github.com/Raidiant-io/notifai/issues/61)) ([0c19acb](https://github.com/Raidiant-io/notifai/commit/0c19acb62d42d3ae95ebe489e56839a5a3ad4ec4))
|
|
41
|
+
* make user-resumable blockers answerable ([#62](https://github.com/Raidiant-io/notifai/issues/62)) ([22e78b5](https://github.com/Raidiant-io/notifai/commit/22e78b5b66c7a1eb2a39ca78675e21a1e82459a9))
|
|
42
|
+
* name press-and-hold on closed-choice iPhone banners ([#68](https://github.com/Raidiant-io/notifai/issues/68)) ([419c7f8](https://github.com/Raidiant-io/notifai/commit/419c7f8d389889662fad45f516fd0cef33c3e344))
|
|
43
|
+
* trigger Notifai guidance consistently ([#58](https://github.com/Raidiant-io/notifai/issues/58)) ([ddf1f94](https://github.com/Raidiant-io/notifai/commit/ddf1f94db6a282b081302e03cd17b204d0231ed1))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Dependencies
|
|
47
|
+
|
|
48
|
+
* The following workspace dependencies were updated
|
|
49
|
+
* dependencies
|
|
50
|
+
* @raidiant/notifai-protocol bumped from 4.1.0 to 5.0.0
|
|
51
|
+
|
|
52
|
+
## [8.0.0](https://github.com/Raidiant-io/notifai/compare/v7.0.2...v8.0.0) (2026-08-24)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### ⚠ BREAKING CHANGES
|
|
56
|
+
|
|
57
|
+
* **cli:** an explicit --session-label no longer overrides a session title supplied by the environment.
|
|
58
|
+
* **cli:** the canonical body of a registered question no longer begins with the question text when context is provided.
|
|
59
|
+
* **cli:** the notify_criteria config key is removed. Its role is now the when-to-notify guidance topic.
|
|
60
|
+
* **cli:** `send --reply --json` prints a single `reply_result` object with the receipt embedded; the standalone receipt line is gone. The `--no-block` flag is removed.
|
|
61
|
+
* **cli:** send no longer accepts --reply-choice or --reply-multi; use --choice and --multi.
|
|
62
|
+
|
|
63
|
+
### Features
|
|
64
|
+
|
|
65
|
+
* add Android protocol and CLI support ([cccc3d1](https://github.com/Raidiant-io/notifai/commit/cccc3d185cbac499651709dbd34d61fd847e9570))
|
|
66
|
+
* add Android protocol and CLI support ([faf2e23](https://github.com/Raidiant-io/notifai/commit/faf2e234b393a2edfbad60cb50c1da18025309fd))
|
|
67
|
+
* **cli:** make session labels environment-neutral for agents ([8df8198](https://github.com/Raidiant-io/notifai/commit/8df8198c658ff4c9ce1cee0354f5d966d902bb12))
|
|
68
|
+
* **cli:** one reply_result object from send --reply --json ([928a980](https://github.com/Raidiant-io/notifai/commit/928a9804e05605a6a6b1cb9098c356ccef678080))
|
|
69
|
+
* **cli:** replace generated fallback session names with later semantic ones ([66bd4a7](https://github.com/Raidiant-io/notifai/commit/66bd4a76bc947283d9dd3c4a807afc4b380c1d0c))
|
|
70
|
+
* **cli:** replace generated fallback session names with later semantic ones ([52fe546](https://github.com/Raidiant-io/notifai/commit/52fe54690f51ef17fab0c1d69da440a29332f6a9))
|
|
71
|
+
* **cli:** replace notify_criteria with layered guidance topics ([7d80708](https://github.com/Raidiant-io/notifai/commit/7d807089e02eba2f32a35fb6cad9eff8b75aaecc))
|
|
72
|
+
* **cli:** spell send's question flags --choice/--multi like ask ([5437816](https://github.com/Raidiant-io/notifai/commit/54378163994866f2239f033605bcf0041dce40fb))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### Bug Fixes
|
|
76
|
+
|
|
77
|
+
* **cli:** stop repeating a question in the notification body ([3e9acb7](https://github.com/Raidiant-io/notifai/commit/3e9acb76b65cae0d1f654657b38b53a4c8a23912))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Dependencies
|
|
81
|
+
|
|
82
|
+
* The following workspace dependencies were updated
|
|
83
|
+
* dependencies
|
|
84
|
+
* @raidiant/notifai-protocol bumped from 4.0.0 to 4.1.0
|
|
85
|
+
|
|
11
86
|
## [7.0.2](https://github.com/Raidiant-io/notifai/compare/v7.0.1...v7.0.2) (2026-08-20)
|
|
12
87
|
|
|
13
88
|
|
package/README.md
CHANGED
|
@@ -5,26 +5,30 @@ Send native phone notifications from agents and local programs.
|
|
|
5
5
|
An agent working while you are away has no way to reach you, so it either
|
|
6
6
|
guesses or sits blocked in a terminal nobody is watching. `notifai` gives
|
|
7
7
|
it a way to tell you something finished, and a way to ask you a question
|
|
8
|
-
and get your answer back
|
|
8
|
+
and get your answer back as a banner on your phone.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
11
|
npm install -g @raidiant/notifai
|
|
12
12
|
notifai init
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
The `notifai` command is always a machine-wide install. Setup scope (this
|
|
16
|
+
project vs this machine) does not change that. If you do not want a global
|
|
17
|
+
bin, `npx --yes @raidiant/notifai@<version>` is supported. Pin the version.
|
|
18
|
+
`hooks install` then writes that same pinned npx invocation into the harness
|
|
19
|
+
adapter. This is slower than a real install and is not the default path.
|
|
19
20
|
|
|
20
21
|
`init` walks the setup one step at a time and tells you the single next
|
|
21
22
|
thing to do. Run it again after each step; it works out what remains.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`notifai init --
|
|
24
|
+
At a human terminal, `init` asks once whether this setup is for this project
|
|
25
|
+
or for every project on this machine. That answer is skill, hooks, and
|
|
26
|
+
config together. Pairing, devices, and credentials stay on this machine
|
|
27
|
+
either way. An unattended caller passes the same choice as a flag:
|
|
28
|
+
`notifai init --setup-scope project` or `notifai init --setup-scope global`.
|
|
29
|
+
`--skills-scope` is an alias of that flag when `--skills` is set. Shared
|
|
30
|
+
project config may live in the repo; personal project preferences live under
|
|
31
|
+
the user config directory and never require a `.gitignore` edit.
|
|
28
32
|
|
|
29
33
|
## Telling you something happened
|
|
30
34
|
|
|
@@ -47,8 +51,8 @@ notifai ask "Deploy the migration to production?" --choice Yes --choice No
|
|
|
47
51
|
```
|
|
48
52
|
|
|
49
53
|
The agent registers the question, ends its turn, and your answer comes
|
|
50
|
-
back on its next turn. On iPhone
|
|
51
|
-
|
|
54
|
+
back on its next turn. On iPhone, press and hold the collapsed banner to
|
|
55
|
+
answer; the choices appear on the expanded card, not on the lock screen.
|
|
52
56
|
|
|
53
57
|
By default a question reaches your devices when the agent turn ends, whether or
|
|
54
58
|
not you are at the keyboard (`ask_grace_seconds = 0`). Set a positive grace
|
|
@@ -117,7 +121,10 @@ do not. `--json` emits the same result with an explicit `exit_code`.
|
|
|
117
121
|
Everything useful is possible non-interactively, so an agent never
|
|
118
122
|
reaches a prompt and hangs.
|
|
119
123
|
|
|
120
|
-
Source, issues and the
|
|
124
|
+
Source, issues and the public/private boundary policy:
|
|
121
125
|
<https://github.com/Raidiant-io/notifai>
|
|
122
126
|
|
|
127
|
+
The URL, self-host exception, guidance-authority and non-exfiltration policy is
|
|
128
|
+
documented in [`docs/TRUST.md`](https://github.com/Raidiant-io/notifai/blob/main/docs/TRUST.md).
|
|
129
|
+
|
|
123
130
|
Apache-2.0.
|
package/dist/atomic-file.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export interface AtomicWriteOptions {
|
|
|
5
5
|
preserveMode?: boolean;
|
|
6
6
|
/** Reject a target owned by another user instead of replacing it. */
|
|
7
7
|
requireCurrentUserOwner?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* POSIX directory mode applied to the parent. Defaults to 0o700 when
|
|
10
|
+
* `requireCurrentUserOwner` is set so credential and config dirs match the
|
|
11
|
+
* hook-adapter and log directories.
|
|
12
|
+
*/
|
|
13
|
+
directoryMode?: number;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
16
|
* Replace one regular file without exposing a truncated intermediate state.
|
|
@@ -14,3 +20,11 @@ export interface AtomicWriteOptions {
|
|
|
14
20
|
* symlink target is refused instead of silently writing through it.
|
|
15
21
|
*/
|
|
16
22
|
export declare function atomicWriteFileSync(file: string, contents: string, options?: AtomicWriteOptions): void;
|
|
23
|
+
/**
|
|
24
|
+
* Create (or tighten) a directory that must not be world-readable.
|
|
25
|
+
*
|
|
26
|
+
* `mkdir` mode applies only to a newly created leaf, so an existing 0755
|
|
27
|
+
* credential directory would otherwise stay world-listable. POSIX chmod of
|
|
28
|
+
* the leaf is the matching repair; Windows has no equivalent mode bits.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ensurePrivateDirectory(directory: string): void;
|
package/dist/atomic-file.js
CHANGED
|
@@ -10,8 +10,12 @@ import path from 'node:path';
|
|
|
10
10
|
*/
|
|
11
11
|
export function atomicWriteFileSync(file, contents, options = {}) {
|
|
12
12
|
const directory = path.dirname(file);
|
|
13
|
-
|
|
14
|
-
const
|
|
13
|
+
const requireCurrentUserOwner = options.requireCurrentUserOwner ?? false;
|
|
14
|
+
const directoryMode = options.directoryMode ?? (requireCurrentUserOwner ? 0o700 : undefined);
|
|
15
|
+
const parent = ensureDirectory(directory, {
|
|
16
|
+
requireCurrentUserOwner,
|
|
17
|
+
...(directoryMode === undefined ? {} : { mode: directoryMode }),
|
|
18
|
+
});
|
|
15
19
|
const target = targetMetadata(file, options.mode ?? 0o600, options.preserveMode ?? true, options.requireCurrentUserOwner ?? false);
|
|
16
20
|
const temp = path.join(directory, `.${path.basename(file)}.notifai-${process.pid}-${randomBytes(6).toString('hex')}.tmp`);
|
|
17
21
|
let handle;
|
|
@@ -39,6 +43,27 @@ export function atomicWriteFileSync(file, contents, options = {}) {
|
|
|
39
43
|
throw err;
|
|
40
44
|
}
|
|
41
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Create (or tighten) a directory that must not be world-readable.
|
|
48
|
+
*
|
|
49
|
+
* `mkdir` mode applies only to a newly created leaf, so an existing 0755
|
|
50
|
+
* credential directory would otherwise stay world-listable. POSIX chmod of
|
|
51
|
+
* the leaf is the matching repair; Windows has no equivalent mode bits.
|
|
52
|
+
*/
|
|
53
|
+
export function ensurePrivateDirectory(directory) {
|
|
54
|
+
ensureDirectory(directory, { mode: 0o700, requireCurrentUserOwner: false });
|
|
55
|
+
}
|
|
56
|
+
function ensureDirectory(directory, options) {
|
|
57
|
+
if (options.mode === undefined)
|
|
58
|
+
mkdirSync(directory, { recursive: true });
|
|
59
|
+
else
|
|
60
|
+
mkdirSync(directory, { recursive: true, mode: options.mode });
|
|
61
|
+
const parent = safeDirectory(directory, options.requireCurrentUserOwner);
|
|
62
|
+
if (options.mode !== undefined && process.platform !== 'win32') {
|
|
63
|
+
chmodSync(directory, options.mode);
|
|
64
|
+
}
|
|
65
|
+
return parent;
|
|
66
|
+
}
|
|
42
67
|
/** Persist the published directory entry where the host supports directory fsync. */
|
|
43
68
|
function syncDirectory(directory) {
|
|
44
69
|
if (process.platform === 'win32')
|
package/dist/atomic-file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-file.js","sourceRoot":"","sources":["../src/atomic-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,QAAQ,EACR,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAA;AAChB,OAAO,IAAI,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"atomic-file.js","sourceRoot":"","sources":["../src/atomic-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReadinessState } from './readiness.js';
|
|
2
|
+
export declare function pathNotifaiEntries(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string[];
|
|
3
|
+
export declare function isExecutablePath(file: string, platform?: NodeJS.Platform): boolean;
|
|
4
|
+
export declare function cliBinReadiness(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform): ReadinessState;
|
package/dist/cli-bin.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { accessSync, constants, existsSync, lstatSync, realpathSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const POSIX_NAMES = ['notifai'];
|
|
4
|
+
const WINDOWS_NAMES = ['notifai.cmd', 'notifai.exe', 'notifai'];
|
|
5
|
+
export function pathNotifaiEntries(env, platform = process.platform) {
|
|
6
|
+
const raw = platform === 'win32' ? (env['Path'] ?? env['PATH'] ?? '') : (env['PATH'] ?? '');
|
|
7
|
+
const delimiter = platform === 'win32' ? ';' : ':';
|
|
8
|
+
const names = platform === 'win32' ? WINDOWS_NAMES : POSIX_NAMES;
|
|
9
|
+
const found = [];
|
|
10
|
+
for (const directory of raw.split(delimiter)) {
|
|
11
|
+
if (directory === '')
|
|
12
|
+
continue;
|
|
13
|
+
for (const name of names) {
|
|
14
|
+
const candidate = path.join(directory, name);
|
|
15
|
+
if (!existsSync(candidate))
|
|
16
|
+
continue;
|
|
17
|
+
if (!found.includes(candidate))
|
|
18
|
+
found.push(candidate);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
export function isExecutablePath(file, platform = process.platform) {
|
|
24
|
+
if (!existsSync(file))
|
|
25
|
+
return false;
|
|
26
|
+
if (platform === 'win32')
|
|
27
|
+
return true;
|
|
28
|
+
try {
|
|
29
|
+
const target = lstatSync(file).isSymbolicLink() ? realpathSync(file) : file;
|
|
30
|
+
accessSync(target, constants.X_OK);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function cliBinReadiness(env, platform = process.platform) {
|
|
38
|
+
const entries = pathNotifaiEntries(env, platform);
|
|
39
|
+
const broken = entries.find((file) => !isExecutablePath(file, platform));
|
|
40
|
+
if (broken !== undefined) {
|
|
41
|
+
return {
|
|
42
|
+
id: 'cli-bin',
|
|
43
|
+
title: 'notifai command',
|
|
44
|
+
status: 'gap',
|
|
45
|
+
detail: `${broken} is on PATH but is not executable`,
|
|
46
|
+
remedy: {
|
|
47
|
+
by: 'user-here',
|
|
48
|
+
summary: 'repair the global notifai command so it can run',
|
|
49
|
+
command: 'npm install -g @raidiant/notifai',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
id: 'cli-bin',
|
|
55
|
+
title: 'notifai command',
|
|
56
|
+
status: 'ready',
|
|
57
|
+
detail: entries[0] === undefined
|
|
58
|
+
? 'this process can run; no notifai binary was on PATH'
|
|
59
|
+
: `${entries[0]} is executable`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=cli-bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-bin.js","sourceRoot":"","sources":["../src/cli-bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function compareCliSemVer(left: string, right: string): number | null;
|
|
2
|
+
export declare function shouldConsultCliRegistry(input: {
|
|
3
|
+
interactive?: boolean;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
}): boolean;
|
|
7
|
+
export declare function latestPublishedCliVersion(fetchImpl?: typeof fetch): Promise<string | null>;
|
|
8
|
+
export declare function newerPublishedCli(local: string | null, latest: string | null): string | null;
|
|
9
|
+
export declare function thisCliVersion(): string | null;
|
|
10
|
+
export declare function resetLatestPublishedCliVersionForTest(): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { packageVersion } from './release.js';
|
|
2
|
+
/**
|
|
3
|
+
* The newest published CLI, read from the registry that publishes it.
|
|
4
|
+
*
|
|
5
|
+
* Which version is newest is a registry fact. Doctor consults it as a
|
|
6
|
+
* best-effort, cached, short-timeout hint: never fatal, and never on a
|
|
7
|
+
* non-interactive or CI invocation — those callers already get structured
|
|
8
|
+
* support state from the server when they are signed in.
|
|
9
|
+
*/
|
|
10
|
+
const DIST_TAGS_URL = 'https://registry.npmjs.org/-/package/@raidiant/notifai/dist-tags';
|
|
11
|
+
const REQUEST_TIMEOUT_MS = 2_000;
|
|
12
|
+
const CACHE_TTL_MS = 60 * 60 * 1000;
|
|
13
|
+
let cached = null;
|
|
14
|
+
export function compareCliSemVer(left, right) {
|
|
15
|
+
const parse = (value) => {
|
|
16
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value);
|
|
17
|
+
if (!match)
|
|
18
|
+
return null;
|
|
19
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
20
|
+
};
|
|
21
|
+
const a = parse(left);
|
|
22
|
+
const b = parse(right);
|
|
23
|
+
if (a === null || b === null)
|
|
24
|
+
return null;
|
|
25
|
+
for (let index = 0; index < 3; index += 1) {
|
|
26
|
+
if (a[index] !== b[index])
|
|
27
|
+
return a[index] - b[index];
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
export function shouldConsultCliRegistry(input) {
|
|
32
|
+
if (input.json === true)
|
|
33
|
+
return false;
|
|
34
|
+
if (input.interactive !== true)
|
|
35
|
+
return false;
|
|
36
|
+
const ci = input.env?.['CI'];
|
|
37
|
+
return ci !== '1' && ci !== 'true';
|
|
38
|
+
}
|
|
39
|
+
export async function latestPublishedCliVersion(fetchImpl = fetch) {
|
|
40
|
+
if (cached !== null && Date.now() - cached.at < CACHE_TTL_MS)
|
|
41
|
+
return cached.value;
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetchImpl(DIST_TAGS_URL, {
|
|
44
|
+
headers: { accept: 'application/json' },
|
|
45
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok)
|
|
48
|
+
return cached?.value ?? null;
|
|
49
|
+
const body = await response.json();
|
|
50
|
+
if (typeof body !== 'object' || body === null)
|
|
51
|
+
return cached?.value ?? null;
|
|
52
|
+
const latest = body.latest;
|
|
53
|
+
if (typeof latest !== 'string' || compareCliSemVer(latest, '0.0.0') === null) {
|
|
54
|
+
return cached?.value ?? null;
|
|
55
|
+
}
|
|
56
|
+
cached = { value: latest, at: Date.now() };
|
|
57
|
+
return latest;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return cached?.value ?? null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function newerPublishedCli(local, latest) {
|
|
64
|
+
if (local === null || latest === null)
|
|
65
|
+
return null;
|
|
66
|
+
const comparison = compareCliSemVer(latest, local);
|
|
67
|
+
return comparison !== null && comparison > 0 ? latest : null;
|
|
68
|
+
}
|
|
69
|
+
export function thisCliVersion() {
|
|
70
|
+
return packageVersion();
|
|
71
|
+
}
|
|
72
|
+
export function resetLatestPublishedCliVersionForTest() {
|
|
73
|
+
cached = null;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=cli-release.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-release.js","sourceRoot":"","sources":["../src/cli-release.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface ApiClient {
|
|
|
22
22
|
machine_name: string;
|
|
23
23
|
credential_hash: string;
|
|
24
24
|
poll_verifier_hash: string;
|
|
25
|
+
confirmation_hash: string;
|
|
25
26
|
}): Promise<BeginPairingResponse>;
|
|
26
27
|
pollPairing(pairingId: string, pollVerifier: string): Promise<PollPairingResponse>;
|
|
27
28
|
accessStatus(): Promise<AccountAccessResponse>;
|
package/dist/client.js
CHANGED
|
@@ -104,6 +104,7 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
104
104
|
...clientHeaders,
|
|
105
105
|
},
|
|
106
106
|
signal,
|
|
107
|
+
...(bearer ? { redirect: 'error' } : {}),
|
|
107
108
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
108
109
|
});
|
|
109
110
|
}
|
|
@@ -133,7 +134,9 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
133
134
|
}
|
|
134
135
|
return {
|
|
135
136
|
beginPairing: (body) => call('POST', '/api/v1/pairings', body),
|
|
136
|
-
pollPairing: (pairingId, pollVerifier) => call('POST', `/api/v1/pairings/${pairingId}/poll`, {
|
|
137
|
+
pollPairing: (pairingId, pollVerifier) => call('POST', `/api/v1/pairings/${encodeURIComponent(pairingId)}/poll`, {
|
|
138
|
+
poll_verifier: pollVerifier,
|
|
139
|
+
}),
|
|
137
140
|
accessStatus: () => call('GET', '/api/v1/account/access'),
|
|
138
141
|
listDevices: () => call('GET', '/api/v1/devices'),
|
|
139
142
|
capabilities: (platform = 'ios', appVersion, appBuild) => {
|
|
@@ -143,11 +146,11 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
143
146
|
if (appBuild !== undefined)
|
|
144
147
|
query.set('app_build', appBuild);
|
|
145
148
|
const suffix = query.size > 0 ? `?${query.toString()}` : '';
|
|
146
|
-
return call('GET', `/api/v1/capabilities/${platform}${suffix}`);
|
|
149
|
+
return call('GET', `/api/v1/capabilities/${encodeURIComponent(platform)}${suffix}`);
|
|
147
150
|
},
|
|
148
151
|
compatibility: () => call('GET', '/api/v1/compatibility'),
|
|
149
152
|
submit: (body, waitSeconds) => call('POST', `/api/v1/notifications?wait_seconds=${waitSeconds}`, body, waitSeconds),
|
|
150
|
-
evidence: (requestId) => call('GET', `/api/v1/notifications/${requestId}`),
|
|
153
|
+
evidence: (requestId) => call('GET', `/api/v1/notifications/${encodeURIComponent(requestId)}`),
|
|
151
154
|
replies: (requestId, { waitSeconds, afterSeq }) => call('GET', `/api/v1/notifications/${encodeURIComponent(requestId)}/replies?wait_seconds=${waitSeconds}&after_seq=${afterSeq}`, undefined, waitSeconds),
|
|
152
155
|
closeReplies: (requestId) => call('POST', `/api/v1/notifications/${encodeURIComponent(requestId)}/replies/close`),
|
|
153
156
|
putAgentAcknowledgement: (requestId, body) => call('PUT', `/api/v1/notifications/${encodeURIComponent(requestId)}/agent-acknowledgement`, body),
|
|
@@ -166,11 +169,18 @@ export function createClient(baseUrl, bearer, options = {}) {
|
|
|
166
169
|
// Media can be large, so this gets its own allowance rather than the
|
|
167
170
|
// per-request budget — but still a finite one.
|
|
168
171
|
signal: AbortSignal.timeout(Math.max(budgetMs, 60_000)),
|
|
172
|
+
// The grant names one exact destination and carries signed headers.
|
|
173
|
+
// A redirect would replay both against an origin the server never
|
|
174
|
+
// granted, so it is refused rather than followed.
|
|
175
|
+
redirect: 'manual',
|
|
169
176
|
});
|
|
170
177
|
}
|
|
171
178
|
catch (err) {
|
|
172
179
|
throw new NetworkError(`Upload failed: ${String(err)}`);
|
|
173
180
|
}
|
|
181
|
+
if (response.status >= 300 && response.status < 400) {
|
|
182
|
+
throw new NetworkError(`Upload redirect is refused, not followed: the grant's headers are signed for one exact destination`);
|
|
183
|
+
}
|
|
174
184
|
if (!response.ok) {
|
|
175
185
|
throw new NetworkError(`Upload rejected with status ${response.status}`);
|
|
176
186
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAsBA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,4BAA4B,CAAA;AAGnC,MAAM,OAAO,YAAa,SAAQ,KAAK;IAE5B;IACA;IAEA;IACA;IACA;IANT,YACS,MAAc,EACd,IAAY,EACnB,OAAe,EACR,aAA4B,IAAI,EAChC,UAAmB,IAAI,EACvB,iBAAwC,IAAI;QAEnD,KAAK,CAAC,OAAO,CAAC,CAAA;QAPP,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAEZ,eAAU,GAAV,UAAU,CAAsB;QAChC,YAAO,GAAP,OAAO,CAAgB;QACvB,mBAAc,GAAd,cAAc,CAA8B;IAGrD,CAAC;CACF;AAED,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAY;IACpD,IAAI,GAAG,YAAY,YAAY;QAAE,OAAO,IAAI,CAAA;IAC5C,IAAI,CAAC,CAAC,GAAG,YAAY,YAAY,CAAC;QAAE,OAAO,KAAK,CAAA;IAChD,OAAO,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAA;AACtE,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,GAAY,EAAE,IAAY,EAAE,OAAe;IACjE,MAAM,IAAI,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IACjD,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QACrD,OAAO,IAAI,YAAY,CAAC,GAAG,IAAI,2BAA2B,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,IAAI,YAAY,CAAC,mBAAmB,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AACpE,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/commands-auth.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type CommandDeps } from './commands-core.js';
|
|
2
|
+
/**
|
|
3
|
+
* Keep the one-time confirmation secret out of the HTTP request that opens
|
|
4
|
+
* the dashboard. URL fragments are browser-local and are not sent by GET,
|
|
5
|
+
* prefetchers, referrers, or link scanners.
|
|
6
|
+
*/
|
|
7
|
+
export declare function pairingApprovalUrl(approveUrl: string, confirmationSecret: string): string;
|
|
2
8
|
export declare function loginCommand(deps: CommandDeps, flags: {
|
|
3
9
|
name?: string;
|
|
4
10
|
baseUrl?: string;
|
package/dist/commands-auth.js
CHANGED
|
@@ -4,16 +4,28 @@ import { randomBytes } from 'node:crypto';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import { NetworkError } from './client.js';
|
|
6
6
|
import {} from './config.js';
|
|
7
|
+
import { checkApproveUrl } from './url-policy.js';
|
|
7
8
|
import { EXIT, authedClient, loadLoggedConfig, makeClient, reportError, } from './commands-core.js';
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
// login / logout / auth status
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Keep the one-time confirmation secret out of the HTTP request that opens
|
|
14
|
+
* the dashboard. URL fragments are browser-local and are not sent by GET,
|
|
15
|
+
* prefetchers, referrers, or link scanners.
|
|
16
|
+
*/
|
|
17
|
+
export function pairingApprovalUrl(approveUrl, confirmationSecret) {
|
|
18
|
+
const url = new URL(approveUrl);
|
|
19
|
+
url.hash = new URLSearchParams({ confirmation_secret: confirmationSecret }).toString();
|
|
20
|
+
return url.toString();
|
|
21
|
+
}
|
|
11
22
|
export async function loginCommand(deps, flags) {
|
|
12
23
|
const config = loadLoggedConfig(deps, { cwd: deps.cwd, env: deps.env, flags: { base_url: flags.baseUrl } });
|
|
13
24
|
const baseUrl = config.base_url.value;
|
|
14
25
|
const machineName = flags.name ?? os.hostname();
|
|
15
26
|
const secret = randomBytes(32).toString('base64url');
|
|
16
27
|
const pollVerifier = randomBytes(24).toString('base64url');
|
|
28
|
+
const confirmationSecret = randomBytes(32).toString('base64url');
|
|
17
29
|
const client = makeClient(deps, baseUrl, null);
|
|
18
30
|
let begin;
|
|
19
31
|
try {
|
|
@@ -21,23 +33,36 @@ export async function loginCommand(deps, flags) {
|
|
|
21
33
|
machine_name: machineName,
|
|
22
34
|
credential_hash: sha256Hex(secret),
|
|
23
35
|
poll_verifier_hash: sha256Hex(pollVerifier),
|
|
36
|
+
confirmation_hash: sha256Hex(confirmationSecret),
|
|
24
37
|
});
|
|
25
38
|
}
|
|
26
39
|
catch (err) {
|
|
27
40
|
return reportError(deps, err);
|
|
28
41
|
}
|
|
42
|
+
const approveUrl = pairingApprovalUrl(begin.approve_url, confirmationSecret);
|
|
43
|
+
// The approval URL is the server's choice, and this machine is about to put
|
|
44
|
+
// it in front of the user's browser. A compromised or misconfigured service
|
|
45
|
+
// must not be able to aim that anywhere it likes, so the pairing stops here
|
|
46
|
+
// rather than showing a link the user would reasonably trust.
|
|
47
|
+
const approvable = checkApproveUrl(begin.approve_url, baseUrl, config.approve_origins.value);
|
|
48
|
+
if (!approvable.ok) {
|
|
49
|
+
deps.io.err(`Pairing stopped: ${approvable.reason}`);
|
|
50
|
+
deps.io.err('next: If you self-host with the dashboard on its own origin, allow it with ' +
|
|
51
|
+
'`notifai config set approve_origins <origin>` and run `notifai login` again.');
|
|
52
|
+
return EXIT.auth;
|
|
53
|
+
}
|
|
29
54
|
const interactive = deps.io.interactive === true;
|
|
30
55
|
if (interactive) {
|
|
31
56
|
await deps.io.intro?.('Notifai sign in');
|
|
32
|
-
await deps.io.note?.(`Code: ${begin.code}\n${
|
|
57
|
+
await deps.io.note?.(`Code: ${begin.code}\n${approveUrl}`, 'Approve this machine');
|
|
33
58
|
}
|
|
34
59
|
else {
|
|
35
60
|
deps.io.out(`Pairing code: ${begin.code}`);
|
|
36
|
-
deps.io.out(`Approve this machine at: ${
|
|
61
|
+
deps.io.out(`Approve this machine at: ${approveUrl}`);
|
|
37
62
|
deps.io.out('Waiting for approval…');
|
|
38
63
|
}
|
|
39
64
|
if (flags.open !== false)
|
|
40
|
-
deps.io.openUrl(
|
|
65
|
+
deps.io.openUrl(approveUrl);
|
|
41
66
|
const expiresAt = new Date(begin.expires_at).getTime();
|
|
42
67
|
const intervalMs = Math.max(begin.poll_interval_seconds, 1) * 1000;
|
|
43
68
|
const now = deps.now ?? Date.now;
|