aegis-desktop 0.8.6 → 0.8.7
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/docs/marketing-assets/README.md +89 -0
- package/lib/local/autonomous.js +69 -2
- package/lib/local/engine.js +10 -0
- package/lib/local/entitlement.js +487 -0
- package/lib/local/telemetry.js +467 -0
- package/lib/settings.js +33 -0
- package/main.js +185 -5
- package/package.json +2 -1
- package/preload.js +34 -0
- package/renderer/app.js +315 -36
- package/renderer/index.html +122 -54
- package/renderer/style.css +91 -7
- package/vendor/aegis.js +146 -2
- package/vendor/credentials.js +4 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Marketing assets — captions, claim, and tagged links
|
|
2
|
+
|
|
3
|
+
The three PNGs in this directory are the **existing** asset kit (1440×900, shot
|
|
4
|
+
against the real app — see `docs/marketing-log.md`, 2026-09-22 entry). Images
|
|
5
|
+
carry no link of their own, so the outbound link for each placement lives here,
|
|
6
|
+
in the same directory as the image it belongs to. Nothing in this directory is a
|
|
7
|
+
new asset: these are the captions for the three files below, rewritten for the
|
|
8
|
+
Phase 5 promotion pass (`docs/upgrade-conversion-plan.md` §5).
|
|
9
|
+
|
|
10
|
+
**The one claim (Phase 3, verbatim, same sentence as the welcome panel):**
|
|
11
|
+
|
|
12
|
+
> **The autonomous queue keeps running after you close the laptop.**
|
|
13
|
+
|
|
14
|
+
Plus the honesty line that sits next to it:
|
|
15
|
+
|
|
16
|
+
> Everything on your machine is free, permanently. The cloud drain is what you
|
|
17
|
+
> pay for.
|
|
18
|
+
|
|
19
|
+
(`desktop/renderer/app.js` → `PAID_PROMISE` / `FREE_HONESTY`, rendered by
|
|
20
|
+
`#welcome-template` in `desktop/renderer/index.html`.)
|
|
21
|
+
|
|
22
|
+
**What the caption must not be:** a tour of the three model classes, or the free
|
|
23
|
+
local row written up as if it were the product. The screenshots show the picker
|
|
24
|
+
and the diff card — that is *what the image is of*, not the *offer*. One claim
|
|
25
|
+
per asset; the class list stays out of the caption.
|
|
26
|
+
|
|
27
|
+
**Channel rule.** Facebook and Reddit are image-friendly; X and YouTube captions
|
|
28
|
+
follow `docs/launch-copy-x-youtube.md` §2/§3. Every link is
|
|
29
|
+
`https://aegiscloud.org/go/upgrade?s=<channel>&c=<campaign>` with `s` in
|
|
30
|
+
`(x, youtube, reddit, facebook)` — the only channels `SIGNUP_SOURCES` in
|
|
31
|
+
`aegis1/app.py` accepts — and one campaign id per channel, stable across every
|
|
32
|
+
asset on that channel.
|
|
33
|
+
|
|
34
|
+
| Image | Placement | Channel `s` | Campaign `c` | Tagged link (paste this exact URL) |
|
|
35
|
+
|---|---|---|---|---|
|
|
36
|
+
| `01-model-class-picker.png` | X post (picker + claim in the reply) | `x` | `x_launch` | `https://aegiscloud.org/go/upgrade?s=x&c=x_launch` |
|
|
37
|
+
| `01-model-class-picker.png` | Reddit image post (r/LocalLLaMA, r/ollama — link in the closing block, after the limitations) | `reddit` | `reddit_launch` | `https://aegiscloud.org/go/upgrade?s=reddit&c=reddit_launch` |
|
|
38
|
+
| `01-model-class-picker.png` | Facebook Page + groups (image + claim + link) | `facebook` | `facebook_launch` | `https://aegiscloud.org/go/upgrade?s=facebook&c=facebook_launch` |
|
|
39
|
+
| `02-answer-complete.png` | YouTube Short / long-form B-roll; `Upgrade:` line in the description | `youtube` | `yt_launch` | `https://aegiscloud.org/go/upgrade?s=youtube&c=yt_launch` |
|
|
40
|
+
| `02-answer-complete.png` | X post (answer in flight) | `x` | `x_launch` | `https://aegiscloud.org/go/upgrade?s=x&c=x_launch` |
|
|
41
|
+
| `03-tool-approval-diff.png` | X pinned post / thread 3- (the diff card) | `x` | `x_launch` | `https://aegiscloud.org/go/upgrade?s=x&c=x_launch` |
|
|
42
|
+
| `03-tool-approval-diff.png` | Reddit GIF+text post (r/ChatGPTCoding) | `reddit` | `reddit_launch` | `https://aegiscloud.org/go/upgrade?s=reddit&c=reddit_launch` |
|
|
43
|
+
| `03-tool-approval-diff.png` | YouTube thumbnail / description | `youtube` | `yt_launch` | `https://aegiscloud.org/go/upgrade?s=youtube&c=yt_launch` |
|
|
44
|
+
|
|
45
|
+
Every row above is registered in `docs/marketing-log.md` §7 so post-hoc revenue
|
|
46
|
+
can be joined to the channel and the asset face that produced it.
|
|
47
|
+
|
|
48
|
+
## Captions
|
|
49
|
+
|
|
50
|
+
### `01-model-class-picker.png` — the class picker
|
|
51
|
+
|
|
52
|
+
> The autonomous queue keeps running after you close the laptop. Everything on
|
|
53
|
+
> your machine is free, permanently — the cloud drain is what you pay for.
|
|
54
|
+
> npm i -g aegis-desktop
|
|
55
|
+
> https://aegiscloud.org/go/upgrade?s=x&c=x_launch
|
|
56
|
+
|
|
57
|
+
Facebook variant (same claim, same tagged shape, different channel):
|
|
58
|
+
|
|
59
|
+
> The autonomous queue keeps running after you close the laptop. Cloud-backed
|
|
60
|
+
> drain, survives a reboot, fans a task out across workers, memory carried
|
|
61
|
+
> between tasks. Everything on your machine is free, permanently.
|
|
62
|
+
> npm i -g aegis-desktop
|
|
63
|
+
> https://aegiscloud.org/go/upgrade?s=facebook&c=facebook_launch
|
|
64
|
+
|
|
65
|
+
Do **not** caption this image with the picker's three rows as the pitch. The
|
|
66
|
+
picker is a fact about the product; the claim above is the offer.
|
|
67
|
+
|
|
68
|
+
### `02-answer-complete.png` — an answer, complete
|
|
69
|
+
|
|
70
|
+
> The autonomous queue keeps running after you close the laptop — the work does
|
|
71
|
+
> not stop when the laptop does. Everything on your machine is free, permanently.
|
|
72
|
+
> Install: `npm i -g aegis-desktop && aegis`
|
|
73
|
+
> https://aegiscloud.org/go/upgrade?s=youtube&c=yt_launch
|
|
74
|
+
|
|
75
|
+
### `03-tool-approval-diff.png` — the diff/approval card
|
|
76
|
+
|
|
77
|
+
> The diff card is the whole product locally — and it is free, permanently. The
|
|
78
|
+
> pocket-book item is the drain that keeps running after you close the laptop.
|
|
79
|
+
> Install: `npm i -g aegis-desktop && aegis`
|
|
80
|
+
> https://aegiscloud.org/go/upgrade?s=x&c=x_launch
|
|
81
|
+
|
|
82
|
+
## Before posting (§0 of `docs/marketing-plan-social.md`)
|
|
83
|
+
|
|
84
|
+
1. Re-verify the version (`desktop/package.json`) and re-shoot the image if the
|
|
85
|
+
UI moved; log that check in `docs/marketing-log.md` §6.
|
|
86
|
+
2. Confirm the link still resolves and still carries `s=`/`c=`. A bare
|
|
87
|
+
`aegiscloud.org/subscribe` (bare, untagged) link files as `direct` and the channel
|
|
88
|
+
gets no row.
|
|
89
|
+
3. Never invent a number in a caption (marketing-log §2 rule 1).
|
package/lib/local/autonomous.js
CHANGED
|
@@ -402,7 +402,28 @@ function errorText(err) {
|
|
|
402
402
|
* @param {object} [opts.git] Injectable git-scope (tests).
|
|
403
403
|
* @param {function} [opts.now] Clock.
|
|
404
404
|
*/
|
|
405
|
-
function createQueueWorker({
|
|
405
|
+
function createQueueWorker({
|
|
406
|
+
engine,
|
|
407
|
+
env = process.env,
|
|
408
|
+
log = () => {},
|
|
409
|
+
git = gitScope,
|
|
410
|
+
now = Date.now,
|
|
411
|
+
// May a task fan out AT ALL this session? The default preserves every host's
|
|
412
|
+
// behaviour (a CLI drain, a systemd timer: no entitlement, no clamp), and
|
|
413
|
+
// main.js passes the queue's entitlement gate — see
|
|
414
|
+
// desktop/lib/local/entitlement.js and Phase 4 of
|
|
415
|
+
// docs/upgrade-conversion-plan.md.
|
|
416
|
+
//
|
|
417
|
+
// This exists because the fan-out is otherwise reachable around the desktop's
|
|
418
|
+
// IPC gate: a task queued by the CLI or written into queue.jsonl by hand
|
|
419
|
+
// carries `singlePass: false`, and main.js had already accepted it long
|
|
420
|
+
// before anyone asked about entitlement. Refusing the whole task would be
|
|
421
|
+
// worse than the leak (the free tier keeps working — that is a promise), so
|
|
422
|
+
// the request is honoured as a SINGLE PASS and said out loud via a `note`
|
|
423
|
+
// frame: the run is clamped, never silently different from what the card
|
|
424
|
+
// shows.
|
|
425
|
+
canFanOut = () => true,
|
|
426
|
+
} = {}) {
|
|
406
427
|
if (!engine || typeof engine.chat !== 'function') {
|
|
407
428
|
throw new Error('autonomous: a local engine with chat() is required');
|
|
408
429
|
}
|
|
@@ -415,6 +436,18 @@ function createQueueWorker({ engine, env = process.env, log = () => {}, git = gi
|
|
|
415
436
|
}
|
|
416
437
|
};
|
|
417
438
|
|
|
439
|
+
/** `canFanOut` is host-supplied, so it is treated like any other injected
|
|
440
|
+
* seam: a predicate that throws must not abort a drain — it fails CLOSED
|
|
441
|
+
* (no fan-out), because the alternative is spending the multiplier the gate
|
|
442
|
+
* exists to withhold. */
|
|
443
|
+
const safeCanFanOut = () => {
|
|
444
|
+
try {
|
|
445
|
+
return canFanOut() !== false;
|
|
446
|
+
} catch {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
418
451
|
/**
|
|
419
452
|
* Run ONE queued task. Never throws: a failed task is data (so the drain can
|
|
420
453
|
* decide whether to continue), not an exception that aborts the queue.
|
|
@@ -437,7 +470,21 @@ function createQueueWorker({ engine, env = process.env, log = () => {}, git = gi
|
|
|
437
470
|
const model = resolveModel({ model: item.model, env });
|
|
438
471
|
const ignored = ignoredEnvModel({ model: item.model, env });
|
|
439
472
|
if (ignored) emit({ type: 'note', taskId: item.id, note: ignored });
|
|
440
|
-
const
|
|
473
|
+
const fanoutAsked = resolveFanout(item, env);
|
|
474
|
+
// The clamp. `canFanOut()` is the entitlement answer (true everywhere
|
|
475
|
+
// except a non-Pro desktop session), and it can only ever take a fan-out
|
|
476
|
+
// AWAY: a task the session is not entitled to fan out runs as one plain
|
|
477
|
+
// turn on the same tier, exactly like a `singlePass` task.
|
|
478
|
+
const fanout = fanoutAsked && safeCanFanOut();
|
|
479
|
+
if (fanoutAsked && !fanout) {
|
|
480
|
+
emit({
|
|
481
|
+
type: 'note',
|
|
482
|
+
taskId: item.id,
|
|
483
|
+
note:
|
|
484
|
+
'fan-out needs Pro (the cloud drain that keeps running after you close ' +
|
|
485
|
+
'the laptop) — this task ran as a single pass',
|
|
486
|
+
});
|
|
487
|
+
}
|
|
441
488
|
const effort = resolveEffort({ effort: item.effort, env, fanout });
|
|
442
489
|
const rounds = maxRounds(env, item.maxRounds);
|
|
443
490
|
// Approval requests have no one to answer them here; see the header.
|
|
@@ -496,6 +543,26 @@ function createQueueWorker({ engine, env = process.env, log = () => {}, git = gi
|
|
|
496
543
|
// the default for exactly that reason; the retry/write-up passes in
|
|
497
544
|
// the engine send `brain: false` for the same one-call reason.
|
|
498
545
|
autonomous: fanout,
|
|
546
|
+
// THE OPT-OUT HAS TO BE SAID OUT LOUD, or the clamp above is
|
|
547
|
+
// decoration. `autonomous: false` does NOT reach the wire as a
|
|
548
|
+
// "no fan-out": engine.js derives its brain flag as
|
|
549
|
+
// `opts.singlePass ? false : opts.autonomous ? true : undefined`, so
|
|
550
|
+
// false-and-not-singlePass evaluates to `undefined`, and an absent
|
|
551
|
+
// `brain` means "leave the model id's own default alone". The model
|
|
552
|
+
// here IS `nexus-brain`, and aegis1 services/pool_brain.py
|
|
553
|
+
// parse_brain_request enables the fan-out when
|
|
554
|
+
// `is_brain_model(spec)` — no flag or worker count required:
|
|
555
|
+
//
|
|
556
|
+
// enabled = not flag_off and (is_brain_model(spec) or flag_on or ...)
|
|
557
|
+
//
|
|
558
|
+
// So a clamped task went out asking for the full workers+1
|
|
559
|
+
// fan-out and got it, while the log said it ran as a single pass.
|
|
560
|
+
// `brain: false` is the only instruction that outranks the model id
|
|
561
|
+
// (pool_brain's own docstring: "the only way to ask for one provider
|
|
562
|
+
// call on a brain id"), and `singlePass: true` is how a caller
|
|
563
|
+
// produces it. This also fixes the plain `--single-pass` task, which
|
|
564
|
+
// never reached the wire as one for the same reason.
|
|
565
|
+
singlePass: !fanout,
|
|
499
566
|
effort,
|
|
500
567
|
workers: fanout ? item.workers || undefined : undefined,
|
|
501
568
|
// The turn's working directory rides on `env`: engine.js reads the
|
package/lib/local/engine.js
CHANGED
|
@@ -1296,6 +1296,16 @@ function createLocalEngine({
|
|
|
1296
1296
|
// field is off, which is what keeps every existing caller (the
|
|
1297
1297
|
// renderer's IPC payloads included) on the cheap read.
|
|
1298
1298
|
recallDeep: payload && payload.recallDeep === true,
|
|
1299
|
+
// The fan-out opt-out, and it must be an explicit `true` for the same
|
|
1300
|
+
// reason `recallDeep` is. `undefined` is NOT an opt-out: the wire flag
|
|
1301
|
+
// below is `singlePass ? false : autonomous ? true : undefined`, and an
|
|
1302
|
+
// absent `brain` flag lets the *model id* turn the fan-out on by itself
|
|
1303
|
+
// (aegis1 pool_brain.parse_brain_request: `is_brain_model(spec)` alone
|
|
1304
|
+
// enables it). So a clamped task on a brain model fanned out anyway
|
|
1305
|
+
// while the log claimed a single pass — the caller's intent never
|
|
1306
|
+
// reached the wire. Forwarding it here is what lets the refusal be real
|
|
1307
|
+
// rather than a local boolean nobody downstream could see.
|
|
1308
|
+
singlePass: payload && payload.singlePass === true,
|
|
1299
1309
|
onReasoning,
|
|
1300
1310
|
idleTimeoutMs: autonomous ? AUTONOMOUS_IDLE_TIMEOUT_MS : undefined,
|
|
1301
1311
|
// A caller with no live streaming surface (a `--no-stream` CLI flag, a
|