francois 0.17.8 → 0.17.11

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/icon.png CHANGED
Binary file
package/lib/desktop.js CHANGED
@@ -38,10 +38,38 @@ function startMenuShortcut(productName, appData = process.env.APPDATA) {
38
38
  return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', `${productName}.lnk`);
39
39
  }
40
40
 
41
+ /**
42
+ * Where a launched Francois should START, which is anywhere but its own package.
43
+ *
44
+ * A process's working directory is an open handle on that directory, and it is
45
+ * inherited by everything the app spawns. Pointing it at `…/francois/vendor`
46
+ * meant the self-update helper — a child of the app — held the exact directory
47
+ * npm has to rename during `npm i -g francois@latest`, so every self-update
48
+ * failed with `EBUSY: resource busy or locked, rename '...\francois\vendor'`.
49
+ * The home directory owes nothing to the install and is never replaced.
50
+ */
51
+ function shortcutWorkingDir(home = os.homedir()) {
52
+ return home;
53
+ }
54
+
41
55
  function uninstallRegistryKey(channel) {
42
56
  return `HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${appId(channel)}`;
43
57
  }
44
58
 
59
+ /**
60
+ * The AppUserModelID toast notifications are sent under. Must equal the tauri
61
+ * `identifier` of the channel's config (tauri.conf.json / tauri.dev.conf.json):
62
+ * tauri-plugin-notification tags every toast from an installed app with that
63
+ * identifier, and Windows silently drops toasts whose AUMID it has never seen.
64
+ */
65
+ function aumid(channel) {
66
+ return channel === 'dev' ? 'com.francois.dev' : 'com.francois.desktop';
67
+ }
68
+
69
+ function aumidRegistryKey(channel) {
70
+ return `HKCU\\Software\\Classes\\AppUserModelId\\${aumid(channel)}`;
71
+ }
72
+
45
73
  function desktopEntryPath(channel, home = os.homedir()) {
46
74
  return path.join(home, '.local', 'share', 'applications', `${appId(channel)}.desktop`);
47
75
  }
@@ -103,7 +131,7 @@ function installWindows({ executable, productName, channel, appVersion, notes })
103
131
  [
104
132
  '$s = (New-Object -ComObject WScript.Shell).CreateShortcut(' + psQuote(shortcut) + ');',
105
133
  '$s.TargetPath = ' + psQuote(executable) + ';',
106
- '$s.WorkingDirectory = ' + psQuote(path.dirname(executable)) + ';',
134
+ '$s.WorkingDirectory = ' + psQuote(shortcutWorkingDir()) + ';',
107
135
  "$s.Description = 'Mission control for your Claude Code fleet';",
108
136
  '$s.Save()',
109
137
  ].join(' '),
@@ -127,12 +155,22 @@ function installWindows({ executable, productName, channel, appVersion, notes })
127
155
  attempt('reg', ['add', key, '/v', name, '/t', type, '/d', data, '/f']),
128
156
  );
129
157
  if (!wrote) notes.push('could not register the uninstall entry.');
158
+
159
+ // Registering the AUMID is what lets toast notifications through: a
160
+ // WScript.Shell .lnk cannot carry System.AppUserModel.ID, so use the registry
161
+ // route for unpackaged apps (the same one Chrome uses) instead.
162
+ const toastKey = aumidRegistryKey(channel);
163
+ const registered =
164
+ attempt('reg', ['add', toastKey, '/v', 'DisplayName', '/t', 'REG_SZ', '/d', productName, '/f']) &&
165
+ attempt('reg', ['add', toastKey, '/v', 'IconUri', '/t', 'REG_SZ', '/d', ICON_SOURCE, '/f']);
166
+ if (!registered) notes.push('could not register the notification identity — toasts may not show.');
130
167
  }
131
168
 
132
169
  function removeWindows({ productName, channel }) {
133
170
  const shortcut = startMenuShortcut(productName);
134
171
  if (shortcut) fs.rmSync(shortcut, { force: true });
135
172
  attempt('reg', ['delete', uninstallRegistryKey(channel), '/f']);
173
+ attempt('reg', ['delete', aumidRegistryKey(channel), '/f']);
136
174
  }
137
175
 
138
176
  /** Single-quoted PowerShell literal — no expansion, '' escapes a quote. */
@@ -244,11 +282,14 @@ module.exports = {
244
282
  ICON_SOURCE,
245
283
  appId,
246
284
  applicationsDir,
285
+ aumid,
286
+ aumidRegistryKey,
247
287
  desktopEntry,
248
288
  desktopEntryPath,
249
289
  desktopIconPath,
250
290
  install,
251
291
  remove,
292
+ shortcutWorkingDir,
252
293
  startMenuShortcut,
253
294
  uninstallRegistryKey,
254
295
  };
package/manifest.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "repo": "antoine-gmnz/francois",
3
- "tag": "v0.17.8",
3
+ "tag": "v0.17.11",
4
4
  "channel": "stable",
5
- "appVersion": "0.17.8",
5
+ "appVersion": "0.17.11",
6
6
  "productName": "Francois",
7
7
  "assets": {
8
- "darwin-universal": { "name": "francois-darwin-universal.tar.gz", "sha256": "3478ad1402c523b2452c5472873701b68ee1fc2da9bb01b79d5db34aef695306" },
9
- "linux-x64": { "name": "francois-linux-x64.tar.gz", "sha256": "a6b210861d7b9dab585d8258c2a520d636d5e927958f1409db2816e6893d5129" },
10
- "win32-x64": { "name": "francois-win32-x64.zip", "sha256": "55780de3390000ff5cab96e276ded946dfff42b0124e4fb2c91c230497ec94a8" }
8
+ "darwin-universal": { "name": "francois-darwin-universal.tar.gz", "sha256": "1c50fe7bff3886f82addab96818cdc8c7a8eae8da699fa412874509268d85f45" },
9
+ "linux-x64": { "name": "francois-linux-x64.tar.gz", "sha256": "b0f17fef53ff3626d1cbb90dbbba32d06b46895a94645d75032c566d9387eebd" },
10
+ "win32-x64": { "name": "francois-win32-x64.zip", "sha256": "7d898313eea6f5e0e94edd76e680986f87696d26e8ef956a59842ebacb270887" }
11
11
  }
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "francois",
3
- "version": "0.17.8",
3
+ "version": "0.17.11",
4
4
  "description": "Mission control for your Claude Code fleet — installs the Francois desktop app without an installer.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "Antoine Gimenez",