@mnstry/atelier 0.2.0-alpha.10 → 0.2.0-alpha.12
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/CHANGELOG.md +385 -0
- package/README.md +10 -6
- package/contracts/atelier-obsidian-generation-manifest.v2.schema.json +372 -0
- package/contracts/compat-baseline.json +1 -1
- package/docs/assurance-controls.md +3 -2
- package/docs/install.md +3 -3
- package/docs/local-services.md +62 -11
- package/docs/obsidian-contract.md +681 -39
- package/docs/obsidian-plugin.md +585 -0
- package/docs/obsidian.md +292 -48
- package/docs/release-engineering.md +7 -1
- package/docs/upgrade.md +32 -3
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/flat-note-path.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/hidden-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/identity-in-the-body.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/link-breaking-name.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-identity-region.v2.json +156 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-layout-version.v2.json +159 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/note-named-like-a-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/unknown-region-kind.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/v1-contract-version.v2.json +161 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/valid/three-notes.v2.json +160 -0
- package/fixtures/obsidian/edits/workspace.json +1 -1
- package/fixtures/obsidian/materialization/expected-full.json +21 -21
- package/fixtures/obsidian/materialization/expected-full.layout-1.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +7 -7
- package/fixtures/obsidian/materialization/expected-scoped.layout-1.json +39 -0
- package/package.json +7 -3
- package/plugins/obsidian/main.js +444 -0
- package/plugins/obsidian/manifest.json +9 -0
- package/plugins/obsidian/styles.css +22 -0
- package/src/commands/obsidian.mjs +89 -10
- package/src/contracts/corpus.mjs +6 -0
- package/src/egress/forbidden-egress.mjs +2 -0
- package/src/project/private-state.mjs +10 -1
- package/src/projection/obsidian/contracts.mjs +224 -18
- package/src/projection/obsidian/edits/apply.mjs +12 -7
- package/src/projection/obsidian/edits/regions.mjs +137 -42
- package/src/projection/obsidian/materialize/index.mjs +6 -3
- package/src/projection/obsidian/materialize/path-registry.mjs +443 -57
- package/src/projection/obsidian/materialize/prepare-view.mjs +382 -131
- package/src/projection/obsidian/materialize/redaction.mjs +327 -0
- package/src/projection/obsidian/materialize/settings.mjs +113 -34
- package/src/projection/obsidian/plugin-bridge/bundle.mjs +50 -0
- package/src/projection/obsidian/plugin-bridge/channel.mjs +174 -0
- package/src/projection/obsidian/plugin-bridge/index.mjs +10 -0
- package/src/projection/obsidian/proposals/observation.mjs +10 -5
- package/src/projection/obsidian/publication/bridge-script.mjs +11 -5
- package/src/projection/obsidian/publication/index.mjs +2 -1
- package/src/projection/obsidian/publication/publisher.mjs +173 -26
- package/src/projection/obsidian/publication/transport.mjs +124 -17
- package/src/projection/obsidian/publication/vault-list.mjs +199 -0
- package/src/projection/obsidian/recovery/restart.mjs +3 -1
- package/src/projection/obsidian/recovery/store.mjs +11 -4
- package/src/projection/obsidian/selection-ui/focus.mjs +14 -7
- package/src/projection/obsidian/selection-ui/selection-state.mjs +2 -2
- package/src/projection/obsidian/selection-ui/selection.mjs +8 -3
- package/src/runtime/obsidian/app-capability.mjs +115 -14
- package/src/runtime/obsidian/app-production-seams.mjs +126 -37
- package/src/runtime/obsidian/app-registration.mjs +166 -0
- package/src/runtime/obsidian/engine.mjs +127 -11
- package/src/runtime/obsidian/extension-points.mjs +1 -1
- package/src/runtime/obsidian/index.mjs +5 -1
- package/src/runtime/obsidian/launch-plan.mjs +88 -0
- package/src/runtime/obsidian/lifecycle.mjs +105 -14
- package/src/runtime/obsidian/opening.mjs +240 -51
- package/src/runtime/obsidian/pending-edits.mjs +10 -0
- package/src/runtime/obsidian/plugin-channel.mjs +281 -0
- package/src/runtime/obsidian/plugin-choice.mjs +170 -0
- package/src/runtime/obsidian/plugin-drift.mjs +76 -0
- package/src/runtime/obsidian/plugin-presence.mjs +48 -0
- package/src/runtime/obsidian/service-main.mjs +19 -4
- package/src/runtime/obsidian/service-record.mjs +36 -0
- package/src/runtime/obsidian/service-server.mjs +50 -12
- package/src/runtime/obsidian/service.mjs +100 -6
- package/src/runtime/obsidian/state-store.mjs +13 -1
- package/src/server/local-sidecar.mjs +24 -5
- package/src/server/server.mjs +18 -5
- package/src/upgrade/upgrade.mjs +1 -1
- package/templates/external-project-workspace/pack-lifecycle.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,390 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0-alpha.12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Atelier's own Obsidian plugin (phase 1: presence and status). Every vault
|
|
8
|
+
the maintenance service publishes now carries it under
|
|
9
|
+
`.obsidian/plugins/atelier-projection/` with its entry in
|
|
10
|
+
`.obsidian/community-plugins.json`; the person never installs anything, and
|
|
11
|
+
Obsidian asks once per vault whether to trust the vault's plugins. Once
|
|
12
|
+
trusted, a status bar item says whether the view is current, updating, held
|
|
13
|
+
for an edit, stale or out of reach of the service, and "Atelier: show
|
|
14
|
+
status" says why. The plugin writes nothing, runs nothing it is sent and
|
|
15
|
+
reaches only the workspace's maintenance service on its literal loopback
|
|
16
|
+
address. Without it (restricted mode, an app older than 1.13.7, before the
|
|
17
|
+
prompt is answered, or turned off in the vault) everything works as before.
|
|
18
|
+
See `docs/obsidian-plugin.md`.
|
|
19
|
+
- A person who turns Atelier's plugin off in a vault, or uninstalls it there,
|
|
20
|
+
is followed. Once the app has the plugin's entry in
|
|
21
|
+
`community-plugins.json` (it was in place before an app opened the vault,
|
|
22
|
+
or the plugin ran there), a list without it is recorded as the person's
|
|
23
|
+
decision (private state, `state/plugin/choices/`): the entry is not added
|
|
24
|
+
back, a deleted plugin folder is not made again, and the plugin files still
|
|
25
|
+
there are kept current. An entry published while an app held the vault is
|
|
26
|
+
only offered until then, since that app may write back the list it read
|
|
27
|
+
before, and a list without it is no decision of the person's. `status`, `open` and the new
|
|
28
|
+
`atelier obsidian plugin show` report `turned-off-in-this-vault`. Turning the
|
|
29
|
+
plugin on again in Obsidian's settings is followed the same way;
|
|
30
|
+
`atelier obsidian plugin on --scope ID` brings the entry and the files back
|
|
31
|
+
(the way back after an uninstall): at once, on a tick that names the view,
|
|
32
|
+
while the maintenance service runs (with `--adapter=obsidian-cli`, one of
|
|
33
|
+
an earlier release is replaced by the installed one first, as `open` does),
|
|
34
|
+
and otherwise at the view's next publication.
|
|
35
|
+
- The maintenance service answers five plugin commands (`/plugin/challenge`,
|
|
36
|
+
`/plugin/hello`, `/plugin/lease`, `/plugin/release`, `/plugin/status`,
|
|
37
|
+
protocol `atelier-obsidian-plugin-channel/v2`) and grants presence and
|
|
38
|
+
read-only status of one view to a plugin that proves it holds that vault's
|
|
39
|
+
key. The key is random per vault, kept owner-only in private state and in
|
|
40
|
+
that vault's plugin data file, and never crosses the wire: the service
|
|
41
|
+
proves it holds the key over its own exact address before the plugin sends
|
|
42
|
+
anything that names the vault, and every later request and answer is sealed
|
|
43
|
+
with a key for that session and a counter that only goes up. A program that
|
|
44
|
+
takes the service's port while the service is down learns nothing it can
|
|
45
|
+
use: an answer the session's key does not seal ends the session in the
|
|
46
|
+
plugin, so such a program receives one command of a session at most; a
|
|
47
|
+
challenge is answered once and only within thirty seconds of the time it
|
|
48
|
+
names, and at most four handshakes wait per view. `status` reports which
|
|
49
|
+
views a plugin holds open, and so do `atelier obsidian status` and `open`.
|
|
50
|
+
- While one launch of the plugin holds a view open and the command-line
|
|
51
|
+
tool gives no version (no vault open yet, or no answer in time), the app
|
|
52
|
+
version the plugin reports counts as checked (reason `plugin-reported`), in
|
|
53
|
+
the service's adapter factory and in `open`, unless the process table shows
|
|
54
|
+
no app running (a lease outlives a crashed app by a few seconds, and then
|
|
55
|
+
vouches for no version). A version the tool does give
|
|
56
|
+
decides, since the tool may reach another app holding the same vault, and
|
|
57
|
+
whether the app and its tool are installed is still the probe's answer.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- Node.js 24 is supported alongside Node.js 22. The engines range is now
|
|
62
|
+
`>=22.18.0 <23 || >=24.13.1 <25`, so an install on Node 24 no longer warns.
|
|
63
|
+
Graph and projection files come out byte-identical on either major: CI runs
|
|
64
|
+
the complete suite on 24.13.1 and on the newest 24, and a new
|
|
65
|
+
`cross-node-bytes` job builds one workspace on 22 and on 24 and compares
|
|
66
|
+
every file written. The byte-determinism test names the supported majors
|
|
67
|
+
and checks them against `package.json`. The floor is 24.13.1, not the
|
|
68
|
+
first 24 LTS, because earlier 24 releases have an `fs.rmSync` defect
|
|
69
|
+
(nodejs/node#61020): removing a symbolic link to a directory throws
|
|
70
|
+
`EISDIR`, and a broken symbolic link is silently left in place. The
|
|
71
|
+
complete suite fails on 24.11.0 for that reason.
|
|
72
|
+
- **Breaking:** Obsidian generation manifests of the new vault layout are
|
|
73
|
+
`atelier-obsidian-generation-manifest/v2`, a new contract major that records
|
|
74
|
+
`layoutVersion: 2` and each note's identity region, and a vault an earlier
|
|
75
|
+
release published is laid out again once (below). The v1 contract is
|
|
76
|
+
unchanged and still validates every generation an earlier release wrote; a
|
|
77
|
+
reader of generation manifests must accept both majors.
|
|
78
|
+
- Obsidian views are laid out in vault layout 2, so a file name reads as the
|
|
79
|
+
note's title and the vault reads as the repositories. Folders mirror each
|
|
80
|
+
repository under a folder named after it, so a view's folders show the
|
|
81
|
+
repository identity and source directory chain of its notes, as its notes'
|
|
82
|
+
`atelier-repo` and `atelier-source` properties do; this is acceptable for
|
|
83
|
+
every vault, scoped ones included. A note's file name is its title (the
|
|
84
|
+
front-matter `title`, else its first H1, else the file stem as written),
|
|
85
|
+
with a trailing source extension dropped and made safe for macOS, Linux,
|
|
86
|
+
Windows and Obsidian links. Two notes of a view that would share a name in
|
|
87
|
+
one folder, compared case- and normalization-insensitively, are told apart
|
|
88
|
+
by the source file stem and then by a short stable id; a name that collides
|
|
89
|
+
with nothing carries no hash. A wrapped file keeps its own name beside its
|
|
90
|
+
note (`<file name>.md`), and an embedded file is copied to its mirrored
|
|
91
|
+
path. Links name their target by its full vault path. A repository folder
|
|
92
|
+
that would be `notes` or `attachments` spelled another way is told apart by
|
|
93
|
+
a short id, since an upgraded vault keeps those two folders of the earlier
|
|
94
|
+
layout.
|
|
95
|
+
- Each view allocates its paths among its own notes, seeded from what its
|
|
96
|
+
prior generation published: a path does not change on retitle or when
|
|
97
|
+
other files come and go, a note that leaves the view releases its path
|
|
98
|
+
there (a renamed source takes its name back), a note of another view never
|
|
99
|
+
causes a qualifier, and a lost path registry costs no view its paths. One
|
|
100
|
+
identity may have different paths in different views. The registry keeps
|
|
101
|
+
each view's allocation in a section of its own, and drops the sections of
|
|
102
|
+
views the engine no longer maintains. `@mnstry/atelier/obsidian/materialize`
|
|
103
|
+
adds `allocateViewPaths`; `allocateWorkspacePaths` is deprecated: it still
|
|
104
|
+
allocates the earlier layout's paths for a whole workspace, exactly as
|
|
105
|
+
before, and Atelier no longer calls it.
|
|
106
|
+
- A source that does not fit is laid out anyway and reported, never a reason
|
|
107
|
+
to refuse the workspace: a folder chain too long for the path budget keeps a
|
|
108
|
+
readable prefix and a short stable id, or, when not even that fits, the note
|
|
109
|
+
sits directly in its repository's folder; a source folder that meets a file
|
|
110
|
+
of the same name is qualified with an id. Only a name that does not fit even
|
|
111
|
+
in its repository's folder refuses the view. What is worth a look (these, an
|
|
112
|
+
author's own identity keys shadowing the generated ones, generated text that
|
|
113
|
+
names a note outside the view without refusing it) is recorded in the
|
|
114
|
+
manifest and the view's freshness entry, and shown by
|
|
115
|
+
`atelier obsidian status`, naming the note.
|
|
116
|
+
- Every note names its identity in three generated front-matter properties,
|
|
117
|
+
`atelier-id`, `atelier-repo` and `atelier-source`, or, when its own front
|
|
118
|
+
matter could not take them unchanged in meaning, in a generated block at
|
|
119
|
+
its end. An edit to them is never applied to a source.
|
|
120
|
+
- A vault published by an earlier release is laid out again once. Every
|
|
121
|
+
earlier path is retired through the publisher's remove units: moved to the
|
|
122
|
+
recovery area, or kept where it is when somebody edited it. The app's
|
|
123
|
+
bookmarks, open tabs and graph positions of the earlier paths are lost once.
|
|
124
|
+
A view that holds a note for an open edit keeps its earlier layout until the
|
|
125
|
+
edit is applied or withdrawn, and such an edit still applies; an edit that
|
|
126
|
+
closes on a tick (automatic mode applies on the tick it observes) keeps it
|
|
127
|
+
for that tick, so the file the person edited becomes the published note and
|
|
128
|
+
is retired like any other. See "Vault layout" in `docs/obsidian-contract.md`.
|
|
129
|
+
- The redaction guard is re-based on the readable layout. Atelier never
|
|
130
|
+
generates a reference to a note outside a view: every path the emitter
|
|
131
|
+
writes must be one allocated to the view, and every identity block names
|
|
132
|
+
its own note. Generated prose repeats author text (the titles, summaries
|
|
133
|
+
and tags of in-view notes), which is carried as authored; the deny-list over
|
|
134
|
+
it, read as a reader sees it with the emitter's escapes removed and in NFC,
|
|
135
|
+
follows the audience, as defence in depth. For a note the audience may not
|
|
136
|
+
see it refuses an unambiguous identifier (an identity qualified by any
|
|
137
|
+
repository, a repository-qualified path, a repository-relative path with a
|
|
138
|
+
folder, a vault path) and reports a bare-word identity or a file name at a
|
|
139
|
+
repository's root (`README.md`); for a note the audience may see but the
|
|
140
|
+
view does not select it only reports. A view may newly refuse with
|
|
141
|
+
`redaction-failure`; the refusal names the rule and the in-view note, never
|
|
142
|
+
the value. Both rules run over every note, cached ones included. The deny
|
|
143
|
+
matcher is one automaton: building it takes time and memory linear in the
|
|
144
|
+
values it holds, on every preparation, and it reads each text once, however
|
|
145
|
+
many there are.
|
|
146
|
+
- A focus query names each note by an anchored regular-expression path term
|
|
147
|
+
(`path:/^…$/`, query version `obsidian-graph-search-paths/v2`), so it matches
|
|
148
|
+
exactly the selected notes; a focus persisted with the earlier version is
|
|
149
|
+
still read.
|
|
150
|
+
- `atelier obsidian open` makes the first open of a view automatic: Obsidian
|
|
151
|
+
no longer has to be quit, and no vault folder has to be opened by hand. It
|
|
152
|
+
makes the app know the view's vault as one of its vaults before it opens
|
|
153
|
+
it. With Obsidian running and
|
|
154
|
+
answering its command line, the vault is added through the app itself
|
|
155
|
+
(`vault-open` through `obsidian-cli eval`, a constant script with the folder
|
|
156
|
+
as a base64 JSON payload), verified in the app's own vault list by real
|
|
157
|
+
path, and opened; `open` waits until the app answers for exactly that vault
|
|
158
|
+
and then asks for the view again, so the first publication runs through the
|
|
159
|
+
app with every editor check. Quitting Obsidian is no longer needed. With
|
|
160
|
+
Obsidian not running, the view is published on the path with no app, and
|
|
161
|
+
the vault is added to the app's `obsidian.json` before the app is started
|
|
162
|
+
on it: only while the process table shows, positively, that no Obsidian
|
|
163
|
+
runs (read again immediately before the rename), only a file Obsidian
|
|
164
|
+
created, owned by this user and not a link, atomically (a temporary file in
|
|
165
|
+
the same directory, fsynced, renamed), keeping every other key and entry,
|
|
166
|
+
and with a timestamped backup beside it; a write that fails at any step
|
|
167
|
+
leaves the file as it was and nothing beside it; an Obsidian that never ran
|
|
168
|
+
on the account (no such file) is asked to be started once. With Obsidian running
|
|
169
|
+
and no vault open, a vault the app already lists is opened by path; one it
|
|
170
|
+
does not list is answered as `app-version-unsupported` / `no-vault-open`
|
|
171
|
+
(open any vault, or quit Obsidian, then open again) and nothing is
|
|
172
|
+
written. Every failure is typed and names its next step. See "What `open`
|
|
173
|
+
does in each state of Obsidian" in docs/obsidian.md and "Obsidian's vault
|
|
174
|
+
list" in docs/obsidian-contract.md.
|
|
175
|
+
- A tick requested over the service's listener may name one view
|
|
176
|
+
(`POST /tick` with `scopeId`; `requestServiceTick({ scopeId })`), which the
|
|
177
|
+
engine then prepares and publishes once more, whatever its state
|
|
178
|
+
(`engine.requestPreparation(scopeId)`, a one-shot request consumed at the
|
|
179
|
+
next tick), with the app's qualification asked again. `open` names its view
|
|
180
|
+
on both ticks it requests. A view whose last publication did not settle is
|
|
181
|
+
also tried again without a request: as soon as the app looks different (it
|
|
182
|
+
quit or started, or opened or closed a vault in its list), and otherwise
|
|
183
|
+
after a delay that starts at 30 seconds (`DEFAULT_PUBLICATION_RETRY_MS`) and
|
|
184
|
+
doubles, up to the full reconciliation interval. Before, it waited for a
|
|
185
|
+
change or the five-minute reconciliation. What the app looks like is read
|
|
186
|
+
from the process table and its vault list alone (`appStateSignature({
|
|
187
|
+
processes, settings })`), so nothing runs in Obsidian to find out; the app
|
|
188
|
+
is asked for its version only when a view is about to be published, and
|
|
189
|
+
the service's adapter factory asks it without blocking
|
|
190
|
+
(`createQualifiedAdapterFactory` answers a promise for an app probe with
|
|
191
|
+
`inspect()`). `engine.requestPreparation` takes a request only for a view
|
|
192
|
+
the project declared at the last tick (at most 64 before the first), and
|
|
193
|
+
answers whether it did. The service listener answers a POST member it does
|
|
194
|
+
not know, or a view on a stop, with 400 `request-member-unknown` instead of
|
|
195
|
+
409, and checks a view against the scope contract's own identifier.
|
|
196
|
+
- Command-line calls to the app reach only the vault they are about. A call
|
|
197
|
+
about a vault names it first (`vault=<id>`), so it reaches that vault's
|
|
198
|
+
window whichever window has focus; only when that id would name another
|
|
199
|
+
vault first does it run in the vault's folder, and not at all when a vault
|
|
200
|
+
the app lists before it at a folder above it (a home folder, say) would take
|
|
201
|
+
a call run there. The vault root, the settings entry, the vault check and
|
|
202
|
+
the publication bridge's check that the app holds this vault use the path as
|
|
203
|
+
the file system stores it, so on macOS a data root given in another letter
|
|
204
|
+
case neither hides a vault listed above it nor routes a call there, and a
|
|
205
|
+
store written before, or an app that holds the vault under another spelling
|
|
206
|
+
or through a link, keeps working: publication through the app, and `open`'s
|
|
207
|
+
check that the app answers for the vault. Publication calls do so only while the app's list
|
|
208
|
+
shows the view's vault open; every other call runs in a directory that is
|
|
209
|
+
no vault. Maintenance never reopens a vault window that was closed and never
|
|
210
|
+
reaches another vault, and the directory a command or service was started
|
|
211
|
+
in no longer picks, or opens, a vault. `open` never adds a view's vault
|
|
212
|
+
inside a folder Obsidian already lists as a vault, which would show the
|
|
213
|
+
view's notes too: it answers `launch-failed` / `vault-inside-another-vault`.
|
|
214
|
+
A listed folder is compared as written first; its real path is read only
|
|
215
|
+
when its last component is the vault root's, so a vault on a mount that
|
|
216
|
+
does not answer is never waited on. The maintenance service is started in
|
|
217
|
+
the root directory.
|
|
218
|
+
- A view's vault that Obsidian has open in more than one window, one per
|
|
219
|
+
entry of its vault list that names the folder (under another letter case,
|
|
220
|
+
or through a link), is not published: a publication would coordinate with
|
|
221
|
+
one window only. The view reports `publisher-conflict` /
|
|
222
|
+
`vault-open-in-several-windows` (removing the extra entries from Obsidian's
|
|
223
|
+
vault list clears it; closing a window may not, since Obsidian keeps the last
|
|
224
|
+
window it closed marked open), and `open` answers the same, names the entries (`open in Obsidian
|
|
225
|
+
as: …`, `duplicates` in JSON) and launches nothing. While one entry of the
|
|
226
|
+
folder has a window, calls and `open` reach only that entry, so a closed
|
|
227
|
+
entry of the same folder is never opened beside it.
|
|
228
|
+
- Obsidian's settings file is not written larger than 4 MiB
|
|
229
|
+
(`obsidian-settings-too-large`), nor through a second name (a hard link,
|
|
230
|
+
`obsidian-settings-unsafe`). Of Atelier's backups beside it, the first (the
|
|
231
|
+
list as it was before Atelier wrote it) and the latest are kept. A write
|
|
232
|
+
that cannot be read back is `registration-not-read-back`, no longer
|
|
233
|
+
`app-started-during-registration`. A Flatpak or snap build of Obsidian on
|
|
234
|
+
Linux, which never reads that file, is recognised (the build whose vault
|
|
235
|
+
list was written last; its installation only when no build wrote one), and
|
|
236
|
+
the file is neither read nor written for it (`obsidian-sandboxed`); the
|
|
237
|
+
vault is added through the running app. An addition the running app did not answer is looked up in
|
|
238
|
+
its list, and is `addition-not-answered` when it is not there, no longer
|
|
239
|
+
`app-did-not-list-its-vaults`. Adding a vault through the app also puts its
|
|
240
|
+
folder in the operating system's recent documents (Recent Items on macOS),
|
|
241
|
+
as Obsidian's own "open folder as vault" does.
|
|
242
|
+
- A maintenance service still running an earlier release after an upgrade
|
|
243
|
+
is replaced by `open`. Every service records the release it runs, the
|
|
244
|
+
package version and a digest of every runtime module it ships (`src/`,
|
|
245
|
+
`contracts/`), in its record's `executable.ext.release`
|
|
246
|
+
(`releaseIdentity()`). A service of the workspace that proves itself ours
|
|
247
|
+
but runs an earlier version than the installed one, the same version with
|
|
248
|
+
another entry module or other modules, records no release, or refuses a
|
|
249
|
+
tick that names a view (as 0.2.0-alpha.11 and earlier do), is stopped
|
|
250
|
+
through its own listener and the installed release is started under the
|
|
251
|
+
consent already recorded; `open` shows `service: restarted (outdated)`
|
|
252
|
+
(`service.restarted` in JSON). A service of a later version (versions
|
|
253
|
+
ordered as semantic versions, 0.2.0-alpha.11 < 0.2.0-alpha.12 < 0.2.0), or
|
|
254
|
+
of one that cannot be ordered, is never replaced by an earlier release, so
|
|
255
|
+
two installations used on one workspace do not replace each other's service
|
|
256
|
+
on every open: `open` answers `service-unavailable` /
|
|
257
|
+
`service-other-release`, whose next step is `atelier obsidian service stop`,
|
|
258
|
+
then open again. The same version string on both sides is compared by
|
|
259
|
+
content even when it cannot be ordered (a fork's `dev`), so that next step
|
|
260
|
+
cannot loop. A tick refused because a concurrent command replaced the
|
|
261
|
+
runtime just before is asked of the runtime that took its place, and
|
|
262
|
+
nothing is restarted. A busy service is not stopped, and nothing that does
|
|
263
|
+
not prove itself ours is touched. `requestServiceTick` takes the start
|
|
264
|
+
options of the installed entry as `service` for this (`runtimeRelease`,
|
|
265
|
+
`releaseStanding`).
|
|
266
|
+
- The `status` next step for `publisher-conflict` / `editor-uncoordinated`
|
|
267
|
+
names `atelier obsidian open` (which adds the vault to Obsidian and
|
|
268
|
+
publishes through it) or quitting Obsidian; after `open` itself tried, it
|
|
269
|
+
names quitting Obsidian. `launch-failed` no longer asks for a vault folder
|
|
270
|
+
to be opened by hand.
|
|
271
|
+
- The settings unit owns two more things in a vault: the `atelier-projection`
|
|
272
|
+
entry of `community-plugins.json` (appended while the person wants the
|
|
273
|
+
plugin there; every other entry kept in order; written only over the exact
|
|
274
|
+
bytes the decision was made on, so a change the person makes meanwhile
|
|
275
|
+
stops that unit as `settings-changed` and the view is tried again) and the
|
|
276
|
+
plugin's four files, whose digests are pinned in the generation manifest
|
|
277
|
+
under `ext["mnstry.atelier.obsidian"].settings`.
|
|
278
|
+
Whatever occupies a plugin path is displaced to recovery, never lost; a
|
|
279
|
+
plugin path a person has to repair (a link, a folder where a file goes, a
|
|
280
|
+
file nobody may read, a folder nobody may write) never holds a view back; a
|
|
281
|
+
generation whose plugin files the person repaired, changed or removed is
|
|
282
|
+
published again as it is at the maintenance service's next tick; and
|
|
283
|
+
another writer racing a plugin file makes the view try again.
|
|
284
|
+
`isUserOwnedSettingsPath` answers `false` for these paths.
|
|
285
|
+
- Vault roots that Atelier places under its data root are created private
|
|
286
|
+
(`0700`), and an existing one is made private before the plugin's key is
|
|
287
|
+
written into it. A vault root Atelier did not place never receives the key
|
|
288
|
+
unless it is already private, and a vault path under the data root that is
|
|
289
|
+
a link to another folder never receives it, and its mode is never changed.
|
|
290
|
+
- A settings file nobody may read (`core-plugins.json`, say) is reported as
|
|
291
|
+
`path-unsafe` and left for the person instead of failing the publication.
|
|
292
|
+
- `plugins/` is part of the egress scan, and the release audit requires the
|
|
293
|
+
plugin's three files in the package.
|
|
294
|
+
|
|
295
|
+
### Fixed
|
|
296
|
+
|
|
297
|
+
- A publication no longer stops with an untyped `state leaf changed while
|
|
298
|
+
opening` when another program replaces a note by rename at the instant the
|
|
299
|
+
publisher opens it (an editor or sync tool saving the note). The note is
|
|
300
|
+
read again, since every rename leaves a complete file; a note that keeps
|
|
301
|
+
being replaced is reported as `disk-changed` for that note only, and the
|
|
302
|
+
other notes are published. No bytes were ever lost: the publication threw
|
|
303
|
+
before writing.
|
|
304
|
+
- `open` no longer makes Obsidian forget which of your vaults to reopen. A
|
|
305
|
+
quit Obsidian started with an `obsidian://` link opens only that vault and
|
|
306
|
+
drops the reopen flag of every other one; `open` now starts it plainly on
|
|
307
|
+
macOS (it reopens every vault it had open, and the view's vault, which `open`
|
|
308
|
+
added flagged to reopen) and hands it the vault's link through its command
|
|
309
|
+
line once the app itself answers there, never through the operating system.
|
|
310
|
+
On Linux a quit Obsidian is still started with the link (a known limit).
|
|
311
|
+
- `open` names the vault by its id (`obsidian://open?vault=<id>`) where the id
|
|
312
|
+
reaches it first, instead of its path, which Obsidian matches against its
|
|
313
|
+
vault list by string prefix.
|
|
314
|
+
- Obsidian with its command line turned off (the default of a new
|
|
315
|
+
installation) answers every command with "Command line interface is not
|
|
316
|
+
enabled"; that answer was read as an unreadable version. It is now
|
|
317
|
+
`app-cli-unavailable` / `cli-turned-off`, whose next step names the setting
|
|
318
|
+
(Settings > General > Advanced > Command line interface).
|
|
319
|
+
- A view whose prepared generation was already the committed one (on the
|
|
320
|
+
first tick of a service, or prepared again on request) was marked `stale`
|
|
321
|
+
when the app could not be qualified, for example while it ran with no vault
|
|
322
|
+
open: the editor adapter was built before the publisher found nothing to
|
|
323
|
+
publish. That adapter is now built only when the publisher needs one.
|
|
324
|
+
- After a refused publication, the tick `open` asked for did not try the view
|
|
325
|
+
again (the engine attempted only views with changes), so `open` reported the
|
|
326
|
+
old conflict until the five-minute reconciliation.
|
|
327
|
+
- `open` launched `obsidian://open?path=` for a vault the app did not know,
|
|
328
|
+
and Obsidian showed "Vault not found. Unable to find a vault for the URL";
|
|
329
|
+
`open` then answered `launch-failed` / `app-did-not-answer-for-this-vault`.
|
|
330
|
+
- The production check that the app answers for a vault parsed the app's
|
|
331
|
+
answer twice and so never saw an answer; it now reads it once (twice only
|
|
332
|
+
for a quoted string), compares real paths, and asks from inside the vault
|
|
333
|
+
folder.
|
|
334
|
+
- An app whose vault window is still loading answers a command with `Error:
|
|
335
|
+
Command "version" not found`; that was read as an unreadable version and
|
|
336
|
+
ended `open` as `app-version-unsupported`. It is now read as not up yet.
|
|
337
|
+
- `atelier dev` on a port that was already taken (8137 by default) printed
|
|
338
|
+
only `[internal-error] command failed without a safe diagnostic`, and the
|
|
339
|
+
message that says what to do appeared only with `ATELIER_DEBUG=1`. It is now
|
|
340
|
+
`port-in-use`, naming the port, with `--port=<free port>` as the next step.
|
|
341
|
+
A port the operating system refuses (EACCES, usually one below 1024) is
|
|
342
|
+
`port-permission-denied` with the same remedy, and a `--port` or `PORT`
|
|
343
|
+
value that is not a port from 0 to 65535 is `port-invalid`, naming which
|
|
344
|
+
one, instead of Node's `ERR_SOCKET_BAD_PORT`. Other failures without a
|
|
345
|
+
typed code are still redacted.
|
|
346
|
+
|
|
347
|
+
## 0.2.0-alpha.11
|
|
348
|
+
|
|
349
|
+
### Fixed
|
|
350
|
+
|
|
351
|
+
- `status` now gives the same reason-specific next step as `open` for a
|
|
352
|
+
publication that stopped because an Obsidian that may hold the vault could
|
|
353
|
+
not be coordinated with (`publisher-conflict`, reason
|
|
354
|
+
`editor-uncoordinated`). The text no longer names one cause, since the
|
|
355
|
+
reason has several: another vault open, a command line that did not answer,
|
|
356
|
+
an unknown process table (on Linux, any app on a system Electron), or an app
|
|
357
|
+
started during an app-closed publication. The advice depends on whether the
|
|
358
|
+
view was ever published. Before a first publication: quit Obsidian (on
|
|
359
|
+
Linux, also any app that runs on a system Electron) so the view is
|
|
360
|
+
published, then `atelier obsidian open` starts Obsidian on it. After one:
|
|
361
|
+
the same, or open the view in the app as it is with `atelier obsidian open
|
|
362
|
+
--allow-stale`. A concurrent publisher keeps its advice.
|
|
363
|
+
- The service's app qualification checks no version for an app that is not
|
|
364
|
+
running, and that answer is remembered for up to ten seconds. An editor
|
|
365
|
+
adapter built on it could coordinate with an Obsidian started inside that
|
|
366
|
+
window, whose version nobody had checked, and publish through it in-app.
|
|
367
|
+
The adapter now receives the qualification and does not coordinate, or ask
|
|
368
|
+
the app anything, unless a version was checked. An answer that checked no
|
|
369
|
+
version (the app was not running, or none was found) is no longer reused,
|
|
370
|
+
so the next publication after the app starts reads its version and
|
|
371
|
+
coordinates. A publication that stops this way is tried again on the next
|
|
372
|
+
change or at the next full reconciliation, every five minutes.
|
|
373
|
+
- On macOS the app is found only at `/Applications/Obsidian.app`. An
|
|
374
|
+
Obsidian installed elsewhere is `app-missing`: nothing is published through
|
|
375
|
+
it, and publication waits while it runs. This is now documented.
|
|
376
|
+
- On Linux an app started through a differently named link or launcher was
|
|
377
|
+
read as absent, because the process name is the name it was started
|
|
378
|
+
through. A process whose name is not recognised is now identified by the
|
|
379
|
+
executable `/proc/<pid>/exe` resolves to. A read failure other than a
|
|
380
|
+
process that exited or whose executable this user may not read, or a table
|
|
381
|
+
in which nothing resolves, is `unknown`.
|
|
382
|
+
- The `ps` call of the process probe had no timeout, so a hung `ps` blocked
|
|
383
|
+
the service. It is killed after five seconds and the reading is `unknown`.
|
|
384
|
+
- Documented that from 0.2.0-alpha.10 the app-free publication path runs in
|
|
385
|
+
production, with its re-probe window of up to two seconds plus one note
|
|
386
|
+
while Obsidian is not running (see "Known limits" in `docs/obsidian.md`).
|
|
387
|
+
|
|
3
388
|
## 0.2.0-alpha.10
|
|
4
389
|
|
|
5
390
|
### Fixed
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ governed projections
|
|
|
54
54
|
You can see the complete loop in a disposable sample workspace:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
57
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.12
|
|
58
58
|
npx mnstry-atelier init --fixture=sample-workspace --target ./sample
|
|
59
59
|
npx mnstry-atelier graph --project ./sample/atelier.project.json
|
|
60
60
|
npx mnstry-atelier project --project ./sample/atelier.project.json
|
|
@@ -374,11 +374,11 @@ to MNSTRY, and a document MNSTRY declines can still be fully conformant.
|
|
|
374
374
|
|
|
375
375
|
## Install and go deeper
|
|
376
376
|
|
|
377
|
-
Node.js
|
|
378
|
-
remains in alpha:
|
|
377
|
+
Node.js 22 (22.18.0 or later) or Node.js 24 (24.13.1 or later) is required.
|
|
378
|
+
Pin the prerelease while the package remains in alpha:
|
|
379
379
|
|
|
380
380
|
```bash
|
|
381
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
381
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.12
|
|
382
382
|
```
|
|
383
383
|
|
|
384
384
|
Then choose the path that matches what you are building:
|
|
@@ -397,7 +397,7 @@ Then choose the path that matches what you are building:
|
|
|
397
397
|
|
|
398
398
|
## Status and command reference
|
|
399
399
|
|
|
400
|
-
Current package: `@mnstry/atelier@0.2.0-alpha.
|
|
400
|
+
Current package: `@mnstry/atelier@0.2.0-alpha.12`.
|
|
401
401
|
|
|
402
402
|
The alpha package is usable and contract-tested, but its library API may still
|
|
403
403
|
change before a stable release. Pin the exact version in production toolchains.
|
|
@@ -452,7 +452,11 @@ edit made in the editor before anything is republished. A body edit is applied
|
|
|
452
452
|
to its source manually or under a scoped policy the user installs; anything
|
|
453
453
|
structural stays a copy-only proposal. Atelier never commits to Git for it.
|
|
454
454
|
It is disabled unless a project enables it, and it is proven on one platform
|
|
455
|
-
and one app version.
|
|
455
|
+
and one app version. Every vault it publishes carries
|
|
456
|
+
[Atelier's own Obsidian plugin](docs/obsidian-plugin.md), which Obsidian asks
|
|
457
|
+
you to trust once per vault; it shows whether the view is current, writes
|
|
458
|
+
nothing, and talks only to the local maintenance service, and the projection
|
|
459
|
+
works the same without it. Read [what it does and its known limits](docs/obsidian.md),
|
|
456
460
|
the [contract](docs/obsidian-contract.md) and the
|
|
457
461
|
[maintenance service](docs/local-services.md) before relying on it. The
|
|
458
462
|
package subpaths are `@mnstry/atelier/obsidian` and
|