@dopame/acde-linux-x64 0.0.0-stage → 0.0.10

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 (165) hide show
  1. package/LICENSE +8 -0
  2. package/bin/acde +66 -0
  3. package/bin/acde.cmd +45 -0
  4. package/bin/acdebin-dbg +4 -0
  5. package/bin/app/brand/art.mjs +53 -0
  6. package/bin/app/brand/brand.css +39 -0
  7. package/bin/app/brand/lockup.mjs +17 -0
  8. package/bin/app/brand/mark.mjs +124 -0
  9. package/bin/app/dist/broker.js +1 -0
  10. package/bin/app/dist/web.js +1 -0
  11. package/bin/app/greet/index.html +177 -0
  12. package/bin/app/greet/package.json +16 -0
  13. package/bin/app/package.json +35 -0
  14. package/lib/acde/LICENSE +8 -0
  15. package/lib/acde/dist/chunk-0n69bv5q.js +1 -0
  16. package/lib/acde/dist/chunk-1cw92x19.js +1 -0
  17. package/lib/acde/dist/chunk-458e14zb.js +1 -0
  18. package/lib/acde/dist/chunk-88mxgp54.js +1 -0
  19. package/lib/acde/dist/chunk-8dv09ven.js +1 -0
  20. package/lib/acde/dist/chunk-8fxsj5h6.js +1 -0
  21. package/lib/acde/dist/chunk-8pf3kk8v.js +1 -0
  22. package/lib/acde/dist/chunk-8r65bz1e.js +1 -0
  23. package/lib/acde/dist/chunk-9hpnkmxn.js +1 -0
  24. package/lib/acde/dist/chunk-a7w41xr9.js +1 -0
  25. package/lib/acde/dist/chunk-db7qqt8a.js +1 -0
  26. package/lib/acde/dist/chunk-e3rzr5fj.js +1 -0
  27. package/lib/acde/dist/chunk-ehms6xjp.js +1 -0
  28. package/lib/acde/dist/chunk-ggykhab6.js +1 -0
  29. package/lib/acde/dist/chunk-gt70e8h2.js +1 -0
  30. package/lib/acde/dist/chunk-hcf0xd5c.js +1 -0
  31. package/lib/acde/dist/chunk-hdrvz9sa.js +1 -0
  32. package/lib/acde/dist/chunk-jjq2mn94.js +1 -0
  33. package/lib/acde/dist/chunk-k8f4m94e.js +7 -0
  34. package/lib/acde/dist/chunk-mtgy1bm0.js +1 -0
  35. package/lib/acde/dist/chunk-mtw6p7kv.js +1 -0
  36. package/lib/acde/dist/chunk-nwkq7cpp.js +1 -0
  37. package/lib/acde/dist/chunk-nyr54266.js +1 -0
  38. package/lib/acde/dist/chunk-p0h5kspx.js +1 -0
  39. package/lib/acde/dist/chunk-pdj3h86e.js +1 -0
  40. package/lib/acde/dist/chunk-q9dbzac5.js +1 -0
  41. package/lib/acde/dist/chunk-qrhbrjzk.js +1 -0
  42. package/lib/acde/dist/chunk-s8p0y5ap.js +1 -0
  43. package/lib/acde/dist/chunk-t0xqxe00.js +1 -0
  44. package/lib/acde/dist/chunk-tgn5c2mw.js +1 -0
  45. package/lib/acde/dist/chunk-wmj6zwvn.js +1 -0
  46. package/lib/acde/dist/chunk-wnk81fkc.js +1 -0
  47. package/lib/acde/dist/chunk-x208z38w.js +1 -0
  48. package/lib/acde/dist/chunk-xq76vbg6.js +1 -0
  49. package/lib/acde/dist/chunk-xr9gh5x2.js +1 -0
  50. package/lib/acde/dist/chunk-yytnhz6e.js +1 -0
  51. package/lib/acde/dist/chunk-z0ts25s1.js +1 -0
  52. package/lib/acde/dist/chunk-zdyw1ckt.js +1 -0
  53. package/lib/acde/dist/cli.js +1 -0
  54. package/lib/acde/dist/driver/index.js +1 -0
  55. package/lib/acde/dist/e2e/index.js +1 -0
  56. package/lib/acde/dist/types/configs.d.ts +1 -0
  57. package/lib/acde/dist/types/driver/acde.d.ts +106 -0
  58. package/lib/acde/dist/types/driver/clock.d.ts +32 -0
  59. package/lib/acde/dist/types/driver/expect.d.ts +60 -0
  60. package/lib/acde/dist/types/driver/format.d.ts +16 -0
  61. package/lib/acde/dist/types/driver/index.d.ts +13 -0
  62. package/lib/acde/dist/types/driver/input.d.ts +75 -0
  63. package/lib/acde/dist/types/driver/locator.d.ts +151 -0
  64. package/lib/acde/dist/types/driver/logs.d.ts +26 -0
  65. package/lib/acde/dist/types/driver/png.d.ts +31 -0
  66. package/lib/acde/dist/types/driver/poll.d.ts +4 -0
  67. package/lib/acde/dist/types/driver/screenshot.d.ts +37 -0
  68. package/lib/acde/dist/types/driver/trace.d.ts +26 -0
  69. package/lib/acde/dist/types/driver/types.d.ts +213 -0
  70. package/lib/acde/dist/types/driver/video.d.ts +14 -0
  71. package/lib/acde/dist/types/driver/window.d.ts +77 -0
  72. package/lib/acde/dist/types/e2e/index.d.ts +78 -0
  73. package/lib/acde/dist/types/e2e/settings.d.ts +39 -0
  74. package/lib/acde/dist/types/ipc.d.ts +24 -0
  75. package/lib/acde/dist/types/paths.d.ts +5 -0
  76. package/lib/acde/dist/types/pm.d.ts +20 -0
  77. package/lib/acde/dist/types/runtime.d.ts +49 -0
  78. package/lib/acde/dist/types/sessions.d.ts +80 -0
  79. package/lib/acde/dist/types/sockets.d.ts +4 -0
  80. package/lib/acde/launcher.d.mts +25 -0
  81. package/lib/acde/launcher.mjs +1 -0
  82. package/lib/acde/package.json +31 -0
  83. package/lib/acde/skills/acde/SKILL.md +114 -0
  84. package/lib/acde/skills/acde/references/guide/agents.md +50 -0
  85. package/lib/acde/skills/acde/references/guide/assets.md +40 -0
  86. package/lib/acde/skills/acde/references/guide/backend.md +94 -0
  87. package/lib/acde/skills/acde/references/guide/capabilities.md +157 -0
  88. package/lib/acde/skills/acde/references/guide/config.md +73 -0
  89. package/lib/acde/skills/acde/references/guide/desktop.md +165 -0
  90. package/lib/acde/skills/acde/references/guide/dev.md +66 -0
  91. package/lib/acde/skills/acde/references/guide/doctor.md +144 -0
  92. package/lib/acde/skills/acde/references/guide/files.md +181 -0
  93. package/lib/acde/skills/acde/references/guide/frameworks.md +74 -0
  94. package/lib/acde/skills/acde/references/guide/install.md +34 -0
  95. package/lib/acde/skills/acde/references/guide/mcp.md +64 -0
  96. package/lib/acde/skills/acde/references/guide/open.md +107 -0
  97. package/lib/acde/skills/acde/references/guide/services.md +147 -0
  98. package/lib/acde/skills/acde/references/guide/testing.md +378 -0
  99. package/lib/acde/skills/acde/references/guide/tools.md +291 -0
  100. package/lib/acde/skills/acde/references/guide/window.md +167 -0
  101. package/lib/acde/skills/acde/references/reference/cli.md +221 -0
  102. package/lib/acde/skills/acde/references/reference/config.md +51 -0
  103. package/lib/acde/skills/acde/references/reference/env.md +44 -0
  104. package/lib/acde/skills/acde/references/reference/manifest.md +92 -0
  105. package/lib/acde/skills/acde/references/reference/modules.md +586 -0
  106. package/lib/acde/skills/acde/references/reference/platforms.md +23 -0
  107. package/lib/acde/skills/acde/references/reference/service.md +47 -0
  108. package/lib/acde/skills/acde/references/reference/testing.md +265 -0
  109. package/lib/acde/skills/acde/references/reference/web.md +94 -0
  110. package/lib/acde/skills/acde/references/start/first-app.md +87 -0
  111. package/lib/acde/skills/acde/references/start/install.md +232 -0
  112. package/lib/acde/skills/acde/references/start/project.md +77 -0
  113. package/lib/acde/templates/app/acde.config.jsonc +27 -0
  114. package/lib/acde/templates/app/gitignore +2 -0
  115. package/lib/acde/templates/app/index.html +22 -0
  116. package/lib/acde/templates/app/package.json +5 -0
  117. package/lib/acde/templates/app/src/i18n.js +26 -0
  118. package/lib/acde/templates/app/src/locales/en.js +4 -0
  119. package/lib/acde/templates/app/src/locales/zh.js +4 -0
  120. package/lib/acde/templates/app/src/main.js +5 -0
  121. package/lib/acde/templates/backend/server.ts +7 -0
  122. package/lib/acde/templates/backend/src/main.js +6 -0
  123. package/lib/acde/templates/desktop/acde.config.jsonc +17 -0
  124. package/lib/acde/templates/desktop/gitignore +2 -0
  125. package/lib/acde/templates/desktop/index.html +19 -0
  126. package/lib/acde/templates/desktop/package.json +5 -0
  127. package/lib/acde/templates/desktop/src/desktop.js +16 -0
  128. package/lib/acde/templates/desktop/src/i18n.js +26 -0
  129. package/lib/acde/templates/desktop/src/locales/en.js +4 -0
  130. package/lib/acde/templates/desktop/src/locales/zh.js +4 -0
  131. package/lib/acde/templates/framework/acde.config.jsonc +26 -0
  132. package/lib/acde-api/LICENSE +5 -0
  133. package/lib/acde-api/config.schema.json +563 -0
  134. package/lib/acde-api/package.json +51 -0
  135. package/lib/acde-api/src/app.d.ts +36 -0
  136. package/lib/acde-api/src/frame.d.ts +15 -0
  137. package/lib/acde-api/src/frame.js +1 -0
  138. package/lib/acde-api/src/index.d.ts +11 -0
  139. package/lib/acde-api/src/index.js +1 -0
  140. package/lib/acde-api/src/modules/app-modules.d.ts +81 -0
  141. package/lib/acde-api/src/modules/modules.d.ts +113 -0
  142. package/lib/acde-api/src/modules/shell-modules.d.ts +32 -0
  143. package/lib/acde-api/src/runtime/level.d.ts +1 -0
  144. package/lib/acde-api/src/runtime/level.js +1 -0
  145. package/lib/acde-api/src/runtime/load.d.ts +8 -0
  146. package/lib/acde-api/src/runtime/load.js +1 -0
  147. package/lib/acde-api/src/runtime/places.d.ts +3 -0
  148. package/lib/acde-api/src/runtime/places.js +1 -0
  149. package/lib/acde-api/src/runtime/platforms.d.ts +5 -0
  150. package/lib/acde-api/src/runtime/platforms.js +1 -0
  151. package/lib/acde-api/src/shell.d.ts +14 -0
  152. package/lib/acde-api/src/types/capabilities.d.ts +34 -0
  153. package/lib/acde-api/src/types/config.d.ts +55 -0
  154. package/lib/acde-api/src/types/events.d.ts +151 -0
  155. package/lib/acde-api/src/types/manifest.d.ts +83 -0
  156. package/lib/acde-api/src/types/office.d.ts +118 -0
  157. package/lib/acde-api/src/types/page.d.ts +1090 -0
  158. package/lib/acde-api/src/types/permissions.d.ts +50 -0
  159. package/lib/acde-api/src/types/service.d.ts +35 -0
  160. package/lib/acde-api/src/validate.d.ts +64 -0
  161. package/lib/acde-api/src/validate.js +1 -0
  162. package/lib/acde-api/src/vite.d.ts +3 -0
  163. package/lib/acde-api/src/vite.js +9 -0
  164. package/package.json +11 -4
  165. package/README.md +0 -3
@@ -0,0 +1,586 @@
1
+ # acde: modules
2
+
3
+ Every module has both named exports and a default export:
4
+
5
+ ```js
6
+ import { fs } from 'acde:fs'
7
+ import fs from 'acde:fs'
8
+ ```
9
+
10
+ Types are in `@dopame/acde-api`: `@dopame/acde-api/app` declares the modules an app can import, `@dopame/acde-api/shell` adds the shell modules, and the package root declares all of them. For how capabilities are requested and approved, see [Capabilities](../guide/capabilities.md).
11
+
12
+ Who can import what. "App" is an app of its own package; a "system app" ships in a desktop's package (the `apps` of a desktop config). A name in a cell is the capability the app must declare:
13
+
14
+ | Module | App | System app | Desktop page |
15
+ | --- | --- | --- | --- |
16
+ | `acde:window` | ✓ | ✓ | |
17
+ | `acde:capabilities` | ✓ | ✓ | ✓ |
18
+ | `acde:apps` | ✓ | ✓ | ✓ |
19
+ | `acde:open` | ✓ (opening an app needs `open`) | ✓ (same) | ✓ |
20
+ | `acde:fs` | `fs` | `fs` | ✓ |
21
+ | `acde:terminal` | `terminal` | `terminal` | ✓ |
22
+ | `acde:network` | `network` | `network` | ✓ |
23
+ | `acde:office` | `office` | `office` | ✓ |
24
+ | `acde:openPath` `acde:handlers` | `openFiles` | `openFiles` | ✓ |
25
+ | `acde:launch` | `launch` or `slots` | `launch` or `slots` | ✓ |
26
+ | `acde:windows` `acde:packages` `acde:session` `acde:desktops` `acde:inspect` `acde:desktop` `acde:setDefault` `acde:permissions` (shell modules) | | The module's name: `"windows": true` | ✓ |
27
+ | `acde:<service>` | The service's name in `services` | The same | ✓ |
28
+
29
+ A module the platform lacks is missing for everyone there (see [Platform support](platforms.md)). An import that is not allowed fails with a `TypeError` whose message names the missing declaration, such as `acde:terminal needs "terminal" in the capabilities of Notes`. Desktop services cannot import `acde:` modules; a service that the desktop grants shell modules gets them from `ctx.import()`; see [Service API](service.md).
30
+
31
+ Events are subscribed with `on(type, handler)`, which returns an unsubscribe function. `on()` throws a `TypeError` for an event type the module does not have, such as `acde:window has no nosuch event (activate, deactivate, …)`. Two modules take a callback instead: `acde:terminal` (`subscribe`) and `acde:network` (`watch`).
32
+
33
+ ## acde:window
34
+
35
+ ```ts
36
+ interface AcdeWindow {
37
+ readonly id: number
38
+ readonly state: WindowState
39
+ readonly output: WindowOutput
40
+ readonly hostControls: boolean
41
+ readonly minimizable: boolean
42
+ readonly host: WindowHost
43
+ mount(app: WindowApp): void
44
+ setTitle(title: string): void
45
+ setMenus(menus: readonly AppMenu[]): void
46
+ close(): Promise<boolean>
47
+ minimize(): Promise<void>
48
+ requestAttention(): Promise<void>
49
+ requestState(state: WindowState): void
50
+ requestMode(mode: WindowMode): void
51
+ setGrabHint(rect: WindowRect, edges?: readonly ('top' | 'bottom' | 'left' | 'right')[]): void
52
+ setBackdrop(rect: WindowRect | null, effect?: { radius?: number; blur?: number; saturate?: number; brightness?: number }): void
53
+ requestActivate(): void
54
+ requestGrab(kind: 'pointer' | 'keyboard'): void
55
+ releaseGrab(kind: 'pointer' | 'keyboard'): void
56
+ shortcut(combo: string): Promise<void>
57
+ unshortcut(combo: string): Promise<void>
58
+ on(name: 'beforeclose', handler: () => boolean | void | Promise<boolean | void>): () => boolean
59
+ on<R extends AppRequest = AppRequest>(name: 'request', handler: (request: SystemRequest | R) => void): () => boolean
60
+ on(name: 'activate' | 'deactivate', handler: () => void): () => boolean
61
+ on(name: 'state', handler: (event: { readonly state: WindowState; readonly previous: WindowState }) => void): () => boolean
62
+ on(name: 'output', handler: (output: WindowOutput) => void): () => boolean
63
+ on(name: 'hostcontrols', handler: (hostControls: boolean) => void): () => boolean
64
+ on(name: 'minimizable', handler: (minimizable: boolean) => void): () => boolean
65
+ on(name: 'shortcut', handler: (event: ShortcutEvent) => void): () => boolean
66
+ on(name: 'grab', handler: (event: { readonly kind: 'pointer' | 'keyboard'; readonly granted: boolean }) => void): () => boolean
67
+ on(name: 'grablost', handler: (event: { readonly kind: 'lost'; readonly reason: 'esc' | 'other_window' | 'focus_lost' | 'denied' }) => void): () => boolean
68
+ on(name: 'suspend' | 'resume', handler: () => void): () => boolean
69
+ }
70
+
71
+ type WindowState = 'normal' | 'minimized' | 'filled' | 'fullscreen'
72
+ type WindowMode = 'free' | 'fixed' | 'pinned'
73
+
74
+ interface WindowRect { x: number; y: number; width: number; height: number }
75
+ interface WindowOutput { width: number; height: number; scale: number; workArea: WindowRect }
76
+
77
+ interface ShortcutEvent { type: 'shortcut'; window: number; key: string; title: 'session' | 'desktop' | 'app' }
78
+
79
+ interface AppMenu {
80
+ id: string
81
+ label: string
82
+ items: ({ type: 'separator' } | AppMenuItem)[]
83
+ }
84
+ interface AppMenuItem {
85
+ id: string
86
+ label: string
87
+ shortcut?: string
88
+ checked?: boolean
89
+ disabled?: boolean
90
+ onSelect?(): void | Promise<void>
91
+ }
92
+
93
+ interface WindowApp {
94
+ mount(element: Element, host: WindowHost): () => void
95
+ }
96
+ interface WindowHost {
97
+ setTitle(title: string): void
98
+ setMenus(menus: readonly AppMenu[]): void
99
+ }
100
+ ```
101
+
102
+ - Full screen is a state: `requestState('fullscreen')`. `requestMode('fullscreen')` throws a `TypeError`, and so does an unknown state. `requestState` has no result; follow the `state` event. See [Window state](../guide/window.md#window-state).
103
+ - `close()` closes the window without asking `beforeclose`.
104
+ - `minimize()` rejects with `status: 409`, `code: 'unavailable'` for a window that cannot be minimized (`minimizable` is `false`): `window 1048576 cannot be minimized: it fills the host window, which only the host window's own controls minimize here` for the app's window in a session that runs a single app, on macOS and headless. `requestState('minimized')` changes nothing there.
105
+ - `suspend` and `resume` are declared, but the runtime does not send them yet.
106
+ - Coordinates are logical pixels of the page (the content area). Shortcut combos are written like `ctrl+alt+t`: keys are xkb keysym names, the key names X11 and Wayland use (`t`, `return`, `escape`, `f5`, `space`; case doesn't matter), and Shift is part of the combo.
107
+
108
+ ## acde:capabilities
109
+
110
+ ```ts
111
+ interface AcdeCapabilities {
112
+ readonly level: number
113
+ readonly platform: 'linux' | 'win32' | 'darwin'
114
+ readonly mode: 'session' | 'desktop' | 'standalone'
115
+ readonly window: { readonly backdrop: 'full' | 'approximated' | 'unsupported' }
116
+ get(name: string): CapabilityStatus
117
+ list(): CapabilityStatus[]
118
+ request(name: string): Promise<CapabilityStatus>
119
+ }
120
+
121
+ interface CapabilityStatus {
122
+ name: string
123
+ kind: 'system' | 'service'
124
+ state: 'available' | 'not-asked' | 'asking' | 'not-declared' | 'unsupported' | 'service-not-running' | 'shell-only' | 'denied'
125
+ reason: string
126
+ optional: boolean
127
+ }
128
+ ```
129
+
130
+ `level` is the contract level (1). `request(name)` asks the user for a capability that needs approval and resolves with its status after the answer; for anything else it resolves at once. A capability that needs approval is `available` only once the user allowed it: `not-asked` before the prompt, `asking` while it waits, `denied` after a denial or after the approval was taken back. A call that the approval stops fails with `status: 403`, the `capability`, and one of the codes `not_asked`, `asking`, `denied`. Names are capability names, `fs.<place>` for a shared place (`fs.downloads`), service names, and `notifications` (the standard `Notification` API: `available`, or `unsupported` where this way of running has no notifications). See [Capabilities](../guide/capabilities.md#acdecapabilities).
131
+
132
+ ## acde:fs
133
+
134
+ ```ts
135
+ interface AcdeFs {
136
+ roots(): Promise<FinderRoot[]>
137
+ list(root: string, path?: string, hidden?: boolean): Promise<{ root: FinderRoot; path: string; entries: FinderEntry[]; truncated: boolean; skipped?: number }>
138
+ stat(root: string, path: string): Promise<FinderEntry & { path: string }>
139
+ readText(root: string, path: string): Promise<{ text: string; modifiedMs: number; size: number; version: string }>
140
+ readBytes(root: string, path: string): Promise<{ data: Uint8Array; modifiedMs: number; size: number; version: string }>
141
+ readRange(root: string, path: string, offset: number, length: number, options?: { ifVersion?: string }): Promise<{
142
+ data: Uint8Array; offset: number; size: number; modifiedMs: number; version: string
143
+ }>
144
+ writeFile(root: string, path: string, data: string | Uint8Array, options?: {
145
+ create?: 'always' | 'new' | 'never'
146
+ ifVersion?: string
147
+ ifModifiedMs?: number
148
+ mkdirs?: boolean
149
+ }): Promise<FinderEntry>
150
+ openWrite(root: string, path: string, options?: {
151
+ keepExistingData?: boolean
152
+ create?: 'always' | 'new' | 'never'
153
+ ifVersion?: string
154
+ mkdirs?: boolean
155
+ }): Promise<{ id: string; size: number }>
156
+ writeChunk(id: string, offset: number, data: string | Uint8Array): Promise<void>
157
+ truncateWrite(id: string, size: number): Promise<void>
158
+ commitWrite(id: string): Promise<FinderEntry>
159
+ abortWrite(id: string): Promise<void>
160
+ mkdir(root: string, path: string, options?: { recursive?: boolean }): Promise<FinderEntry>
161
+ rename(root: string, from: string, to: string): Promise<FinderEntry>
162
+ delete(root: string, path: string): Promise<{ status: 'deleted'; path: string }>
163
+ copy(fromRoot: string, fromPath: string, toRoot: string, toDir: string): Promise<FinderEntry & { path: string }>
164
+ preview(root: string, path: string): Promise<FinderPreview>
165
+ download(root: string, path: string): Promise<{ name: string; contentType: string; data: Uint8Array }>
166
+ revoke(root: string): Promise<void>
167
+ locate(path: string): Promise<{ root: string; path: string }>
168
+ openPath(root: string, path: string): Promise<{ root: string; path: string; file: string }>
169
+ }
170
+
171
+ interface FinderRoot {
172
+ id: string
173
+ label: string
174
+ source: 'private' | 'place' | 'granted' | 'desktop' | 'downloads' | 'default'
175
+ place?: 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos'
176
+ path?: string
177
+ mode: 'writable' | 'read_only' | 'unavailable'
178
+ reason: string | null
179
+ exists: boolean
180
+ }
181
+
182
+ interface FinderEntry {
183
+ name: string
184
+ kind: 'directory' | 'file' | 'symlink' | 'other'
185
+ size: number
186
+ modifiedMs: number
187
+ target?: string
188
+ version?: string
189
+ inPlace?: true
190
+ }
191
+
192
+ interface FinderPreview {
193
+ path: string
194
+ name: string
195
+ size: number
196
+ modifiedMs: number
197
+ kind: 'text' | 'image' | 'binary' | 'too_large'
198
+ content?: string
199
+ truncated?: boolean
200
+ }
201
+ ```
202
+
203
+ - `root` is the `id` of an item of `roots()`: `private`, a shared place's name, or `folder-…` for a picked folder; never a path. An app's roots have `source` `private`, `place` (with `place`), or `granted`, and no `path`.
204
+ - `path` is there only for system apps that name shared places, and for the desktop page. `locate` and `openPath` work only for them; in other apps they reject with `EACCES`.
205
+ - The desktop page's roots are folders by path (`source` `desktop`, `downloads`, `default`, or `granted`), and their `id` is the path.
206
+ - `list` leaves hidden entries out unless `hidden` is `true`; `skipped` counts entries whose names are not valid Unicode.
207
+ - `copy` copies a file or a folder from one root into the folder `toDir` of another; a taken name gets a number (`todo 2.md`), and `path` is where the copy is in `toRoot`.
208
+ - `revoke` gives back a folder the user picked.
209
+ - `version` is an opaque string for one state of a file; pass it as `ifVersion` and a write (or a `readRange`) of a file that changed since fails with `ECONFLICT`. `inPlace` marks a write that could not go through a temporary file. See [Safe writes](../guide/files.md#safe-writes).
210
+ - `openWrite` starts a write in parts: `writeChunk` at any offset, `truncateWrite`, then `commitWrite` puts the file in place (or `abortWrite`). A write left alone for 10 minutes is given up. See [Large files](../guide/files.md#large-files).
211
+ - Limits: `readText` / `readBytes` / `writeFile` 64 MiB; `readRange` / `writeChunk` 16 MiB a call; `list` 5000 entries (`truncated`); `preview` text 256 KiB.
212
+ - `download` answers the type the system knows for the file's name as `contentType`: `text/markdown` for `note.md`, `image/png` for `shot.png`, and `application/octet-stream` for a name it knows no type for (`data.xyz`). The types come from the system, as described under [Files and links](../guide/open.md#files-and-links).
213
+ - Errors have `code` and `status`; see [Files and folders](../guide/files.md#errors).
214
+
215
+ ## acde:open
216
+
217
+ ```ts
218
+ interface AcdeOpen {
219
+ (app: string, request?: null, options?: OpenWindowOptions): Promise<{ window: number }>
220
+ <R extends AppRequest>(app: string, request: R, options?: OpenWindowOptions): Promise<{ window: number }>
221
+ (app: string, request: SystemRequest): Promise<Opened>
222
+ (request: SystemRequest): Promise<Opened>
223
+ }
224
+
225
+ type AppRequest<K extends string = string, Fields extends object = Record<never, never>> = { readonly kind: K } & { readonly [P in keyof Fields]: Fields[P] }
226
+
227
+ type SystemRequest =
228
+ | { kind: 'open-file'; handle?: FileSystemFileHandle | FileSystemDirectoryHandle; url?: string; root?: string; path?: string; file?: string | null }
229
+ | { kind: 'open-link'; url: string }
230
+
231
+ interface OpenWindowOptions { parent?: number; modal?: boolean }
232
+ interface Opened { kind: 'acde' | 'native'; id: string; window?: number }
233
+ ```
234
+
235
+ - `open(app)` and `open(app, request)` need the `open` capability to allow `app`. A request to an app must have a `kind` that the target lists in its `requests` (400 `untyped_request`, 409 `not_accepted`).
236
+ - `open(request)` with a system request and no app needs `openFiles`: the system picks the app. With an app, the target must declare the file's type (`mimeTypes`) or the link's scheme (`schemes`).
237
+ - A receiving app gets a file as `{ kind: 'open-file', handle }`; a system app that names shared places also gets `url`. Send a file with `handle` (one this page got from a picker, a drop, or a request) or, where the caller knows paths, with `url`.
238
+ - A receiver types its own requests with `AppRequest<K, Fields>`: `type ShowImage = AppRequest<'show-image', { image: string }>`, then `self.on<ShowImage>('request', …)` narrows by `kind`. `SystemRequest` and `AppRequest` are also in `acde:window`'s `request` event (above).
239
+
240
+ See [Opening apps](../guide/open.md).
241
+
242
+ ## acde:apps
243
+
244
+ ```ts
245
+ interface AcdeAppList {
246
+ list(): Promise<DesktopApp[]>
247
+ open: AcdeOpen
248
+ on(type: 'changed', handler: (event: { type: 'appschanged'; apps: readonly DesktopApp[] }) => void): () => boolean
249
+ }
250
+
251
+ interface DesktopApp { id: string; label: string; icon: string; script: string }
252
+ ```
253
+
254
+ `list()` gives the apps that can run here; `open` is the function of `acde:open`. `changed` fires with the new list when it changes. In 0.0.9 it also fires when the user denies a camera or microphone that an app requires, or takes that approval back, because the app leaves the list; after 0.0.9, a denial keeps the app in the list. Installing packages and native programs are in [`acde:packages`](#acdepackages).
255
+
256
+ ## acde:openPath / acde:handlers / acde:setDefault
257
+
258
+ ```ts
259
+ function openPath(path: string, options?: { app?: string; select?: boolean }): Promise<{ kind: 'acde' | 'native'; id: string; window?: number }>
260
+ function handlers(path: string): Promise<{ mime: string; association: string | null; handlers: OpenHandler[] }>
261
+ function setDefault(association: string, handler: string): Promise<string>
262
+
263
+ interface OpenHandler {
264
+ kind: 'acde' | 'native'
265
+ id: string
266
+ label: string
267
+ icon: string
268
+ default: boolean
269
+ system?: boolean
270
+ }
271
+ ```
272
+
273
+ `path` can be a path or a URI. `openPath` and `handlers` need `openFiles`; for an app the file must be inside one of its roots (404 `outside_roots`). `setDefault` is a shell module. `association` is the key a choice is remembered under: the MIME type, or on Windows the file's extension (`.md`); `mime` is `''` when the system knows no type for the file. `system` marks the program that is the system's own default (Windows).
274
+
275
+ ## acde:launch
276
+
277
+ Needs `launch` (for `start`) or `slots` (for `embed`).
278
+
279
+ ```ts
280
+ interface AcdeLaunch {
281
+ start(argv: readonly string[], options?: LaunchOptions): Promise<number>
282
+ embed(argv: readonly string[], options?: LaunchOptions): Promise<number>
283
+ close(window: number): Promise<boolean>
284
+ list(): NativeWindow[]
285
+ on<K extends WindowsEventType>(type: K, handler: (event: WindowsEventMap[K]) => void): () => boolean
286
+ }
287
+
288
+ interface LaunchOptions { cwd?: string; env?: Record<string, string> }
289
+ ```
290
+
291
+ `start` runs a native program and `embed` runs one embedded in the app's own window (Dopame OS only). `close` closes a window the app started; any other window is rejected with 403 `forbidden`. The events are those of [`acde:windows`](#acdewindows).
292
+
293
+ ## acde:terminal
294
+
295
+ Needs `terminal`.
296
+
297
+ ```ts
298
+ interface AcdeTerminals {
299
+ open(cols: number, rows: number): Promise<{ id: string }>
300
+ subscribe(id: string, callback: (events: TerminalEvent[]) => void): Promise<void>
301
+ input(id: string, data: string): Promise<{ ok: true }>
302
+ resize(id: string, cols: number, rows: number): Promise<{ cols: number; rows: number }>
303
+ close(id: string): Promise<{ closed: string }>
304
+ }
305
+
306
+ type TerminalEvent =
307
+ | { type: 'output'; data: string }
308
+ | { type: 'cwd'; path: string }
309
+ | { type: 'exit'; code: number | null; signal: number | null }
310
+ ```
311
+
312
+ ## acde:network
313
+
314
+ Linux only (Dopame OS, or any Linux with NetworkManager). Needs `network`.
315
+
316
+ ```ts
317
+ interface AcdeNetwork {
318
+ state(): Promise<NetworkState>
319
+ networks(scan?: boolean): Promise<WifiNetwork[]>
320
+ connect(ssid: string, password?: string): Promise<void>
321
+ activate(iface: string): Promise<void>
322
+ disconnect(iface: string): Promise<void>
323
+ setWifiEnabled(on: boolean): Promise<void>
324
+ watch(callback: (state: NetworkState) => void): void
325
+ }
326
+
327
+ interface NetworkState {
328
+ wifiEnabled: boolean
329
+ connectivity: 'full' | 'limited' | 'portal' | 'none' | 'unknown'
330
+ primary: 'wifi' | 'ethernet' | string | null
331
+ devices: NetworkDevice[]
332
+ }
333
+
334
+ interface NetworkDevice {
335
+ iface: string
336
+ type: 'wifi' | 'ethernet'
337
+ state: 'connected' | 'connecting' | 'disconnecting' | 'disconnected' | 'unavailable' | 'unmanaged' | 'failed'
338
+ connection: string | null
339
+ ip4: string | null
340
+ ssid?: string
341
+ strength?: number
342
+ carrier?: boolean
343
+ speed?: number
344
+ }
345
+
346
+ interface WifiNetwork {
347
+ ssid: string
348
+ strength: number
349
+ secure: boolean
350
+ band: '2.4GHz' | '5GHz'
351
+ active: boolean
352
+ saved: boolean
353
+ }
354
+ ```
355
+
356
+ ## acde:session
357
+
358
+ Shell module: only the desktop page and the system apps of its package (with `"session": true` in their capabilities) can import it; an ordinary app cannot, even if it declares `session`.
359
+
360
+ ```ts
361
+ interface AcdeSession {
362
+ own(): boolean
363
+ logout(): Promise<void>
364
+ reboot(): Promise<void>
365
+ poweroff(): Promise<void>
366
+ setLanguage(tag: string): Promise<void>
367
+ }
368
+ ```
369
+
370
+ `own()`: whether this is a real Dopame OS session (`false` in the host window of `acde dev`). After `setLanguage`, pages receive `languagechange`.
371
+
372
+ ## acde:desktops
373
+
374
+ Shell module.
375
+
376
+ ```ts
377
+ interface AcdeDesktops {
378
+ list(): Promise<{ desktops: DesktopInfo[]; current: string }>
379
+ choose(id: string, remember?: boolean): Promise<void>
380
+ }
381
+
382
+ interface DesktopInfo {
383
+ kind: 'acde' | 'session'
384
+ id: string
385
+ name: string
386
+ description: string | null
387
+ package: string | null
388
+ }
389
+ ```
390
+
391
+ `kind: 'session'` is another Wayland session on the system (GNOME, KDE, ...).
392
+
393
+ ## acde:packages
394
+
395
+ Shell module.
396
+
397
+ ```ts
398
+ interface AcdePackages {
399
+ refresh(): Promise<DesktopApp[]>
400
+ install(dir: string): Promise<{ id: string; dir: string; apps: DesktopApp[] }>
401
+ natives(): Promise<NativeApp[]>
402
+ runNative(id: string): Promise<number>
403
+ on(type: 'changed', handler: (event: { type: 'appschanged'; apps: readonly DesktopApp[] }) => void): () => boolean
404
+ }
405
+
406
+ interface NativeApp {
407
+ id: string
408
+ name: string
409
+ comment: string | null
410
+ icon: string
411
+ keywords: string[]
412
+ wmClass: string | null
413
+ }
414
+ ```
415
+
416
+ `natives()` lists the system's native programs (`.desktop` files, Linux only).
417
+
418
+ ## acde:windows
419
+
420
+ Shell module: every window of the session.
421
+
422
+ ```ts
423
+ interface AcdeWindows {
424
+ on<K extends WindowsEventType>(type: K, handler: (event: WindowsEventMap[K]) => void): () => boolean
425
+ list(): NativeWindow[]
426
+ close(id: number): void
427
+ requestState(id: number, state: WindowState): Promise<void>
428
+ kill(id: number): Promise<boolean>
429
+ focus(id: number): Promise<void>
430
+ hide(id: number): Promise<void>
431
+ show(id: number): Promise<void>
432
+ invokeMenu(id: number, menu: string, item: string): Promise<void>
433
+ stats(): Promise<Record<string, number>>
434
+ animate(id: number, options: {
435
+ opacity?: number; scale?: number; dx?: number; dy?: number
436
+ duration?: number; delay?: number
437
+ easing?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out'
438
+ }): Promise<void>
439
+ shortcut(combo: string, scope?: 'session' | 'desktop'): Promise<void>
440
+ unshortcut(combo: string, scope?: 'session' | 'desktop'): Promise<void>
441
+ launch(argv: string[], options?: { cwd?: string; env?: Record<string, string>; slot?: boolean }): number | Promise<number>
442
+ }
443
+
444
+ interface NativeWindow { id: number; appId: string | null; title: string | null; web?: boolean; state: WindowState }
445
+ ```
446
+
447
+ `WindowsEventType`: `open` `close` `title` `state` `activate` `minsize` `parent` `menus` `crash` `hang` `responsive` `attention` `ready` `show` `animated`. Every event is `{ type: 'window<name>', window: number, … }`:
448
+
449
+ | Event | Extra fields |
450
+ | --- | --- |
451
+ | `open` | `appId`, `title`, `web`, `icon` |
452
+ | `title` | `appId`, `title` |
453
+ | `state` | `state`, `previous` |
454
+ | `minsize` | `width`, `height` |
455
+ | `parent` | `parent` |
456
+ | `menus` | `menus`: the app's menus, each item with `action: boolean` instead of `onSelect` |
457
+ | `crash` | `reason` |
458
+
459
+ - A window's state changes are reported by `state`; full screen and minimizing have no events of their own. `requestState` rejects an unknown state with a `TypeError`.
460
+ - `close(id)` asks the app (`beforeclose`); `kill(id)` ends the window without asking.
461
+ - `animate` may finish at once where the runtime cannot animate; `animated` still fires.
462
+ - Every row of `list()` has the window's current `state`, and `list()` has every window, also in a system app that started after a window opened.
463
+ - An app starts native programs with [`acde:launch`](#acdelaunch), not with `launch` here.
464
+
465
+ ## acde:desktop
466
+
467
+ Shell module.
468
+
469
+ ```ts
470
+ interface AcdeDesktop {
471
+ apps(): Promise<{ apps: DesktopApp[] }>
472
+ pin(element: Element, options?: { margin?: number; reserve?: PanelReserve }): PinnedPanel
473
+ setDoubleClick(action: 'fill' | 'minimize' | 'none'): void
474
+ setFrameTheme(theme: FrameTheme | null): void
475
+ on(type: 'notification', handler: (e: NotificationEvent) => void): () => boolean
476
+ on(type: 'download', handler: (e: DownloadEvent) => void): () => boolean
477
+ on(type: 'screenshot', handler: (e: { type: 'screenshot'; path: string; width: number; height: number } | { type: 'screenshot'; path: string; error: string }) => void): () => boolean
478
+ on(type: 'shortcut', handler: (e: ShortcutEvent) => void): () => boolean
479
+ on(type: 'edge', handler: (e: { type: 'screenedge'; edge: 'top' | 'right' | 'bottom' | 'left'; entered: boolean; output: string }) => void): () => boolean
480
+ }
481
+
482
+ interface PanelReserve { edge: 'top' | 'bottom' | 'left' | 'right'; thickness?: number }
483
+ interface PinnedPanel {
484
+ update(): void
485
+ unpin(): void
486
+ reserve(reserve: PanelReserve | null): void
487
+ }
488
+
489
+ interface FrameTheme {
490
+ active?: FrameColors
491
+ inactive?: FrameColors
492
+ light?: { active?: FrameColors; inactive?: FrameColors }
493
+ buttonStyle?: 'dots' | 'glyphs'
494
+ buttonSide?: 'left' | 'right'
495
+ titleAlign?: 'center' | 'start'
496
+ }
497
+ interface FrameColors { background?: string; title?: string; line?: string; buttons?: string }
498
+
499
+ interface NotificationEvent {
500
+ type: 'notification'
501
+ window: number
502
+ app: string | null
503
+ label: string | null
504
+ title: string
505
+ body: string
506
+ icon: string
507
+ tag: string
508
+ }
509
+
510
+ interface DownloadEvent {
511
+ type: 'download'
512
+ window: number
513
+ app: string | null
514
+ path: string
515
+ name: string
516
+ size: number
517
+ }
518
+ ```
519
+
520
+ - `edge` fires when the pointer reaches (`entered: true`) or leaves an edge of the screen, once per edge (twice in a corner); `output` is the screen's name, `host` in a host window. It takes no focus, for a desktop that shows its hidden menu bar or Dock.
521
+ - `light`: the title bar colors for windows whose page does not declare dark support (`color-scheme` without `dark`); `active` / `inactive` are then for pages that do. Every `setFrameTheme` call gives the whole theme; a bad value throws a `TypeError`, and an unknown key is ignored with a warning in the log.
522
+ - A system app that declares `"desktop": true` gets `apps()`, `setDoubleClick`, `setFrameTheme`, and the `notification`, `download` and `edge` events; `pin`, `screenshot` and `shortcut` are the desktop page's only.
523
+
524
+ See [Desktops](../guide/desktop.md#panels).
525
+
526
+ ## acde:permissions
527
+
528
+ Shell module: the approval prompts and the decisions about them.
529
+
530
+ ```ts
531
+ interface AcdePermissions {
532
+ pending(): Promise<PermissionPrompt[]>
533
+ answer(id: number, allow: boolean): Promise<void>
534
+ list(app?: string): Promise<PermissionDecision[]>
535
+ revoke(app: string, capability: string): Promise<boolean>
536
+ on(type: 'prompt', handler: (event: PermissionPrompt & { type: 'permissionprompt' }) => void): () => boolean
537
+ on(type: 'answered', handler: (event: { type: 'permissionanswered'; id: number; app: string; capability: string; allowed: boolean }) => void): () => boolean
538
+ }
539
+
540
+ interface PermissionPrompt {
541
+ id: number
542
+ app: string
543
+ label: string
544
+ capability: string
545
+ reason: 'request'
546
+ }
547
+
548
+ interface PermissionDecision {
549
+ app: string
550
+ label: string | null
551
+ capability: string
552
+ decision: 'granted' | 'denied'
553
+ folder?: { label: string; exists: boolean }
554
+ }
555
+ ```
556
+
557
+ A prompt's `capability` is `camera` or `microphone`, the only capabilities that ask; in a decision it can also be `fs.folder-…`, a folder the user picked for the app (with `folder`). `revoke` resolves to `false` when there was no decision. In 0.0.9, a prompt for a camera or microphone that the app requires comes when the app starts, with `reason: 'launch'`; after 0.0.9, every prompt comes on first use, with `reason: 'request'`. See [Desktops](../guide/desktop.md#camera-and-microphone-prompts).
558
+
559
+ ## acde:inspect
560
+
561
+ Shell module, also given to desktop services as `ctx.inspect`. Turns the screen into a text semantic tree and acts on it by ref; meant for AI agents. `acde inspect` / `acde act` and MCP are built on it.
562
+
563
+ ```ts
564
+ interface AcdeInspect {
565
+ snapshot(options?: {
566
+ window?: string; ref?: string; all?: boolean; diff?: boolean
567
+ limit?: number; find?: string; tree?: boolean; offscreen?: boolean
568
+ }): Promise<{ text: string; windows: InspectWindow[]; trees: Record<string, InspectNode[]> }>
569
+ act(ref: string, action: InspectAction, arg?: string | number | readonly string[] | null): Promise<{ ok: boolean; text: string }>
570
+ run(steps: readonly { ref: string; action: InspectAction; text?: InspectArg }[]): Promise<{ ok: boolean; text: string }>
571
+ open(name: string): Promise<{ ok: boolean; text: string }>
572
+ query(options: { selector: string; window?: string; ref?: string; limit?: number; attributes?: readonly string[] }): Promise<{ window: string; count: number; nodes: InspectMatch[] }>
573
+ }
574
+
575
+ type InspectAction = 'click' | 'double' | 'right' | 'hover' | 'drag' | 'type' | 'settext' | 'select' | 'key' | 'scroll' | 'focus' | 'close' | 'minimize' | 'restore' | 'fill' | 'fullscreen'
576
+ ```
577
+
578
+ Window refs are `w0` (the desktop), `w1`, ...; element refs look like `w3.12`. The text form has one node per line:
579
+
580
+ ```text
581
+ - button "New Folder" [pressed] [w1.52]
582
+ ```
583
+
584
+ ## acde:office
585
+
586
+ Linux only. An editing service for office documents (text, spreadsheets, presentations), used by Dopame's bundled office apps; the API is still subject to change.
@@ -0,0 +1,23 @@
1
+ # Platform support
2
+
3
+ The host window used for development (`acde dev`) runs on macOS, Windows, and Linux; Dopame OS itself is Linux. The runtime is built for macOS on Apple silicon and Intel, Linux on x86_64, and Windows on x64; see [Install](../start/install.md#supported-platforms).
4
+
5
+ | | macOS | Windows | Linux (development) | Dopame OS |
6
+ | --- | --- | --- | --- | --- |
7
+ | Page rendering (requires a GPU) | Metal | DX12 | Vulkan (a hardware driver) | Vulkan |
8
+ | `acde:fs`, file pickers | ✓ | ✓ | ✓ | ✓ |
9
+ | Sound, clipboard | ✓ | ✓ | ✓ | ✓ |
10
+ | System notifications | ✓ | Only in a desktop, which shows them | ✓ | Shown by the desktop |
11
+ | App backends | ✓ | ✓ (see [Backend](../guide/backend.md#on-windows)) | ✓ | ✓ |
12
+ | `acde:terminal` (`terminal`) | ✓ | ✓ | ✓ | ✓ |
13
+ | Camera and microphone (`camera`, `microphone`) | ✓ | | ✓ | ✓ |
14
+ | `acde:network`, `acde:office` (`network`, `office`) | | | ✓ | ✓ |
15
+ | Native programs embedded in a window (`slots`) | | | | ✓ |
16
+ | WebGPU, voice input | | | ✓ | ✓ |
17
+ | Native programs (`.desktop`) | | | ✓ | ✓ |
18
+ | Headless (`--headless`, `acde test`) | ✓ | ✓ | ✓ (requires a GPU) | |
19
+
20
+ - Capabilities a platform lacks are not faked. An app that **requires** one does not start there (`Notes needs "network" (unsupported on this platform)`) and is left out of the app list; one that lists it in `optional` starts, `load()` gives `null` for the module, and `acde:capabilities` reports `state: 'unsupported'`. See [Capabilities](../guide/capabilities.md#optional-capabilities).
21
+ - `acde check --platform windows` (or `macos`, `linux`) tells you before you run there: `error: macos (standalone): app "notes" requires network, which this mode lacks`, or a `note:` when the capability is optional. The data is `PLATFORM_SUPPORT` in `@dopame/acde-api`, per platform and way of running: `session` (Dopame OS), `desktop` (a desktop in the host window), `standalone` (a single app in the host window). A page reads its own from `acde:capabilities` (`platform`, `mode`).
22
+ - The runtime draws pages only on the GPU, so Linux CI without a GPU cannot run headless. macOS CI runners can.
23
+ - On Linux the runtime needs the **hardware Vulkan driver** of the machine's GPU; a software renderer (lavapipe / llvmpipe) is not accepted. Without one, `acde dev` fails at startup with a log line that says so and names the GPU the system has: `web layer unavailable: the system has a GPU (card0, kernel driver i915) but no usable Vulkan driver was found: install the GPU's Vulkan driver (for example vulkan-intel, vulkan-radeon, nvidia-utils) or point VK_DRIVER_FILES at its ICD (…)`. The fix is to install the driver, then check that `vulkaninfo --summary` lists the GPU: for Mesa, `vulkan-intel` or `vulkan-radeon` on Arch, `mesa-vulkan-drivers` on Debian, Ubuntu and Fedora; for NVIDIA, the vendor's driver.
@@ -0,0 +1,47 @@
1
+ # Service API
2
+
3
+ The default export of a desktop service module is a `ServiceFactory`. See [Services](../guide/services.md) for usage.
4
+
5
+ ```ts
6
+ type ServiceFactory = (context: ServiceContext) => ServiceInstance | Promise<ServiceInstance>
7
+
8
+ interface ServiceInstance {
9
+ readonly api: Readonly<Record<string, object>>
10
+ readonly sync?: readonly string[]
11
+ close?(): unknown
12
+ }
13
+
14
+ interface ServiceContext {
15
+ readonly name: string
16
+ readonly package: string
17
+ readonly state: string
18
+ readonly native: Readonly<Record<string, unknown>> | null
19
+ readonly env: Readonly<Record<string, string | undefined>>
20
+ readonly inspect?: AcdeInspect
21
+ log(line: string): void
22
+ spawn(argv: readonly string[], options?: SpawnOptions): ServiceProcess
23
+ import<K extends keyof ServiceModuleTypes>(specifier: `acde:${K}`): Promise<{ readonly default: ServiceModuleTypes[K] } & { readonly [N in K]: ServiceModuleTypes[K] }>
24
+ }
25
+
26
+ // ServiceModuleTypes: windows, packages, session, desktops, inspect, setDefault, and desktop with apps() only
27
+
28
+ interface SpawnOptions {
29
+ readonly cwd?: string
30
+ readonly env?: Readonly<Record<string, string>>
31
+ onLine?(line: string): void
32
+ onExit?(code: number | null, signal: number | null): void
33
+ }
34
+
35
+ interface ServiceProcess {
36
+ readonly pid: number
37
+ write(text: string): boolean
38
+ end(): void
39
+ kill(signal?: 'SIGTERM' | 'SIGKILL' | 'SIGINT' | 'SIGHUP'): boolean
40
+ }
41
+ ```
42
+
43
+ - `api`: namespace → object. Each namespace becomes the module `acde:<namespace>`; functions on the object become callable methods, and strings / numbers / booleans become read-only fields.
44
+ - `sync`: a list of `"namespace.method"` entries. These methods are called once when a page starts, the results are cached in the page, and from then on they return synchronously in the page (good for configuration that doesn't change).
45
+ - `close()`: called when the desktop stops.
46
+ - An app lists the **service name** (the key in the desktop config's `services`) in its own `services`, and the runtime then gives it every namespace of that service. `acde check` and `acde build` let an import of a namespace that is not the service's own name pass with a `note:`, because they cannot know a service's namespaces (see [Services](../guide/services.md#call-it)). A service name cannot be that of a system module or capability, and a namespace with such a name is left out.
47
+ - `import('acde:windows')`: resolves to the module (`{ windows, default }`) when the desktop grants it to this service: `"services": { "clock": { "module": "services/clock.ts", "modules": ["windows"] } }` in `acde.config.jsonc`, which `acde build` writes into the manifest as `"services": [{ "name": "clock", "modules": ["windows"] }]` (see [Manifest](manifest.md#desktopmanifest)). Otherwise it rejects with `acde:windows is a shell module: list it in the modules of service "clock" in the desktop manifest`. A service cannot use a static `import` of an `acde:` module (the build rejects it). A service gets the modules a desktop page gets, with two exceptions: `acde:desktop` is `apps()` only, and `acde:permissions` is not given. Calls that the types declare as promises return promises in a service too. See [Services](../guide/services.md#shell-modules-in-a-service).