aegis-desktop 1.0.5 → 1.0.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.
@@ -8,15 +8,7 @@
8
8
  * Pure: Electron, the store and the provisioner are injected.
9
9
  */
10
10
 
11
- function provisionFailureText(result) {
12
- const err = result && result.error;
13
- if (!err) return 'the server returned no account key';
14
- if (err.status === 429) return 'too many sign-ups from this network — try again later';
15
- if (!err.status || err.status === 408 || err.status >= 500) return 'could not reach aegiscloud.org';
16
- return `the server refused the request (HTTP ${err.status})`;
17
- }
18
-
19
- function createAccountKey({ aegis, credentials, persistKey, provision, onChange }) {
11
+ function createAccountKey({ aegis, credentials, persistKey, provision, describeFailure, onChange }) {
20
12
  let failure = null;
21
13
  let inFlight = null;
22
14
 
@@ -26,7 +18,7 @@ function createAccountKey({ aegis, credentials, persistKey, provision, onChange
26
18
  }
27
19
 
28
20
  function recordStartup(result) {
29
- failure = (result.key || aegis.apiKey) ? null : provisionFailureText(result);
21
+ failure = (result.key || aegis.apiKey) ? null : describeFailure(result);
30
22
  onChange(status());
31
23
  }
32
24
 
@@ -39,7 +31,7 @@ function createAccountKey({ aegis, credentials, persistKey, provision, onChange
39
31
  persistKey(result.key);
40
32
  failure = null;
41
33
  } else {
42
- failure = provisionFailureText(result);
34
+ failure = describeFailure(result);
43
35
  }
44
36
  const state = status();
45
37
  onChange(state);
@@ -55,4 +47,9 @@ function createAccountKey({ aegis, credentials, persistKey, provision, onChange
55
47
  return { status, recordStartup, registerAccount };
56
48
  }
57
49
 
58
- module.exports = { createAccountKey, provisionFailureText };
50
+ // A key this host saved on purpose outranks one minted for a fresh device at startup.
51
+ function startupKeyOverride({ mintedKey, hostKey }) {
52
+ return mintedKey && hostKey && hostKey !== mintedKey ? hostKey : null;
53
+ }
54
+
55
+ module.exports = { createAccountKey, startupKeyOverride };
package/main.js CHANGED
@@ -152,7 +152,7 @@ try {
152
152
  // backed by transport-only modules in desktop/lib/. No brain logic enters here.
153
153
  const os = require('node:os');
154
154
  const { createLocalEngine } = require('./lib/local/engine.js');
155
- const { createAccountKey } = require('./lib/account-key.js');
155
+ const { createAccountKey, startupKeyOverride } = require('./lib/account-key.js');
156
156
  const { createSettingsStore, isReservedNamespace } = require('./lib/settings.js');
157
157
  const sessionStore = require('./lib/sync/sessions.js');
158
158
  const memoryQueue = require('./lib/sync/memory-queue.js');
@@ -3330,10 +3330,12 @@ function bootstrap() {
3330
3330
  aegis,
3331
3331
  credentials,
3332
3332
  persistKey: (key) => settings.setAegisKey(key),
3333
- provision: autoProvision.autoProvisionApiKey,
3333
+ provision: (client, creds) => autoProvision.autoProvisionApiKey(client, creds, { app: 'desktop' }),
3334
+ describeFailure: autoProvision.provisionFailureText,
3334
3335
  onChange: (state) => broadcastAccountStatus(state),
3335
3336
  });
3336
- const earlyKeyProvision = autoProvision.autoProvisionApiKey(aegis, credentials).then((result) => {
3337
+ const keyPresentAtStart = Boolean(aegis.apiKey);
3338
+ const earlyKeyProvision = autoProvision.autoProvisionApiKey(aegis, credentials, { app: 'desktop' }).then((result) => {
3337
3339
  accountKey.recordStartup(result);
3338
3340
  if (!result.key && !aegis.apiKey) {
3339
3341
  const reason =
@@ -3910,13 +3912,25 @@ function bootstrap() {
3910
3912
  // registration attempt, and earlyKeyProvision's own .then() already
3911
3913
  // warns on failure, so there is nothing further to report if it
3912
3914
  // didn't land.
3913
- await earlyKeyProvision;
3914
- const chosen = credentials.preferNewest(settings.aegisRawKey(), settings.aegisKeySavedAt());
3915
- if (chosen.key) {
3916
- aegis.setApiKey(chosen.key);
3917
- // The shared store is the newer write: bring this host's encrypted copy
3918
- // up to date so a CLI rotation does not have to be redone in Settings.
3919
- if (chosen.source === 'shared') settings.setAegisKey(chosen.key);
3915
+ const early = await earlyKeyProvision;
3916
+ // A key minted just now only means "no key was saved yet" — a key the user
3917
+ // saved in Settings before the shared store mirrored it would otherwise be
3918
+ // overwritten by the device's fresh account on this first start.
3919
+ const override = startupKeyOverride({
3920
+ mintedKey: keyPresentAtStart ? '' : early.key,
3921
+ hostKey: credentials.normalizeApiKey(settings.aegisRawKey()),
3922
+ });
3923
+ if (override) {
3924
+ credentials.saveApiKey(override);
3925
+ aegis.setApiKey(override);
3926
+ } else {
3927
+ const chosen = credentials.preferNewest(settings.aegisRawKey(), settings.aegisKeySavedAt());
3928
+ if (chosen.key) {
3929
+ aegis.setApiKey(chosen.key);
3930
+ // The shared store is the newer write: bring this host's encrypted copy
3931
+ // up to date so a CLI rotation does not have to be redone in Settings.
3932
+ if (chosen.source === 'shared') settings.setAegisKey(chosen.key);
3933
+ }
3920
3934
  }
3921
3935
  }
3922
3936
  // ── presence beacon ───────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aegis-desktop",
3
3
  "productName": "AEGIS Desktop",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "description": "Thin Electron host for AEGIS — a local chat UI over the shared client/aegis.js transport. Ships transport + UI only; engine logic stays server-side.",
6
6
  "author": {
7
7
  "name": "AEGIS Code",
package/vendor/aegis.js CHANGED
@@ -855,10 +855,11 @@ function createClient(opts = {}) {
855
855
  * zero balance. Re-registering an already-known fingerprint does NOT
856
856
  * re-grant it; the pool balance gate still applies once the allowance is
857
857
  * spent or the 30 days lapse. Sends no API key of its own — there isn't
858
- * one yet.
858
+ * one yet. `opts.app` names the host ('cli' | 'desktop' | 'mcp') so the
859
+ * server files this machine under its real name, not 'other'.
859
860
  */
860
861
  async function registerDevice(fingerprint, opts) {
861
- return apiPost('/api/v1/auth/register-device', { fingerprint }, undefined, opts);
862
+ return apiPost('/api/v1/auth/register-device', { fingerprint, app: opts && opts.app }, undefined, opts);
862
863
  }
863
864
 
864
865
  /** Build the OpenAI-style messages array from either a full history or the
@@ -43,30 +43,47 @@ function isTransientRegisterError(err) {
43
43
  return !status || status === 408 || status >= 500;
44
44
  }
45
45
 
46
+ // Why automatic registration gave up, in words a host can show the user.
47
+ function provisionFailureText(result) {
48
+ const err = result && result.error;
49
+ if (!err) return 'the server returned no account key';
50
+ if (err.status === 429) return 'too many sign-ups from this network — try again later';
51
+ if (err.status >= 500) return `aegiscloud.org failed to register this device: ${err.message}`;
52
+ if (isTransientRegisterError(err)) return 'could not reach aegiscloud.org';
53
+ return `the server refused the request (HTTP ${err.status})`;
54
+ }
55
+
46
56
  /**
47
57
  * @param {object} aegisClient A client from client/aegis.js (createClient()).
48
58
  * @param {object} credentials client/credentials.js (or a compatible double).
49
- * @param {number[]} [retryDelaysMs] Wait before each retry of a transient failure.
50
- * @returns {Promise<{ok: boolean, key: string, error?: Error}>}
59
+ * @param {{app?: string, retryDelaysMs?: number[]}} [options] `app` names the host
60
+ * for the server's presence panel; `retryDelaysMs` waits before each retry of a
61
+ * transient failure.
62
+ * @returns {Promise<{ok: boolean, key: string, trialGranted?: boolean, error?: Error}>}
51
63
  * `ok` is true only when a usable key was obtained AND persisted to the
52
64
  * shared store; `aegisClient.setApiKey()` has already been called on
53
65
  * success, so the caller's client is immediately usable either way it
54
- * inspects `ok`.
66
+ * inspects `ok`. `trialGranted` is false when the server withheld the
67
+ * welcome trial (the account and key are still valid).
55
68
  */
56
- async function autoProvisionApiKey(aegisClient, credentials, retryDelaysMs = DEVICE_REGISTER_RETRY_DELAYS_MS) {
69
+ async function autoProvisionApiKey(aegisClient, credentials, options = {}) {
70
+ if (Array.isArray(options)) {
71
+ throw new TypeError('autoProvisionApiKey takes { app, retryDelaysMs }; a bare retry array is no longer accepted');
72
+ }
57
73
  if (aegisClient.apiKey) return { ok: true, key: aegisClient.apiKey };
74
+ const { app, retryDelaysMs = DEVICE_REGISTER_RETRY_DELAYS_MS } = options;
58
75
  let lastError = null;
59
76
  for (let attempt = 0; attempt <= retryDelaysMs.length; attempt++) {
60
77
  if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, retryDelaysMs[attempt - 1]));
61
78
  const controller = new AbortController();
62
79
  const timer = setTimeout(() => controller.abort(), DEVICE_REGISTER_TIMEOUT_MS);
63
80
  try {
64
- const data = await aegisClient.registerDevice(machineFingerprint(), { signal: controller.signal });
81
+ const data = await aegisClient.registerDevice(machineFingerprint(), { app, signal: controller.signal });
65
82
  const key = data && typeof data.api_key === 'string' ? data.api_key.trim() : '';
66
83
  if (!key) return { ok: false, key: '' };
67
84
  const saved = credentials.saveApiKey(key);
68
85
  aegisClient.setApiKey(key);
69
- return { ok: !!saved.ok, key };
86
+ return { ok: !!saved.ok, key, trialGranted: data.trial_granted };
70
87
  } catch (err) {
71
88
  lastError = err;
72
89
  if (!isTransientRegisterError(err)) break;
@@ -77,4 +94,4 @@ async function autoProvisionApiKey(aegisClient, credentials, retryDelaysMs = DEV
77
94
  return { ok: false, key: '', error: lastError };
78
95
  }
79
96
 
80
- module.exports = { autoProvisionApiKey, DEVICE_REGISTER_TIMEOUT_MS, DEVICE_REGISTER_RETRY_DELAYS_MS };
97
+ module.exports = { autoProvisionApiKey, provisionFailureText, DEVICE_REGISTER_TIMEOUT_MS, DEVICE_REGISTER_RETRY_DELAYS_MS };
@@ -111,8 +111,7 @@ function persistedInstallId() {
111
111
  * 4. `hostname:platform:arch` — last resort, kept only so this never throws.
112
112
  *
113
113
  * Only step 4 is guessable by a third party; steps 2 and 3 are both per-host
114
- * unique. `machineIdentityKind()` reports which one was used, so aegis1-side
115
- * abuse review can tell a genuine re-registration from a forged one.
114
+ * unique.
116
115
  *
117
116
  * @param {string} [overrideId] - test-only override for the raw machine identity.
118
117
  */
@@ -136,15 +135,9 @@ function machineIdentity(overrideId) {
136
135
  };
137
136
  }
138
137
 
139
- /** Which identity class the fingerprint on THIS host is derived from. */
140
- function machineIdentityKind() {
141
- return machineIdentity().kind;
142
- }
143
-
144
138
  module.exports = {
145
139
  machineFingerprint,
146
140
  machineIdentity,
147
- machineIdentityKind,
148
141
  readMachineId,
149
142
  persistedInstallId,
150
143
  };