@openparachute/vault 0.6.5-rc.9 → 0.6.5
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/.parachute/module.json +1 -0
- package/core/src/core.test.ts +7 -7
- package/core/src/mcp.ts +1 -1
- package/core/src/schema.ts +5 -5
- package/core/src/seed-packs.test.ts +222 -56
- package/core/src/seed-packs.ts +334 -70
- package/core/src/tag-hierarchy.ts +1 -1
- package/core/src/tag-schemas.ts +2 -2
- package/core/src/types.ts +1 -1
- package/package.json +1 -1
- package/src/admin-spa.ts +2 -1
- package/src/auth.ts +1 -1
- package/src/cli.ts +317 -53
- package/src/export-watch.ts +1 -1
- package/src/live-frame-parity.test.ts +201 -0
- package/src/module-manifest.ts +8 -0
- package/src/onboarding-seed.test.ts +82 -20
- package/src/onboarding-seed.ts +2 -2
- package/src/routes.ts +3 -3
- package/src/routing.test.ts +2 -2
- package/src/routing.ts +18 -6
- package/src/self-register.test.ts +19 -0
- package/src/self-register.ts +6 -1
- package/src/server.ts +56 -0
- package/src/services-manifest.ts +8 -0
- package/src/subscriptions.ts +100 -42
- package/src/tag-scope.ts +3 -3
- package/src/test-support/live-frame-corpus.ts +72 -0
- package/src/token-store.ts +2 -2
- package/src/transcription/build.test.ts +86 -5
- package/src/transcription/build.ts +87 -7
- package/src/transcription/capability.test.ts +25 -0
- package/src/transcription/capability.ts +27 -2
- package/src/transcription/download.test.ts +101 -0
- package/src/transcription/download.ts +71 -0
- package/src/transcription/install-python.test.ts +366 -0
- package/src/transcription/install-python.ts +471 -0
- package/src/transcription/providers/onnx-asr.test.ts +229 -0
- package/src/transcription/providers/onnx-asr.ts +239 -0
- package/src/transcription/providers/parakeet-mlx.test.ts +290 -0
- package/src/transcription/providers/parakeet-mlx.ts +242 -0
- package/src/transcription/select.test.ts +166 -1
- package/src/transcription/select.ts +234 -1
- package/src/transcription/tiers.test.ts +197 -0
- package/src/transcription/tiers.ts +184 -0
- package/src/vault-create.test.ts +19 -14
- package/src/vault.test.ts +1 -1
- package/src/ws-server.ts +408 -0
- package/src/ws-subscribe.test.ts +474 -0
- package/src/ws-subscribe.ts +242 -0
- package/src/subscribe.test.ts +0 -609
- package/src/subscribe.ts +0 -248
package/core/src/seed-packs.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* A pack is a named bundle of `tags` (upserted) + `notes` (created only when
|
|
6
6
|
* no note already lives at the path). Three packs ship today:
|
|
7
7
|
*
|
|
8
|
-
* - `welcome` — the
|
|
9
|
-
*
|
|
8
|
+
* - `welcome` — the five-guide welcome ring (Welcome, Capture anything, Tags
|
|
9
|
+
* and the graph, Connect your AI, Yours to keep) + the `capture` / `guide`
|
|
10
|
+
* tags the Notes surface expects. Default-seeded on creation.
|
|
10
11
|
* - `getting-started` — the AI-facing start-here guide (SKILL.md-style
|
|
11
12
|
* doctrine addressed to a connected assistant). Default-seeded.
|
|
12
13
|
* - `surface-starter` — the living starter guide for building a custom
|
|
@@ -14,6 +15,12 @@
|
|
|
14
15
|
* Surface Starter is out of the default seed) — added on demand via
|
|
15
16
|
* `parachute-vault add-pack surface-starter` or a console affordance.
|
|
16
17
|
*
|
|
18
|
+
* Guides are the vault's skill files — the Parachute equivalent of a
|
|
19
|
+
* `SKILL.md`. They're tagged `#guide` (GUIDE_TAG): notes that teach how this
|
|
20
|
+
* vault works and how to work it, for you and your AI alike. Some (the welcome
|
|
21
|
+
* ring) are voiced to the person; some (Getting Started) to the assistant; all
|
|
22
|
+
* are plain markdown either reads — so no per-guide audience field is needed.
|
|
23
|
+
*
|
|
17
24
|
* This module is the single source of truth for pack content across BOTH
|
|
18
25
|
* runtimes: the bun vault (`src/onboarding-seed.ts` default seed + the
|
|
19
26
|
* `add-pack` CLI verb) and the cloud vault DO (parachute-cloud
|
|
@@ -26,23 +33,63 @@
|
|
|
26
33
|
* Getting Started / Surface Starter path + content constants.
|
|
27
34
|
*/
|
|
28
35
|
|
|
29
|
-
import type { Store } from "./types.ts";
|
|
36
|
+
import type { Store, TagFieldSchema } from "./types.ts";
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Default vault descriptions — the single source of truth
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The `vault-info` description a **brand-new** vault ships with. It's the first
|
|
44
|
+
* thing a connected AI reads about the vault, so it doubles as a nudge: orient
|
|
45
|
+
* the person, read the Getting Started brief, do one small real thing, and — once
|
|
46
|
+
* real structure lands — REPLACE this text with a current picture of the vault.
|
|
47
|
+
*
|
|
48
|
+
* Exported as the single source of truth; the runtimes (bun vault default seed +
|
|
49
|
+
* cloud vault DO) consume it in a follow-up PR. Not wired into `applySeedPack`
|
|
50
|
+
* here — a description is vault metadata, not a pack note.
|
|
51
|
+
*/
|
|
52
|
+
export const DEFAULT_VAULT_DESCRIPTION =
|
|
53
|
+
'This is a brand-new personal vault — just its starter guides so far; the person it belongs to is probably new to Parachute. Before helping them set it up, read the "Getting Started" note — it\'s your onboarding brief. Short version: start with a conversation, not a filing system; learn how they want to use this and where their notes live today; do one small real thing first. When the vault has real structure, replace this description (vault-info { description: "..." }) with a current picture of what lives here and how to work it — so every future session, yours or another AI\'s, starts oriented.';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The `vault-info` description a vault gets when it was **imported/restored** —
|
|
57
|
+
* it arrived with content. Flips the AI's first job from seed-it to learn-it:
|
|
58
|
+
* orient over the existing shape, reflect it back, and only then replace this
|
|
59
|
+
* text with a current picture.
|
|
60
|
+
*/
|
|
61
|
+
export const IMPORTED_VAULT_DESCRIPTION =
|
|
62
|
+
'This vault was imported — it arrived with its own notes, tags, and history. Orient before you write: vault-info { include_stats: true }, list-tags, read a sample, and read "Getting Started" if present. Your first job is to learn the shape that\'s already here and reflect it back to the person — not to propose new structure cold. When you understand it, replace this description (vault-info { description: "..." }) with a current picture.';
|
|
30
63
|
|
|
31
64
|
// ---------------------------------------------------------------------------
|
|
32
65
|
// Pack shape
|
|
33
66
|
// ---------------------------------------------------------------------------
|
|
34
67
|
|
|
35
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* A tag declaration a pack upserts (identity row, not a schema migration).
|
|
70
|
+
*
|
|
71
|
+
* `fields` carries an optional typed-metadata schema — the same
|
|
72
|
+
* `Record<field, { type, enum?, indexed? }>` shape `update-tag` accepts — so a
|
|
73
|
+
* pack can declare a schema-carrying tag. The applier passes it straight
|
|
74
|
+
* through to `upsertTagRecord`.
|
|
75
|
+
*/
|
|
36
76
|
export interface SeedPackTag {
|
|
37
77
|
name: string;
|
|
38
78
|
description: string;
|
|
39
79
|
parent_names?: string[];
|
|
80
|
+
fields?: Record<string, TagFieldSchema>;
|
|
40
81
|
}
|
|
41
82
|
|
|
42
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* A note a pack seeds — created only when no note exists at `path`. `tags` +
|
|
85
|
+
* `metadata` are applied at create time (both runtimes' `createNote` accepts
|
|
86
|
+
* them); a guide note carries `tags: ["guide"]`.
|
|
87
|
+
*/
|
|
43
88
|
export interface SeedPackNote {
|
|
44
89
|
path: string;
|
|
45
90
|
content: string;
|
|
91
|
+
tags?: string[];
|
|
92
|
+
metadata?: Record<string, unknown>;
|
|
46
93
|
}
|
|
47
94
|
|
|
48
95
|
/** A named bundle of starter tags + notes. */
|
|
@@ -83,58 +130,173 @@ export const NOTES_REQUIRED_TAGS: ReadonlyArray<SeedPackTag> = [
|
|
|
83
130
|
},
|
|
84
131
|
];
|
|
85
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The `guide` tag — the vault's skill-file tag. Guides are the Parachute
|
|
135
|
+
* equivalent of a `SKILL.md`: notes that teach how this vault works and how to
|
|
136
|
+
* work it, for you and your AI alike. Plain markdown, so either reads them.
|
|
137
|
+
*
|
|
138
|
+
* Declared by every pack that ships guide notes (welcome / getting-started /
|
|
139
|
+
* surface-starter). The upserts converge on one row, so each pack stays
|
|
140
|
+
* self-sufficient — applying any one of them alone still lands the tag.
|
|
141
|
+
*/
|
|
142
|
+
export const GUIDE_TAG: SeedPackTag = {
|
|
143
|
+
name: "guide",
|
|
144
|
+
description:
|
|
145
|
+
"Guides — the vault's skill files. Notes that teach how this vault works and how to work it, for you and your AI alike. Plain markdown, so either reads them.",
|
|
146
|
+
};
|
|
147
|
+
|
|
86
148
|
export const WELCOME_PATH = "Welcome to your vault 🪂";
|
|
87
|
-
export const
|
|
149
|
+
export const CAPTURE_ANYTHING_PATH = "Capture anything";
|
|
150
|
+
export const TAGS_GRAPH_PATH = "Tags and the graph";
|
|
88
151
|
export const CONNECT_AI_PATH = "Connect your AI";
|
|
152
|
+
export const YOURS_TO_KEEP_PATH = "Yours to keep";
|
|
89
153
|
|
|
90
154
|
/**
|
|
91
|
-
* Build the `welcome` pack:
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
155
|
+
* Build the `welcome` pack: the five-guide welcome ring — Welcome, Capture
|
|
156
|
+
* anything, Tags and the graph, Connect your AI, Yours to keep — plus the
|
|
157
|
+
* `capture` / `guide` tags. The guides are ordinary notes tagged `#guide`
|
|
158
|
+
* (the vault's skill-file tag). They form a
|
|
159
|
+
* small linked web (Welcome → all four; the rest chain 2→3→4→5→1; Connect
|
|
160
|
+
* your AI also links the AI-facing [[Getting Started]]) so the graph view
|
|
161
|
+
* shows a connected structure from minute one. Deletable like anything else.
|
|
95
162
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* origin) the line stays generic.
|
|
163
|
+
* These five bodies are the ratified guides (already live at
|
|
164
|
+
* parachute.computer/guides). `consoleOrigin` is the origin of the operator's
|
|
165
|
+
* console (the cloud console, or a hub portal) — when known, the Connect-your-AI
|
|
166
|
+
* note names it; when omitted (the bun vault seeds at create time, often
|
|
167
|
+
* pre-expose, and must never bake in a loopback origin) the line stays generic.
|
|
102
168
|
*/
|
|
103
169
|
export function welcomePack(opts: { consoleOrigin?: string } = {}): SeedPack {
|
|
104
170
|
const consoleLine = opts.consoleOrigin
|
|
105
171
|
? `Grab the connection URL from your console at ${opts.consoleOrigin}.`
|
|
106
172
|
: `Grab the connection URL from your console.`;
|
|
173
|
+
const guideNote = { tags: ["guide"] };
|
|
107
174
|
return {
|
|
108
175
|
name: "welcome",
|
|
109
176
|
description:
|
|
110
|
-
"
|
|
111
|
-
|
|
177
|
+
"The five-guide welcome ring (Welcome, Capture anything, Tags and the graph, Connect your AI, Yours to keep) — the vault's #guide skill files — plus the capture/guide tags. Seeded by default on new vaults.",
|
|
178
|
+
// capture (Notes surface) + guide (the skill-file tag). Upserts are idempotent.
|
|
179
|
+
tags: [...NOTES_REQUIRED_TAGS, GUIDE_TAG],
|
|
112
180
|
// `[[wikilinks]]` resolve by note path — pending links auto-resolve when
|
|
113
181
|
// the target is created, so order only affects how briefly a link sits
|
|
114
|
-
// unresolved during the seed.
|
|
182
|
+
// unresolved during the seed. Every [[target]] here resolves to a note the
|
|
183
|
+
// default seed writes (the four siblings + [[Getting Started]] from the
|
|
184
|
+
// getting-started pack) — no dangling links on a fresh vault.
|
|
115
185
|
notes: [
|
|
116
186
|
{
|
|
117
187
|
path: WELCOME_PATH,
|
|
188
|
+
tags: ["guide"],
|
|
118
189
|
content: `# ${WELCOME_PATH}
|
|
119
190
|
|
|
120
191
|
This vault is yours.
|
|
121
|
-
|
|
122
|
-
Notes can link to each
|
|
192
|
+
|
|
193
|
+
Write anything — ideas, people, plans, wifi passwords. Notes can link to each
|
|
194
|
+
other, like this: [[${CAPTURE_ANYTHING_PATH}]]. Follow that link and keep going — these
|
|
195
|
+
guides form a small web you can wander:
|
|
196
|
+
|
|
197
|
+
- [[${CAPTURE_ANYTHING_PATH}]] — get thoughts in and link them together
|
|
198
|
+
- [[${TAGS_GRAPH_PATH}]] — structure that grows out of what you write
|
|
199
|
+
- [[${CONNECT_AI_PATH}]] — any AI can read and write your vault
|
|
200
|
+
- [[${YOURS_TO_KEEP_PATH}]] — export everything, anytime
|
|
201
|
+
|
|
202
|
+
They're ordinary notes, tagged #guide. Edit them, add to them, or delete the
|
|
203
|
+
lot — the vault is yours, remember.
|
|
123
204
|
`,
|
|
124
205
|
},
|
|
125
206
|
{
|
|
126
|
-
path:
|
|
127
|
-
|
|
207
|
+
path: CAPTURE_ANYTHING_PATH,
|
|
208
|
+
...guideNote,
|
|
209
|
+
content: `# ${CAPTURE_ANYTHING_PATH}
|
|
210
|
+
|
|
211
|
+
The one habit that makes a vault work: when a thought strikes, write it down.
|
|
212
|
+
Don't sort it. Don't file it. Let it land.
|
|
213
|
+
|
|
214
|
+
A note can be two words or two pages. The blog idea on a trail, the follow-up
|
|
215
|
+
from a meeting, the dream at 2am — capture first. Organizing can happen later,
|
|
216
|
+
or never; that's what tags and your AI are for.
|
|
217
|
+
|
|
218
|
+
To connect two notes, wrap a note's name in double square brackets:
|
|
219
|
+
[[${WELCOME_PATH}]] is a link back to where you started. If the note
|
|
220
|
+
doesn't exist yet, the link waits patiently and connects the moment it does.
|
|
128
221
|
|
|
129
|
-
|
|
222
|
+
That's the whole trick. Storage is easy — connection is everything. A linked
|
|
223
|
+
note is a thought your future self, and your AI, can find again.
|
|
224
|
+
|
|
225
|
+
Next: [[${TAGS_GRAPH_PATH}]].
|
|
226
|
+
`,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
path: TAGS_GRAPH_PATH,
|
|
230
|
+
...guideNote,
|
|
231
|
+
content: `# ${TAGS_GRAPH_PATH}
|
|
232
|
+
|
|
233
|
+
Notes and links come first. Tags come later, when you notice a pattern.
|
|
234
|
+
|
|
235
|
+
A tag answers "what kind of thing is this?" — #person, #recipe, #idea. Tag a
|
|
236
|
+
few notes the same way and a view lights up: all your people, all your recipes,
|
|
237
|
+
everything half-finished. Your vault starts with just two:
|
|
238
|
+
|
|
239
|
+
- #capture — the Notes app puts it on whatever you write or speak in directly,
|
|
240
|
+
marking your own raw words.
|
|
241
|
+
- #guide — these notes.
|
|
242
|
+
|
|
243
|
+
Underneath, everything is one graph: every note a node, every link an edge.
|
|
244
|
+
Open the graph view and watch it grow; ask a connected AI "what's near this
|
|
245
|
+
note?" and it walks the edges for you.
|
|
246
|
+
|
|
247
|
+
The structure isn't a filing system you design on day one. It grows out of what
|
|
248
|
+
you actually write — so don't over-organize an empty vault. Write, link, and
|
|
249
|
+
let the shape appear.
|
|
250
|
+
|
|
251
|
+
Next: [[${CONNECT_AI_PATH}]].
|
|
130
252
|
`,
|
|
131
253
|
},
|
|
132
254
|
{
|
|
133
255
|
path: CONNECT_AI_PATH,
|
|
256
|
+
...guideNote,
|
|
134
257
|
content: `# ${CONNECT_AI_PATH}
|
|
135
258
|
|
|
136
|
-
Your vault speaks MCP
|
|
137
|
-
|
|
259
|
+
Your vault speaks MCP — an open standard — so any AI can read and write it:
|
|
260
|
+
Claude, ChatGPT, Claude Code, Cursor, or an agent you build. ${consoleLine}
|
|
261
|
+
|
|
262
|
+
Once you're connected, try:
|
|
263
|
+
|
|
264
|
+
- "Read the Getting Started note and help me set this vault up."
|
|
265
|
+
- "What did I want to write about?"
|
|
266
|
+
- "Tag my untagged notes."
|
|
267
|
+
|
|
268
|
+
Your AI sees what you see — the same notes, links, and tags — and what it
|
|
269
|
+
writes lands here, as notes you can read, edit, or delete. There's even a note
|
|
270
|
+
in this vault written for it: [[${GETTING_STARTED_PATH}]], the vault-design brief your
|
|
271
|
+
AI reads so you don't have to.
|
|
272
|
+
|
|
273
|
+
One memory, shared with every AI you choose to connect. That's the point.
|
|
274
|
+
|
|
275
|
+
Next: [[${YOURS_TO_KEEP_PATH}]].
|
|
276
|
+
`,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
path: YOURS_TO_KEEP_PATH,
|
|
280
|
+
...guideNote,
|
|
281
|
+
content: `# ${YOURS_TO_KEEP_PATH}
|
|
282
|
+
|
|
283
|
+
Everything here — every note, tag, and link — exports as plain markdown files
|
|
284
|
+
with the structure intact. On Parachute Cloud, it's the Export button in your
|
|
285
|
+
console. Self-hosting, it's one command: \`parachute-vault export <dir>\`.
|
|
286
|
+
|
|
287
|
+
What you get is a folder any app can open, and another Parachute can import
|
|
288
|
+
losslessly. Move from our cloud to your own machine, or the other way — same
|
|
289
|
+
software, same format, nothing held hostage between the doors.
|
|
290
|
+
|
|
291
|
+
We build it this way on purpose. A connection you can't leave isn't a
|
|
292
|
+
relationship. You can always leave — which is exactly why you can trust
|
|
293
|
+
staying.
|
|
294
|
+
|
|
295
|
+
Export isn't a feature we grudgingly support; it's the moral center of the
|
|
296
|
+
design. We test it constantly, because a promise you don't test is just a
|
|
297
|
+
hope.
|
|
298
|
+
|
|
299
|
+
Back to [[${WELCOME_PATH}]].
|
|
138
300
|
`,
|
|
139
301
|
},
|
|
140
302
|
],
|
|
@@ -156,24 +318,98 @@ export const SURFACE_STARTER_PATH = "Surface Starter";
|
|
|
156
318
|
/**
|
|
157
319
|
* Body of the seeded `Getting Started` note.
|
|
158
320
|
*
|
|
159
|
-
* Voice: addressed to the connected AI ("you"), practical, SKILL.md-style.
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
321
|
+
* Voice: addressed to the connected AI ("you"), warm and practical, SKILL.md-style.
|
|
322
|
+
* ONE note, two labeled parts:
|
|
323
|
+
* - Part 1 — Onboarding the person: lead with a real conversation, leverage
|
|
324
|
+
* prior context, offer to survey their content, do one small real thing
|
|
325
|
+
* first, the data-in menu (import UI / MCP bridge / paste-and-file — NOT the
|
|
326
|
+
* CLI, NOT Obsidian-first), the arrived-with-content branch, and closing the
|
|
327
|
+
* loop by updating the vault description (see DEFAULT_VAULT_DESCRIPTION).
|
|
328
|
+
* - Part 2 — Vault mechanics: the preserved reference — what a vault is + the
|
|
329
|
+
* tool list, tags-vs-paths-vs-schemas, write gotchas, the custom-surface
|
|
330
|
+
* pointer, and "Adapt this note."
|
|
331
|
+
*
|
|
332
|
+
* Deliberately points AIs at the import UI + MCP bridge, never at a
|
|
333
|
+
* `parachute-vault import` CLI (that verb doesn't exist on cloud — the old copy
|
|
334
|
+
* shipped that live bug to every hosted user). Mentions the Surface Starter PACK
|
|
335
|
+
* by name (no wikilink — the note isn't seeded by default, and a dangling
|
|
336
|
+
* `[[Surface Starter]]` would sit unresolved on every fresh vault).
|
|
165
337
|
*/
|
|
166
338
|
export const GETTING_STARTED_CONTENT = `# Getting Started
|
|
167
339
|
|
|
168
|
-
This is the **start-here guide** for this Parachute vault —
|
|
169
|
-
\`SKILL.md
|
|
170
|
-
|
|
171
|
-
need to re-read it every session. It
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
340
|
+
This is the **start-here guide** for this Parachute vault — instructions for a
|
|
341
|
+
connected AI (you), like a \`SKILL.md\`. Read it when the vault is new, when
|
|
342
|
+
you're orienting yourself, or when the person says *"help me set up my
|
|
343
|
+
parachute."* You don't need to re-read it every session. It has two parts:
|
|
344
|
+
**how to onboard the person** (start there if they're new) and **vault
|
|
345
|
+
mechanics** (reference for operating the vault). It's a starting point, not a
|
|
346
|
+
script — and it's editable (see "Adapt this note" at the end).
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Part 1 — Onboarding the person
|
|
351
|
+
|
|
352
|
+
### Start with a conversation, not a filing system
|
|
353
|
+
|
|
354
|
+
If this vault is fresh, this is probably the person's first real session with
|
|
355
|
+
it. Don't start building — orient them, briefly, in your own words: this vault
|
|
356
|
+
is **their** memory, one place every AI they choose to connect can read and
|
|
357
|
+
write, and they can export all of it anytime as plain markdown. Then have a
|
|
358
|
+
real conversation:
|
|
359
|
+
|
|
360
|
+
- **How do they imagine using this?** A journal, a project brain, a people
|
|
361
|
+
directory, meeting notes, a commonplace book — or they don't know yet
|
|
362
|
+
(that's fine; capture-first works).
|
|
363
|
+
- **Where do their notes and data live now?** Another app, a pile of markdown,
|
|
364
|
+
another Parachute — or nowhere yet.
|
|
365
|
+
- **What's alive for them right now?** A project, a decision, a trip, a draft.
|
|
366
|
+
The first note should be about something real.
|
|
367
|
+
|
|
368
|
+
**Use what you already know.** If you have memory or context about this person
|
|
369
|
+
from working with them, bring it — propose the two or three kinds of
|
|
370
|
+
information *they specifically* would want here ("you mention a lot of book
|
|
371
|
+
ideas — want a place for those?"). Propose, confirm, then write; never
|
|
372
|
+
bulk-create structure they didn't agree to. You can also **offer to survey**
|
|
373
|
+
what they already have — "want me to look through what you've brought in and
|
|
374
|
+
suggest a few ways to organize it?" — then read a sample and propose, don't
|
|
375
|
+
impose.
|
|
376
|
+
|
|
377
|
+
### Do one small real thing first
|
|
378
|
+
|
|
379
|
+
The failure mode of every notes tool is over-organizing an empty vault. So:
|
|
380
|
+
**one small real thing this session.** Capture a thought they actually have,
|
|
381
|
+
file one document, or bring in one small batch — then stop and show them what
|
|
382
|
+
happened. Structure grows from real notes (that's Part 2's design vocabulary).
|
|
383
|
+
Setup is a relationship over many sessions, not an install step.
|
|
384
|
+
|
|
385
|
+
Five short guides ship in this vault for the person to read (tagged \`#guide\`):
|
|
386
|
+
[[Welcome to your vault 🪂]], [[Capture anything]], [[Tags and the graph]],
|
|
387
|
+
[[Connect your AI]], and [[Yours to keep]]. Point them there for anything you'd
|
|
388
|
+
otherwise lecture about — they're the return-point between sessions. If a
|
|
389
|
+
session ends mid-setup, leave a breadcrumb (update this note or the vault
|
|
390
|
+
description) so the next one picks up where you left off.
|
|
391
|
+
|
|
392
|
+
### If this vault already has content
|
|
393
|
+
|
|
394
|
+
Some vaults arrive full — an import, a restore, months of use. Then your first
|
|
395
|
+
job is to **learn it, not seed it**: \`vault-info { include_stats: true }\`,
|
|
396
|
+
\`list-tags\`, read a sample (\`query-notes { search: "..." }\`), then reflect the
|
|
397
|
+
shape back to the person and ask what's missing or wrong. Propose structure
|
|
398
|
+
only once you can describe what's already there.
|
|
399
|
+
|
|
400
|
+
### Close the loop: describe the vault
|
|
401
|
+
|
|
402
|
+
When the first real structure lands, update the vault description —
|
|
403
|
+
\`vault-info { description: "..." }\` — so every future session (yours or another
|
|
404
|
+
AI's) starts oriented: what this vault is for, its main tags, its conventions.
|
|
405
|
+
The default description says "brand-new vault"; once that stops being true,
|
|
406
|
+
replace it. Keep it a few sentences (the projection carries the schema detail,
|
|
407
|
+
and this note carries the richer conventions — see "Adapt this note"). Bringing
|
|
408
|
+
their existing data in is covered under "Bringing existing notes in" below.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Part 2 — Vault mechanics
|
|
177
413
|
|
|
178
414
|
## What a Parachute vault is
|
|
179
415
|
|
|
@@ -267,33 +503,40 @@ A few behaviors worth knowing before you write at scale:
|
|
|
267
503
|
|
|
268
504
|
(Full design guide, with copy-paste examples: https://parachute.computer/scripting/)
|
|
269
505
|
|
|
270
|
-
##
|
|
506
|
+
## Bringing existing notes in
|
|
507
|
+
|
|
508
|
+
If the person already keeps notes somewhere, help them bring what matters —
|
|
509
|
+
start with what's alive for them, not the whole archive. What works today:
|
|
271
510
|
|
|
272
|
-
|
|
273
|
-
|
|
511
|
+
- **Connect the source over MCP (the flexible path).** If their AI client can
|
|
512
|
+
add other MCP servers — a Google Drive server, a Notion server, a filesystem
|
|
513
|
+
server — connect it alongside this vault in the same session. Then read from
|
|
514
|
+
there and \`create-note\` here: a selective, conversational migration, no
|
|
515
|
+
export file needed.
|
|
516
|
+
- **Paste and file.** They paste anything — a doc, a list, an export — and you
|
|
517
|
+
\`create-note\` it (batch mode for many at once).
|
|
518
|
+
- **Import a Parachute export.** Moving between Parachute vaults (cloud ⇄
|
|
519
|
+
self-host) is lossless — on Parachute Cloud, the console's Import button —
|
|
520
|
+
ids, tags, links, schemas, and attachments round-trip.
|
|
274
521
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- **Ad hoc / pasted content:** just \`create-note\` it. Then help the operator tag
|
|
281
|
-
and schematize: read a sample of imported notes, propose a small tag
|
|
282
|
-
vocabulary, and apply it.
|
|
522
|
+
Bringing a whole Obsidian or Markdown library in is getting easier, and a
|
|
523
|
+
flexible import is on the way; for now the connect-over-MCP and paste paths
|
|
524
|
+
handle it without anyone touching a terminal. Whatever they bring, offer to
|
|
525
|
+
survey it and suggest a first structure — read a sample, propose a small tag
|
|
526
|
+
vocabulary, confirm, then apply. Don't impose structure silently.
|
|
283
527
|
|
|
284
|
-
After
|
|
285
|
-
\`list-tags\`
|
|
286
|
-
spot-check.
|
|
528
|
+
After bringing content in, orient: \`vault-info\` for the schema picture,
|
|
529
|
+
\`list-tags\` for the vocabulary that arrived, \`query-notes { search: "..." }\`
|
|
530
|
+
to spot-check.
|
|
287
531
|
|
|
288
532
|
## Later: a custom surface
|
|
289
533
|
|
|
290
534
|
Building a custom UI over the vault (a dashboard, a notes app) is usually **not**
|
|
291
|
-
the starting point — get the notes and structure right first.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
\`
|
|
295
|
-
|
|
296
|
-
\`@openparachute/surface-client\` + \`@openparachute/surface-render\`.
|
|
535
|
+
the starting point — get the notes and structure right first. When the person
|
|
536
|
+
wants one, add the **Surface Starter** guide to this vault — an optional pack
|
|
537
|
+
(not installed by default) that covers building a surface with
|
|
538
|
+
\`@openparachute/surface-client\` + \`@openparachute/surface-render\`. On Parachute
|
|
539
|
+
Cloud it's the console's add-pack affordance.
|
|
297
540
|
|
|
298
541
|
## Adapt this note
|
|
299
542
|
|
|
@@ -313,8 +556,16 @@ export const GETTING_STARTED_PACK: SeedPack = {
|
|
|
313
556
|
name: "getting-started",
|
|
314
557
|
description:
|
|
315
558
|
"The AI-facing start-here guide: vault design vocabulary (tags/paths/schemas), imports, write gotchas. Seeded by default on new vaults.",
|
|
316
|
-
|
|
317
|
-
|
|
559
|
+
// Declares GUIDE_TAG too — each pack is self-sufficient; the upsert converges
|
|
560
|
+
// with the welcome pack's. This guide is written FOR the AI.
|
|
561
|
+
tags: [GUIDE_TAG],
|
|
562
|
+
notes: [
|
|
563
|
+
{
|
|
564
|
+
path: GETTING_STARTED_PATH,
|
|
565
|
+
tags: ["guide"],
|
|
566
|
+
content: GETTING_STARTED_CONTENT,
|
|
567
|
+
},
|
|
568
|
+
],
|
|
318
569
|
};
|
|
319
570
|
|
|
320
571
|
// ---------------------------------------------------------------------------
|
|
@@ -450,8 +701,16 @@ export const SURFACE_STARTER_PACK: SeedPack = {
|
|
|
450
701
|
name: "surface-starter",
|
|
451
702
|
description:
|
|
452
703
|
"A living starter guide for building a custom surface (UI) over this vault with the published surface packages. Opt-in — not seeded by default.",
|
|
453
|
-
|
|
454
|
-
|
|
704
|
+
// A guide too (written for the AI) — declares GUIDE_TAG so the opt-in
|
|
705
|
+
// add-pack path lands the tag even if this pack is applied on its own.
|
|
706
|
+
tags: [GUIDE_TAG],
|
|
707
|
+
notes: [
|
|
708
|
+
{
|
|
709
|
+
path: SURFACE_STARTER_PATH,
|
|
710
|
+
tags: ["guide"],
|
|
711
|
+
content: SURFACE_STARTER_CONTENT,
|
|
712
|
+
},
|
|
713
|
+
],
|
|
455
714
|
};
|
|
456
715
|
|
|
457
716
|
// ---------------------------------------------------------------------------
|
|
@@ -541,18 +800,23 @@ export async function applySeedPack(
|
|
|
541
800
|
await store.upsertTagRecord(decl.name, {
|
|
542
801
|
description: decl.description,
|
|
543
802
|
...(decl.parent_names ? { parent_names: decl.parent_names } : {}),
|
|
803
|
+
...(decl.fields ? { fields: decl.fields } : {}),
|
|
544
804
|
});
|
|
545
805
|
result.tags.push(decl.name);
|
|
546
806
|
}
|
|
547
807
|
|
|
548
|
-
for (const
|
|
549
|
-
const existing = await store.getNoteByPath(path);
|
|
808
|
+
for (const note of pack.notes) {
|
|
809
|
+
const existing = await store.getNoteByPath(note.path);
|
|
550
810
|
if (existing) {
|
|
551
|
-
result.skippedNotes.push(path);
|
|
811
|
+
result.skippedNotes.push(note.path);
|
|
552
812
|
continue;
|
|
553
813
|
}
|
|
554
|
-
await store.createNote(content, {
|
|
555
|
-
|
|
814
|
+
await store.createNote(note.content, {
|
|
815
|
+
path: note.path,
|
|
816
|
+
tags: note.tags,
|
|
817
|
+
metadata: note.metadata,
|
|
818
|
+
});
|
|
819
|
+
result.seededNotes.push(note.path);
|
|
556
820
|
}
|
|
557
821
|
|
|
558
822
|
return result;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* History: pre-v14 vaults stored hierarchy in notes-as-config at
|
|
10
10
|
* `_tags/<name>`. The v14 migration (see core/src/schema.ts:migrateToV14)
|
|
11
11
|
* lifts those parent declarations onto the tags row and the resolver here
|
|
12
|
-
* was swapped accordingly. See
|
|
12
|
+
* was swapped accordingly. See docs/contracts/tag-data-model.md.
|
|
13
13
|
*
|
|
14
14
|
* Resolution model:
|
|
15
15
|
* - Lazy: built on first access, cached on the store.
|
package/core/src/tag-schemas.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Each tag row carries: human-readable description, indexed metadata field
|
|
5
5
|
* declarations (`fields`), typed-link relationship declarations
|
|
6
6
|
* (`relationships`), and the hierarchy parent list (`parent_names`).
|
|
7
|
-
* See
|
|
7
|
+
* See docs/contracts/tag-data-model.md.
|
|
8
8
|
*
|
|
9
9
|
* This module retains the historical `tag-schemas` filename and exports
|
|
10
10
|
* (`TagSchema`, `listTagSchemas`, `getTagSchema`, `upsertTagSchema`,
|
|
@@ -51,7 +51,7 @@ export interface TagFieldSchema {
|
|
|
51
51
|
* `{ target_tag, cardinality }` declaration — but `relationships` is now an
|
|
52
52
|
* opaque vocabulary map (see `TagRelationshipMap` / `validateRelationships`),
|
|
53
53
|
* so this is one valid value shape among many, not a required one.
|
|
54
|
-
* See
|
|
54
|
+
* See docs/contracts/tag-data-model.md §Relationships.
|
|
55
55
|
*/
|
|
56
56
|
export type TagRelCardinality = "one" | "optional" | "many" | "many-required";
|
|
57
57
|
|
package/core/src/types.ts
CHANGED
|
@@ -406,7 +406,7 @@ export interface Store {
|
|
|
406
406
|
|
|
407
407
|
// Tag records — full v14 identity row (description + fields + typed
|
|
408
408
|
// relationships + parent_names + timestamps). See
|
|
409
|
-
//
|
|
409
|
+
// docs/contracts/tag-data-model.md.
|
|
410
410
|
listTagRecords(): Promise<TagRecord[]>;
|
|
411
411
|
getTagRecord(tag: string): Promise<TagRecord | null>;
|
|
412
412
|
/**
|
package/package.json
CHANGED
package/src/admin-spa.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Per-vault mount (vault#252): the SPA lives under `/vault/<name>/admin/*`
|
|
10
10
|
* rather than the origin-rooted `/admin/*` it shipped with. The hub only
|
|
11
|
-
* proxies `/vault/<name>/*` paths (per
|
|
11
|
+
* proxies `/vault/<name>/*` paths (per the module protocol —
|
|
12
|
+
* https://github.com/ParachuteComputer/parachute-hub/blob/main/docs/contracts/module-protocol.md),
|
|
12
13
|
* so an origin-rooted SPA is unreachable through the hub. Asset URLs are
|
|
13
14
|
* relative (Vite `base: "./"`), so the same bundle works at any mount
|
|
14
15
|
* point — no rebuild per vault.
|
package/src/auth.ts
CHANGED
|
@@ -123,7 +123,7 @@ export interface AuthResult {
|
|
|
123
123
|
* Tag-allowlist (root tag names) for tag-scoped tokens. NULL = unscoped
|
|
124
124
|
* (current full-vault behavior). Hub-issued JWTs and legacy YAML keys
|
|
125
125
|
* always have null — tag scoping is a per-token vault-DB attribute, not
|
|
126
|
-
* an OAuth-claim concern. See
|
|
126
|
+
* an OAuth-claim concern. See docs/contracts/tag-scoped-tokens.md.
|
|
127
127
|
*/
|
|
128
128
|
scoped_tags: string[] | null;
|
|
129
129
|
/**
|