@dualityinstitute/blink 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -34
- package/dist/cli.js +20 -16
- package/dist/electron/main.js +754 -5992
- package/dist/electron/preload.cjs +31 -4
- package/dist/electron/renderer/popup.css +125 -15
- package/dist/electron/renderer/popup.html +17 -4
- package/dist/electron/renderer/popup.js +126 -59
- package/dist/electron/renderer/settings.css +439 -0
- package/dist/electron/renderer/settings.html +93 -0
- package/dist/electron/renderer/settings.js +4067 -0
- package/package.json +2 -2
- package/vendor/Blink.app/Contents/Helpers/BlinkAudio +0 -0
- package/vendor/Blink.app/Contents/MacOS/Blink +0 -0
- package/vendor/Blink.app/Contents/_CodeSignature/CodeResources +2 -2
- package/dist/electron/fsevents-1bczt1x4.node +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# Blink
|
|
2
2
|
|
|
3
|
-
Blink is a macOS menu-bar meeting copilot. Monitor
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
floating popup.
|
|
3
|
+
Blink is a macOS menu-bar meeting copilot. Monitor privately stages captured
|
|
4
|
+
screenshots, and Meeting adds the live transcript, until you explicitly Ask.
|
|
5
|
+
Blink Cloud streams every answer into a floating popup.
|
|
7
6
|
|
|
8
|
-
-
|
|
7
|
+
- Global capture hotkeys that stage screenshots for the next Ask
|
|
9
8
|
- Live `Them`/`You` Meeting transcript and private screenshot context
|
|
10
|
-
- Explicit Ask button and
|
|
9
|
+
- Explicit Ask button and `⇧⌘Return` submission
|
|
11
10
|
- Presenter notes that remain local to the Mac
|
|
12
11
|
- Privacy mode for excluding Blink windows from supported screen capture
|
|
13
12
|
- One-command Bun installation with an interactive terminal setup
|
|
@@ -24,7 +23,7 @@ credentials on the Mac.
|
|
|
24
23
|
## Install
|
|
25
24
|
|
|
26
25
|
```bash
|
|
27
|
-
bunx @dualityinstitute/blink install
|
|
26
|
+
bunx @dualityinstitute/blink@latest install
|
|
28
27
|
```
|
|
29
28
|
|
|
30
29
|
The terminal UI asks for the access token, writes `~/.blink/config.json`, and
|
|
@@ -34,7 +33,7 @@ at its default URL, so only the token is needed.
|
|
|
34
33
|
For non-interactive installation:
|
|
35
34
|
|
|
36
35
|
```bash
|
|
37
|
-
bunx @dualityinstitute/blink install --token blink_customer_token
|
|
36
|
+
bunx @dualityinstitute/blink@latest install --token blink_customer_token
|
|
38
37
|
```
|
|
39
38
|
|
|
40
39
|
Pass `--api-url` as well to point the app at a different gateway, such as a
|
|
@@ -51,19 +50,20 @@ the app bundle, gateway health, and token.
|
|
|
51
50
|
|
|
52
51
|
## Monitor
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
conversation. Two global capture shortcuts are
|
|
57
|
-
Meeting is on:
|
|
53
|
+
Monitor stages screenshots for an explicit Ask. Nothing is sent until you
|
|
54
|
+
submit, so one Ask can carry several captures at once, and every Monitor Ask
|
|
55
|
+
receives a fresh server-side conversation. Two global capture shortcuts are
|
|
56
|
+
registered while Monitor or Meeting is on:
|
|
58
57
|
|
|
59
58
|
| Capture | Shortcut |
|
|
60
59
|
|---|---|
|
|
61
60
|
| Select a region | `⇧⌘2` |
|
|
62
61
|
| Whole main display | `⇧⌘1` |
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
Captures are staged privately under `~/.blink/captures/` in both modes and are
|
|
64
|
+
never answered automatically. Select **Ask** or press `⇧⌘Return` to submit every
|
|
65
|
+
staged screenshot in one request; with Meeting active the current transcript is
|
|
66
|
+
included as well.
|
|
67
67
|
|
|
68
68
|
The first capture asks for **Screen & System Audio Recording** permission under
|
|
69
69
|
the signed **Blink** app identity.
|
|
@@ -72,11 +72,13 @@ the signed **Blink** app identity.
|
|
|
72
72
|
|
|
73
73
|
Enable **Meeting** in the tray or popup. Blink starts a new identity-scoped
|
|
74
74
|
Meeting conversation, captures system audio as `Them` and microphone audio as
|
|
75
|
-
`You`, and keeps a rolling two-minute transcript
|
|
75
|
+
`You`, and keeps a rolling two-minute transcript for the model. The popup widens
|
|
76
|
+
to show the full running transcript beside the answer; scroll back at any point
|
|
77
|
+
and it stops following new speech until you return to the bottom. Captures made with the
|
|
76
78
|
global shortcuts are retained in a private staging directory. The popup shows
|
|
77
79
|
which transcript and screenshot context is ready.
|
|
78
80
|
|
|
79
|
-
Select **Ask** or press
|
|
81
|
+
Select **Ask** or press `⇧⌘Return` to submit one snapshot containing the
|
|
80
82
|
available transcript and staged screenshots. Subsequent Asks reuse the same
|
|
81
83
|
Meeting conversation. A failed Ask retains its context for retry; after a
|
|
82
84
|
successful Ask, only the screenshots included in that submission are removed.
|
|
@@ -89,19 +91,22 @@ Run `blink ungrant` to clear both permission records and make macOS ask again.
|
|
|
89
91
|
|
|
90
92
|
### Saving transcripts
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
appended to a Markdown file under:
|
|
94
|
+
Every Meeting is appended to a Markdown file on your Mac, one per session:
|
|
94
95
|
|
|
95
96
|
```text
|
|
96
97
|
~/.blink/transcripts/2026-08-30-143022.md
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
two-minute transcript snapshot when one is available.
|
|
100
|
+
Saving is local only and independent from submission: Ask always sends the
|
|
101
|
+
current two-minute transcript snapshot when one is available, no more.
|
|
102
|
+
|
|
103
|
+
Open **Meeting Transcripts…** from the tray to read, search, and filter past
|
|
104
|
+
sessions by date. Search runs across every saved transcript and reports how many
|
|
105
|
+
times each session matched.
|
|
101
106
|
|
|
102
107
|
## Answer lifecycle and cancellation
|
|
103
108
|
|
|
104
|
-
- Monitor
|
|
109
|
+
- Monitor Asks are always fresh and independent.
|
|
105
110
|
- Starting Meeting resets that access identity's Meeting conversation.
|
|
106
111
|
- Every Ask during the active Meeting continues that conversation.
|
|
107
112
|
- **Stop** cancels the active request both locally and at the gateway.
|
|
@@ -110,9 +115,9 @@ two-minute transcript snapshot when one is available.
|
|
|
110
115
|
|
|
111
116
|
Subscribe at [duality.institute/blink](https://duality.institute/blink). After
|
|
112
117
|
Stripe Checkout the claim page shows your access token exactly once; paste it
|
|
113
|
-
into `bunx @dualityinstitute/blink install`.
|
|
118
|
+
into `bunx @dualityinstitute/blink@latest install`.
|
|
114
119
|
|
|
115
|
-
Every Monitor
|
|
120
|
+
Every Monitor Ask and every Meeting Ask counts as one answer against
|
|
116
121
|
your monthly limit. The popup shows `used / limit` for the current period, and
|
|
117
122
|
`blink doctor` prints the same together with the account email and status.
|
|
118
123
|
Cancelled or failed answers are not counted. When the limit is reached, or the
|
|
@@ -131,16 +136,16 @@ Cloud.
|
|
|
131
136
|
|
|
132
137
|
Blink Cloud receives:
|
|
133
138
|
|
|
134
|
-
- A new watched-directory PNG while Monitor is on
|
|
135
139
|
- The current two-minute transcript and staged screenshots only when Ask is used
|
|
136
140
|
- The bearer access token needed to authorize and isolate the request
|
|
137
141
|
|
|
138
142
|
The desktop stores the access token in `~/.blink/config.json`. Answer metadata
|
|
139
|
-
and text are appended locally to `~/.blink/answers.jsonl
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
and text are appended locally to `~/.blink/answers.jsonl`, and meeting
|
|
144
|
+
transcripts to `~/.blink/transcripts/`. Presenter Notes stay
|
|
145
|
+
local in `~/.blink/presenter.md` and are never submitted. Captures are staged
|
|
146
|
+
under `~/.blink/captures/` and cleared when Monitor and Meeting are both off.
|
|
142
147
|
|
|
143
|
-
Blink Cloud does not retain your content: a Monitor
|
|
148
|
+
Blink Cloud does not retain your content: a Monitor Ask's conversation is
|
|
144
149
|
deleted as soon as the answer is returned, and a Meeting conversation is deleted
|
|
145
150
|
when you turn Meeting off (or after two hours without an Ask). Only usage counts
|
|
146
151
|
and token totals are kept for billing.
|
|
@@ -151,11 +156,9 @@ and token totals are kept for billing.
|
|
|
151
156
|
|---|---|---|
|
|
152
157
|
| `apiUrl` | Blink Cloud | Gateway base URL; override only for a self-hosted or local gateway |
|
|
153
158
|
| `accessToken` | Empty | Bearer token issued for Blink Cloud |
|
|
154
|
-
| `screenshotDir` | `~/blinkDir/` | Watched PNG directory |
|
|
155
159
|
| `hideFromCapture` | `true` | Enables privacy mode |
|
|
156
|
-
| `showTranscript` | `true` | Shows the live transcript
|
|
157
|
-
| `saveTranscript` | `false` | Saves full meeting transcripts locally |
|
|
160
|
+
| `showTranscript` | `true` | Shows the live transcript panel during a meeting |
|
|
158
161
|
|
|
159
162
|
Blink materializes new defaults without deleting unrelated settings from an
|
|
160
|
-
existing config file. The obsolete `sessionMode
|
|
161
|
-
removed automatically.
|
|
163
|
+
existing config file. The obsolete `sessionMode`, `hotkeyPreset`, and
|
|
164
|
+
`screenshotDir` keys are removed automatically.
|
package/dist/cli.js
CHANGED
|
@@ -19862,14 +19862,17 @@ function date4(params) {
|
|
|
19862
19862
|
return _coercedDate(ZodDate, params);
|
|
19863
19863
|
}
|
|
19864
19864
|
// src/core/cloud-protocol.ts
|
|
19865
|
+
var OptionalPrompt = exports_external.preprocess((value) => value === null || value === "" ? undefined : value, exports_external.string().trim().min(1).max(4000).optional());
|
|
19865
19866
|
var MonitorRequestSchema = exports_external.object({
|
|
19866
19867
|
kind: exports_external.literal("monitor"),
|
|
19867
|
-
|
|
19868
|
+
images: exports_external.array(exports_external.instanceof(File)).min(1).readonly(),
|
|
19869
|
+
prompt: OptionalPrompt
|
|
19868
19870
|
});
|
|
19869
19871
|
var MeetingRequestSchema = exports_external.object({
|
|
19870
19872
|
kind: exports_external.literal("meeting"),
|
|
19871
19873
|
text: exports_external.preprocess((value) => value === null ? undefined : value, exports_external.string().trim().min(1).optional()),
|
|
19872
|
-
images: exports_external.array(exports_external.instanceof(File)).readonly()
|
|
19874
|
+
images: exports_external.array(exports_external.instanceof(File)).readonly(),
|
|
19875
|
+
prompt: OptionalPrompt
|
|
19873
19876
|
});
|
|
19874
19877
|
var CloudAnswerRequestSchema = exports_external.discriminatedUnion("kind", [MonitorRequestSchema, MeetingRequestSchema]).superRefine((request, context) => {
|
|
19875
19878
|
if (request.kind === "meeting" && request.text === undefined && request.images.length === 0) {
|
|
@@ -19962,9 +19965,15 @@ var DEFAULTS = {
|
|
|
19962
19965
|
accessToken: "",
|
|
19963
19966
|
hideFromCapture: true,
|
|
19964
19967
|
showTranscript: true,
|
|
19965
|
-
|
|
19968
|
+
monitorPrompt: "",
|
|
19969
|
+
meetingPrompt: ""
|
|
19966
19970
|
};
|
|
19967
|
-
var OBSOLETE_KEYS = [
|
|
19971
|
+
var OBSOLETE_KEYS = [
|
|
19972
|
+
"sessionMode",
|
|
19973
|
+
"hotkeyPreset",
|
|
19974
|
+
"saveTranscript",
|
|
19975
|
+
"screenshotDir"
|
|
19976
|
+
];
|
|
19968
19977
|
var RawConfigSchema = exports_external.record(exports_external.string(), exports_external.unknown());
|
|
19969
19978
|
var HttpUrlSchema = exports_external.url().refine((value) => {
|
|
19970
19979
|
const protocol = new URL(value).protocol;
|
|
@@ -19980,9 +19989,6 @@ function getBlinkHome() {
|
|
|
19980
19989
|
function getConfigPath() {
|
|
19981
19990
|
return path.join(getBlinkHome(), "config.json");
|
|
19982
19991
|
}
|
|
19983
|
-
function expandTilde(filePath) {
|
|
19984
|
-
return filePath.startsWith("~") ? path.join(os.homedir(), filePath.slice(1)) : filePath;
|
|
19985
|
-
}
|
|
19986
19992
|
function parseHttpUrl(value) {
|
|
19987
19993
|
const parsed = HttpUrlSchema.safeParse(value);
|
|
19988
19994
|
if (!parsed.success)
|
|
@@ -20035,21 +20041,19 @@ function loadConfig() {
|
|
|
20035
20041
|
throw new ConfigFileError("accessToken must be a string");
|
|
20036
20042
|
merged.accessToken = token.data;
|
|
20037
20043
|
}
|
|
20038
|
-
if (user["screenshotDir"] !== undefined) {
|
|
20039
|
-
const directory = exports_external.string().safeParse(user["screenshotDir"]);
|
|
20040
|
-
if (!directory.success) {
|
|
20041
|
-
throw new ConfigFileError("screenshotDir must be a string");
|
|
20042
|
-
}
|
|
20043
|
-
merged.screenshotDir = expandTilde(directory.data);
|
|
20044
|
-
}
|
|
20045
20044
|
if (user["hideFromCapture"] !== undefined) {
|
|
20046
20045
|
merged.hideFromCapture = user["hideFromCapture"] === true;
|
|
20047
20046
|
}
|
|
20048
20047
|
if (user["showTranscript"] !== undefined) {
|
|
20049
20048
|
merged.showTranscript = user["showTranscript"] === true;
|
|
20050
20049
|
}
|
|
20051
|
-
|
|
20052
|
-
|
|
20050
|
+
for (const key of ["monitorPrompt", "meetingPrompt"]) {
|
|
20051
|
+
if (user[key] === undefined)
|
|
20052
|
+
continue;
|
|
20053
|
+
const prompt = exports_external.string().safeParse(user[key]);
|
|
20054
|
+
if (!prompt.success)
|
|
20055
|
+
throw new ConfigFileError(`${key} must be a string`);
|
|
20056
|
+
merged[key] = prompt.data.trim();
|
|
20053
20057
|
}
|
|
20054
20058
|
const missing = Object.keys(merged).filter((key) => !(key in persisted));
|
|
20055
20059
|
if (missing.length > 0 || hadObsolete) {
|