@dualityinstitute/blink 0.0.2 → 0.0.4
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 +16 -11
- package/dist/cli.js +18 -81
- package/dist/electron/main.js +865 -529
- package/dist/electron/preload.cjs +31 -0
- package/dist/electron/renderer/popup.css +125 -15
- package/dist/electron/renderer/popup.html +17 -3
- package/dist/electron/renderer/popup.js +121 -42
- 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/README.md
CHANGED
|
@@ -24,7 +24,7 @@ credentials on the Mac.
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
bunx @dualityinstitute/blink install
|
|
27
|
+
bunx @dualityinstitute/blink@latest install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
The terminal UI asks for the access token, writes `~/.blink/config.json`, and
|
|
@@ -34,7 +34,7 @@ at its default URL, so only the token is needed.
|
|
|
34
34
|
For non-interactive installation:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
bunx @dualityinstitute/blink install --token blink_customer_token
|
|
37
|
+
bunx @dualityinstitute/blink@latest install --token blink_customer_token
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Pass `--api-url` as well to point the app at a different gateway, such as a
|
|
@@ -72,7 +72,9 @@ 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
|
|
|
@@ -89,15 +91,18 @@ 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
|
|
|
@@ -110,7 +115,7 @@ 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
120
|
Every Monitor screenshot and every Meeting Ask counts as one answer against
|
|
116
121
|
your monthly limit. The popup shows `used / limit` for the current period, and
|
|
@@ -136,7 +141,8 @@ Blink Cloud receives:
|
|
|
136
141
|
- The bearer access token needed to authorize and isolate the request
|
|
137
142
|
|
|
138
143
|
The desktop stores the access token in `~/.blink/config.json`. Answer metadata
|
|
139
|
-
and text are appended locally to `~/.blink/answers.jsonl
|
|
144
|
+
and text are appended locally to `~/.blink/answers.jsonl`, and meeting
|
|
145
|
+
transcripts to `~/.blink/transcripts/`. Presenter Notes stay
|
|
140
146
|
local in `~/.blink/presenter.md` and are never submitted. Meeting captures are
|
|
141
147
|
staged under `~/.blink/meeting-captures/` and cleared when Meeting ends.
|
|
142
148
|
|
|
@@ -153,8 +159,7 @@ and token totals are kept for billing.
|
|
|
153
159
|
| `accessToken` | Empty | Bearer token issued for Blink Cloud |
|
|
154
160
|
| `screenshotDir` | `~/blinkDir/` | Watched PNG directory |
|
|
155
161
|
| `hideFromCapture` | `true` | Enables privacy mode |
|
|
156
|
-
| `showTranscript` | `true` | Shows the live transcript
|
|
157
|
-
| `saveTranscript` | `false` | Saves full meeting transcripts locally |
|
|
162
|
+
| `showTranscript` | `true` | Shows the live transcript panel during a meeting |
|
|
158
163
|
|
|
159
164
|
Blink materializes new defaults without deleting unrelated settings from an
|
|
160
165
|
existing config file. The obsolete `sessionMode` and `hotkeyPreset` keys are
|
package/dist/cli.js
CHANGED
|
@@ -1146,30 +1146,6 @@ var u$1 = class u2 extends V {
|
|
|
1146
1146
|
});
|
|
1147
1147
|
}
|
|
1148
1148
|
};
|
|
1149
|
-
class n extends V {
|
|
1150
|
-
get userInputWithCursor() {
|
|
1151
|
-
if (this.state === "submit")
|
|
1152
|
-
return this.userInput;
|
|
1153
|
-
const t2 = this.userInput;
|
|
1154
|
-
if (this.cursor >= t2.length)
|
|
1155
|
-
return `${this.userInput}\u2588`;
|
|
1156
|
-
const r = t2.slice(0, this.cursor), s = t2.slice(this.cursor, this.cursor + 1), e = t2.slice(this.cursor + 1);
|
|
1157
|
-
return `${r}${styleText("inverse", s)}${e}`;
|
|
1158
|
-
}
|
|
1159
|
-
get cursor() {
|
|
1160
|
-
return this._cursor;
|
|
1161
|
-
}
|
|
1162
|
-
constructor(t2) {
|
|
1163
|
-
super({
|
|
1164
|
-
...t2,
|
|
1165
|
-
initialUserInput: t2.initialUserInput ?? t2.initialValue
|
|
1166
|
-
}), this.on("userInput", (r) => {
|
|
1167
|
-
this._setValue(r);
|
|
1168
|
-
}), this.on("finalize", () => {
|
|
1169
|
-
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
1149
|
|
|
1174
1150
|
// node_modules/@clack/prompts/dist/index.mjs
|
|
1175
1151
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
@@ -1290,44 +1266,6 @@ var SELECT_INSTRUCTIONS = [
|
|
|
1290
1266
|
`${styleText2("dim", "Enter:")} confirm`
|
|
1291
1267
|
];
|
|
1292
1268
|
var i = `${styleText2("gray", S_BAR)} `;
|
|
1293
|
-
var text = (e) => new n({
|
|
1294
|
-
validate: e.validate,
|
|
1295
|
-
placeholder: e.placeholder,
|
|
1296
|
-
defaultValue: e.defaultValue,
|
|
1297
|
-
initialValue: e.initialValue,
|
|
1298
|
-
output: e.output,
|
|
1299
|
-
signal: e.signal,
|
|
1300
|
-
input: e.input,
|
|
1301
|
-
render() {
|
|
1302
|
-
const i2 = e?.withGuide ?? settings.withGuide, s = `${`${i2 ? `${styleText2("gray", S_BAR)}
|
|
1303
|
-
` : ""}${symbol(this.state)} `}${e.message}
|
|
1304
|
-
`, c2 = e.placeholder && e.placeholder.length > 0 ? styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1)) : styleText2(["inverse", "hidden"], "_"), o2 = this.userInput ? this.userInputWithCursor : c2, l = this.value ?? "";
|
|
1305
|
-
switch (this.state) {
|
|
1306
|
-
case "error": {
|
|
1307
|
-
const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d = i2 ? styleText2("yellow", S_BAR_END) : "";
|
|
1308
|
-
return `${s.trim()}
|
|
1309
|
-
${r2}${o2}
|
|
1310
|
-
${d}${n2}
|
|
1311
|
-
`;
|
|
1312
|
-
}
|
|
1313
|
-
case "submit": {
|
|
1314
|
-
const n2 = l ? ` ${styleText2("dim", l)}` : "", r2 = i2 ? styleText2("gray", S_BAR) : "";
|
|
1315
|
-
return `${s}${r2}${n2}`;
|
|
1316
|
-
}
|
|
1317
|
-
case "cancel": {
|
|
1318
|
-
const n2 = l ? ` ${styleText2(["strikethrough", "dim"], l)}` : "", r2 = i2 ? styleText2("gray", S_BAR) : "";
|
|
1319
|
-
return `${s}${r2}${n2}${l.trim() ? `
|
|
1320
|
-
${r2}` : ""}`;
|
|
1321
|
-
}
|
|
1322
|
-
default: {
|
|
1323
|
-
const n2 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "";
|
|
1324
|
-
return `${s}${n2}${o2}
|
|
1325
|
-
${r2}
|
|
1326
|
-
`;
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
}).prompt();
|
|
1331
1269
|
|
|
1332
1270
|
// src/cli.ts
|
|
1333
1271
|
import * as fs3 from "fs";
|
|
@@ -10356,8 +10294,8 @@ function ko_default() {
|
|
|
10356
10294
|
};
|
|
10357
10295
|
}
|
|
10358
10296
|
// node_modules/zod/v4/locales/lt.js
|
|
10359
|
-
var capitalizeFirstCharacter = (
|
|
10360
|
-
return
|
|
10297
|
+
var capitalizeFirstCharacter = (text) => {
|
|
10298
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
10361
10299
|
};
|
|
10362
10300
|
function getUnitTypeFromNumber(number2) {
|
|
10363
10301
|
const abs = Math.abs(number2);
|
|
@@ -19924,14 +19862,17 @@ function date4(params) {
|
|
|
19924
19862
|
return _coercedDate(ZodDate, params);
|
|
19925
19863
|
}
|
|
19926
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());
|
|
19927
19866
|
var MonitorRequestSchema = exports_external.object({
|
|
19928
19867
|
kind: exports_external.literal("monitor"),
|
|
19929
|
-
image: exports_external.instanceof(File)
|
|
19868
|
+
image: exports_external.instanceof(File),
|
|
19869
|
+
prompt: OptionalPrompt
|
|
19930
19870
|
});
|
|
19931
19871
|
var MeetingRequestSchema = exports_external.object({
|
|
19932
19872
|
kind: exports_external.literal("meeting"),
|
|
19933
19873
|
text: exports_external.preprocess((value) => value === null ? undefined : value, exports_external.string().trim().min(1).optional()),
|
|
19934
|
-
images: exports_external.array(exports_external.instanceof(File)).readonly()
|
|
19874
|
+
images: exports_external.array(exports_external.instanceof(File)).readonly(),
|
|
19875
|
+
prompt: OptionalPrompt
|
|
19935
19876
|
});
|
|
19936
19877
|
var CloudAnswerRequestSchema = exports_external.discriminatedUnion("kind", [MonitorRequestSchema, MeetingRequestSchema]).superRefine((request, context) => {
|
|
19937
19878
|
if (request.kind === "meeting" && request.text === undefined && request.images.length === 0) {
|
|
@@ -20024,9 +19965,10 @@ var DEFAULTS = {
|
|
|
20024
19965
|
accessToken: "",
|
|
20025
19966
|
hideFromCapture: true,
|
|
20026
19967
|
showTranscript: true,
|
|
20027
|
-
|
|
19968
|
+
monitorPrompt: "",
|
|
19969
|
+
meetingPrompt: ""
|
|
20028
19970
|
};
|
|
20029
|
-
var OBSOLETE_KEYS = ["sessionMode", "hotkeyPreset"];
|
|
19971
|
+
var OBSOLETE_KEYS = ["sessionMode", "hotkeyPreset", "saveTranscript"];
|
|
20030
19972
|
var RawConfigSchema = exports_external.record(exports_external.string(), exports_external.unknown());
|
|
20031
19973
|
var HttpUrlSchema = exports_external.url().refine((value) => {
|
|
20032
19974
|
const protocol = new URL(value).protocol;
|
|
@@ -20110,8 +20052,13 @@ function loadConfig() {
|
|
|
20110
20052
|
if (user["showTranscript"] !== undefined) {
|
|
20111
20053
|
merged.showTranscript = user["showTranscript"] === true;
|
|
20112
20054
|
}
|
|
20113
|
-
|
|
20114
|
-
|
|
20055
|
+
for (const key of ["monitorPrompt", "meetingPrompt"]) {
|
|
20056
|
+
if (user[key] === undefined)
|
|
20057
|
+
continue;
|
|
20058
|
+
const prompt = exports_external.string().safeParse(user[key]);
|
|
20059
|
+
if (!prompt.success)
|
|
20060
|
+
throw new ConfigFileError(`${key} must be a string`);
|
|
20061
|
+
merged[key] = prompt.data.trim();
|
|
20115
20062
|
}
|
|
20116
20063
|
const missing = Object.keys(merged).filter((key) => !(key in persisted));
|
|
20117
20064
|
if (missing.length > 0 || hadObsolete) {
|
|
@@ -20361,17 +20308,7 @@ async function selectCredentials(args, current) {
|
|
|
20361
20308
|
};
|
|
20362
20309
|
}
|
|
20363
20310
|
intro("Blink Cloud setup");
|
|
20364
|
-
|
|
20365
|
-
if (apiUrl === undefined) {
|
|
20366
|
-
const result = await text({
|
|
20367
|
-
message: "Blink Cloud URL",
|
|
20368
|
-
initialValue: current.apiUrl,
|
|
20369
|
-
validate: (value) => validationMessage(() => normalizeApiUrl(value ?? ""))
|
|
20370
|
-
});
|
|
20371
|
-
if (isCancel(result) || typeof result !== "string")
|
|
20372
|
-
return cancelSetup();
|
|
20373
|
-
apiUrl = result;
|
|
20374
|
-
}
|
|
20311
|
+
const apiUrl = explicitUrl ?? current.apiUrl;
|
|
20375
20312
|
let accessToken = explicitToken;
|
|
20376
20313
|
if (accessToken === undefined) {
|
|
20377
20314
|
const result = await password({
|