@raidiant/notifai 11.7.1 → 12.0.0-beta.13
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 +57 -0
- package/NOTICE +16 -0
- package/THIRD_PARTY_NOTICES +725 -0
- package/bin/notifai.mjs +11420 -0
- package/data/install.ps1 +318 -0
- package/data/release.json +1 -0
- package/inventory.json +1 -0
- package/npm-adapter-files.json +52 -0
- package/package.json +14 -44
- package/README.md +0 -168
- package/dist/agent-update-notice.d.ts +0 -7
- package/dist/agent-update-notice.js +0 -77
- package/dist/agent-update-notice.js.map +0 -1
- package/dist/atomic-file.d.ts +0 -30
- package/dist/atomic-file.js +0 -148
- package/dist/atomic-file.js.map +0 -1
- package/dist/attendant-update.d.ts +0 -15
- package/dist/attendant-update.js +0 -96
- package/dist/attendant-update.js.map +0 -1
- package/dist/claude-wake.d.ts +0 -108
- package/dist/claude-wake.js +0 -369
- package/dist/claude-wake.js.map +0 -1
- package/dist/cli-bin.d.ts +0 -26
- package/dist/cli-bin.js +0 -220
- package/dist/cli-bin.js.map +0 -1
- package/dist/cli-contract.d.ts +0 -16
- package/dist/cli-contract.js +0 -27
- package/dist/cli-contract.js.map +0 -1
- package/dist/cli-release.d.ts +0 -36
- package/dist/cli-release.js +0 -85
- package/dist/cli-release.js.map +0 -1
- package/dist/client.d.ts +0 -109
- package/dist/client.js +0 -217
- package/dist/client.js.map +0 -1
- package/dist/codex-answer-control.d.ts +0 -14
- package/dist/codex-answer-control.js +0 -41
- package/dist/codex-answer-control.js.map +0 -1
- package/dist/codex-answer-presentation.d.ts +0 -18
- package/dist/codex-answer-presentation.js +0 -113
- package/dist/codex-answer-presentation.js.map +0 -1
- package/dist/codex-input-lifecycle.d.ts +0 -10
- package/dist/codex-input-lifecycle.js +0 -63
- package/dist/codex-input-lifecycle.js.map +0 -1
- package/dist/codex-native-control.d.ts +0 -13
- package/dist/codex-native-control.js +0 -120
- package/dist/codex-native-control.js.map +0 -1
- package/dist/codex-native-turn.d.ts +0 -37
- package/dist/codex-native-turn.js +0 -155
- package/dist/codex-native-turn.js.map +0 -1
- package/dist/codex-question-bindings.d.ts +0 -76
- package/dist/codex-question-bindings.js +0 -151
- package/dist/codex-question-bindings.js.map +0 -1
- package/dist/codex-queue-control.d.ts +0 -21
- package/dist/codex-queue-control.js +0 -59
- package/dist/codex-queue-control.js.map +0 -1
- package/dist/codex-tool-messages.d.ts +0 -7
- package/dist/codex-tool-messages.js +0 -126
- package/dist/codex-tool-messages.js.map +0 -1
- package/dist/codex-wake.d.ts +0 -58
- package/dist/codex-wake.js +0 -135
- package/dist/codex-wake.js.map +0 -1
- package/dist/command-session.d.ts +0 -14
- package/dist/command-session.js +0 -31
- package/dist/command-session.js.map +0 -1
- package/dist/commands-acknowledge.d.ts +0 -13
- package/dist/commands-acknowledge.js +0 -116
- package/dist/commands-acknowledge.js.map +0 -1
- package/dist/commands-agent-sessions.d.ts +0 -6
- package/dist/commands-agent-sessions.js +0 -51
- package/dist/commands-agent-sessions.js.map +0 -1
- package/dist/commands-ask.d.ts +0 -62
- package/dist/commands-ask.js +0 -503
- package/dist/commands-ask.js.map +0 -1
- package/dist/commands-auth.d.ts +0 -76
- package/dist/commands-auth.js +0 -421
- package/dist/commands-auth.js.map +0 -1
- package/dist/commands-close.d.ts +0 -6
- package/dist/commands-close.js +0 -273
- package/dist/commands-close.js.map +0 -1
- package/dist/commands-config.d.ts +0 -31
- package/dist/commands-config.js +0 -272
- package/dist/commands-config.js.map +0 -1
- package/dist/commands-core.d.ts +0 -145
- package/dist/commands-core.js +0 -190
- package/dist/commands-core.js.map +0 -1
- package/dist/commands-devices.d.ts +0 -41
- package/dist/commands-devices.js +0 -144
- package/dist/commands-devices.js.map +0 -1
- package/dist/commands-doctor.d.ts +0 -25
- package/dist/commands-doctor.js +0 -1553
- package/dist/commands-doctor.js.map +0 -1
- package/dist/commands-guidance.d.ts +0 -27
- package/dist/commands-guidance.js +0 -150
- package/dist/commands-guidance.js.map +0 -1
- package/dist/commands-harness-context.d.ts +0 -57
- package/dist/commands-harness-context.js +0 -102
- package/dist/commands-harness-context.js.map +0 -1
- package/dist/commands-hook-attend.d.ts +0 -70
- package/dist/commands-hook-attend.js +0 -655
- package/dist/commands-hook-attend.js.map +0 -1
- package/dist/commands-hook-diagnostics.d.ts +0 -37
- package/dist/commands-hook-diagnostics.js +0 -136
- package/dist/commands-hook-diagnostics.js.map +0 -1
- package/dist/commands-hook-install.d.ts +0 -26
- package/dist/commands-hook-install.js +0 -784
- package/dist/commands-hook-install.js.map +0 -1
- package/dist/commands-hook-run.d.ts +0 -29
- package/dist/commands-hook-run.js +0 -933
- package/dist/commands-hook-run.js.map +0 -1
- package/dist/commands-hook-shape.d.ts +0 -19
- package/dist/commands-hook-shape.js +0 -48
- package/dist/commands-hook-shape.js.map +0 -1
- package/dist/commands-init.d.ts +0 -42
- package/dist/commands-init.js +0 -901
- package/dist/commands-init.js.map +0 -1
- package/dist/commands-io.d.ts +0 -2
- package/dist/commands-io.js +0 -117
- package/dist/commands-io.js.map +0 -1
- package/dist/commands-logs.d.ts +0 -30
- package/dist/commands-logs.js +0 -201
- package/dist/commands-logs.js.map +0 -1
- package/dist/commands-native-acknowledge.d.ts +0 -8
- package/dist/commands-native-acknowledge.js +0 -114
- package/dist/commands-native-acknowledge.js.map +0 -1
- package/dist/commands-project.d.ts +0 -4
- package/dist/commands-project.js +0 -41
- package/dist/commands-project.js.map +0 -1
- package/dist/commands-receive.d.ts +0 -3
- package/dist/commands-receive.js +0 -59
- package/dist/commands-receive.js.map +0 -1
- package/dist/commands-send-support.d.ts +0 -59
- package/dist/commands-send-support.js +0 -258
- package/dist/commands-send-support.js.map +0 -1
- package/dist/commands-send.d.ts +0 -34
- package/dist/commands-send.js +0 -900
- package/dist/commands-send.js.map +0 -1
- package/dist/commands-setup-proof.d.ts +0 -43
- package/dist/commands-setup-proof.js +0 -134
- package/dist/commands-setup-proof.js.map +0 -1
- package/dist/commands-skill.d.ts +0 -21
- package/dist/commands-skill.js +0 -199
- package/dist/commands-skill.js.map +0 -1
- package/dist/commands-sounds.d.ts +0 -12
- package/dist/commands-sounds.js +0 -35
- package/dist/commands-sounds.js.map +0 -1
- package/dist/commands-update-check.d.ts +0 -6
- package/dist/commands-update-check.js +0 -102
- package/dist/commands-update-check.js.map +0 -1
- package/dist/commands-update-resume.d.ts +0 -7
- package/dist/commands-update-resume.js +0 -143
- package/dist/commands-update-resume.js.map +0 -1
- package/dist/commands-update-skill.d.ts +0 -5
- package/dist/commands-update-skill.js +0 -36
- package/dist/commands-update-skill.js.map +0 -1
- package/dist/commands-update.d.ts +0 -11
- package/dist/commands-update.js +0 -286
- package/dist/commands-update.js.map +0 -1
- package/dist/commands.d.ts +0 -26
- package/dist/commands.js +0 -27
- package/dist/commands.js.map +0 -1
- package/dist/config-schema.d.ts +0 -62
- package/dist/config-schema.js +0 -302
- package/dist/config-schema.js.map +0 -1
- package/dist/config.d.ts +0 -181
- package/dist/config.js +0 -357
- package/dist/config.js.map +0 -1
- package/dist/credentials.d.ts +0 -96
- package/dist/credentials.js +0 -342
- package/dist/credentials.js.map +0 -1
- package/dist/file-lock.d.ts +0 -34
- package/dist/file-lock.js +0 -367
- package/dist/file-lock.js.map +0 -1
- package/dist/generated-session-label.d.ts +0 -2
- package/dist/generated-session-label.js +0 -152
- package/dist/generated-session-label.js.map +0 -1
- package/dist/guidance-content.d.ts +0 -53
- package/dist/guidance-content.js +0 -248
- package/dist/guidance-content.js.map +0 -1
- package/dist/guidance-render.d.ts +0 -25
- package/dist/guidance-render.js +0 -47
- package/dist/guidance-render.js.map +0 -1
- package/dist/guidance.d.ts +0 -69
- package/dist/guidance.js +0 -173
- package/dist/guidance.js.map +0 -1
- package/dist/harness-session-title.d.ts +0 -22
- package/dist/harness-session-title.js +0 -74
- package/dist/harness-session-title.js.map +0 -1
- package/dist/harnesses.d.ts +0 -65
- package/dist/harnesses.js +0 -159
- package/dist/harnesses.js.map +0 -1
- package/dist/hermes-attendant.d.ts +0 -38
- package/dist/hermes-attendant.js +0 -337
- package/dist/hermes-attendant.js.map +0 -1
- package/dist/hermes-plugin.d.ts +0 -11
- package/dist/hermes-plugin.js +0 -299
- package/dist/hermes-plugin.js.map +0 -1
- package/dist/hook-acknowledgements.d.ts +0 -70
- package/dist/hook-acknowledgements.js +0 -490
- package/dist/hook-acknowledgements.js.map +0 -1
- package/dist/hook-adapter.d.ts +0 -48
- package/dist/hook-adapter.js +0 -419
- package/dist/hook-adapter.js.map +0 -1
- package/dist/hook-events.d.ts +0 -105
- package/dist/hook-events.js +0 -130
- package/dist/hook-events.js.map +0 -1
- package/dist/hook-gates.d.ts +0 -7
- package/dist/hook-gates.js +0 -20
- package/dist/hook-gates.js.map +0 -1
- package/dist/hook-input.d.ts +0 -10
- package/dist/hook-input.js +0 -64
- package/dist/hook-input.js.map +0 -1
- package/dist/hook-lifecycle.d.ts +0 -112
- package/dist/hook-lifecycle.js +0 -2031
- package/dist/hook-lifecycle.js.map +0 -1
- package/dist/hook-project-sessions.d.ts +0 -28
- package/dist/hook-project-sessions.js +0 -141
- package/dist/hook-project-sessions.js.map +0 -1
- package/dist/hook-question-lock.d.ts +0 -29
- package/dist/hook-question-lock.js +0 -217
- package/dist/hook-question-lock.js.map +0 -1
- package/dist/hook-question-retirement.d.ts +0 -74
- package/dist/hook-question-retirement.js +0 -314
- package/dist/hook-question-retirement.js.map +0 -1
- package/dist/hook-question-state.d.ts +0 -58
- package/dist/hook-question-state.js +0 -239
- package/dist/hook-question-state.js.map +0 -1
- package/dist/hook-session-state.d.ts +0 -127
- package/dist/hook-session-state.js +0 -488
- package/dist/hook-session-state.js.map +0 -1
- package/dist/hook-types.d.ts +0 -476
- package/dist/hook-types.js +0 -4
- package/dist/hook-types.js.map +0 -1
- package/dist/injection-render.d.ts +0 -36
- package/dist/injection-render.js +0 -61
- package/dist/injection-render.js.map +0 -1
- package/dist/install-hooks.d.ts +0 -470
- package/dist/install-hooks.js +0 -1622
- package/dist/install-hooks.js.map +0 -1
- package/dist/integration-health.d.ts +0 -26
- package/dist/integration-health.js +0 -133
- package/dist/integration-health.js.map +0 -1
- package/dist/interactive.d.ts +0 -22
- package/dist/interactive.js +0 -644
- package/dist/interactive.js.map +0 -1
- package/dist/invocation-context.d.ts +0 -47
- package/dist/invocation-context.js +0 -164
- package/dist/invocation-context.js.map +0 -1
- package/dist/local-path.d.ts +0 -7
- package/dist/local-path.js +0 -27
- package/dist/local-path.js.map +0 -1
- package/dist/logging.d.ts +0 -157
- package/dist/logging.js +0 -599
- package/dist/logging.js.map +0 -1
- package/dist/main-run.d.ts +0 -1
- package/dist/main-run.js +0 -42
- package/dist/main-run.js.map +0 -1
- package/dist/main.d.ts +0 -2
- package/dist/main.js +0 -12
- package/dist/main.js.map +0 -1
- package/dist/native-answer-operation.d.ts +0 -70
- package/dist/native-answer-operation.js +0 -247
- package/dist/native-answer-operation.js.map +0 -1
- package/dist/native-skills.d.ts +0 -64
- package/dist/native-skills.js +0 -167
- package/dist/native-skills.js.map +0 -1
- package/dist/node-floor.d.ts +0 -19
- package/dist/node-floor.js +0 -32
- package/dist/node-floor.js.map +0 -1
- package/dist/npm-invocation.d.ts +0 -34
- package/dist/npm-invocation.js +0 -53
- package/dist/npm-invocation.js.map +0 -1
- package/dist/openclaw-continuation-bridge.d.ts +0 -7
- package/dist/openclaw-continuation-bridge.js +0 -64
- package/dist/openclaw-continuation-bridge.js.map +0 -1
- package/dist/openclaw-gateway-readiness.d.ts +0 -1
- package/dist/openclaw-gateway-readiness.js +0 -24
- package/dist/openclaw-gateway-readiness.js.map +0 -1
- package/dist/openclaw-generation.d.ts +0 -21
- package/dist/openclaw-generation.js +0 -115
- package/dist/openclaw-generation.js.map +0 -1
- package/dist/openclaw-message-bridge.d.ts +0 -8
- package/dist/openclaw-message-bridge.js +0 -93
- package/dist/openclaw-message-bridge.js.map +0 -1
- package/dist/openclaw-pending.d.ts +0 -8
- package/dist/openclaw-pending.js +0 -47
- package/dist/openclaw-pending.js.map +0 -1
- package/dist/openclaw-plugin.d.ts +0 -75
- package/dist/openclaw-plugin.js +0 -1353
- package/dist/openclaw-plugin.js.map +0 -1
- package/dist/openclaw-session-access.d.ts +0 -12
- package/dist/openclaw-session-access.js +0 -79
- package/dist/openclaw-session-access.js.map +0 -1
- package/dist/opencode-plugin.d.ts +0 -88
- package/dist/opencode-plugin.js +0 -286
- package/dist/opencode-plugin.js.map +0 -1
- package/dist/orca-session-title.d.ts +0 -11
- package/dist/orca-session-title.js +0 -111
- package/dist/orca-session-title.js.map +0 -1
- package/dist/pack-feedback-slice.d.ts +0 -35
- package/dist/pack-feedback-slice.js +0 -126
- package/dist/pack-feedback-slice.js.map +0 -1
- package/dist/pairing-qr.d.ts +0 -5
- package/dist/pairing-qr.js +0 -24
- package/dist/pairing-qr.js.map +0 -1
- package/dist/pending-pairing.d.ts +0 -38
- package/dist/pending-pairing.js +0 -70
- package/dist/pending-pairing.js.map +0 -1
- package/dist/platform.d.ts +0 -67
- package/dist/platform.js +0 -140
- package/dist/platform.js.map +0 -1
- package/dist/process-identity.d.ts +0 -26
- package/dist/process-identity.js +0 -82
- package/dist/process-identity.js.map +0 -1
- package/dist/program.d.ts +0 -59
- package/dist/program.js +0 -691
- package/dist/program.js.map +0 -1
- package/dist/project-enablement.d.ts +0 -21
- package/dist/project-enablement.js +0 -71
- package/dist/project-enablement.js.map +0 -1
- package/dist/question-settlement-process.d.ts +0 -17
- package/dist/question-settlement-process.js +0 -35
- package/dist/question-settlement-process.js.map +0 -1
- package/dist/question-timing.d.ts +0 -46
- package/dist/question-timing.js +0 -48
- package/dist/question-timing.js.map +0 -1
- package/dist/readiness.d.ts +0 -153
- package/dist/readiness.js +0 -149
- package/dist/readiness.js.map +0 -1
- package/dist/release.d.ts +0 -31
- package/dist/release.js +0 -52
- package/dist/release.js.map +0 -1
- package/dist/send-attempts.d.ts +0 -26
- package/dist/send-attempts.js +0 -139
- package/dist/send-attempts.js.map +0 -1
- package/dist/send.d.ts +0 -115
- package/dist/send.js +0 -421
- package/dist/send.js.map +0 -1
- package/dist/session-activation.d.ts +0 -13
- package/dist/session-activation.js +0 -64
- package/dist/session-activation.js.map +0 -1
- package/dist/session-attendant-probe.d.ts +0 -85
- package/dist/session-attendant-probe.js +0 -173
- package/dist/session-attendant-probe.js.map +0 -1
- package/dist/session-attendant-state.d.ts +0 -70
- package/dist/session-attendant-state.js +0 -256
- package/dist/session-attendant-state.js.map +0 -1
- package/dist/session-attendant.d.ts +0 -143
- package/dist/session-attendant.js +0 -520
- package/dist/session-attendant.js.map +0 -1
- package/dist/session-delivery.d.ts +0 -249
- package/dist/session-delivery.js +0 -576
- package/dist/session-delivery.js.map +0 -1
- package/dist/session-handoff.d.ts +0 -98
- package/dist/session-handoff.js +0 -118
- package/dist/session-handoff.js.map +0 -1
- package/dist/session-input-wakes.d.ts +0 -78
- package/dist/session-input-wakes.js +0 -239
- package/dist/session-input-wakes.js.map +0 -1
- package/dist/session-inputs.d.ts +0 -57
- package/dist/session-inputs.js +0 -434
- package/dist/session-inputs.js.map +0 -1
- package/dist/session-labels.d.ts +0 -56
- package/dist/session-labels.js +0 -396
- package/dist/session-labels.js.map +0 -1
- package/dist/session-message-handoff.d.ts +0 -39
- package/dist/session-message-handoff.js +0 -107
- package/dist/session-message-handoff.js.map +0 -1
- package/dist/setup-destinations.d.ts +0 -23
- package/dist/setup-destinations.js +0 -44
- package/dist/setup-destinations.js.map +0 -1
- package/dist/skill-integrity.d.ts +0 -54
- package/dist/skill-integrity.js +0 -164
- package/dist/skill-integrity.js.map +0 -1
- package/dist/skill-source/manifest.json +0 -40
- package/dist/skill-source/notifai/SKILL.md +0 -350
- package/dist/skill-source/notifai/references/diagnostics.md +0 -36
- package/dist/skill-source/notifai/references/harness-setup.md +0 -384
- package/dist/skill-source/notifai/references/native-questions.md +0 -51
- package/dist/skill-source/notifai/references/notes-and-edits.md +0 -54
- package/dist/skill-source/notifai/references/send-details.md +0 -14
- package/dist/skill-source/notifai/references/updates.md +0 -116
- package/dist/skill-source/notifai/references/writing-examples.md +0 -56
- package/dist/sound-ref.d.ts +0 -23
- package/dist/sound-ref.js +0 -28
- package/dist/sound-ref.js.map +0 -1
- package/dist/ui/banner.d.ts +0 -22
- package/dist/ui/banner.js +0 -87
- package/dist/ui/banner.js.map +0 -1
- package/dist/ui/config-view.d.ts +0 -30
- package/dist/ui/config-view.js +0 -108
- package/dist/ui/config-view.js.map +0 -1
- package/dist/ui/help.d.ts +0 -44
- package/dist/ui/help.js +0 -107
- package/dist/ui/help.js.map +0 -1
- package/dist/ui/theme.d.ts +0 -90
- package/dist/ui/theme.js +0 -244
- package/dist/ui/theme.js.map +0 -1
- package/dist/update-handoff.d.ts +0 -29
- package/dist/update-handoff.js +0 -69
- package/dist/update-handoff.js.map +0 -1
- package/dist/url-policy.d.ts +0 -85
- package/dist/url-policy.js +0 -369
- package/dist/url-policy.js.map +0 -1
- package/dist/version.d.ts +0 -40
- package/dist/version.js +0 -132
- package/dist/version.js.map +0 -1
- package/dist/wake-support.d.ts +0 -40
- package/dist/wake-support.js +0 -95
- package/dist/wake-support.js.map +0 -1
package/dist/interactive.js
DELETED
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The interactive app — what `notifai` does when a person runs it.
|
|
3
|
-
*
|
|
4
|
-
* Everything here is a shell around commands that already exist. `init`,
|
|
5
|
-
* `login`, `doctor`, `send`, `hooks install` and `config set` keep their
|
|
6
|
-
* behaviour, their validation and their exit codes; this module decides what to
|
|
7
|
-
* offer, in what order, and how to say it. That is deliberate: a second
|
|
8
|
-
* implementation of "set a config value" would be a second set of bugs, and the
|
|
9
|
-
* menu would slowly stop agreeing with the flags.
|
|
10
|
-
*
|
|
11
|
-
* The module is imported dynamically, only after the caller is known to be a
|
|
12
|
-
* human at a terminal. Nothing an agent runs — `send`, `ask`, and above all the
|
|
13
|
-
* hook that runs in front of every prompt the user types — may pay to load a
|
|
14
|
-
* prompt library it will never draw.
|
|
15
|
-
*
|
|
16
|
-
* Three things shape the layout:
|
|
17
|
-
*
|
|
18
|
-
* First-run is `init`, not a second sign-in/device split. An unsigned machine
|
|
19
|
-
* enters that journey immediately; a remaining blocker is offered as Finish
|
|
20
|
-
* setup. Once send-prerequisites are ready, the menu leads with the test
|
|
21
|
-
* notification, because seeing the thing arrive on a device is what makes the
|
|
22
|
-
* product real.
|
|
23
|
-
*
|
|
24
|
-
* Detail is earned, not given. A settings group shows fourteen one-line
|
|
25
|
-
* summaries, and the paragraph explaining a key appears once the reader has
|
|
26
|
-
* picked that key.
|
|
27
|
-
*
|
|
28
|
-
* Cancelling always goes back one level and never leaves the terminal in a
|
|
29
|
-
* strange state.
|
|
30
|
-
*/
|
|
31
|
-
import * as clack from '@clack/prompts';
|
|
32
|
-
import { EXIT, assessReadiness, canDeviceReceive, configSetCommand, deviceInventory, doctorCommand, hooksInstallCommand, hooksUninstallCommand, initCommand, loginCommand, logoutCommand, sendCommand, } from './commands.js';
|
|
33
|
-
import { loadConfig } from './config.js';
|
|
34
|
-
import { detectedHarnesses, findInstallations, } from './install-hooks.js';
|
|
35
|
-
import { CONFIG_GROUPS, acceptedValues, boundsHint, configInfo, configKeysByGroup, describeSource, formatValue, } from './config-schema.js';
|
|
36
|
-
import { firstBlocker, refreshAfterMenuAction, } from './readiness.js';
|
|
37
|
-
import { printBanner, tagline } from './ui/banner.js';
|
|
38
|
-
import { card, glyph, mark, style } from './ui/theme.js';
|
|
39
|
-
/** Clack returns a symbol when the user pressed Ctrl-C or Escape. */
|
|
40
|
-
function cancelled(value) {
|
|
41
|
-
return clack.isCancel(value);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Which hook actions this directory and setup can actually perform.
|
|
45
|
-
*
|
|
46
|
-
* Install is offered only when a harness is detectable here. Uninstall is
|
|
47
|
-
* offered only when Notifai is actually wired, and it names what it removes:
|
|
48
|
-
* there is one installation per harness, for this machine.
|
|
49
|
-
*/
|
|
50
|
-
export function routingHookActions(input) {
|
|
51
|
-
const options = [];
|
|
52
|
-
const wired = input.installations.length > 0;
|
|
53
|
-
if (input.canInstall) {
|
|
54
|
-
options.push({
|
|
55
|
-
value: 'install',
|
|
56
|
-
label: input.hooksReady || wired ? 'Re-install hooks' : 'Install hooks',
|
|
57
|
-
hint: 'this machine; enable or disable Notifai per project separately',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
if (wired) {
|
|
61
|
-
options.push({
|
|
62
|
-
value: 'uninstall',
|
|
63
|
-
label: 'Uninstall hooks on this machine',
|
|
64
|
-
hint: harnessHint(input.installations),
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
options.push({ value: 'settings', label: 'Change question settings', hint: 'when a question may leave this terminal' }, { value: 'back', label: '← Back' });
|
|
68
|
-
return options;
|
|
69
|
-
}
|
|
70
|
-
function harnessHint(installations) {
|
|
71
|
-
return [...new Set(installations.map((installation) => installation.harness))].join(', ');
|
|
72
|
-
}
|
|
73
|
-
export async function interactiveCommand(deps, version) {
|
|
74
|
-
await printBanner();
|
|
75
|
-
process.stdout.write(` ${tagline(version)}\n`);
|
|
76
|
-
let readiness = await assess(deps);
|
|
77
|
-
if (stateById(readiness, 'credential')?.status !== 'ready') {
|
|
78
|
-
await initCommand(deps, {});
|
|
79
|
-
readiness = await assess(deps);
|
|
80
|
-
}
|
|
81
|
-
let first = true;
|
|
82
|
-
for (;;) {
|
|
83
|
-
renderStatus(readiness, { compact: !first });
|
|
84
|
-
first = false;
|
|
85
|
-
const choice = await clack.select({
|
|
86
|
-
message: 'What would you like to do?',
|
|
87
|
-
options: menuFor(readiness),
|
|
88
|
-
});
|
|
89
|
-
// Root is the only place Escape/Ctrl-C quits. Every nested prompt returns.
|
|
90
|
-
if (cancelled(choice))
|
|
91
|
-
return farewell();
|
|
92
|
-
switch (choice) {
|
|
93
|
-
case 'quit':
|
|
94
|
-
return farewell();
|
|
95
|
-
case 'setup':
|
|
96
|
-
await initCommand(deps, {});
|
|
97
|
-
readiness = await refresh(deps, readiness, refreshAfterMenuAction('setup', true));
|
|
98
|
-
break;
|
|
99
|
-
case 'account': {
|
|
100
|
-
const changed = await accountScreen(deps, readiness);
|
|
101
|
-
readiness = await refresh(deps, readiness, refreshAfterMenuAction('account', changed));
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case 'test':
|
|
105
|
-
await testNotificationScreen(deps);
|
|
106
|
-
break;
|
|
107
|
-
case 'devices':
|
|
108
|
-
await devicesScreen(deps);
|
|
109
|
-
break;
|
|
110
|
-
case 'settings': {
|
|
111
|
-
const result = await settingsScreen(deps);
|
|
112
|
-
readiness = await refresh(deps, readiness, refreshAfterMenuAction('settings', result !== 'unchanged', { remote: result === 'remote' }));
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
case 'routing': {
|
|
116
|
-
const changed = await routingScreen(deps, readiness);
|
|
117
|
-
readiness = await refresh(deps, readiness, refreshAfterMenuAction('routing', changed));
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
case 'doctor':
|
|
121
|
-
// One assessment serves the report and the redraw. Doctor does not
|
|
122
|
-
// change setup, so running the graph again would only wait.
|
|
123
|
-
readiness = await assess(deps);
|
|
124
|
-
await doctorCommand(deps, {}, { readiness });
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function farewell() {
|
|
130
|
-
clack.outro(style.dim(`${glyph.bullet} bye`));
|
|
131
|
-
return EXIT.ok;
|
|
132
|
-
}
|
|
133
|
-
async function assess(deps, options = {}) {
|
|
134
|
-
const spinner = clack.spinner();
|
|
135
|
-
spinner.start('Checking your setup');
|
|
136
|
-
try {
|
|
137
|
-
return await assessReadiness(deps, options);
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
spinner.stop(style.dim('Setup checked'));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function refresh(deps, previous, next) {
|
|
144
|
-
return next === null ? previous : assess(deps, { previous, refresh: next });
|
|
145
|
-
}
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
// Status
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
function stateById(readiness, id) {
|
|
150
|
-
return readiness.states.find((state) => state.id === id);
|
|
151
|
-
}
|
|
152
|
-
function toneOf(state) {
|
|
153
|
-
if (state === undefined)
|
|
154
|
-
return 'pending';
|
|
155
|
-
switch (state.status) {
|
|
156
|
-
case 'ready':
|
|
157
|
-
return 'ok';
|
|
158
|
-
case 'gap':
|
|
159
|
-
return 'bad';
|
|
160
|
-
case 'optional-gap':
|
|
161
|
-
return 'warn';
|
|
162
|
-
case 'unknown':
|
|
163
|
-
return 'pending';
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* The status card: four lines that answer "is this working", with the detail
|
|
168
|
-
* behind `doctor` rather than in front of everyone.
|
|
169
|
-
*
|
|
170
|
-
* `doctor` reports fourteen states because that is what a report is for. Home
|
|
171
|
-
* shows the four that change what you would do next, which is why this reads
|
|
172
|
-
* the same readiness model rather than a second one that could disagree.
|
|
173
|
-
*/
|
|
174
|
-
function renderStatus(readiness, options) {
|
|
175
|
-
const rows = [
|
|
176
|
-
{
|
|
177
|
-
label: 'Account',
|
|
178
|
-
state: stateById(readiness, 'credential'),
|
|
179
|
-
summary: (s) => (s?.status === 'ready' ? accountSummary(readiness) : 'not signed in'),
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
label: 'Devices',
|
|
183
|
-
state: stateById(readiness, 'devices'),
|
|
184
|
-
summary: (s) => shorten(s?.detail ?? 'unknown'),
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
label: 'Project',
|
|
188
|
-
state: stateById(readiness, 'project'),
|
|
189
|
-
summary: (s) => (s?.status === 'ready' ? firstQuoted(s.detail) : 'not named'),
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
label: 'Questions',
|
|
193
|
-
state: stateById(readiness, 'hooks'),
|
|
194
|
-
summary: (s) => (s?.status === 'ready' ? 'routing to your devices' : shorten(s?.detail ?? 'not wired')),
|
|
195
|
-
},
|
|
196
|
-
];
|
|
197
|
-
if (options.compact === true) {
|
|
198
|
-
const summary = rows.map((row) => `${mark(toneOf(row.state))} ${style.dim(row.label)}`).join(' ');
|
|
199
|
-
process.stdout.write(`\n ${summary}\n\n`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const labelWidth = Math.max(...rows.map((row) => row.label.length));
|
|
203
|
-
const body = rows.map((row) => `${mark(toneOf(row.state))} ${style.dim(row.label.padEnd(labelWidth))} ${row.summary(row.state)}`);
|
|
204
|
-
process.stdout.write('\n');
|
|
205
|
-
for (const line of card('Status', body))
|
|
206
|
-
process.stdout.write(`${line}\n`);
|
|
207
|
-
process.stdout.write('\n');
|
|
208
|
-
}
|
|
209
|
-
function accountSummary(readiness) {
|
|
210
|
-
const auth = stateById(readiness, 'auth');
|
|
211
|
-
const email = auth?.detail.match(/\(([^)]+@[^)]+)\)/)?.[1];
|
|
212
|
-
return email ?? 'signed in';
|
|
213
|
-
}
|
|
214
|
-
function firstQuoted(text) {
|
|
215
|
-
return text.match(/"([^"]+)"/)?.[1] ?? text;
|
|
216
|
-
}
|
|
217
|
-
function shorten(text, limit = 46) {
|
|
218
|
-
const flat = text.replace(/\s+/g, ' ').trim();
|
|
219
|
-
const clipped = flat.split(' — ')[0] ?? flat;
|
|
220
|
-
return clipped.length > limit ? `${clipped.slice(0, limit - 1)}…` : clipped;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* The menu, ordered by what is actually in the way.
|
|
224
|
-
*
|
|
225
|
-
* A fixed menu would put "Send a test notification" first for someone who has
|
|
226
|
-
* not signed in — an action that cannot succeed, offered ahead of the one that
|
|
227
|
-
* would unblock it.
|
|
228
|
-
*/
|
|
229
|
-
function menuFor(readiness) {
|
|
230
|
-
const signedIn = stateById(readiness, 'credential')?.status === 'ready';
|
|
231
|
-
const devices = stateById(readiness, 'devices');
|
|
232
|
-
const hasDevice = devices?.status === 'ready';
|
|
233
|
-
const routing = stateById(readiness, 'hooks');
|
|
234
|
-
const blocker = firstBlocker(readiness);
|
|
235
|
-
const options = [];
|
|
236
|
-
if (blocker !== null) {
|
|
237
|
-
options.push({
|
|
238
|
-
value: 'setup',
|
|
239
|
-
label: 'Finish setup',
|
|
240
|
-
hint: shorten(blocker.title.toLowerCase(), 40),
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
else if (signedIn && hasDevice) {
|
|
244
|
-
options.push({
|
|
245
|
-
value: 'test',
|
|
246
|
-
label: 'Send a test notification',
|
|
247
|
-
hint: 'prove the whole path, end to end',
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
options.push({ value: 'settings', label: 'Settings', hint: 'sounds, timing, presence, project' }, {
|
|
251
|
-
value: 'routing',
|
|
252
|
-
label: 'Question routing',
|
|
253
|
-
hint: routing?.status === 'ready' ? 'wired — review or change' : 'let agents reach you when you step away',
|
|
254
|
-
});
|
|
255
|
-
if (signedIn && hasDevice) {
|
|
256
|
-
options.push({ value: 'devices', label: 'Devices', hint: 'what can receive right now' });
|
|
257
|
-
}
|
|
258
|
-
options.push({ value: 'doctor', label: 'Run diagnostics', hint: 'check every part of the setup' });
|
|
259
|
-
if (signedIn) {
|
|
260
|
-
options.push({ value: 'account', label: 'Account', hint: 'plan, machine, sign out' });
|
|
261
|
-
}
|
|
262
|
-
options.push({ value: 'quit', label: 'Quit' });
|
|
263
|
-
return options;
|
|
264
|
-
}
|
|
265
|
-
// ---------------------------------------------------------------------------
|
|
266
|
-
// Test notification
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
|
-
async function testNotificationScreen(deps) {
|
|
269
|
-
const config = loadConfig({ cwd: deps.cwd, env: deps.env });
|
|
270
|
-
const project = config.project.value;
|
|
271
|
-
const title = await clack.text({
|
|
272
|
-
message: 'Title',
|
|
273
|
-
placeholder: 'Test from Notifai',
|
|
274
|
-
defaultValue: 'Test from Notifai',
|
|
275
|
-
});
|
|
276
|
-
if (cancelled(title))
|
|
277
|
-
return;
|
|
278
|
-
const summary = await clack.text({
|
|
279
|
-
message: 'Summary',
|
|
280
|
-
placeholder: 'If you can read this on your device, it works.',
|
|
281
|
-
defaultValue: 'If you can read this on your device, it works.',
|
|
282
|
-
});
|
|
283
|
-
if (cancelled(summary))
|
|
284
|
-
return;
|
|
285
|
-
const kind = await clack.select({
|
|
286
|
-
message: 'Kind',
|
|
287
|
-
options: [
|
|
288
|
-
{ value: 'update', label: 'Update', hint: 'something happened' },
|
|
289
|
-
{ value: 'done', label: 'Done', hint: 'work finished' },
|
|
290
|
-
],
|
|
291
|
-
initialValue: 'done',
|
|
292
|
-
});
|
|
293
|
-
if (cancelled(kind))
|
|
294
|
-
return;
|
|
295
|
-
clack.log.step('Sending, then waiting for your devices to confirm…');
|
|
296
|
-
const code = await sendCommand(deps, {
|
|
297
|
-
title: String(title),
|
|
298
|
-
summary: String(summary),
|
|
299
|
-
kind: String(kind),
|
|
300
|
-
...(project !== null ? { project } : {}),
|
|
301
|
-
});
|
|
302
|
-
if (code === EXIT.ok)
|
|
303
|
-
clack.log.success('Delivered. Check the device it landed on.');
|
|
304
|
-
else
|
|
305
|
-
clack.log.error('That did not get through — `Run diagnostics` will say why.');
|
|
306
|
-
}
|
|
307
|
-
// ---------------------------------------------------------------------------
|
|
308
|
-
// Devices
|
|
309
|
-
// ---------------------------------------------------------------------------
|
|
310
|
-
async function devicesScreen(deps) {
|
|
311
|
-
const spinner = clack.spinner();
|
|
312
|
-
spinner.start('Loading your devices');
|
|
313
|
-
const devices = await deviceInventory(deps);
|
|
314
|
-
spinner.stop(style.dim('Devices loaded'));
|
|
315
|
-
if (devices === null) {
|
|
316
|
-
clack.log.warn('Could not reach the service. Sign in, or check the connection.');
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
if (devices.length === 0) {
|
|
320
|
-
clack.note('No devices are registered yet.\n\n' +
|
|
321
|
-
'Install a supported Notifai Companion App, sign in with the same\n' +
|
|
322
|
-
'Account, and allow notifications when it asks.', 'No devices');
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
const body = devices
|
|
326
|
-
.map((device) => {
|
|
327
|
-
const ready = canDeviceReceive(device);
|
|
328
|
-
const detail = ready ? 'ready' : `not ready ${glyph.bullet} permission: ${device.permission_status}`;
|
|
329
|
-
return (`${mark(ready ? 'ok' : 'warn')} ${style.value(device.display_name)} ${style.dim(`(${device.platform})`)}\n` +
|
|
330
|
-
` ${style.dim(detail)}\n` +
|
|
331
|
-
` ${style.dim(device.device_id)}`);
|
|
332
|
-
})
|
|
333
|
-
.join('\n\n');
|
|
334
|
-
clack.note(body, `${devices.length} device${devices.length === 1 ? '' : 's'}`);
|
|
335
|
-
}
|
|
336
|
-
// ---------------------------------------------------------------------------
|
|
337
|
-
// Settings
|
|
338
|
-
// ---------------------------------------------------------------------------
|
|
339
|
-
async function settingsScreen(deps) {
|
|
340
|
-
let result = 'unchanged';
|
|
341
|
-
for (;;) {
|
|
342
|
-
const config = loadConfig({ cwd: deps.cwd, env: deps.env });
|
|
343
|
-
const group = await clack.select({
|
|
344
|
-
message: 'Settings',
|
|
345
|
-
options: [
|
|
346
|
-
...CONFIG_GROUPS.map((entry) => ({
|
|
347
|
-
value: entry.id,
|
|
348
|
-
label: entry.title,
|
|
349
|
-
hint: changedCount(config, entry.id),
|
|
350
|
-
})),
|
|
351
|
-
{ value: 'back', label: '← Back' },
|
|
352
|
-
],
|
|
353
|
-
});
|
|
354
|
-
if (cancelled(group) || group === 'back')
|
|
355
|
-
return result;
|
|
356
|
-
const next = await settingsGroupScreen(deps, group);
|
|
357
|
-
if (next === 'remote')
|
|
358
|
-
result = 'remote';
|
|
359
|
-
else if (next === 'local' && result === 'unchanged')
|
|
360
|
-
result = 'local';
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
/** "3 changed" beats repeating the group blurb the reader already saw. */
|
|
364
|
-
function changedCount(config, group) {
|
|
365
|
-
const keys = configKeysByGroup(group);
|
|
366
|
-
const changed = keys.filter((info) => config[info.key].source !== 'default').length;
|
|
367
|
-
return changed === 0 ? style.dim('all default') : style.dim(`${changed} changed`);
|
|
368
|
-
}
|
|
369
|
-
async function settingsGroupScreen(deps, group) {
|
|
370
|
-
let result = 'unchanged';
|
|
371
|
-
for (;;) {
|
|
372
|
-
const config = loadConfig({ cwd: deps.cwd, env: deps.env });
|
|
373
|
-
const info = CONFIG_GROUPS.find((entry) => entry.id === group);
|
|
374
|
-
const keys = configKeysByGroup(group);
|
|
375
|
-
const key = await clack.select({
|
|
376
|
-
message: info.title,
|
|
377
|
-
options: [
|
|
378
|
-
...keys.map((entry) => {
|
|
379
|
-
const resolved = config[entry.key];
|
|
380
|
-
const source = describeSource(resolved.source);
|
|
381
|
-
const value = formatValue(entry.key, resolved.value);
|
|
382
|
-
return {
|
|
383
|
-
value: entry.key,
|
|
384
|
-
label: entry.label,
|
|
385
|
-
hint: source.label === 'default' ? style.dim(value) : `${value} ${style.dim(`(${source.label})`)}`,
|
|
386
|
-
};
|
|
387
|
-
}),
|
|
388
|
-
{ value: 'back', label: '← Back' },
|
|
389
|
-
],
|
|
390
|
-
});
|
|
391
|
-
if (cancelled(key) || key === 'back')
|
|
392
|
-
return result;
|
|
393
|
-
const next = await settingDetailScreen(deps, key);
|
|
394
|
-
if (next === 'remote')
|
|
395
|
-
result = 'remote';
|
|
396
|
-
else if (next === 'local' && result === 'unchanged')
|
|
397
|
-
result = 'local';
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* One key, fully explained, with the option to change it.
|
|
402
|
-
*
|
|
403
|
-
* This is where the paragraph lives. Everything above stayed one line per key
|
|
404
|
-
* so that arriving here means the reader has already said which key they care
|
|
405
|
-
* about.
|
|
406
|
-
*/
|
|
407
|
-
async function settingDetailScreen(deps, key) {
|
|
408
|
-
const config = loadConfig({ cwd: deps.cwd, env: deps.env });
|
|
409
|
-
const info = configInfo(key);
|
|
410
|
-
const resolved = config[key];
|
|
411
|
-
const source = describeSource(resolved.source);
|
|
412
|
-
const lines = [
|
|
413
|
-
info.detail,
|
|
414
|
-
'',
|
|
415
|
-
`${style.dim('Now')} ${style.value(formatValue(key, resolved.value))} ${style.dim(`${glyph.bullet} ${source.label}`)}`,
|
|
416
|
-
`${style.dim('Accepts')} ${acceptedValues(key)}`,
|
|
417
|
-
`${style.dim('Key')} ${style.code(key)}`,
|
|
418
|
-
];
|
|
419
|
-
clack.note(lines.join('\n'), info.label);
|
|
420
|
-
const action = await clack.select({
|
|
421
|
-
message: 'Change it?',
|
|
422
|
-
options: [
|
|
423
|
-
// Not lower-cased: "Change only when i have stepped away" is what
|
|
424
|
-
// `toLowerCase` makes of a label that starts a sentence.
|
|
425
|
-
{ value: 'change', label: `Change this setting`, hint: style.code(info.key) },
|
|
426
|
-
{ value: 'back', label: '← Back' },
|
|
427
|
-
],
|
|
428
|
-
initialValue: 'back',
|
|
429
|
-
});
|
|
430
|
-
if (cancelled(action) || action === 'back')
|
|
431
|
-
return 'unchanged';
|
|
432
|
-
const value = await promptForValue(deps, key);
|
|
433
|
-
if (value === null)
|
|
434
|
-
return 'unchanged';
|
|
435
|
-
const layer = await chooseLayer();
|
|
436
|
-
if (layer === null)
|
|
437
|
-
return 'unchanged';
|
|
438
|
-
const code = await configSetCommand(deps, key, value, {
|
|
439
|
-
yes: true,
|
|
440
|
-
...(layer === 'project' ? { project: true } : {}),
|
|
441
|
-
...(layer === 'local' ? { local: true } : {}),
|
|
442
|
-
});
|
|
443
|
-
if (code !== EXIT.ok)
|
|
444
|
-
return 'unchanged';
|
|
445
|
-
clack.log.success(`${info.label} is now ${formatValueRaw(key, value)}`);
|
|
446
|
-
return 'local';
|
|
447
|
-
}
|
|
448
|
-
function formatValueRaw(key, raw) {
|
|
449
|
-
const info = configInfo(key);
|
|
450
|
-
if (info.kind === 'boolean')
|
|
451
|
-
return raw === 'true' ? 'yes' : 'no';
|
|
452
|
-
return raw;
|
|
453
|
-
}
|
|
454
|
-
/** The prompt shape follows the key's type; validation stays in `config set`. */
|
|
455
|
-
async function promptForValue(deps, key) {
|
|
456
|
-
const info = configInfo(key);
|
|
457
|
-
if (info.kind === 'boolean') {
|
|
458
|
-
const answer = await clack.select({
|
|
459
|
-
message: info.label,
|
|
460
|
-
options: [
|
|
461
|
-
{ value: 'true', label: 'Yes' },
|
|
462
|
-
{ value: 'false', label: 'No' },
|
|
463
|
-
],
|
|
464
|
-
});
|
|
465
|
-
return cancelled(answer) ? null : String(answer);
|
|
466
|
-
}
|
|
467
|
-
if (info.kind === 'enum') {
|
|
468
|
-
const answer = await clack.select({
|
|
469
|
-
message: info.label,
|
|
470
|
-
options: (info.choices ?? []).map((choice) => ({
|
|
471
|
-
value: choice,
|
|
472
|
-
label: choice,
|
|
473
|
-
...(info.choiceHints?.[choice] !== undefined ? { hint: info.choiceHints[choice] } : {}),
|
|
474
|
-
})),
|
|
475
|
-
});
|
|
476
|
-
return cancelled(answer) ? null : String(answer);
|
|
477
|
-
}
|
|
478
|
-
// The one key whose legal values the machine knows better than the reader.
|
|
479
|
-
if (key === 'devices')
|
|
480
|
-
return await promptForDevices(deps);
|
|
481
|
-
const bounds = boundsHint(key);
|
|
482
|
-
const answer = await clack.text({
|
|
483
|
-
message: info.label,
|
|
484
|
-
...(info.example !== undefined ? { placeholder: info.example } : {}),
|
|
485
|
-
validate: (input) => {
|
|
486
|
-
const trimmed = (input ?? '').trim();
|
|
487
|
-
if (trimmed === '')
|
|
488
|
-
return 'Enter a value, or press Escape to go back.';
|
|
489
|
-
if (info.kind === 'integer') {
|
|
490
|
-
if (!/^-?\d+$/.test(trimmed))
|
|
491
|
-
return `${info.label} is a whole number of seconds.`;
|
|
492
|
-
const range = boundsRange(key);
|
|
493
|
-
if (bounds !== null && range !== null) {
|
|
494
|
-
const parsed = Number(trimmed);
|
|
495
|
-
if (parsed < range.min || parsed > range.max)
|
|
496
|
-
return `Must be between ${bounds}.`;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
if (info.kind === 'url' && !/^https?:\/\//.test(trimmed))
|
|
500
|
-
return 'Must start with http:// or https://.';
|
|
501
|
-
return undefined;
|
|
502
|
-
},
|
|
503
|
-
});
|
|
504
|
-
return cancelled(answer) ? null : String(answer).trim();
|
|
505
|
-
}
|
|
506
|
-
function boundsRange(key) {
|
|
507
|
-
const hint = boundsHint(key);
|
|
508
|
-
if (hint === null)
|
|
509
|
-
return null;
|
|
510
|
-
const [min, max] = hint.replace(/[^\d–]/g, '').split('–');
|
|
511
|
-
if (min === undefined || max === undefined)
|
|
512
|
-
return null;
|
|
513
|
-
return { min: Number(min), max: Number(max) };
|
|
514
|
-
}
|
|
515
|
-
async function promptForDevices(deps) {
|
|
516
|
-
const spinner = clack.spinner();
|
|
517
|
-
spinner.start('Loading your devices');
|
|
518
|
-
const devices = await deviceInventory(deps);
|
|
519
|
-
spinner.stop(style.dim('Devices loaded'));
|
|
520
|
-
if (devices === null || devices.length === 0) {
|
|
521
|
-
clack.log.warn('No devices to choose from yet.');
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
const picked = await clack.multiselect({
|
|
525
|
-
message: 'Which devices should receive by default?',
|
|
526
|
-
options: devices.map((device) => ({
|
|
527
|
-
value: device.device_id,
|
|
528
|
-
label: device.display_name,
|
|
529
|
-
hint: canDeviceReceive(device) ? device.platform : `${device.platform} ${glyph.bullet} not ready`,
|
|
530
|
-
})),
|
|
531
|
-
required: true,
|
|
532
|
-
});
|
|
533
|
-
return cancelled(picked) ? null : picked.join(',');
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Which layer the value lands in.
|
|
537
|
-
*
|
|
538
|
-
* Asked every time rather than defaulting, because the difference between
|
|
539
|
-
* "my machine" and "everyone who clones this repository" is not recoverable by
|
|
540
|
-
* guessing, and the shared file is the one that ends up in a commit.
|
|
541
|
-
*/
|
|
542
|
-
async function chooseLayer() {
|
|
543
|
-
const layer = await clack.select({
|
|
544
|
-
message: 'Where should this apply?',
|
|
545
|
-
options: [
|
|
546
|
-
{ value: 'global', label: 'This machine', hint: 'every project you work on here' },
|
|
547
|
-
{ value: 'project', label: 'This project, shared', hint: '.notifai/config.toml — commit it' },
|
|
548
|
-
{ value: 'local', label: 'This project, just me', hint: 'stored on this machine, not in the repo' },
|
|
549
|
-
],
|
|
550
|
-
initialValue: 'global',
|
|
551
|
-
});
|
|
552
|
-
return cancelled(layer) ? null : layer;
|
|
553
|
-
}
|
|
554
|
-
// ---------------------------------------------------------------------------
|
|
555
|
-
// Question routing
|
|
556
|
-
// ---------------------------------------------------------------------------
|
|
557
|
-
/**
|
|
558
|
-
* Which harnesses are wired, without the install paths.
|
|
559
|
-
*
|
|
560
|
-
* The underlying state lists every installation with its absolute file path,
|
|
561
|
-
* which is the right level of detail for `doctor` and far too much for a
|
|
562
|
-
* summary card — four paths wrap across six lines and push the settings
|
|
563
|
-
* underneath them out of alignment.
|
|
564
|
-
*/
|
|
565
|
-
function wiringSummary(hooks) {
|
|
566
|
-
if (hooks === undefined)
|
|
567
|
-
return 'unknown';
|
|
568
|
-
const named = hooks.detail.replace(/\s*\([^)]*\)/g, '').trim();
|
|
569
|
-
return named === '' ? hooks.detail : named;
|
|
570
|
-
}
|
|
571
|
-
/** Returns true when something changed and the status card needs refreshing. */
|
|
572
|
-
async function routingScreen(deps, readiness) {
|
|
573
|
-
const hooks = stateById(readiness, 'hooks');
|
|
574
|
-
const config = loadConfig({ cwd: deps.cwd, env: deps.env });
|
|
575
|
-
const installations = findInstallations(deps.env, deps.hookAdapterHome);
|
|
576
|
-
const row = (label, value) => `${style.dim(label.padEnd(16))}${value}`;
|
|
577
|
-
clack.note('When an agent registers a question, it appears in your terminal first.\n' +
|
|
578
|
-
'After the grace below, it goes to your Companion devices as a\n' +
|
|
579
|
-
'notification with a supported native or in-app answer surface, and\n' +
|
|
580
|
-
'your answer comes back to the agent —\n' +
|
|
581
|
-
'whether or not you are still at this machine.\n\n' +
|
|
582
|
-
[
|
|
583
|
-
row('Wired to', wiringSummary(hooks)),
|
|
584
|
-
row('Send', config.ask_notifications.value ? 'yes' : 'no — questions stay in the terminal'),
|
|
585
|
-
row('Grace', formatValue('ask_grace_seconds', config.ask_grace_seconds.value)),
|
|
586
|
-
].join('\n'), 'Question routing');
|
|
587
|
-
const action = await clack.select({
|
|
588
|
-
message: 'Question routing',
|
|
589
|
-
options: routingHookActions({
|
|
590
|
-
canInstall: detectedHarnesses(deps.cwd, deps.env).length > 0,
|
|
591
|
-
installations,
|
|
592
|
-
hooksReady: hooks?.status === 'ready',
|
|
593
|
-
}),
|
|
594
|
-
});
|
|
595
|
-
if (cancelled(action) || action === 'back')
|
|
596
|
-
return false;
|
|
597
|
-
if (action === 'settings') {
|
|
598
|
-
await settingsGroupScreen(deps, 'questions');
|
|
599
|
-
return true;
|
|
600
|
-
}
|
|
601
|
-
if (action === 'install') {
|
|
602
|
-
hooksInstallCommand(deps, {});
|
|
603
|
-
return true;
|
|
604
|
-
}
|
|
605
|
-
for (const installation of installations) {
|
|
606
|
-
hooksUninstallCommand(deps, { harness: installation.harness });
|
|
607
|
-
}
|
|
608
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
// ---------------------------------------------------------------------------
|
|
611
|
-
// Account
|
|
612
|
-
// ---------------------------------------------------------------------------
|
|
613
|
-
/** Returns true when something changed and the status card needs refreshing. */
|
|
614
|
-
async function accountScreen(deps, readiness) {
|
|
615
|
-
const signedIn = stateById(readiness, 'credential')?.status === 'ready';
|
|
616
|
-
if (!signedIn) {
|
|
617
|
-
await initCommand(deps, {});
|
|
618
|
-
return true;
|
|
619
|
-
}
|
|
620
|
-
const auth = stateById(readiness, 'auth');
|
|
621
|
-
const credential = stateById(readiness, 'credential');
|
|
622
|
-
clack.note(`${style.dim('Machine')} ${credential?.detail ?? 'unknown'}\n${style.dim('Account')} ${auth?.detail ?? 'unknown'}`, 'Account');
|
|
623
|
-
const action = await clack.select({
|
|
624
|
-
message: 'Account',
|
|
625
|
-
options: [
|
|
626
|
-
{ value: 'relogin', label: 'Pair this machine again' },
|
|
627
|
-
{ value: 'logout', label: 'Sign out', hint: 'removes the stored credential' },
|
|
628
|
-
{ value: 'back', label: '← Back' },
|
|
629
|
-
],
|
|
630
|
-
initialValue: 'back',
|
|
631
|
-
});
|
|
632
|
-
if (cancelled(action) || action === 'back')
|
|
633
|
-
return false;
|
|
634
|
-
if (action === 'logout') {
|
|
635
|
-
const sure = await clack.confirm({ message: 'Remove the stored credential?', initialValue: false });
|
|
636
|
-
if (cancelled(sure) || sure !== true)
|
|
637
|
-
return false;
|
|
638
|
-
logoutCommand(deps);
|
|
639
|
-
return true;
|
|
640
|
-
}
|
|
641
|
-
await loginCommand(deps, {});
|
|
642
|
-
return true;
|
|
643
|
-
}
|
|
644
|
-
//# sourceMappingURL=interactive.js.map
|