@trygocode/notify 0.6.1 → 0.6.3
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/assets/GoCodeNotifier.app/Contents/MacOS/GoCodeNotifier +0 -0
- package/assets/GoCodeNotifier.app/Contents/Resources/gocodechime.caf +0 -0
- package/assets/GoCodeNotifier.app/Contents/_CodeSignature/CodeResources +11 -0
- package/assets/gocodechime.caf +0 -0
- package/dist/src/cli.js +114 -3
- package/dist/src/repo_key.js +14 -2
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
<data>
|
|
9
9
|
GbhU1RfeSzs8A7x841vrx8S9NpM=
|
|
10
10
|
</data>
|
|
11
|
+
<key>Resources/gocodechime.caf</key>
|
|
12
|
+
<data>
|
|
13
|
+
SDZ8imBpH6RxFu4bY6v7z/lR3AE=
|
|
14
|
+
</data>
|
|
11
15
|
</dict>
|
|
12
16
|
<key>files2</key>
|
|
13
17
|
<dict>
|
|
@@ -18,6 +22,13 @@
|
|
|
18
22
|
80/V6KfdsY5g+nVZNtqimRwukW+CXDnk+bV0NlYa75w=
|
|
19
23
|
</data>
|
|
20
24
|
</dict>
|
|
25
|
+
<key>Resources/gocodechime.caf</key>
|
|
26
|
+
<dict>
|
|
27
|
+
<key>hash2</key>
|
|
28
|
+
<data>
|
|
29
|
+
l6vi4c8XtFvpGbej45ku/0CP9/B2vPF7hNoUSCQ1kPQ=
|
|
30
|
+
</data>
|
|
31
|
+
</dict>
|
|
21
32
|
</dict>
|
|
22
33
|
<key>rules</key>
|
|
23
34
|
<dict>
|
|
Binary file
|
package/dist/src/cli.js
CHANGED
|
@@ -20,8 +20,11 @@ import { serveStdio } from "./mcp.js";
|
|
|
20
20
|
import { setup } from "./setup.js";
|
|
21
21
|
import { uninstall } from "./uninstall.js";
|
|
22
22
|
import { cmdConfig } from "./config.js";
|
|
23
|
-
import { onStop } from "./on_stop.js";
|
|
24
|
-
import { notifyDesktop, requestDesktopPermission } from "./desktop_notify.js";
|
|
23
|
+
import { onStop, clickTarget, projectLabel } from "./on_stop.js";
|
|
24
|
+
import { notifyDesktop, requestDesktopPermission, desktopDisabledByEnv, } from "./desktop_notify.js";
|
|
25
|
+
import { resolveNotifySettings } from "./config.js";
|
|
26
|
+
import { deriveRepoIdentity } from "./repo_key.js";
|
|
27
|
+
import { decorateTitle, decorateBody } from "./notify_copy.js";
|
|
25
28
|
import { gatherDoctor, formatDoctor } from "./doctor.js";
|
|
26
29
|
/** Subcommands the finished CLI will expose (see PRD §4.1). */
|
|
27
30
|
export const COMMANDS = [
|
|
@@ -210,6 +213,98 @@ export async function cmdLogin(args, deps = {}) {
|
|
|
210
213
|
}
|
|
211
214
|
return 1;
|
|
212
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Fire a branded desktop banner for a `send` (any kind — notably
|
|
218
|
+
* `awaiting_input` questions and `error`s, which never flow through the
|
|
219
|
+
* `on-stop` finish dispatcher). This is the parity twin of `on_stop.fireDesktop`
|
|
220
|
+
* for the plain-`send` path, so EVERY notification the phone gets can also pop on
|
|
221
|
+
* the computer (PRD §3).
|
|
222
|
+
*
|
|
223
|
+
* Behaviour:
|
|
224
|
+
* - Resolves this repo's identity + merged settings (60s-TTL cache) so the
|
|
225
|
+
* banner is gated on the SAME server-synced `desktop.enabled` as the phone.
|
|
226
|
+
* - Skips silently when desktop is disabled, or when even a best-effort banner
|
|
227
|
+
* can't be built. NEVER throws, NEVER blocks, NEVER affects the exit code
|
|
228
|
+
* (the phone push remains the source of truth).
|
|
229
|
+
* - Decorates title/body WORD-FOR-WORD like the phone push via the shared
|
|
230
|
+
* {@link decorateTitle}/{@link decorateBody}, and reuses the project label +
|
|
231
|
+
* click target (open the IDE for this project) the finish banner uses.
|
|
232
|
+
*
|
|
233
|
+
* An explicit `--title`/`--body` on the send wins over the per-kind default
|
|
234
|
+
* (matching the phone, where an explicit title/body overrides the default copy).
|
|
235
|
+
*/
|
|
236
|
+
export async function fireSendDesktop(payload, deps) {
|
|
237
|
+
// Hard machine-level opt-out (headless / CI / SSH / tests): bail BEFORE any
|
|
238
|
+
// settings network call so this path is a true no-op when desktop banners are
|
|
239
|
+
// disabled for the box. Mirrors `notifyDesktop`'s own env check, but earlier —
|
|
240
|
+
// so we never even resolve settings (which would otherwise add a fetch the
|
|
241
|
+
// phone-send unit tests don't expect).
|
|
242
|
+
if (desktopDisabledByEnv())
|
|
243
|
+
return undefined;
|
|
244
|
+
// Resolve repo identity (for the project label + settings scoping + click
|
|
245
|
+
// target). Best-effort: a derive failure → undefined repo → label falls back
|
|
246
|
+
// to the cwd basename, exactly like the finish path.
|
|
247
|
+
const cwd = process.cwd();
|
|
248
|
+
let repo;
|
|
249
|
+
try {
|
|
250
|
+
repo = await deriveRepoIdentity(cwd);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
repo = undefined;
|
|
254
|
+
}
|
|
255
|
+
// Resolve merged settings for this repo (fail-safe: if nothing is known the
|
|
256
|
+
// resolver returns conservative defaults, where `desktop` is absent → ON by
|
|
257
|
+
// default, matching the finish banner's `desktop?.enabled !== false`).
|
|
258
|
+
let resolved;
|
|
259
|
+
try {
|
|
260
|
+
resolved = await resolveNotifySettings({
|
|
261
|
+
home: deps.home,
|
|
262
|
+
repo,
|
|
263
|
+
fetchImpl: deps.fetchImpl,
|
|
264
|
+
timeoutMs: deps.timeoutMs,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
const settings = resolved.settings;
|
|
271
|
+
// Gate on the SAME setting as the finish banner (ON by default; only an
|
|
272
|
+
// explicit `false` silences the computer banner).
|
|
273
|
+
if (settings.desktop?.enabled === false)
|
|
274
|
+
return undefined;
|
|
275
|
+
const source = payload.source;
|
|
276
|
+
const project = payload.project ?? projectLabel(repo, cwd);
|
|
277
|
+
// An explicit --title/--body wins (matches the phone send overriding default
|
|
278
|
+
// copy); otherwise the shared decorators produce the per-kind default folded
|
|
279
|
+
// with the source label + project, identical to the phone push.
|
|
280
|
+
const title = payload.title
|
|
281
|
+
? payload.title
|
|
282
|
+
: decorateTitle({ kind: payload.kind, source, project });
|
|
283
|
+
const body = payload.body
|
|
284
|
+
? payload.body
|
|
285
|
+
: decorateBody({ kind: payload.kind, project });
|
|
286
|
+
const desktopImpl = deps.notifyDesktopImpl ?? notifyDesktop;
|
|
287
|
+
try {
|
|
288
|
+
return await desktopImpl({
|
|
289
|
+
title,
|
|
290
|
+
body,
|
|
291
|
+
kind: payload.kind,
|
|
292
|
+
sound: settings.desktop?.sound !== false,
|
|
293
|
+
// Clicking the banner opens the IDE window for THIS project (the agent's
|
|
294
|
+
// cwd) — same behaviour as the finish banner. Omitted (display-only)
|
|
295
|
+
// when we can't name the IDE or the path.
|
|
296
|
+
click: clickTarget(source, cwd),
|
|
297
|
+
}, {
|
|
298
|
+
home: deps.home,
|
|
299
|
+
timeoutMs: deps.timeoutMs,
|
|
300
|
+
timestamp: deps.timestamp,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// A desktop banner must never block or fail a send.
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
213
308
|
/**
|
|
214
309
|
* Handle `gocode-notify send --kind K [--title T] [--body B] [--source S]
|
|
215
310
|
* [--project P] [--dedupe-key D] [--server URL]`.
|
|
@@ -220,6 +315,10 @@ export async function cmdLogin(args, deps = {}) {
|
|
|
220
315
|
* even on network/server failure, so a hook never blocks the agent's turn. On a
|
|
221
316
|
* delivery failure the payload is queued to the offline outbox; on a successful
|
|
222
317
|
* send the backlog is flushed opportunistically (server is known reachable).
|
|
318
|
+
*
|
|
319
|
+
* In parallel with the phone push it fires a branded desktop banner via
|
|
320
|
+
* {@link fireSendDesktop} (gated on `desktop.enabled`), so questions/errors
|
|
321
|
+
* raised through `send` pop on the computer too — not just chat finishes.
|
|
223
322
|
*/
|
|
224
323
|
export async function cmdSend(args, deps = {}) {
|
|
225
324
|
const flags = parseFlags(args);
|
|
@@ -270,7 +369,19 @@ export async function cmdSend(args, deps = {}) {
|
|
|
270
369
|
timestamp: deps.timestamp,
|
|
271
370
|
server,
|
|
272
371
|
};
|
|
273
|
-
|
|
372
|
+
// Fire the phone push and the branded desktop banner IN PARALLEL so the
|
|
373
|
+
// computer is told the same instant the phone is (PRD §3) — for EVERY kind
|
|
374
|
+
// that flows through `send`, notably `awaiting_input` (questions), which the
|
|
375
|
+
// `on-stop` finish dispatcher never sees. Without this, only chat-FINISH
|
|
376
|
+
// banners popped on the desktop and questions/errors raised via `send` were
|
|
377
|
+
// phone-only. The banner is gated on the SAME server-synced `desktop.enabled`
|
|
378
|
+
// setting as the finish banner, decorated WORD-FOR-WORD identically (same
|
|
379
|
+
// emoji + source label + project), and is best-effort + total (never throws,
|
|
380
|
+
// never blocks, never changes the exit code). See {@link fireSendDesktop}.
|
|
381
|
+
const [result] = await Promise.all([
|
|
382
|
+
send(payload, sendOpts),
|
|
383
|
+
fireSendDesktop(payload, deps).catch(() => undefined),
|
|
384
|
+
]);
|
|
274
385
|
if (result.ok) {
|
|
275
386
|
// Server is reachable → drain any notifications queued while offline.
|
|
276
387
|
await flush((p) => send(p, sendOpts), deps);
|
package/dist/src/repo_key.js
CHANGED
|
@@ -87,8 +87,20 @@ export async function deriveRepoIdentity(cwd = process.cwd(), git = makeGitRunne
|
|
|
87
87
|
const url = res.code === 0 ? res.stdout.trim() : "";
|
|
88
88
|
const normalized = normalizeRemoteUrl(url);
|
|
89
89
|
if (!normalized) {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
// `path.basename("/")` is "" (the hook can run with cwd = filesystem root,
|
|
91
|
+
// e.g. when the IDE spawns it detached without a project cwd). Keep a STABLE
|
|
92
|
+
// dedup key in that case (`local:repo`) but DON'T surface the meaningless
|
|
93
|
+
// literal "repo" as the user-facing label — leave `repo_label` EMPTY so the
|
|
94
|
+
// display layer (`projectLabel`) falls through to its smarter cwd-walk /
|
|
95
|
+
// omits the project rather than showing "GoCode · repo". (Previously this
|
|
96
|
+
// hard-coded `|| "repo"` for BOTH the key and the label, which is the cause
|
|
97
|
+
// of the "repo" project name users saw on root-cwd hook runs.)
|
|
98
|
+
const base = path.basename(path.resolve(cwd));
|
|
99
|
+
return {
|
|
100
|
+
repo_key: `local:${base || "repo"}`,
|
|
101
|
+
repo_label: base,
|
|
102
|
+
synced: false,
|
|
103
|
+
};
|
|
92
104
|
}
|
|
93
105
|
return {
|
|
94
106
|
repo_key: repoKeyFromNormalized(normalized),
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Single source of truth for the CLI version. Keep in sync with package.json.
|
|
2
|
-
export const VERSION = "0.6.
|
|
2
|
+
export const VERSION = "0.6.3";
|
package/package.json
CHANGED