@omega.js/desktop 0.53.0 → 0.54.1

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.
Files changed (161) hide show
  1. package/README.md +38 -38
  2. package/dist/cli-run.js +4 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/commands/cdp/client.js +1 -1
  5. package/dist/commands/cdp.js +1 -1
  6. package/dist/commands/clean.js +2 -3
  7. package/dist/commands/dev.js +25 -0
  8. package/dist/commands/lib/ensure-target.js +12 -17
  9. package/dist/commands/lib/migrate.js +17 -0
  10. package/dist/commands/logs.js +1 -1
  11. package/dist/commands/release.js +1 -1
  12. package/dist/commands/test.js +4 -4
  13. package/dist/commands/update.js +5 -4
  14. package/dist/defaults/.github/workflows/build.yml +18 -18
  15. package/dist/defaults/_.gitignore +0 -2
  16. package/dist/defaults/_mas/README.md +3 -3
  17. package/dist/defaults/config/certs/README.md +1 -1
  18. package/dist/defaults/config/omega.json5 +36 -36
  19. package/dist/defaults/docs/README.md +3 -3
  20. package/dist/defaults/gulpfile.js +1 -1
  21. package/dist/defaults/hooks/build/post.js +1 -1
  22. package/dist/defaults/hooks/build/pre.js +1 -1
  23. package/dist/defaults/hooks/notarize/post.js +2 -2
  24. package/dist/defaults/hooks/release/post.js +1 -1
  25. package/dist/defaults/hooks/release/pre.js +1 -1
  26. package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
  27. package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
  28. package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
  29. package/dist/defaults/src/integrations/context-menu/index.js +11 -11
  30. package/dist/defaults/src/integrations/menu/index.js +5 -5
  31. package/dist/defaults/src/integrations/tray/index.js +9 -9
  32. package/dist/defaults/src/main.js +2 -2
  33. package/dist/defaults/src/preload.js +1 -1
  34. package/dist/defaults/test/README.md +3 -3
  35. package/dist/defaults/test/_init.js +1 -1
  36. package/dist/gulp/tasks/audit.js +5 -8
  37. package/dist/lib/restart-manager/index.js +1 -1
  38. package/dist/lib/restart-manager/install.js +1 -1
  39. package/dist/lib/restart-manager/protocol.js +1 -1
  40. package/dist/main.js +4 -3
  41. package/dist/preload.js +1 -1
  42. package/dist/test/suites/build/audit.test.js +20 -7
  43. package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
  44. package/dist/test/suites/build/cli.test.js +28 -0
  45. package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
  46. package/dist/test/suites/build/defaults-scaffold.test.js +19 -5
  47. package/dist/test/suites/build/deploy-direct.test.js +7 -5
  48. package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
  49. package/dist/test/suites/build/deploy-hook.test.js +4 -2
  50. package/dist/test/suites/build/dev-verb.test.js +67 -0
  51. package/dist/test/suites/build/ensure-target.test.js +11 -3
  52. package/dist/test/suites/build/merge-line-files.test.js +6 -6
  53. package/dist/test/suites/build/migrate.test.js +29 -0
  54. package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
  55. package/dist/test/suites/build/runner-env-write.test.js +73 -0
  56. package/dist/test/suites/build/runner.test.js +9 -8
  57. package/dist/test/suites/build/setup-scripts.test.js +27 -0
  58. package/dist/test/suites/build/validate-config.test.js +13 -2
  59. package/dist/test/suites/build/verb-logs.test.js +20 -0
  60. package/dist/test/suites/renderer/window-desktop-surface.test.js +1 -1
  61. package/dist/utils/build-pipeline.js +4 -4
  62. package/dist/utils/runner-env.js +13 -28
  63. package/dist/vendor/config/company.js +46 -14
  64. package/dist/vendor/config/defaults.js +30 -7
  65. package/dist/vendor/config/edit.js +25 -3
  66. package/dist/vendor/config/env-delivery.js +1 -1
  67. package/dist/vendor/config/env-schema.js +3 -6
  68. package/dist/vendor/config/env.js +34 -22
  69. package/dist/vendor/config/index.js +13 -17
  70. package/dist/vendor/config/load.js +15 -7
  71. package/dist/vendor/config/repo.js +10 -27
  72. package/dist/vendor/config/schema-client.js +64 -0
  73. package/dist/vendor/config/schema-cloud.js +38 -0
  74. package/dist/vendor/config/schema-manager.js +118 -0
  75. package/dist/vendor/config/schema-overrides.js +68 -0
  76. package/dist/vendor/config/schema.js +99 -152
  77. package/dist/vendor/config/validate.js +97 -77
  78. package/dist/vendor/devkit/agents-md.js +233 -0
  79. package/dist/vendor/devkit/attach-log-file.js +15 -1
  80. package/dist/vendor/devkit/ci-workflows.js +30 -30
  81. package/dist/vendor/devkit/cli-router.js +13 -7
  82. package/dist/vendor/devkit/defaults-engine.js +9 -43
  83. package/dist/vendor/devkit/deploy-snapshot.js +44 -9
  84. package/dist/vendor/devkit/env-lines.js +183 -0
  85. package/dist/vendor/devkit/local.js +62 -10
  86. package/dist/vendor/devkit/lockfile.js +32 -13
  87. package/dist/vendor/devkit/logger.js +7 -2
  88. package/dist/vendor/devkit/merge-line-files.js +219 -176
  89. package/dist/vendor/devkit/omega-bin.js +208 -111
  90. package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
  91. package/dist/vendor/devkit/preludes/index.js +1 -0
  92. package/dist/vendor/devkit/target-picker.js +45 -0
  93. package/dist/vendor/devkit/test/dashed-files.js +37 -0
  94. package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
  95. package/dist/vendor/devkit/update.js +15 -15
  96. package/dist/vendor/devkit/verb-scripts.js +40 -0
  97. package/dist/vendor/devkit/verbs.js +170 -0
  98. package/package.json +18 -24
  99. package/dist/commands/install.js +0 -37
  100. package/dist/defaults/AGENTS.md +0 -119
  101. package/dist/defaults/CLAUDE.md +0 -1
  102. package/dist/vendor/config/env-retired.js +0 -137
  103. package/dist/vendor/config/retired-keys.js +0 -635
  104. package/docs/analytics.md +0 -140
  105. package/docs/app-state.md +0 -92
  106. package/docs/audit.md +0 -69
  107. package/docs/auth.md +0 -284
  108. package/docs/auto-updater.md +0 -243
  109. package/docs/boot-sequence.md +0 -44
  110. package/docs/build-system.md +0 -169
  111. package/docs/cdp-debugging.md +0 -169
  112. package/docs/common-mistakes.md +0 -21
  113. package/docs/config-schema.md +0 -120
  114. package/docs/context-menu.md +0 -112
  115. package/docs/context.md +0 -81
  116. package/docs/css.md +0 -84
  117. package/docs/deep-link.md +0 -186
  118. package/docs/environment-detection.md +0 -112
  119. package/docs/fontawesome.md +0 -109
  120. package/docs/hooks.md +0 -89
  121. package/docs/icons.md +0 -79
  122. package/docs/index.md +0 -328
  123. package/docs/installer-options.md +0 -165
  124. package/docs/ipc.md +0 -61
  125. package/docs/lib-modules.md +0 -53
  126. package/docs/logging.md +0 -227
  127. package/docs/menu.md +0 -160
  128. package/docs/releasing.md +0 -239
  129. package/docs/remote-config.md +0 -118
  130. package/docs/remote-scripts.md +0 -144
  131. package/docs/restart-manager.md +0 -144
  132. package/docs/runner.md +0 -290
  133. package/docs/sentry.md +0 -97
  134. package/docs/shared/agent-docs.md +0 -89
  135. package/docs/shared/analytics.md +0 -612
  136. package/docs/shared/brands.md +0 -57
  137. package/docs/shared/breaking-changes.md +0 -917
  138. package/docs/shared/config.md +0 -1948
  139. package/docs/shared/deploys.md +0 -341
  140. package/docs/shared/icons.md +0 -219
  141. package/docs/shared/local-dev.md +0 -167
  142. package/docs/shared/logging.md +0 -205
  143. package/docs/shared/monitoring.md +0 -167
  144. package/docs/shared/publishing.md +0 -187
  145. package/docs/shared/rulings.md +0 -34
  146. package/docs/shared/testing.md +0 -147
  147. package/docs/shared/theming.md +0 -629
  148. package/docs/shared/translation.md +0 -342
  149. package/docs/shared/updates.md +0 -61
  150. package/docs/signing.md +0 -293
  151. package/docs/startup.md +0 -142
  152. package/docs/storage.md +0 -59
  153. package/docs/templating.md +0 -101
  154. package/docs/test-boot-layer.md +0 -157
  155. package/docs/test-framework.md +0 -362
  156. package/docs/themes.md +0 -149
  157. package/docs/tooltips.md +0 -99
  158. package/docs/tray.md +0 -164
  159. package/docs/usage.md +0 -58
  160. package/docs/verts.md +0 -62
  161. package/docs/windows.md +0 -149
package/docs/signing.md DELETED
@@ -1,293 +0,0 @@
1
- # Code Signing
2
-
3
- Cross-platform code signing reference. Covers macOS (sign + notarize), Windows (EV token + cloud), and Linux (no signing required for AppImage/deb but Snap/Flatpak have their own conventions).
4
-
5
- ## tl;dr
6
-
7
- - **macOS production**: Developer ID Application cert + notarization API key. Files in `config/certs/`. Env vars point at them.
8
- - **Windows production**: EV USB token (self-hosted runner now) or cloud signing (future, pluggable via `platforms.windows.signing.strategy`).
9
- - **Linux**: No signing for AppImage/deb. Snap/Flatpak have their own pipelines.
10
-
11
- ## Where files live
12
-
13
- Signing material lives in the SIGNING TREE and is read there, never copied into a target
14
- ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)):
15
-
16
- ```
17
- <company brand>/company/.omega/certificates/apple/ # read FIRST when the brand names a company
18
- <your brand>/.omega/certificates/apple/ # read second
19
- certificates/DEVELOPER_ID_APPLICATION_G2.p12 # what CSC_LINK resolves to
20
- certificates/DEVELOPER_ID_INSTALLER_G2.p12 # only for .pkg builds (rare)
21
- AuthKey_XXXXXXXXXX.p8 # what APPLE_API_KEY resolves to
22
-
23
- <your-app>/
24
- build/
25
- entitlements.mac.plist # universal, already in @omega.js/desktop defaults
26
- icon.icns / icon.ico / icon.png # per-brand
27
- config/certs/ # gitignored, and EMPTY on a developer machine:
28
- # only a RUNNER writes here, decoding the pushed secrets
29
- README.md # the ONE tracked file here (#913): it says the above
30
- .env # gitignored, and it carries no signing PATH: only
31
- # CSC_KEY_PASSWORD and the APPLE_* three
32
- ```
33
-
34
- There is no provisioning profile: Developer ID is direct distribution, signed and notarized,
35
- and needs none.
36
-
37
- ## Where the macOS certificate comes from (the lookup order)
38
-
39
- Every desktop boot (the CLI and gulp alike) derives the signing paths ONCE, at the env load
40
- ([src/utils/load-env.js](../src/utils/load-env.js), over `@omega.js/devkit/signing-env`), so
41
- the build, `omega validate-certs` and the deploy precheck's secret publish all read the same
42
- answer. `CSC_LINK` resolves in ONE order:
43
-
44
- 1. **`CSC_LINK`** in the environment — an explicit answer always wins.
45
- 2. **The COMPANY's tree**, when the brand names a company and that company is on this
46
- machine: a brand with a company reuses the company tree first, always
47
- ([#677](https://github.com/Omega-JS-Stack/omega/issues/677)). A brand is NEVER physically
48
- inside its company folder: membership is its `company: { id }` key, resolved through
49
- `@omega.js/config`'s `resolveCompany` (the ecosystem's one company rule), and the
50
- certificate is one more relative path through that resolver's `file()`:
51
- `<company>/company/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
52
- 3. **The brand's own signing tree**:
53
- `<brandRoot>/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
54
- `@omega.js/manager`'s certificates service produces the tree
55
- ([the manager guide](../../../docs/manager/index.md)).
56
- 4. **The macOS Keychain** — electron-builder's own identity auto-discovery.
57
-
58
- `APPLE_API_KEY` follows the same order for `AuthKey_<APPLE_API_KEY_ID>.p8` (an unset id means
59
- there is no filename to look for). Both derived values are ABSOLUTE paths into the tree.
60
-
61
- **Keychain stays the default.** A project with no signing tree resolves to it exactly as
62
- before. Two further conditions keep a build on the Keychain rather than handing
63
- electron-builder a file it cannot use: no `CSC_KEY_PASSWORD` in the `.env` cascade, or a
64
- password that does not OPEN the `.p12` (verified with `openssl pkcs12 -legacy`, retried
65
- without the flag for LibreSSL — stock macOS openssl — which reads legacy containers but
66
- rejects the flag; a missing openssl is named as the reason rather than blamed on the
67
- password). Either case prints one line naming the file and the reason, and leaves the key
68
- unset rather than failing the build silently.
69
-
70
- The password itself never lives in config: `CSC_KEY_PASSWORD` comes from the `.env`
71
- cascade (the manager's certificates service writes it to the signing root's `.env`),
72
- and the config validator hard-fails secret-shaped config keys.
73
-
74
- ## What's universal vs per-brand
75
-
76
- **Universal (one set per Apple Developer team / Windows signing identity):**
77
- - Developer ID Application cert (`.p12`)
78
- - Developer ID Installer cert (`.p12`) — only if you ship `.pkg`
79
- - App Store Connect API key (`.p8`)
80
- - Apple Team ID, Apple ID, app-specific password
81
- - Windows EV USB token
82
-
83
- **Per-brand (per app):**
84
- - Provisioning profile (`.provisionprofile`) — only required if your app uses entitlements that Apple gates (push notifications, in-app purchase, network extensions, app groups, etc.)
85
- - Icons (`icon.icns`, `icon.ico`, `icon.png`)
86
-
87
- So for ITW Creative Works' multiple Electron apps signed by the same team, you generally drop the **same** `developer-id-application.p12` and `AuthKey_*.p8` into each app's `config/certs/`. The `.env` paths are identical across apps. Provisioning profiles, if needed, are unique per app.
88
-
89
- ## macOS setup
90
-
91
- ### 1. Apple Developer prerequisites
92
-
93
- You need an Apple Developer Program membership ($99/year). Inside it:
94
-
95
- 1. **Developer ID Application certificate** — generate at Apple Developer → Certificates → `+` → "Developer ID Application." Download the `.cer`, install in Keychain Access, then export from Keychain as `.p12` with a password.
96
- 2. **App Store Connect API key** (preferred over Apple ID + app-specific password) — generate at App Store Connect → Users and Access → Keys → `+`. Download the `.p8` once (Apple won't show it again). Note the Key ID and Issuer ID shown on the page.
97
- 3. **Team ID** — visible in your Apple Developer membership page.
98
-
99
- ### 2. Drop files into your project
100
-
101
- ```bash
102
- cp ~/Downloads/developer-id-application.p12 config/certs/
103
- cp ~/Downloads/AuthKey_XXXXXXXXXX.p8 config/certs/
104
- ```
105
-
106
- ### 3. Wire `.env`
107
-
108
- ```bash
109
- CSC_LINK=config/certs/developer-id-application.p12
110
- CSC_KEY_PASSWORD=<password you set on export>
111
-
112
- APPLE_API_KEY=config/certs/AuthKey_XXXXXXXXXX.p8
113
- APPLE_API_KEY_ID=XXXXXXXXXX # 10 chars from key filename
114
- APPLE_API_ISSUER=00000000-0000-... # UUID from App Store Connect
115
-
116
- APPLE_TEAM_ID=XXXXXXXXXX
117
- ```
118
-
119
- > **Empty values are safe.** The `.env` template ships these as `CSC_LINK=""` placeholders; @omega.js/desktop deletes empty/whitespace signing vars after loading `.env` (`src/utils/sanitize-signing-env.js`) so they read as *unset*. Without the guard, app-builder-lib only null-checks — `importCertificate('')` resolves `''` to the project root and the build dies with `"<projectRoot> not a file"`. With no `CSC_LINK` at all, electron-builder falls back to Keychain identity auto-discovery (so local `package:quick` builds still sign when an identity is installed).
120
-
121
- ### 4. Verify
122
-
123
- ```bash
124
- npx omega validate-certs
125
- ```
126
-
127
- This checks:
128
- - `CSC_LINK` exists and is readable
129
- - macOS Keychain has a Developer ID Application identity, and ONLY when `CSC_LINK` names no certificate: electron-builder imports a `CSC_LINK` `.p12` into its own temporary keychain, so the login keychain says nothing about that build
130
- - API key file exists and `APPLE_API_KEY_ID` + `APPLE_API_ISSUER` are set
131
- - Team ID matches the cert
132
-
133
- The electron-builder floor is 26.16.1 (the package's peer range, installed by ensure-target for any consumer below it): every earlier build hands the `.p12` password to `security set-key-partition-list`, which wants the temporary keychain's own password, and the macos-26 runner image rejects the mismatch (electron-userland/electron-builder#10066, [#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
134
-
135
- ### 5. Build + notarize
136
-
137
- ```bash
138
- npm run release # signs + notarizes + publishes
139
- ```
140
-
141
- @omega.js/desktop's built-in notarize is wired as electron-builder's `afterSign` hook (via `gulp/build-config`) and calls `@electron/notarize` with the API key creds. Consumers can extend it with an optional `hooks/notarize/post.js` for post-notarization work.
142
-
143
- ## Windows setup
144
-
145
- ### Self-hosted EV USB token (recommended for v1)
146
-
147
- Buy an EV code-signing cert from Sectigo, DigiCert, SSL.com, etc. Get a physical USB token (you can't transfer EV certs).
148
-
149
- 1. Plug the token into a Windows machine that's registered as a self-hosted GitHub Actions runner labeled `windows`, `self-hosted`, `ev-token`.
150
- 2. Install the token's middleware (SafeNet, etc.) on that machine.
151
- 3. Set `.env` (on the runner):
152
- ```
153
- # config: { signing: { windows: { strategy: 'self-hosted' } } }
154
- WIN_EV_TOKEN_PATH=<cert SHA1 thumbprint, or a path to a .pfx>
155
- WIN_CSC_KEY_PASSWORD=<token PIN>
156
- ```
157
- 4. The `windows-sign` job in `.github/workflows/build.yml` will route signing to that runner.
158
-
159
- Setting the box up is [`docs/runner.md`](runner.md) — the runner home, the Startup-folder auto-start, the commands, and the Session 0 / Session 1 rule that makes the whole thing work.
160
-
161
- **A workflow that targets this runner fires only on dispatch**, and the box enforces it ([#875](https://github.com/Omega-JS-Stack/omega/issues/875)). Whoever can start a job on the signing box can sign binaries with your token, so two guards stand between a stray trigger and `signtool`:
162
-
163
- - The generated `windows-sign` job runs only on `workflow_dispatch`, the one trigger the file carries (#923). A `push` or `pull_request` trigger added to `build.yml` later SKIPS the sign job instead of signing what it built.
164
- - The runner itself refuses the job before its first step. `npx omega runner install` writes `job-started.js` beside the runner and points every registration's `ACTIONS_RUNNER_HOOK_JOB_STARTED` at it; the hook exits nonzero, failing the job with one line, unless the event is a dispatch, `GITHUB_REPOSITORY` is on `allowed-repos.txt` and `GITHUB_ACTOR` is on `allowed-actors.txt`. Both lists live on the box and are yours to edit ([`docs/runner.md`](runner.md) § One-time setup).
165
-
166
- `npx omega sign-windows` is the strategy-aware command that drives `signtool` (or the cloud provider CLI).
167
-
168
- Every signed artifact is verified before the job moves on, and the log says so in the mac lane's shape: `Signed and verified: signtool verify /pa accepts <file>.` mirrors macOS's `Stapled and verified: ...`, so a deploy log proves the Windows signature was checked rather than only claimed ([#918](https://github.com/Omega-JS-Stack/omega/issues/918)). A failing verify aborts the job.
169
-
170
- ### Cloud signing (future migration)
171
-
172
- Once the framework's cloud strategy is finalized, set the provider in `config/omega.json5`:
173
- ```
174
- # config: { targets: { win: { signing: { strategy: 'cloud', cloud: { provider: 'azure' } } } } }
175
- # provider: 'azure' | 'sslcom' | 'digicert'
176
- # Provider-specific creds (secrets) live in .env Custom Values section.
177
- ```
178
-
179
- Provider modules will live in `src/lib/sign-providers/{azure,sslcom,digicert}.js` (Pass 3 work).
180
-
181
- ### Local (developer-machine) fallback
182
-
183
- If no signing runner is available, CI uploads the unsigned `.exe` and a developer manually signs locally:
184
- ```
185
- # config: { signing: { windows: { strategy: 'local' } } }
186
- ```
187
-
188
- ## Pushing secrets to GitHub Actions
189
-
190
- `omega deploy`'s precheck pushes the target's **composed env** to the repo's GitHub Actions secrets over the same shared `gh` transport the other three frameworks use (`gh secret set`, which seals each value with the repo's public key locally): values travel on **stdin**, never in argv, and are never logged. For env vars whose value is a path to a local file (`.p12`, `.p8`, etc.), the secret value pushed is the **base64-encoded file contents**: the workflow decodes back to a temp file at job start.
191
-
192
- There is no standalone `omega push-secrets` verb ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the push happens where it matters, and the manage walk's `repo` service runs the same function when you want a brand brought current without deploying.
193
-
194
- ```bash
195
- # Make sure the brand root's .env holds CSC_KEY_PASSWORD + the APPLE_* three, and `gh auth login` has run
196
- npx omega deploy
197
-
198
- # See exactly what it would publish, and publish nothing
199
- npx omega deploy --dry-run
200
- ```
201
-
202
- Behavior:
203
- - The source is `composeTargetEnv({ targetDir, target: 'desktop' })`: company `.env` ← brand `.env` ← target `.env`, the brand-side layers filtered by the env schema to the keys the desktop target reads. The brand root's `.env` is the one file you keep; a target `.env` is an optional per-key override.
204
- - Files only — the shell is never a source — and an empty value never claims a key, so unset keys simply don't publish.
205
- - Auto-detects "is this a path?" — relative or absolute paths ending in `.p12`/`.pem`/`.cer`/`.p8`/`.provisionprofile`/`.crt`/`.key`/`.json` that exist on disk (target root first, brand root second) get base64-encoded.
206
- - Publishes to the brand's SOURCE repo (`repo.org` + `brand.id` → `<brand.id>-omega`), and REFUSES unless the checkout's own remote IS that repo: a fork, a template clone or a vendored target never arms a stranger's Actions with your certificates.
207
- - No PAT: the credential is `gh`'s auth session. A missing or signed-out `gh` fails loudly with install/auth instructions; a CI run, an empty cascade or a remote-less checkout skips loudly, and a checkout whose `origin` is not the derived source repo refuses on the one drift line (`origin is <slug> but config derives <derived>: ...`, [#934](https://github.com/Omega-JS-Stack/omega/issues/934)).
208
- - `CSC_LINK` and `APPLE_API_KEY` are DERIVED, never typed ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the env load resolved them from the signing tree, so the publish sends those files' bytes. A typed value still wins, and a key the config REQUIRES that neither the tree nor the `.env` answers STOPS the deploy (the step is `fatal`), naming both tier paths it looked in and `omega manage --service certificates`, publishing nothing.
209
- - Logs key NAMES only; never a value.
210
-
211
- The corresponding decode step in CI looks like:
212
-
213
- ```yaml
214
- - name: Decode signing assets
215
- run: |
216
- mkdir -p config/certs
217
- echo "${{ secrets.CSC_LINK }}" | base64 --decode > config/certs/dev-id.p12
218
- echo "${{ secrets.APPLE_API_KEY }}" | base64 --decode > config/certs/AuthKey.p8
219
- env:
220
- CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
221
- APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
222
- APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
223
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
224
- - name: Build and release
225
- run: npm run release
226
- env:
227
- CSC_LINK: config/certs/dev-id.p12
228
- APPLE_API_KEY: config/certs/AuthKey.p8
229
- GH_TOKEN: ${{ secrets.GH_TOKEN }}
230
- ```
231
-
232
- This is wired up automatically in the `.github/workflows/build.yml` template (lands in Pass 3).
233
-
234
- ## CI integration
235
-
236
- GitHub Actions secrets you'll need (per repo):
237
-
238
- | Secret | Purpose |
239
- |---|---|
240
- | `GH_TOKEN` | Cross-repo publish, secret rotation |
241
- | `CSC_LINK` | base64-encoded `.p12` (workflow decodes to file) |
242
- | `CSC_KEY_PASSWORD` | `.p12` password |
243
- | `APPLE_API_KEY` | base64-encoded `.p8` |
244
- | `APPLE_API_KEY_ID` | Plain string |
245
- | `APPLE_API_ISSUER` | UUID |
246
- | `APPLE_TEAM_ID` | 10-char team ID |
247
- | `WIN_EV_TOKEN_PATH` | Windows EV cert reference — SHA1 thumbprint or `.pfx` path (the ONE name; the `WIN_CSC_LINK` alias is gone) |
248
- | `WIN_CSC_KEY_PASSWORD` | EV token PIN / `.pfx` password |
249
- | `SIGNTOOL_PATH` | Full path to `signtool.exe` on the signing runner |
250
-
251
- The workflow base64-decodes secrets into temp files inside `config/certs/` at job start, runs the build, and the runner's ephemeral filesystem cleans up afterward. Local `.env` files are never committed and never shipped.
252
-
253
- ## Troubleshooting
254
-
255
- ### "Could not find a certificate" on macOS
256
- - Run `security find-identity -v -p codesigning` — you should see "Developer ID Application: <your name> (TEAMID)".
257
- - If absent: re-import your `.p12` to Keychain Access and make sure the private key is included.
258
-
259
- ### Notarization timeout
260
- - Confirm `APPLE_API_KEY` is the absolute path (or relative to repo root) and the file is readable.
261
- - Confirm `APPLE_API_KEY_ID` matches the `XXXXXXXXXX` portion of the `AuthKey_XXXXXXXXXX.p8` filename.
262
- - Confirm `APPLE_API_ISSUER` is the issuer UUID from App Store Connect → Users and Access → Keys.
263
-
264
- ### "Hardened runtime requires entitlements"
265
- - @omega.js/desktop generates `dist/config/entitlements.mac.plist` from defaults + your overrides at build time.
266
- - Defaults cover Electron's needs (allow-jit, network client/server, library validation off, etc.).
267
- - To override or add: set the `entitlements.mac` block in `config/omega.json5`. Setting a key to `null` removes a default.
268
- ```json5
269
- entitlements: {
270
- mac: {
271
- 'com.apple.security.cs.allow-jit': false, // override default
272
- 'com.apple.security.device.camera': true, // add a key
273
- 'com.apple.security.network.server': null, // remove a default
274
- },
275
- }
276
- ```
277
-
278
- ### Windows: "SignTool Error: No certificates were found"
279
- - Token unplugged or middleware not running.
280
- - For cloud: verify `platforms.windows.signing.cloud.provider` in `config/omega.json5` matches the provider whose creds are in `.env`.
281
-
282
- ## What lives in `build/`
283
-
284
- `build/` in a consumer project holds **only** code-signing certificate files. Everything else (entitlements, icons, electron-builder config) is generated by @omega.js/desktop into `dist/config/` at build time.
285
-
286
- - **`config/certs/`** — `.p12`, `.p8`, `.cer`, `.mobileprovision` files. Per-developer / per-CI-runner. **Never commit** — `.gitignore` blocks them.
287
- - **Nothing else.** `entitlements.mac.plist` is generated. App icons + DMG background + tray icons resolve from `config/icons/` (consumer override) → @omega.js/desktop's bundled defaults. `electron-builder.yml` is generated.
288
-
289
- The only file you usually create yourself in `build/` is the cert files in `config/certs/`. See [`config/certs/README.md`](../src/defaults/config/certs/README.md) for the full inventory.
290
-
291
- ## Related docs
292
-
293
- - [`config/certs/README.md`](../src/defaults/config/certs/README.md) — cert file inventory
package/docs/startup.md DELETED
@@ -1,142 +0,0 @@
1
- # Startup
2
-
3
- Controls how the app launches: full normal launch vs completely hidden background app. Also handles open-at-login.
4
-
5
- ## Config
6
-
7
- ```jsonc
8
- "startup": {
9
- "mode": "normal", // user-launch behavior: 'normal' | 'hidden'
10
- "openAtLogin": {
11
- "enabled": true, // OS auto-launches the app at login
12
- "mode": "hidden" // login-launch behavior (defaults to 'hidden')
13
- }
14
- }
15
- ```
16
-
17
- `mode` is what happens when **the user launches the app directly** (clicks the dock icon / Start menu / etc.).
18
-
19
- `openAtLogin.mode` is what happens when **the OS auto-launches the app at login**. It applies *only* when the launch is detected as a login-launch (macOS: `wasOpenedAtLogin` flag; Windows/Linux: presence of the `--omega-launched-at-login` arg @omega.js/desktop passes when registering the login item).
20
-
21
- The default behavior — `mode: 'normal'` + `openAtLogin: { enabled: true, mode: 'hidden' }` — means: the app auto-starts at login but stays out of the way until the user opens it themselves. User-direct launches show the main window like any normal app.
22
-
23
- ## Modes
24
-
25
- ### `normal` (default)
26
-
27
- Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.initialize().then(...)`. In `normal` mode, `show` is `true` so the window appears immediately. Dock visible (macOS), taskbar entry (win/linux).
28
-
29
- ### `hidden`
30
-
31
- App launches **completely invisible**: no dock icon, no Cmd+Tab presence, no taskbar entry. Tray + notifications + networking + IPC + auto-update all still work — only the visible UI is suppressed. Production builds inject `LSUIElement: true` into `Info.plist` (via `gulp/build-config`) so macOS treats the process as a background agent from launch — **zero dock bounce**.
32
-
33
- The `main` window is still created (just with `show: false`) so it sits in @omega.js/desktop's window registry. When the user double-clicks the running app's icon, @omega.js/desktop's `app.on('activate')` (macOS) / `app.on('second-instance')` (win/linux) handler finds `main` in the registry and calls `windows.show('main')` — which auto-runs `app.dock.show()` so the dock icon appears alongside the window. Same thing happens when the consumer manually surfaces UI from a tray click / deep-link / IPC event via `windows.show('main')`. The reverse is automatic too: hiding the last visible window (including the hide-on-close X) re-runs `app.dock.hide()`, so a hidden-mode app never strands a dock icon after its UI is dismissed.
34
-
35
- Use this for: menubar apps, agent apps (clipboard managers, time trackers, system monitors), apps that should be invisible at boot but available on demand.
36
-
37
- **Dev caveat**: in dev (`npm start` / `electron .`), the packaged `Info.plist` isn't in effect, so macOS will still briefly bounce. Production builds (`npm run build` / `npm run release`) get the real zero-bounce behavior.
38
-
39
- > **Note:** the deprecated `'tray-only'` mode is no longer valid — its behavior was always identical to `'hidden'`, so they've been folded into one. Old `tray-only` configs fall back to `'normal'` per `getMode()` validation.
40
-
41
- ## Public API on `omega.startup`
42
-
43
- ```js
44
- omega.startup.getMode() // user-launch mode: 'normal' | 'hidden'
45
- omega.startup.isLaunchHidden() // true if THIS launch is hidden: combines
46
- // user-launch mode + login-launch detection.
47
- // Use this in main.js to gate windows.create().
48
- omega.startup.wasLaunchedAtLogin() // true if the OS auto-launched us at login
49
- omega.startup.applyEarly() // calls app.dock.hide() if needed (called by main.js boot)
50
-
51
- omega.startup.setOpenAtLogin(true) // back-compat boolean form
52
- omega.startup.setOpenAtLogin({ enabled: true, mode: 'hidden' }) // object form
53
- omega.startup.isOpenAtLogin() // read live OS state
54
- ```
55
-
56
- ## Typical main.js pattern
57
-
58
- ```js
59
- omega.initialize().then(() => {
60
- // Always create the main window. In hidden launches, `show: false` keeps it
61
- // invisible until something explicitly calls windows.show('main') — but it's
62
- // in the registry, so @omega.js/desktop's activate/second-instance handlers can find and
63
- // surface it when the user double-clicks the running app.
64
- omega.windows.create('main', {
65
- show: !omega.startup.isLaunchHidden(),
66
- });
67
- });
68
- ```
69
-
70
- Don't conditionally skip `create()` for hidden launches — without `main` in the registry, the dock-click / re-launch handlers have nothing to surface, and the user double-clicking the running app appears to do nothing.
71
-
72
- ## Boot order
73
-
74
- `startup.applyEarly()` is the **first** call in `omega.initialize()`: before `whenReady`, before any other lib. The goal: spend as little time as possible in the dock-bounce window.
75
-
76
- Sequence: applyEarly → before-quit hook → ipc → storage → theme → fontawesome → sentry → protocol → deep-link → auth-flow → app-state → context → usage → whenReady → updater → tray/menu/contextMenu → startup.initialize → auth → remote-config → remote-scripts → analytics → restart-manager → windows.initialize (full list: [boot-sequence.md](boot-sequence.md)). **@omega.js/desktop does not auto-create the main window**: your `main.js` does that inside the `.then()` callback after `initialize()` resolves.
77
-
78
- ## How zero-bounce works on macOS
79
-
80
- `LSUIElement` is an `Info.plist` key that tells macOS *before launch* "this app is a background agent — don't put it in the dock or app switcher." Setting it at runtime (`app.dock.hide()`) is too late — by the time JS runs, the dock-bounce animation has already started.
81
-
82
- @omega.js/desktop handles this at build time:
83
- 1. `gulp/build-config` reads `config/omega.json5`.
84
- 2. If `startup.mode === 'hidden'` **or** `startup.openAtLogin.mode === 'hidden'`, it injects `mac.extendInfo.LSUIElement: true` into the materialized `dist/electron-builder.yml`. (The openAtLogin case matters for `mode: 'normal'` apps that launch hidden at login — without the plist key, the login launch flashes the dock before `applyEarly()`'s `dock.hide()` can run.)
85
- 3. `electron-builder` packages the app with that key in the final `Info.plist`.
86
-
87
- With the key baked, a MANUAL launch also starts dockless — the dock icon appears the moment the main window surfaces (every surface path runs `_ensureDockVisible()` → `app.dock.show()`), so the visible difference is only that the bounce animation is replaced by the icon appearing when the window is ready.
88
-
89
- At runtime, when the consumer first calls `omega.windows.show()` (or the `windows.create()` call resolves with `show: true`), @omega.js/desktop calls `app.dock.show()` so the dock icon appears alongside the window. Reverses cleanly via `app.dock.hide()` if you want to go back to invisible.
90
-
91
- The injection is YAML-text-level (preserves comments, idempotent, merges with existing `extendInfo`). See `src/gulp/tasks/build-config.js`.
92
-
93
- ## Re-surfacing on user re-launch
94
-
95
- When the user double-clicks a running hidden-mode app (or clicks its dock icon on macOS), @omega.js/desktop transparently surfaces the main window — no consumer wiring needed. Mechanisms:
96
-
97
- - **macOS**: `window-manager.initialize()` registers `app.on('activate')` which calls `windows.show('main')` if `main` is in the registry.
98
- - **Windows / Linux**: `deep-link.initialize()` registers `app.on('second-instance')` which does the same. (The OS spawns a duplicate process, the single-instance lock kills it, and forwards its argv to the original instance.)
99
-
100
- Both handlers are no-ops if `main` isn't in the registry, so consumers who genuinely never want a window can omit `windows.create('main', ...)` entirely. Otherwise, with the canonical pattern (`windows.create('main', { show: !isLaunchHidden() })`), hidden-mode apps come back to life on a second click — like CleanMyMac, Rectangle, etc.
101
-
102
- ## Testing the login-launch path locally
103
-
104
- Pass `--omega-launched-at-login` as a command-line arg when launching the .app; @omega.js/desktop treats it identically to a real OS-driven login launch (`startup.wasLaunchedAtLogin()` returns `true`, with `via:argv-flag` in the boot summary log). Useful for testing hidden-mode behavior without configuring login items + rebooting.
105
-
106
- The easiest way is `mgr launch`, which auto-strips `ELECTRON_RUN_AS_NODE` and uses `open -n` under the hood:
107
-
108
- ```bash
109
- # Auto-discover the most recent `mgr package:quick` build:
110
- npx omega launch --args="--omega-launched-at-login"
111
-
112
- # Or pass an explicit path:
113
- npx omega launch /Applications/MyApp.app --args="--omega-launched-at-login"
114
- ```
115
-
116
- If you'd rather call `open` directly, remember to strip `ELECTRON_RUN_AS_NODE` first (the variable leaks into shells from common host processes like VS Code's Claude Code extension and silently breaks Electron):
117
-
118
- ```bash
119
- unset ELECTRON_RUN_AS_NODE
120
- open -n /path/to/MyApp.app --args --omega-launched-at-login
121
-
122
- # Windows / Linux — direct binary launch
123
- "/path/to/MyApp.exe" --omega-launched-at-login
124
- ```
125
-
126
- The boot summary log written by the `startup` lib distinguishes a real login launch (`via:macos-wasOpenedAtLogin`) from a flag-based simulation (`via:argv-flag`).
127
-
128
- ## Pairing with tray/window patterns
129
-
130
- Hidden / agent apps usually want:
131
-
132
- ```jsonc
133
- "startup": { "mode": "hidden" }
134
- ```
135
-
136
- And in `src/integrations/tray/index.js`:
137
-
138
- ```js
139
- tray.update('open', { click: () => omega.windows.show('main') });
140
- ```
141
-
142
- The window is created at boot but invisible. When the user clicks the tray's "Open" item (or double-clicks the app icon), `windows.show('main')` runs, @omega.js/desktop calls `app.dock.show()`, and the user sees both the dock icon and the window appear together.
package/docs/storage.md DELETED
@@ -1,59 +0,0 @@
1
- # Storage
2
-
3
- Persistent KV store accessible from both main and renderer. Backed by [`electron-store`](https://github.com/sindresorhus/electron-store) under the hood.
4
-
5
- ## File location
6
-
7
- ```
8
- macOS: ~/Library/Application Support/<productName>/omega-storage.json
9
- Windows: %APPDATA%/<productName>/omega-storage.json
10
- Linux: ~/.config/<productName>/omega-storage.json
11
- ```
12
-
13
- ## Main-process API (sync, direct disk-backed)
14
-
15
- ```js
16
- omega.storage.get(key, defaultValue) // any
17
- omega.storage.set(key, value)
18
- omega.storage.delete(key)
19
- omega.storage.has(key) // boolean
20
- omega.storage.clear()
21
- omega.storage.onChange(key, fn) // returns unsubscribe fn
22
- omega.storage.getPath() // absolute path to omega-storage.json
23
- ```
24
-
25
- ## Renderer-process API (async, proxied through preload + IPC)
26
-
27
- ```js
28
- await window.desktop.storage.get(key, defaultValue)
29
- await window.desktop.storage.set(key, value)
30
- await window.desktop.storage.delete(key)
31
- await window.desktop.storage.has(key)
32
- await window.desktop.storage.clear()
33
-
34
- const off = window.desktop.storage.onChange(key, ({ value, previous }) => { ... });
35
- // pass '*' as key to receive all changes
36
- off();
37
- ```
38
-
39
- ## Dot-notation paths
40
-
41
- Keys support dot-notation for nested objects natively:
42
-
43
- ```js
44
- omega.storage.set('window.main.bounds', { x: 10, y: 20, w: 800, h: 600 });
45
- omega.storage.get('window.main.bounds.w'); // → 800
46
- ```
47
-
48
- ## Change broadcasts
49
-
50
- Every `set` / `delete` / `clear` in main broadcasts an `desktop:storage:change` IPC event to all renderer windows. The renderer's `window.desktop.storage.onChange` filters by key locally.
51
-
52
- In main, `omega.storage.onChange(key, fn)` registers a callback fired with `(value, previous)`.
53
-
54
- ## Implementation notes
55
-
56
- - Storage initialization is async: `omega.initialize()` `await`s it before any other lib boots, since features like `app-state` and `windows` rely on it.
57
- - IPC handlers (`desktop:storage:get` etc.) are registered on the @omega.js/desktop `ipc` bus, not directly on `ipcMain`. See [ipc.md](ipc.md).
58
- - The store uses `name: 'omega-storage'` (filename `omega-storage.json`). Don't reuse this name in a separate `electron-store` instance.
59
- - `electron-store@11` is ESM-only. The bundler inlines it INTO `main.bundle.js` (the static-specifier `import()` in `lib/storage.js`) — consumers install NOTHING; packaged apps carry it inside the bundle with no runtime resolution. (It used to be a runtime import the bundler was told to ignore, which silently no-op'd storage in packaged consumers — @omega.js/desktop is a devDependency and never ships in the asar.)
@@ -1,101 +0,0 @@
1
- # Templating
2
-
3
- Light token-replacement engine for HTML pages. Uses `{{ var }}` syntax, dot-notation paths, brand/app values from config.
4
-
5
- ## How it works
6
-
7
- 1. Consumer authors `src/views/<name>/index.html` as the **body** of the page (no `<html>`, `<head>`, `<body>` tags).
8
- 2. @omega.js/desktop ships a default page template at `<em>/dist/config/page-template.html`. Consumers can override with their own at `<consumer>/config/page-template.html` if they want to.
9
- 3. At build time, `gulp/html`:
10
- - Reads each `src/views/<name>/index.html`
11
- - Templates its body with the page vars (so the body can use `{{ brand.name }}` etc.)
12
- - Injects the rendered body into the page template's `{{ content }}` slot
13
- - Writes the final HTML to `dist/views/<name>/index.html`
14
- 4. The page template auto-includes:
15
- - `assets/css/main.bundle.css` (compiled from `src/assets/scss/main.scss` by gulp/sass) — present on every page
16
- - `assets/js/components/<page.name>.bundle.js` (per-view renderer bundle) — only the JS for this page
17
-
18
- ## Page name
19
-
20
- `page.name` is derived from the view's path under `src/views/`:
21
- - `src/views/main/index.html` → `page.name = 'main'`
22
- - `src/views/settings/index.html` → `page.name = 'settings'`
23
- - `src/views/blog/post.html` → `page.name = 'blog/post'`
24
-
25
- This naming lines up with the renderer bundle's entry naming so the JS bundle path resolves correctly.
26
-
27
- ## Page template variables
28
-
29
- | Var | Source | Example |
30
- |---|---|---|
31
- | `{{ brand.id }}`, `{{ brand.name }}`, `{{ brand.url }}` | `config.brand.*` | `myapp` / `MyApp` |
32
- | `{{ app.productName }}`, `{{ app.appId }}`, `{{ app.copyright }}` | `config.app.*` | `MyApp` / `com.itwcw.myapp` |
33
- | `{{ page.name }}` | derived from view path | `main` / `settings` |
34
- | `{{ page.title }}` | `extras.title` (in gulp/html) or `app.productName` | `MyApp` |
35
- | `{{ theme.appearance }}` | `config.theme.appearance` (default `'system'`) | `light` / `dark` / `system` — runtime applier replaces with the resolved value ([themes.md](themes.md)) |
36
- | `{{ cacheBust }}` | build timestamp | `1777515223640` |
37
- | `{{ content }}` | rendered body (set by gulp/html) | `<main>...</main>` |
38
-
39
- ## Default page template
40
-
41
- ```html
42
- <!doctype html>
43
- <html data-bs-theme="{{ theme.appearance }}">
44
- <head>
45
- <meta charset="utf-8">
46
- <meta name="viewport" content="width=device-width,initial-scale=1">
47
- <title>{{ page.title }}</title>
48
- <link href="../../assets/css/main.bundle.css?cb={{ cacheBust }}" rel="stylesheet">
49
- </head>
50
- <body>
51
- {{ content }}
52
- <script src="../../assets/js/components/{{ page.name }}.bundle.js?cb={{ cacheBust }}"></script>
53
- </body>
54
- </html>
55
- ```
56
-
57
- ## Overriding the page template
58
-
59
- Drop your own `config/page-template.html` in your project root. @omega.js/desktop picks it up before falling back to its own default.
60
-
61
- ```html
62
- <!-- consumer/config/page-template.html -->
63
- <!doctype html>
64
- <html data-bs-theme="{{ theme.appearance }}" lang="en">
65
- <head>
66
- <meta charset="utf-8">
67
- <title>{{ page.title }} | {{ brand.name }}</title>
68
- <link href="../../assets/css/main.bundle.css?cb={{ cacheBust }}" rel="stylesheet">
69
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700" rel="stylesheet">
70
- </head>
71
- <body class="custom-shell">
72
- {{ content }}
73
- <script src="../../assets/js/components/{{ page.name }}.bundle.js?cb={{ cacheBust }}"></script>
74
- </body>
75
- </html>
76
- ```
77
-
78
- ## Runtime API
79
-
80
- `omega.templating` is also available at runtime if you need to template a string yourself (e.g. dynamic deep-link routes):
81
-
82
- ```js
83
- omega.templating.render('Hello {{ user.name }}', { user: { name: 'Ian' } });
84
- // → 'Hello Ian'
85
-
86
- omega.templating.render('Custom [name]', { name: 'X' }, { brackets: ['[', ']'] });
87
- // → 'Custom X'
88
- ```
89
-
90
- ## Future
91
-
92
- The current page template hardcodes the asset paths. A future pass adds:
93
- - Per-page CSS bundles (currently only one shared `main.bundle.css`)
94
- - Source map references in dev mode
95
- - Inline critical CSS for fast first paint
96
-
97
- For theme-related styling (Bootstrap, classy theme), see [docs/themes.md](themes.md) when that pass lands.
98
-
99
- ## Tests
100
-
101
- `src/test/suites/build/templating.test.js` — render, dot-notation, custom brackets, buildPageVars, renderPage end-to-end. 8 tests.