@symbiosis-lab/moss-api 0.7.11 → 0.10.0

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/dist/index.d.mts CHANGED
@@ -1,12 +1,20 @@
1
1
  //#region src/types/plugin.d.ts
2
2
  /**
3
- * Base plugin types shared across all Moss plugins
3
+ * Base plugin types shared across all moss plugins
4
4
  */
5
5
  interface ProjectInfo {
6
6
  project_type: string;
7
7
  content_folders: string[];
8
8
  total_files: number;
9
9
  homepage_file?: string;
10
+ /**
11
+ * Root folder basename (e.g. "刘果"). Plugins that generate a folder home
12
+ * should name it self-named (`<folder_name>.md`) with a `home: true` marker
13
+ * to match moss's folder-home convention.
14
+ */
15
+ folder_name?: string;
16
+ site_name?: string;
17
+ lang?: string;
10
18
  }
11
19
  interface PluginManifest {
12
20
  name: string;
@@ -20,8 +28,292 @@ interface PluginManifest {
20
28
  }
21
29
  type PluginCategory = "generator" | "deployer" | "syndicator" | "enhancer" | "processor";
22
30
  //#endregion
31
+ //#region src/types/messages.d.ts
32
+ /**
33
+ * Plugin message types for communication with moss
34
+ */
35
+ /**
36
+ * Messages that plugins can send to moss
37
+ */
38
+ type PluginMessage = LogMessage | ProgressMessage | ErrorMessage | CompleteMessage;
39
+ interface LogMessage {
40
+ type: "log";
41
+ level: "log" | "warn" | "error";
42
+ message: string;
43
+ }
44
+ interface ProgressMessage {
45
+ type: "progress";
46
+ phase: string;
47
+ current: number;
48
+ total: number;
49
+ message?: string;
50
+ }
51
+ interface ErrorMessage {
52
+ type: "error";
53
+ error: string;
54
+ context?: string;
55
+ fatal: boolean;
56
+ }
57
+ interface CompleteMessage {
58
+ type: "complete";
59
+ success: boolean;
60
+ error?: string;
61
+ result?: unknown;
62
+ }
63
+ //#endregion
64
+ //#region src/utils/messaging.d.ts
65
+ /**
66
+ * Set the message context for subsequent messages
67
+ * This is typically called automatically by the plugin runtime
68
+ */
69
+ declare function setMessageContext(pluginName: string, hookName: string): void;
70
+ /**
71
+ * Get the current message context
72
+ */
73
+ declare function getMessageContext(): {
74
+ pluginName: string;
75
+ hookName: string;
76
+ };
77
+ /**
78
+ * Send a message to moss
79
+ *
80
+ * Log and progress messages use events (fire-and-forget) to avoid blocking IPC.
81
+ * Complete and error messages use commands (request-response) for acknowledgment.
82
+ */
83
+ declare function sendMessage(message: PluginMessage): Promise<void>;
84
+ /**
85
+ * Report progress to moss
86
+ */
87
+ declare function reportProgress(phase: string, current: number, total: number, message?: string): Promise<void>;
88
+ /**
89
+ * Report an error to moss
90
+ */
91
+ declare function reportError(error: string, context?: string, fatal?: boolean): Promise<void>;
92
+ /**
93
+ * Report completion to moss
94
+ * @param success - Whether the operation succeeded
95
+ * @param result - Optional result data
96
+ * @param error - Optional error message (only used when success is false)
97
+ */
98
+ declare function reportComplete(success: boolean, result?: unknown, error?: string): Promise<void>;
99
+ /**
100
+ * `PluginHook` mirrors the closed Rust enum in
101
+ * `src-tauri/src/plugins/types.rs`. The router (T1) cross-products
102
+ * `PluginHook × TriggerContext` to pick a UI surface for the task.
103
+ * Plugin authors pick the hook that matches what they're doing; they
104
+ * do NOT pick the surface (the router owns that).
105
+ */
106
+ type PluginHook = "import" | "publish" | "deploy" | "syndicate" | "process" | "enhance";
107
+ /**
108
+ * `TriggerContext` mirrors the closed Rust enum. Tells the router *why*
109
+ * the task was invoked so it can pick a surface that matches the user's
110
+ * focus context (onboarding cards → ActionPanel; background sync →
111
+ * Workspace).
112
+ */
113
+ type TriggerContext = "onboarding_flow" | "settings_manual" | "background" | "manual_one";
114
+ /**
115
+ * Escape kind for `awaiting()` calls. The string variants take a
116
+ * free-text affordance label after the colon, mirroring the dev harness
117
+ * dialect (e.g., `"resend:Resend email"`, `"recheck:Recheck DNS"`).
118
+ * Plain `"cancel"` carries no label.
119
+ */
120
+ type EscapeSpec = "cancel" | `resend:${string}` | `recheck:${string}`;
121
+ /**
122
+ * Which axis of the system an advisory is about. Mirrors the Rust
123
+ * `advisory::Scope` (externally-tagged unit enum → bare string).
124
+ */
125
+ type AdvisoryScope = "File" | "Config" | "Environment" | "Remote" | "Account";
126
+ /**
127
+ * How serious the plugin proposes an advisory is. moss CLAMPS this (R13): a
128
+ * `Blocking` proposal with no actionable affordance is demoted to a quiet
129
+ * `NeedsAction` hairline dot. Mirrors the Rust `advisory::Severity`.
130
+ */
131
+ type AdvisorySeverity = "ShippedDegraded" | "NeedsAction" | "Blocking";
132
+ /**
133
+ * A closed set of in-app operations an `AdvisoryAction.InApp` can request.
134
+ * Mirrors the Rust `advisory::AppOp`.
135
+ */
136
+ type AdvisoryAppOp = "MoveFile" | "OpenBilling" | "SignIn" | "RecheckDns";
137
+ /**
138
+ * The recovery affordance for an advisory, expressed as data — mirrors the
139
+ * Rust `advisory::Action` (externally-tagged: `"None"` for the unit variant,
140
+ * `{ Variant: {...} }` for data variants). `Action !== "None"` is the gavel's
141
+ * deciding input for whether a `Blocking` proposal may pop the panel.
142
+ */
143
+ type AdvisoryAction = "None" | {
144
+ Command: {
145
+ run: string;
146
+ label: string;
147
+ };
148
+ } | {
149
+ InApp: {
150
+ op: AdvisoryAppOp;
151
+ args: unknown;
152
+ label: string;
153
+ };
154
+ } | {
155
+ Link: {
156
+ href: string;
157
+ label: string;
158
+ };
159
+ };
160
+ /**
161
+ * A plugin's PROPOSED advisory (pre-clamp). Mirrors the Rust
162
+ * `plugins::types::PluginAdvisory` wire shape exactly so it deserializes
163
+ * directly into `PluginTaskLifecycle::Succeeded/Failed { advisories }`. moss
164
+ * is the only constructor of a final `Advisory` — a plugin can never hand moss
165
+ * one (R13).
166
+ */
167
+ interface AdvisoryProposal {
168
+ /** Which axis of the system this advisory is about. */
169
+ scope: AdvisoryScope;
170
+ /** The severity the plugin REQUESTS. moss clamps it (R13). */
171
+ severity: AdvisorySeverity;
172
+ /** The item this is about — usually a filename. `null` for build-wide. */
173
+ item: string | null;
174
+ /** What happened (free text). */
175
+ what: string;
176
+ /** The recovery affordance the plugin proposes. */
177
+ action: AdvisoryAction;
178
+ }
179
+ interface StartTaskOptions {
180
+ /**
181
+ * Hook the task belongs to. Defaults to "import" — the most common
182
+ * onboarding case. Plugins should pass an explicit hook for non-import
183
+ * work (e.g., a syndicator passes "syndicate").
184
+ */
185
+ hook?: PluginHook;
186
+ /**
187
+ * Trigger context. Defaults to "background" — the safest fallback
188
+ * because Background routes to Workspace+Ambient, the quietest surface.
189
+ * Plugins running inside the onboarding flow should pass
190
+ * "onboarding_flow" explicitly so they reach the ActionPanel hairline.
191
+ */
192
+ trigger?: TriggerContext;
193
+ /**
194
+ * Hint to the router that this task will emit `progress()` updates
195
+ * with fractions. Routers and renderers MAY use this to choose between
196
+ * "fills" vs "pulses" visualizations. Defaults to true.
197
+ */
198
+ hasProgress?: boolean;
199
+ /**
200
+ * Whether the user can cancel this task from the UI. Cancellation
201
+ * plumbing lands in a later ADR phase; the flag is recorded today so
202
+ * renderers can show / hide a cancel affordance.
203
+ */
204
+ cancellable?: boolean;
205
+ /**
206
+ * Plugin-local job id referencing `contributes.jobs[id]` in the plugin's
207
+ * manifest (Step 3 Phase 5, §8 + R13). When set, moss looks up the declared
208
+ * descriptor (a past-tense `verb` + an amount `noun`), normalizes the verb
209
+ * (`Verb::normalized` — moss owns capitalization/length/glyphs), and on a
210
+ * `succeeded(receipt, count)` stamps its OWN typed `Verb` + `Amount { count,
211
+ * noun }` on the Job — rendering "Syndicated · N posts" from moss's value
212
+ * objects, never the plugin's pre-formatted `receipt` string. Omit it for
213
+ * free-text-receipt tasks (the legacy path, byte-identical).
214
+ */
215
+ job?: string;
216
+ }
217
+ /**
218
+ * Lifecycle handle returned by `startTask()`. Calls are fire-and-await:
219
+ * each method returns a promise that resolves once the Rust side has
220
+ * applied the transition to the PanelTask registry. Terminal calls
221
+ * (`succeeded`, `failed`, `cancelled`) remove the task from the registry's
222
+ * tracking store; calling any further method on the same handle will
223
+ * reject with "unknown plugin task id".
224
+ *
225
+ * The state machine matches ADR-015 § Layer 2:
226
+ *
227
+ * Running ↔ Awaiting → Succeeded | Failed | Cancelled
228
+ *
229
+ * `progress()` after `awaiting()` implicitly transitions back to Running
230
+ * (no explicit `resumed()`).
231
+ */
232
+ interface TaskHandle {
233
+ /**
234
+ * In-process task id minted by the Rust registry on `Started`.
235
+ * Exposed for log correlation and tests.
236
+ *
237
+ * Rust-side this is `u64`; specta types `u64` as `string` because
238
+ * JS numbers lose precision above 2^53. The handle carries the
239
+ * exact string through subsequent transitions so no precision is
240
+ * lost in the round-trip.
241
+ */
242
+ readonly id: string;
243
+ /** Push a progress update. `fraction` in [0,1] if known, else undefined for indeterminate. */
244
+ progress(fraction?: number, message?: string): Promise<void>;
245
+ /**
246
+ * Pause for an out-of-band user action. `directive` describes what
247
+ * the user needs to do ("click the link in your email"); `venue`
248
+ * names where ("your email") — both feed the Awaiting renderer's
249
+ * "Waiting for you to [directive] in [venue]" copy.
250
+ *
251
+ * `escape` defaults to "cancel". For non-cancel escapes, pass
252
+ * "resend:<label>" or "recheck:<label>".
253
+ */
254
+ awaiting(directive: string, venue: string, escape?: EscapeSpec): Promise<void>;
255
+ /**
256
+ * PROPOSE an advisory on this task (Step 3 Phase 5, §8 + R13). Accumulates
257
+ * the proposal on the handle; it is flushed into the next terminal call
258
+ * (`succeeded`/`failed`) as `advisories: PluginAdvisory[]`. moss holds the
259
+ * severity gavel server-side: a `Blocking` proposal with no actionable
260
+ * `action` is clamped to a quiet `NeedsAction` dot; an actionable `Blocking`
261
+ * on a `succeeded()` flips the run to `Failed` (invariant #1 — the smart
262
+ * constructor decides, not the plugin).
263
+ *
264
+ * `advise()` does NOT emit on its own; advisories ride the terminal IPC so
265
+ * moss applies them atomically with the success/failure transition. Calling
266
+ * `advise()` after a terminal call has no effect — the handle is spent (the
267
+ * terminal methods set a spent flag and `advise()` no-ops once spent).
268
+ */
269
+ advise(advisory: AdvisoryProposal): Promise<void>;
270
+ /**
271
+ * Terminal: success.
272
+ *
273
+ * @param receipt Optional human-readable receipt (the legacy free-text path).
274
+ * IGNORED for the verb/amount when the task declared a `job` descriptor —
275
+ * moss renders the receipt from its OWN normalized verb + amount instead.
276
+ * @param amount Optional success COUNT. Only meaningful when `startTask` was
277
+ * given a `job` id: moss pairs this count with the descriptor's `noun` to
278
+ * stamp `Amount { count, noun }` and renders "Syndicated · N posts" from its
279
+ * value objects.
280
+ */
281
+ succeeded(receipt?: string, amount?: number): Promise<void>;
282
+ /**
283
+ * Terminal: failure. `recoverable=false` (default) also fires the
284
+ * toast subscriber (ADR-015 § "Plugin-originated failure toasts").
285
+ */
286
+ failed(error: string, recoverable?: boolean): Promise<void>;
287
+ /** Terminal: explicit user cancellation. */
288
+ cancelled(): Promise<void>;
289
+ }
290
+ /**
291
+ * Start a plugin task. Returns a `TaskHandle` whose methods drive the
292
+ * lifecycle (progress → awaiting → succeeded/failed/cancelled).
293
+ *
294
+ * The hook + trigger pair flows into the Rust-side `route_plugin_task`
295
+ * router, which picks `(TaskScope, TaskKind, TaskTone)` — i.e., which
296
+ * UI renderer (Ambient hairline / Inline badge / Narrated titlebar /
297
+ * Awaiting pulse) surfaces the task. Plugin authors do NOT pick the
298
+ * surface; they just describe what they're doing and why.
299
+ *
300
+ * Preferred over `reportProgress()` for new code. The legacy API stays
301
+ * supported until ADR-015 Phase 3 sweeps all 151 call sites.
302
+ *
303
+ * @example
304
+ * const task = await startTask("Importing 42 articles", {
305
+ * hook: "import",
306
+ * trigger: "onboarding_flow",
307
+ * });
308
+ * for (let i = 0; i < articles.length; i++) {
309
+ * await task.progress(i / articles.length, `Article ${i + 1}/${articles.length}`);
310
+ * await importOne(articles[i]);
311
+ * }
312
+ * await task.succeeded(`Imported ${articles.length} articles`);
313
+ */
314
+ declare function startTask(label: string, options?: StartTaskOptions): Promise<TaskHandle>;
315
+ //#endregion
23
316
  //#region src/types/context.d.ts
24
-
25
317
  /**
26
318
  * Base context shared by all hooks
27
319
  *
@@ -35,8 +327,15 @@ interface BaseContext {
35
327
  }
36
328
  /**
37
329
  * Context for before_build hook (process capability)
330
+ *
331
+ * `trigger` is stamped by moss (ADR-015): the plugin reads it to declare task
332
+ * intent via `startTask`, it does NOT guess it. Onboarding card → "onboarding_flow"
333
+ * (drives the ambient hairline); every build/preview rebuild → "background".
334
+ * Optional for backward compatibility; absent ⇒ treat as "background".
38
335
  */
39
- interface BeforeBuildContext extends BaseContext {}
336
+ interface ProcessContext extends BaseContext {
337
+ trigger?: TriggerContext;
338
+ }
40
339
  /**
41
340
  * A node in the universal Page Tree.
42
341
  *
@@ -68,10 +367,8 @@ interface PageNode {
68
367
  nav: boolean;
69
368
  /** Navigation ordering (lower = first) */
70
369
  nav_weight?: number;
71
- /** Don't generate page at all */
370
+ /** Whether this page is a draft — rendered and published at its direct URL but hidden from listings, feeds, and navigation. */
72
371
  draft: boolean;
73
- /** Generate page but hide from parent's child list */
74
- unlisted: boolean;
75
372
  /** Recursively list all nested content */
76
373
  flatten: boolean;
77
374
  /** How children display: "list", "grid", or "sidebar" */
@@ -84,7 +381,7 @@ interface PageNode {
84
381
  /**
85
382
  * Context for on_build hook (generator plugins)
86
383
  */
87
- interface OnBuildContext extends BaseContext {
384
+ interface GenerateContext extends BaseContext {
88
385
  source_files: SourceFiles;
89
386
  /** Resolved Page Tree — universal content model for all generators */
90
387
  page_tree?: PageNode;
@@ -92,13 +389,45 @@ interface OnBuildContext extends BaseContext {
92
389
  /**
93
390
  * Context for on_deploy hook (deployer plugins)
94
391
  */
95
- interface OnDeployContext extends BaseContext {
392
+ interface DeployContext extends BaseContext {
96
393
  site_files: string[];
394
+ /** Custom domain from .moss/config.toml [deployment] section (if configured) */
395
+ domain?: string;
396
+ }
397
+ /**
398
+ * Context for configure_domain hook (custom domain setup on deploy platform)
399
+ *
400
+ * Called after DNS records are configured via moss-oracle. Allows deploy plugins
401
+ * to perform platform-specific domain setup (e.g., GitHub Pages CNAME configuration).
402
+ *
403
+ * This is NOT a separate capability - it's an optional hook on Deploy-capable plugins.
404
+ *
405
+ * ## Idempotency Contract
406
+ *
407
+ * The domain orchestrator calls this hook at multiple lifecycle points:
408
+ * 1. After DNS records are configured (site may not be live yet)
409
+ * 2. After the site is verified live via HTTP 200
410
+ *
411
+ * **Plugins MUST implement this hook as idempotent.** The plugin should:
412
+ * - Check current platform state
413
+ * - Do only the next needed step
414
+ * - Return success as a no-op if already fully configured
415
+ *
416
+ * Example (GitHub Pages):
417
+ * - Call 1: Sets CNAME file via API
418
+ * - Call 2: Verifies CNAME is set, enforces HTTPS via API
419
+ * - Call 3+: Both already done, returns success without changes
420
+ */
421
+ interface ConfigureDomainContext extends BaseContext {
422
+ /** The custom domain being configured (e.g., "example.com") */
423
+ domain: string;
424
+ /** Deployment information from the last deploy */
425
+ deployment: DeploymentInfo;
97
426
  }
98
427
  /**
99
428
  * Context for after_deploy hook (syndicator plugins)
100
429
  */
101
- interface AfterDeployContext extends BaseContext {
430
+ interface SyndicateContext extends BaseContext {
102
431
  site_files: string[];
103
432
  articles: ArticleInfo[];
104
433
  deployment?: DeploymentInfo;
@@ -154,7 +483,7 @@ interface DnsRecord {
154
483
  * DNS configuration provided by deploy plugins
155
484
  *
156
485
  * Plugins are responsible for generating the appropriate DNS records
157
- * for their platform. Moss just passes these through to DNS configuration.
486
+ * for their platform. moss just passes these through to DNS configuration.
158
487
  */
159
488
  interface DnsTarget {
160
489
  /** List of DNS records to configure */
@@ -194,37 +523,66 @@ interface HookResult {
194
523
  deployment?: DeploymentInfo;
195
524
  }
196
525
  //#endregion
197
- //#region src/types/messages.d.ts
526
+ //#region src/types/enhance.d.ts
198
527
  /**
199
- * Plugin message types for communication with Moss
200
- */
201
- /**
202
- * Messages that plugins can send to Moss
528
+ * Types for the content slots system.
529
+ *
530
+ * Plugins declare what content to inject and where using these types.
531
+ * Rust handles insertion during template rendering.
203
532
  */
204
- type PluginMessage = LogMessage | ProgressMessage | ErrorMessage | CompleteMessage;
205
- interface LogMessage {
206
- type: "log";
207
- level: "log" | "warn" | "error";
208
- message: string;
533
+ /** Context passed to a plugin's `enhance()` method. */
534
+ interface EnhanceContext {
535
+ project_path: string;
536
+ moss_dir: string;
537
+ output_dir: string;
538
+ config: Record<string, unknown>;
539
+ project_info: {
540
+ site_name: string;
541
+ lang: string;
542
+ };
543
+ article_map: Record<string, unknown>;
209
544
  }
210
- interface ProgressMessage {
211
- type: "progress";
212
- phase: string;
213
- current: number;
214
- total: number;
215
- message?: string;
545
+ /** Content declaration for a single slot. */
546
+ type EnhanceContent = {
547
+ type: "static";
548
+ html: string;
549
+ } | {
550
+ type: "per-page";
551
+ pages: Record<string, string>;
552
+ };
553
+ /** Result returned from a plugin's `enhance()` call. */
554
+ interface EnhanceResult {
555
+ success: boolean;
556
+ slots: Record<string, EnhanceContent>;
216
557
  }
217
- interface ErrorMessage {
218
- type: "error";
219
- error: string;
220
- context?: string;
221
- fatal: boolean;
558
+ //#endregion
559
+ //#region src/types/social.d.ts
560
+ /** One comment in the .moss/data/social/*.json shared standard.
561
+ * See moss/docs/architecture/social-data-standard.md. */
562
+ interface SocialComment {
563
+ id: string;
564
+ source: string;
565
+ content: string;
566
+ createdAt: string;
567
+ author: {
568
+ displayName: string;
569
+ url?: string;
570
+ };
571
+ replyToId?: string;
572
+ /**
573
+ * Moderation state. Absent or "active" = visible; anything else is filtered
574
+ * at read time. Well-known values: "active" | "removed" | "archived" |
575
+ * "banned" | "collapsed". The Rust side treats this as an open string so
576
+ * future states do not require a client update.
577
+ */
578
+ state?: string;
222
579
  }
223
- interface CompleteMessage {
224
- type: "complete";
225
- success: boolean;
226
- error?: string;
227
- result?: unknown;
580
+ interface SocialArticleData {
581
+ comments: SocialComment[];
582
+ }
583
+ interface SocialDataFile {
584
+ schemaVersion: string;
585
+ articles: Record<string, SocialArticleData>;
228
586
  }
229
587
  //#endregion
230
588
  //#region src/utils/tauri.d.ts
@@ -251,81 +609,37 @@ declare function getTauriCore(): TauriCore;
251
609
  */
252
610
  declare function isTauriAvailable(): boolean;
253
611
  //#endregion
254
- //#region src/utils/messaging.d.ts
255
- /**
256
- * Set the message context for subsequent messages
257
- * This is typically called automatically by the plugin runtime
258
- */
259
- declare function setMessageContext(pluginName: string, hookName: string): void;
260
- /**
261
- * Get the current message context
262
- */
263
- declare function getMessageContext(): {
264
- pluginName: string;
265
- hookName: string;
266
- };
267
- /**
268
- * Send a message to Moss
269
- *
270
- * Log and progress messages use events (fire-and-forget) to avoid blocking IPC.
271
- * Complete and error messages use commands (request-response) for acknowledgment.
272
- */
273
- declare function sendMessage(message: PluginMessage): Promise<void>;
274
- /**
275
- * Report progress to Moss
276
- */
277
- declare function reportProgress(phase: string, current: number, total: number, message?: string): Promise<void>;
278
- /**
279
- * Report an error to Moss
280
- */
281
- declare function reportError(error: string, context?: string, fatal?: boolean): Promise<void>;
282
- /**
283
- * Report completion to Moss
284
- * @param success - Whether the operation succeeded
285
- * @param result - Optional result data
286
- * @param error - Optional error message (only used when success is false)
287
- */
288
- declare function reportComplete(success: boolean, result?: unknown, error?: string): Promise<void>;
289
- //#endregion
290
- //#region src/utils/logger.d.ts
612
+ //#region src/utils/env.d.ts
291
613
  /**
292
- * Logging utilities for plugins
293
- *
294
- * @deprecated All functions in this module are deprecated.
295
- * Use console.log/warn/error directly instead.
614
+ * Plugin-side env-var access.
296
615
  *
297
- * The plugin runtime now auto-forwards all console.* calls to Rust,
298
- * eliminating the need for these async wrappers. Using console.* directly
299
- * is simpler and has no async overhead.
616
+ * Plugins run inside a webview (no Node `process.env`), so reading host
617
+ * environment variables requires a Rust→TS bridge. The
618
+ * `get_plugin_env_var` Tauri command in `src-tauri/src/plugins/runtime.rs`
619
+ * enforces a server-side allow-list — plugins cannot read arbitrary
620
+ * environment variables, only the ones moss has whitelisted for test /
621
+ * harness use.
300
622
  *
301
- * Example migration:
302
- * // Before
303
- * await log("Starting deployment...");
623
+ * Currently allow-listed (see runtime.rs `ALLOWED` constant):
624
+ * - `MOSS_MATTERS_TEST_PROFILE` — bypasses Matters auth and switches to
625
+ * public-fetch mode for the named profile (T8a e2e harness).
626
+ * - `MOSS_MATTERS_DOMAIN` — overrides the Matters domain (e.g. "matters.icu")
627
+ * so moss-claude.sh can target the test env without pre-seeding config.json.
304
628
  *
305
- * // After
306
- * console.log("Starting deployment...");
629
+ * New entries require an explicit Rust-side edit + code review.
307
630
  */
308
631
  /**
309
- * Log an informational message
632
+ * Read a host environment variable into the plugin webview.
310
633
  *
311
- * @deprecated Use console.log() directly instead.
312
- * Plugin runtime auto-forwards all console.* calls to Rust.
313
- */
314
- declare function log(message: string): Promise<void>;
315
- /**
316
- * Log a warning message
317
- *
318
- * @deprecated Use console.warn() directly instead.
319
- * Plugin runtime auto-forwards all console.* calls to Rust.
320
- */
321
- declare function warn(message: string): Promise<void>;
322
- /**
323
- * Log an error message
634
+ * Returns `undefined` if:
635
+ * - Tauri is unavailable (running outside the moss webview),
636
+ * - the variable is not in the server-side allow-list, or
637
+ * - the variable is not set in the host process.
324
638
  *
325
- * @deprecated Use console.error() directly instead.
326
- * Plugin runtime auto-forwards all console.* calls to Rust.
639
+ * Plugins should treat the return value as best-effort: a missing value
640
+ * is the production default, not an error.
327
641
  */
328
- declare function error(message: string): Promise<void>;
642
+ declare function getPluginEnvVar(name: string): Promise<string | undefined>;
329
643
  //#endregion
330
644
  //#region src/utils/browser.d.ts
331
645
  /**
@@ -353,7 +667,7 @@ interface BrowserHandle {
353
667
  closed: Promise<BrowserCloseReason>;
354
668
  }
355
669
  /**
356
- * Open a URL in the plugin browser window
670
+ * Open a URL in the action panel
357
671
  *
358
672
  * Returns a BrowserHandle that can be used to detect when the window is closed.
359
673
  *
@@ -377,9 +691,18 @@ interface BrowserHandle {
377
691
  */
378
692
  declare function openBrowser(url: string): Promise<BrowserHandle>;
379
693
  /**
380
- * Close the plugin browser window
694
+ * Close the action panel
381
695
  */
382
696
  declare function closeBrowser(): Promise<void>;
697
+ /**
698
+ * Ask the app shell to restore the editor in the action panel after a login
699
+ * flow cancel or failure. Clears the onboarding latch and re-mounts the
700
+ * editor (empty-folder onboarding cards) in the action panel slot.
701
+ *
702
+ * Call this after `promptLogin()` returns false on an import (binding /
703
+ * prompt_login) path so the user isn't left with an empty action panel.
704
+ */
705
+ declare function returnToEditor(): Promise<void>;
383
706
  /**
384
707
  * Open a URL in the system's default browser
385
708
  *
@@ -395,42 +718,93 @@ declare function closeBrowser(): Promise<void>;
395
718
  */
396
719
  declare function openSystemBrowser(url: string): Promise<void>;
397
720
  /**
398
- * Open the plugin browser with dynamic HTML content
721
+ * Open the action panel with dynamic HTML content
399
722
  *
400
723
  * Automatically injects a bridge script that exposes `window.mossApi` with:
401
- * - `submit(data)` - emits `moss:browser-form-submit` event
402
- * - `cancel()` - emits `moss:browser-form-cancel` event
403
724
  * - `close()` - closes the browser panel
404
- * - `emit(name, payload)` - escape hatch for custom events
725
+ * - `emit(name, payload)` - emits custom events for plugin-specific communication
405
726
  *
406
727
  * Uses a custom protocol (moss-plugin://) to serve HTML content
407
728
  * without requiring the `webview-data-url` Cargo feature.
408
729
  *
730
+ * **Manual lifecycle control:**
731
+ * After calling this function, the browser panel remains open until you explicitly
732
+ * call `closeBrowser()` or the user closes it. Use `listen()` to handle custom
733
+ * events emitted from the HTML.
734
+ *
409
735
  * @param html - Raw HTML content to display
410
736
  * @example
411
737
  * ```typescript
738
+ * import { openBrowserWithHtml, closeBrowser, listen } from "@symbiosis-lab/moss-api";
739
+ *
740
+ * // Open browser with custom HTML
412
741
  * await openBrowserWithHtml(`
413
742
  * <!DOCTYPE html>
414
743
  * <html>
415
744
  * <head><title>My Form</title></head>
416
745
  * <body>
417
- * <form onsubmit="event.preventDefault(); window.mossApi.submit({ name: 'Alice' })">
418
- * <input name="name" />
746
+ * <form id="myForm">
747
+ * <input id="nameInput" name="name" />
419
748
  * <button type="submit">Submit</button>
420
- * <button type="button" onclick="window.mossApi.cancel()">Cancel</button>
749
+ * <button type="button" onclick="window.mossApi.close()">Cancel</button>
421
750
  * </form>
751
+ * <script>
752
+ * document.getElementById('myForm').addEventListener('submit', (e) => {
753
+ * e.preventDefault();
754
+ * window.mossApi.emit('my-plugin:form-submit', {
755
+ * name: document.getElementById('nameInput').value
756
+ * });
757
+ * });
758
+ * </script>
422
759
  * </body>
423
760
  * </html>
424
761
  * `);
762
+ *
763
+ * // Listen for custom event from HTML
764
+ * const unlisten = await listen('my-plugin:form-submit', (event) => {
765
+ * console.log('User submitted:', event.payload);
766
+ * closeBrowser(); // Explicitly close when done
767
+ * });
425
768
  * ```
426
769
  */
427
770
  declare function openBrowserWithHtml(html: string): Promise<void>;
428
771
  /**
429
772
  * Show an HTML form in the browser panel and wait for the user to submit or cancel.
430
773
  *
431
- * The HTML should use `window.mossApi.submit(data)` to submit form data
432
- * and `window.mossApi.cancel()` to cancel. The bridge script is automatically
433
- * injected by `openBrowserWithHtml`.
774
+ * @deprecated This function couples form lifecycle to moss internals through hidden event listeners.
775
+ * Use `openBrowserWithHtml()` + manual `closeBrowser()` instead for explicit control.
776
+ *
777
+ * **Migration guide:**
778
+ * ```typescript
779
+ * // OLD (deprecated):
780
+ * const result = await showBrowserForm<LoginData>(html);
781
+ * if (result) {
782
+ * console.log("Submitted:", result);
783
+ * }
784
+ *
785
+ * // NEW (recommended):
786
+ * await openBrowserWithHtml(html);
787
+ *
788
+ * // Listen for custom event
789
+ * const unlisten = await listen<LoginData>("my-plugin:submit", (event) => {
790
+ * console.log("Submitted:", event.payload);
791
+ * closeBrowser();
792
+ * });
793
+ *
794
+ * // In your HTML:
795
+ * // <button onclick="window.mossApi.emit('my-plugin:submit', { username: '...' })">Submit</button>
796
+ * // <button onclick="window.mossApi.close()">Cancel</button>
797
+ * ```
798
+ *
799
+ * **Why migrate:**
800
+ * - Explicit browser lifecycle control (no magic auto-close)
801
+ * - No hidden event listeners (`moss:browser-form-submit`, `moss:browser-form-cancel`)
802
+ * - Simpler mental model: open, use, close
803
+ * - Matches modern plugin patterns (see Matters plugin)
804
+ *
805
+ * Note: This deprecated function still listens for `moss:browser-form-submit` and
806
+ * `moss:browser-form-cancel` events for backward compatibility. New code should use
807
+ * `window.mossApi.emit('your-event', data)` and `window.mossApi.close()` instead.
434
808
  *
435
809
  * Returns the submitted data, or `null` if the user cancelled or the timeout expired.
436
810
  * The browser is automatically closed in all cases.
@@ -438,6 +812,7 @@ declare function openBrowserWithHtml(html: string): Promise<void>;
438
812
  * @param html - Raw HTML content with a form
439
813
  * @param options - Optional configuration
440
814
  * @param options.timeoutMs - Maximum time to wait (default: 300000ms / 5 minutes)
815
+ * @param options.closeDelayMs - Optional delay before closing browser (default: 0ms / immediate)
441
816
  * @returns The submitted form data, or null on cancel/timeout
442
817
  *
443
818
  * @example
@@ -453,12 +828,12 @@ declare function openBrowserWithHtml(html: string): Promise<void>;
453
828
  * <input id="user" placeholder="Username" />
454
829
  * <input id="pass" type="password" placeholder="Password" />
455
830
  * <button type="submit">Login</button>
456
- * <button type="button" onclick="window.mossApi.cancel()">Cancel</button>
831
+ * <button type="button" onclick="window.mossApi.close()">Cancel</button>
457
832
  * </form>
458
833
  * <script>
459
834
  * document.getElementById('login').addEventListener('submit', (e) => {
460
835
  * e.preventDefault();
461
- * window.mossApi.submit({
836
+ * window.mossApi.emit('moss:browser-form-submit', {
462
837
  * username: document.getElementById('user').value,
463
838
  * password: document.getElementById('pass').value,
464
839
  * });
@@ -477,11 +852,12 @@ declare function openBrowserWithHtml(html: string): Promise<void>;
477
852
  */
478
853
  declare function showBrowserForm<T>(html: string, options?: {
479
854
  timeoutMs?: number;
855
+ closeDelayMs?: number;
480
856
  }): Promise<T | null>;
481
857
  //#endregion
482
858
  //#region src/utils/filesystem.d.ts
483
859
  /**
484
- * File system operations for Moss plugins
860
+ * File system operations for moss plugins
485
861
  *
486
862
  * These functions provide access to project files (user content).
487
863
  * Project path is auto-detected from the runtime context.
@@ -545,6 +921,21 @@ declare function writeFile(relativePath: string, content: string): Promise<void>
545
921
  * ```
546
922
  */
547
923
  declare function listFiles(): Promise<string[]>;
924
+ /** A project file with home-file annotation from Rust's detect_home_file_in_folder */
925
+ interface ProjectFileEntry {
926
+ path: string;
927
+ is_home: boolean;
928
+ }
929
+ /**
930
+ * List all project files with home-file annotations
931
+ *
932
+ * Each file is annotated with `is_home: true` if it's the detected home file
933
+ * for its containing folder (index.md, README.md, self-named folder note, etc.).
934
+ * Detection uses the same logic as the built-in generator.
935
+ *
936
+ * @returns Array of file entries with is_home annotations
937
+ */
938
+ declare function listProjectTree(): Promise<ProjectFileEntry[]>;
548
939
  /**
549
940
  * Check if a file exists in the project directory
550
941
  *
@@ -580,10 +971,80 @@ declare function fileExists(relativePath: string): Promise<boolean>;
580
971
  * ```
581
972
  */
582
973
  declare function createSymlink(targetPath: string, linkPath: string): Promise<void>;
974
+ /**
975
+ * Read a file from the compiled site directory (.moss/site/)
976
+ *
977
+ * Returns base64-encoded content. Used by deploy plugins to read
978
+ * site files without direct filesystem access.
979
+ *
980
+ * @param relativePath - Path relative to the site directory (e.g., "index.html")
981
+ * @returns Base64-encoded file content
982
+ * @throws Error if file cannot be read
983
+ *
984
+ * @example
985
+ * ```typescript
986
+ * const base64Content = await readSiteFile("index.html");
987
+ * const base64Image = await readSiteFile("assets/logo.png");
988
+ * ```
989
+ */
990
+ declare function readSiteFile(relativePath: string): Promise<string>;
991
+ /**
992
+ * Read a file from the project root as base64
993
+ *
994
+ * Returns base64-encoded content. Used by deploy plugins for
995
+ * source file backup operations.
996
+ *
997
+ * @param relativePath - Path relative to the project root
998
+ * @returns Base64-encoded file content
999
+ * @throws Error if file cannot be read
1000
+ *
1001
+ * @example
1002
+ * ```typescript
1003
+ * const base64Content = await readProjectFileBase64("posts/article.md");
1004
+ * ```
1005
+ */
1006
+ declare function readProjectFileBase64(relativePath: string): Promise<string>;
1007
+ /**
1008
+ * List source files in the project directory
1009
+ *
1010
+ * Returns relative paths of non-hidden files, excluding build artifacts
1011
+ * (.moss/, .git/, node_modules/). Used by deploy plugins for source backup.
1012
+ *
1013
+ * @returns Array of relative file paths
1014
+ * @throws Error if listing fails
1015
+ *
1016
+ * @example
1017
+ * ```typescript
1018
+ * const sourceFiles = await listSourceFiles();
1019
+ * // ["index.md", "posts/hello.md", "assets/logo.png"]
1020
+ * ```
1021
+ */
1022
+ declare function listSourceFiles(): Promise<string[]>;
1023
+ /**
1024
+ * List social data source names from .moss/data/social/*.json
1025
+ *
1026
+ * Returns file stems (without .json extension) for all JSON files
1027
+ * in the project's .moss/data/social/ directory. Returns empty array
1028
+ * if directory doesn't exist.
1029
+ *
1030
+ * @returns Array of source names (e.g., ["comment", "douban", "matters"])
1031
+ */
1032
+ declare function listSocialFiles(): Promise<string[]>;
1033
+ /** File path and size from the compiled site directory */
1034
+ interface SiteFileInfo {
1035
+ path: string;
1036
+ size: number;
1037
+ }
1038
+ /**
1039
+ * List all files in the compiled site directory with their sizes
1040
+ *
1041
+ * @returns Array of file info objects with path and size in bytes
1042
+ */
1043
+ declare function listSiteFilesWithSizes(): Promise<SiteFileInfo[]>;
583
1044
  //#endregion
584
1045
  //#region src/utils/plugin-storage.d.ts
585
1046
  /**
586
- * Plugin storage API for Moss plugins
1047
+ * Plugin storage API for moss plugins
587
1048
  *
588
1049
  * Provides access to a plugin's private storage directory at:
589
1050
  * .moss/plugins/{plugin-name}/
@@ -668,7 +1129,7 @@ declare function pluginFileExists(relativePath: string): Promise<boolean>;
668
1129
  //#endregion
669
1130
  //#region src/utils/http.d.ts
670
1131
  /**
671
- * HTTP operations for Moss plugins
1132
+ * HTTP operations for moss plugins
672
1133
  *
673
1134
  * These functions provide HTTP capabilities that bypass browser CORS
674
1135
  * restrictions by using Rust's HTTP client under the hood.
@@ -770,6 +1231,14 @@ interface PostOptions {
770
1231
  * }
771
1232
  * ```
772
1233
  */
1234
+ /**
1235
+ * Convert an HTML fragment to Markdown via moss's bundled `htmd` converter —
1236
+ * the same converter the rest of the app uses. Plugins call this instead of
1237
+ * shipping their own HTML→Markdown pass, so output (notably hard breaks, which
1238
+ * htmd renders as two trailing spaces rather than a lone backslash) is
1239
+ * consistent app-wide. Returns the input HTML unchanged if conversion fails.
1240
+ */
1241
+ declare function htmlToMarkdown(html: string): Promise<string>;
773
1242
  declare function httpPost(url: string, body: Record<string, unknown>, options?: PostOptions): Promise<FetchResult>;
774
1243
  /**
775
1244
  * Options for HTTP GET requests
@@ -804,6 +1273,65 @@ interface GetOptions {
804
1273
  * ```
805
1274
  */
806
1275
  declare function httpGet(url: string, options?: GetOptions): Promise<FetchResult>;
1276
+ /**
1277
+ * One ordered text field in a multipart/form-data POST.
1278
+ *
1279
+ * Order is preserved because the GraphQL multipart request spec requires
1280
+ * `operations` before `map` before the file parts.
1281
+ */
1282
+ interface MultipartTextField {
1283
+ /** Form field name (e.g. "operations", "map"). */
1284
+ name: string;
1285
+ /** Field value (e.g. the JSON-encoded GraphQL operation). */
1286
+ value: string;
1287
+ }
1288
+ /**
1289
+ * One file part in a multipart/form-data POST. Bytes are passed base64-encoded —
1290
+ * e.g. straight from `readSiteFile`, which already returns base64.
1291
+ */
1292
+ interface MultipartFilePart {
1293
+ /** Form field name for this file (e.g. "0" per the GraphQL multipart spec). */
1294
+ field: string;
1295
+ /** File name reported in the part's Content-Disposition. */
1296
+ filename: string;
1297
+ /** MIME type for the part's Content-Type header. */
1298
+ contentType: string;
1299
+ /** File contents, base64-encoded. */
1300
+ contentBase64: string;
1301
+ }
1302
+ /**
1303
+ * Options for a multipart POST request.
1304
+ */
1305
+ interface MultipartPostOptions {
1306
+ /** Timeout in milliseconds (default: 30000) */
1307
+ timeoutMs?: number;
1308
+ /** Additional headers (Content-Type is set automatically and cannot be overridden) */
1309
+ headers?: Record<string, string>;
1310
+ }
1311
+ /**
1312
+ * Perform an HTTP POST with a `multipart/form-data` body.
1313
+ *
1314
+ * Unlike {@link httpPost} (JSON-only), this sends ordered text fields plus
1315
+ * binary file parts — enabling uploads to GraphQL `singleFileUpload`-style
1316
+ * endpoints. File bytes are passed base64-encoded (so they survive the IPC
1317
+ * boundary and can come directly from {@link readSiteFile}); moss builds the
1318
+ * multipart body, generates the boundary, and sets the Content-Type.
1319
+ *
1320
+ * @example
1321
+ * ```typescript
1322
+ * const res = await httpPostMultipart(endpoint, {
1323
+ * textFields: [
1324
+ * { name: "operations", value: JSON.stringify({ query, variables }) },
1325
+ * { name: "map", value: JSON.stringify({ "0": ["variables.input.file"] }) },
1326
+ * ],
1327
+ * files: [{ field: "0", filename: "photo.jpg", contentType: "image/jpeg", contentBase64 }],
1328
+ * }, { headers: { "x-access-token": token } });
1329
+ * ```
1330
+ */
1331
+ declare function httpPostMultipart(url: string, parts: {
1332
+ textFields?: MultipartTextField[];
1333
+ files?: MultipartFilePart[];
1334
+ }, options?: MultipartPostOptions): Promise<FetchResult>;
807
1335
  /**
808
1336
  * Download a URL and save directly to disk
809
1337
  *
@@ -834,7 +1362,7 @@ declare function downloadAsset(url: string, targetDir: string, options?: Downloa
834
1362
  //#endregion
835
1363
  //#region src/utils/binary.d.ts
836
1364
  /**
837
- * Binary execution for Moss plugins
1365
+ * Binary execution for moss plugins
838
1366
  *
839
1367
  * Allows plugins to execute external binaries (git, npm, etc.)
840
1368
  * in a controlled environment.
@@ -856,6 +1384,15 @@ interface ExecuteOptions {
856
1384
  env?: Record<string, string>;
857
1385
  /** Data to pass to stdin (useful for commands like `git credential fill`) */
858
1386
  stdin?: string;
1387
+ /** Working directory relative to project root (default: project root itself) */
1388
+ workingDir?: string;
1389
+ /**
1390
+ * Callback for real-time stderr output. When provided, stderr lines are
1391
+ * streamed from the Rust backend via Tauri events as they are produced.
1392
+ * Useful for long-running processes like `git push` where you want to
1393
+ * show progress to the user.
1394
+ */
1395
+ onStderr?: (line: string) => void;
859
1396
  }
860
1397
  /**
861
1398
  * Result from binary execution
@@ -910,7 +1447,7 @@ declare function executeBinary(options: ExecuteOptions): Promise<ExecuteResult>;
910
1447
  //#endregion
911
1448
  //#region src/utils/platform.d.ts
912
1449
  /**
913
- * Platform detection utilities for Moss plugins
1450
+ * Platform detection utilities for moss plugins
914
1451
  *
915
1452
  * Detects the current operating system and architecture to enable
916
1453
  * platform-specific binary downloads and operations.
@@ -966,143 +1503,110 @@ declare function getPlatformInfo(): Promise<PlatformInfo>;
966
1503
  */
967
1504
  declare function clearPlatformCache(): void;
968
1505
  //#endregion
969
- //#region src/utils/archive.d.ts
970
- /**
971
- * Archive extraction utilities for Moss plugins
972
- *
973
- * Provides functions to extract .tar.gz and .zip archives using
974
- * system commands (tar, unzip, PowerShell).
975
- */
976
- /**
977
- * Supported archive formats
978
- */
979
- type ArchiveFormat = "tar.gz" | "zip";
980
- /**
981
- * Options for archive extraction
982
- */
983
- interface ExtractOptions {
984
- /** Path to the archive file (absolute) */
985
- archivePath: string;
986
- /** Directory to extract to (absolute) */
987
- destDir: string;
988
- /** Archive format (auto-detected from extension if not provided) */
989
- format?: ArchiveFormat;
990
- /** Timeout in milliseconds (default: 60000) */
991
- timeoutMs?: number;
992
- }
993
- /**
994
- * Result of archive extraction
995
- */
996
- interface ExtractResult {
997
- /** Whether extraction succeeded */
998
- success: boolean;
999
- /** Error message if extraction failed */
1000
- error?: string;
1001
- }
1506
+ //#region src/utils/binary-resolver.d.ts
1002
1507
  /**
1003
- * Extract an archive to a destination directory
1004
- *
1005
- * Uses system commands for extraction:
1006
- * - .tar.gz: `tar -xzf` (macOS/Linux)
1007
- * - .zip: `unzip` (macOS/Linux) or PowerShell `Expand-Archive` (Windows)
1008
- *
1009
- * @param options - Extraction options
1010
- * @returns Extraction result
1508
+ * Binary resolver for moss plugins
1011
1509
  *
1012
- * @example
1013
- * ```typescript
1014
- * const result = await extractArchive({
1015
- * archivePath: "/path/to/hugo.tar.gz",
1016
- * destDir: "/path/to/extract/",
1017
- * });
1510
+ * Thin wrapper around the Rust-side unified binary resolver exposed via Tauri command.
1511
+ * All resolution logic (PATH lookup, cache check, download, extraction) happens in Rust.
1018
1512
  *
1019
- * if (!result.success) {
1020
- * console.error(`Extraction failed: ${result.error}`);
1021
- * }
1022
- * ```
1513
+ * TypeScript types here mirror the Rust serde shapes from binary_resolver.rs.
1023
1514
  */
1024
- declare function extractArchive(options: ExtractOptions): Promise<ExtractResult>;
1025
1515
  /**
1026
- * Make a file executable (Unix only)
1027
- *
1028
- * Runs `chmod +x` on the specified file. No-op on Windows.
1029
- *
1030
- * @param filePath - Absolute path to the file
1031
- * @returns Whether the operation succeeded
1032
- *
1033
- * @example
1034
- * ```typescript
1035
- * await makeExecutable("/path/to/binary");
1036
- * ```
1037
- */
1038
- declare function makeExecutable(filePath: string): Promise<boolean>;
1039
- //#endregion
1040
- //#region src/utils/binary-resolver.d.ts
1041
- /**
1042
- * GitHub source configuration for binary downloads
1516
+ * GitHub Releases download source.
1043
1517
  */
1044
1518
  interface GitHubSource {
1045
1519
  /** Repository owner (e.g., "gohugoio") */
1046
1520
  owner: string;
1047
1521
  /** Repository name (e.g., "hugo") */
1048
1522
  repo: string;
1049
- /**
1050
- * Asset filename pattern with placeholders:
1051
- * - {version}: Version number (e.g., "0.139.0")
1052
- * - {os}: Operating system (darwin, linux, windows)
1053
- * - {arch}: Architecture (arm64, amd64, x64)
1054
- *
1055
- * Example: "hugo_extended_{version}_{os}-{arch}.tar.gz"
1056
- */
1057
- assetPattern: string;
1523
+ /** Asset filename pattern with placeholders: {version}, {os}, {arch} */
1524
+ asset_pattern: string;
1525
+ /** Specific release tag (e.g., "v0.123.0"). If null, fetches "latest". */
1526
+ tag?: string | null;
1058
1527
  }
1059
1528
  /**
1060
- * Binary source configuration per platform
1529
+ * Archive format for downloaded binaries.
1530
+ */
1531
+ type ArchiveFormat = "tar_gz" | "zip" | "raw";
1532
+ /**
1533
+ * Platform-specific download source for a binary.
1061
1534
  */
1062
1535
  interface BinarySource {
1063
- /** GitHub release source */
1064
- github?: GitHubSource;
1065
- /** Direct download URL (with same placeholders as assetPattern) */
1066
- directUrl?: string;
1536
+ /** Fetch a release asset via the GitHub Releases API. */
1537
+ github?: GitHubSource | null;
1538
+ /** A pinned URL to download from directly (no API call needed). */
1539
+ direct_url?: string | null;
1540
+ /** Expected SHA-256 checksum of the downloaded file (hex string). */
1541
+ sha256?: string | null;
1542
+ /** Archive format of the downloaded file. */
1543
+ archive_format?: ArchiveFormat | null;
1544
+ }
1545
+ /**
1546
+ * How to verify a binary works and extract its version.
1547
+ */
1548
+ interface VersionCheck {
1549
+ /** Arguments to pass (e.g., ["--version"]) */
1550
+ args: string[];
1551
+ /** Regex pattern with one capture group to extract the version string. */
1552
+ pattern?: string | null;
1067
1553
  }
1068
1554
  /**
1069
- * Configuration for a binary to resolve
1555
+ * Describes the internal layout of an archive for complex distributions.
1556
+ */
1557
+ interface ArchiveLayout {
1558
+ /** Path to the main binary inside the archive (e.g., "bin/git"). */
1559
+ binary_path: string;
1560
+ /** Directories where all files need chmod +x after extraction. */
1561
+ executable_dirs: string[];
1562
+ }
1563
+ /**
1564
+ * Configuration for a binary that can be resolved, cached, and downloaded.
1565
+ *
1566
+ * Platform-specific sources are keyed by platform string
1567
+ * (e.g., "darwin-arm64", "darwin-x64", "linux-x64", "windows-x64").
1070
1568
  */
1071
1569
  interface BinaryConfig {
1072
- /** Binary name (e.g., "hugo") */
1570
+ /** Human-readable name (e.g., "hugo", "ffmpeg", "git") */
1073
1571
  name: string;
1074
- /** Minimum version required (semver, optional) */
1075
- minVersion?: string;
1076
- /** Command to check version (default: "{name} version") */
1077
- versionCommand?: string;
1078
- /** Regex to extract version from command output */
1079
- versionPattern?: RegExp;
1080
- /** Download sources per platform */
1081
- sources: Partial<Record<PlatformKey, BinarySource>>;
1082
- /** Binary filename inside archive (default: same as name, or name.exe on Windows) */
1083
- binaryName?: string;
1572
+ /** Filename of the binary if different from name. */
1573
+ binary_name?: string | null;
1574
+ /** How to verify the binary works and extract its version string. */
1575
+ version_check?: VersionCheck | null;
1576
+ /** Platform-specific download sources, keyed by platform string. */
1577
+ sources: Record<string, BinarySource>;
1578
+ /** For complex archives where the binary is nested inside the archive. */
1579
+ archive_layout?: ArchiveLayout | null;
1580
+ /** Subdirectory under ~/.moss/bin/ for caching. */
1581
+ cache_dir?: string | null;
1582
+ /** Minimum disk space required (in bytes) before attempting a download. */
1583
+ required_disk_space?: number | null;
1084
1584
  }
1085
1585
  /**
1086
- * Result of binary resolution
1586
+ * How the binary was found during resolution.
1587
+ */
1588
+ type ResolutionSource = "configured_path" | "system_path" | "cache" | "downloaded";
1589
+ /**
1590
+ * The result of successfully resolving a binary.
1087
1591
  */
1088
1592
  interface BinaryResolution {
1089
- /** Absolute path to the binary */
1593
+ /** Absolute path to the binary (or just the name if found in system PATH). */
1090
1594
  path: string;
1091
- /** Detected version (if available) */
1092
- version?: string;
1093
- /** How the binary was found */
1094
- source: "config" | "path" | "plugin-storage" | "downloaded";
1595
+ /** Version string extracted from the binary output, if available. */
1596
+ version?: string | null;
1597
+ /** How the binary was found. */
1598
+ source: ResolutionSource;
1095
1599
  }
1096
1600
  /**
1097
1601
  * Options for binary resolution
1098
1602
  */
1099
1603
  interface ResolveBinaryOptions {
1100
- /** Plugin's configured binary path (from context.config) */
1604
+ /** User-configured binary path to check first. */
1101
1605
  configuredPath?: string;
1102
- /** Whether to auto-download if not found (default: true) */
1606
+ /** Whether to auto-download if not found (default: true). */
1103
1607
  autoDownload?: boolean;
1104
- /** Progress callback for UI feedback */
1105
- onProgress?: (phase: string, message: string) => void;
1608
+ /** Progress callback for download UI feedback. */
1609
+ onProgress?: (binary: string, bytesDownloaded: number, totalBytes?: number) => void;
1106
1610
  }
1107
1611
  /**
1108
1612
  * Error thrown during binary resolution
@@ -1113,37 +1617,43 @@ declare class BinaryResolutionError extends Error {
1113
1617
  constructor(message: string, phase: "detection" | "download" | "extraction" | "validation", cause?: Error | undefined);
1114
1618
  }
1115
1619
  /**
1116
- * Resolve a binary, downloading if necessary
1620
+ * Resolve a binary by invoking the Rust-side unified resolver via Tauri command.
1117
1621
  *
1118
- * Resolution order:
1119
- * 1. Configured path (from plugin config, e.g., hugo_path)
1120
- * 2. System PATH (just the binary name)
1121
- * 3. Plugin bin directory (.moss/plugins/{plugin}/bin/{name})
1122
- * 4. Download from GitHub releases (if autoDownload is true)
1622
+ * Resolution order (handled in Rust):
1623
+ * 1. Check configured path (if provided)
1624
+ * 2. Check system PATH
1625
+ * 3. Check ~/.moss/bin/ cache
1626
+ * 4. Download from configured source (if autoDownload is true)
1123
1627
  *
1124
- * @param config - Binary configuration
1125
- * @param options - Resolution options
1126
- * @returns Resolution result with path and source
1628
+ * @param config - Binary configuration matching the Rust BinaryConfig shape
1629
+ * @param options - Resolution options (configured path, auto-download, progress callback)
1630
+ * @returns Resolution result with path, version, and source
1127
1631
  * @throws BinaryResolutionError if binary cannot be resolved
1128
1632
  *
1129
1633
  * @example
1130
1634
  * ```typescript
1131
- * const hugo = await resolveBinary(HUGO_CONFIG, {
1635
+ * const resolution = await resolveBinary({
1636
+ * name: "hugo",
1637
+ * version_check: { args: ["version"], pattern: "v(\\d+\\.\\d+\\.\\d+)" },
1638
+ * sources: {
1639
+ * "darwin-arm64": {
1640
+ * github: { owner: "gohugoio", repo: "hugo", asset_pattern: "hugo_extended_{version}_darwin-arm64.tar.gz" },
1641
+ * archive_format: "tar_gz",
1642
+ * },
1643
+ * },
1644
+ * }, {
1132
1645
  * configuredPath: context.config.hugo_path,
1133
- * onProgress: (phase, msg) => reportProgress(phase, 0, 1, msg),
1646
+ * onProgress: (binary, bytes, total) => console.log(`${binary}: ${bytes}/${total}`),
1134
1647
  * });
1135
1648
  *
1136
- * await executeBinary({
1137
- * binaryPath: hugo.path,
1138
- * args: ["--version"],
1139
- * });
1649
+ * await executeBinary({ binaryPath: resolution.path, args: ["--version"] });
1140
1650
  * ```
1141
1651
  */
1142
1652
  declare function resolveBinary(config: BinaryConfig, options?: ResolveBinaryOptions): Promise<BinaryResolution>;
1143
1653
  //#endregion
1144
1654
  //#region src/utils/cookies.d.ts
1145
1655
  /**
1146
- * Cookie management for Moss plugins
1656
+ * Cookie management for moss plugins
1147
1657
  *
1148
1658
  * Allows plugins to store and retrieve authentication cookies
1149
1659
  * for external services (e.g., Matters.town, GitHub).
@@ -1210,6 +1720,15 @@ declare function getPluginCookie(): Promise<Cookie[] | null>;
1210
1720
  * ```
1211
1721
  */
1212
1722
  declare function setPluginCookie(cookies: Cookie[]): Promise<void>;
1723
+ /**
1724
+ * Delete ALL cookies on the current plugin's registered (manifest) domain from
1725
+ * the shared WebKit store. Used for force-fresh login: clears any lingering
1726
+ * server session so the login webview presents a real credential screen.
1727
+ *
1728
+ * The plugin's identity is auto-detected from the runtime context.
1729
+ * **Must be called from within a plugin hook.**
1730
+ */
1731
+ declare function clearPluginCookies(): Promise<void>;
1213
1732
  //#endregion
1214
1733
  //#region src/utils/events.d.ts
1215
1734
  /**
@@ -1285,13 +1804,13 @@ declare function waitForEvent<T>(event: string, timeoutMs?: number): Promise<T>;
1285
1804
  /**
1286
1805
  * Toast notification utilities for plugins
1287
1806
  *
1288
- * Allows plugins to display toast notifications in the main Moss UI
1807
+ * Allows plugins to display toast notifications in the main moss UI
1289
1808
  * through Tauri's event system.
1290
1809
  *
1291
1810
  * ## Design Principles
1292
1811
  *
1293
1812
  * 1. **Plugin Full Control**: Plugins specify exactly what appears in toast
1294
- * 2. **Minimal Assumptions**: Moss just renders what plugin says
1813
+ * 2. **Minimal Assumptions**: moss just renders what plugin says
1295
1814
  * 3. **Direct Path**: Plugin → showToast() → Frontend renders
1296
1815
  * 4. **Separation of Concerns**: Toast (UX) is separate from HookResult (flow control)
1297
1816
  */
@@ -1355,13 +1874,13 @@ interface ToastOptions {
1355
1874
  */
1356
1875
  type ToastType = ToastVariant;
1357
1876
  /** Event for showing a new toast */
1358
- declare const TOAST_EVENT = "plugin-toast";
1877
+ declare const TOAST_EVENT = "show-toast";
1359
1878
  /** Event for updating an existing toast by ID */
1360
- declare const TOAST_UPDATE_EVENT = "plugin-toast-update";
1879
+ declare const TOAST_UPDATE_EVENT = "show-toast-update";
1361
1880
  /** Event for dismissing a toast by ID */
1362
- declare const TOAST_DISMISS_EVENT = "plugin-toast-dismiss";
1881
+ declare const TOAST_DISMISS_EVENT = "show-toast-dismiss";
1363
1882
  /**
1364
- * Show a toast notification in the main Moss UI
1883
+ * Show a toast notification in the main moss UI
1365
1884
  *
1366
1885
  * @param options - Toast options or simple message string
1367
1886
  *
@@ -1431,5 +1950,5 @@ declare function updateToast(id: string, options: Partial<Omit<ToastOptions, "id
1431
1950
  */
1432
1951
  declare function dismissToast(id: string): Promise<void>;
1433
1952
  //#endregion
1434
- export { AfterDeployContext, ArchType, ArchiveFormat, ArticleInfo, BaseContext, BeforeBuildContext, BinaryConfig, BinaryResolution, BinaryResolutionError, BinarySource, BrowserCloseReason, BrowserHandle, CompleteMessage, Cookie, DeploymentInfo, DnsRecord, DnsTarget, DownloadOptions, DownloadResult, ErrorMessage, ExecuteOptions, ExecuteResult, ExtractOptions, ExtractResult, FetchOptions, FetchResult, GetOptions, GitHubSource, HookResult, LogMessage, OSType, OnBuildContext, OnDeployContext, PageNode, PlatformInfo, PlatformKey, PluginCategory, PluginManifest, PluginMessage, PostOptions, ProgressMessage, ProjectInfo, ResolveBinaryOptions, SourceFiles, TOAST_DISMISS_EVENT, TOAST_EVENT, TOAST_UPDATE_EVENT, TauriCore, ToastAction, ToastOptions, ToastType, ToastVariant, clearPlatformCache, closeBrowser, createSymlink, dismissToast, downloadAsset, emitEvent, error, executeBinary, extractArchive, fetchUrl, fileExists, getMessageContext, getPlatformInfo, getPluginCookie, getTauriCore, httpGet, httpPost, isEventApiAvailable, isTauriAvailable, listFiles, listPluginFiles, log, makeExecutable, onEvent, openBrowser, openBrowserWithHtml, openSystemBrowser, pluginFileExists, readFile, readPluginFile, reportComplete, reportError, reportProgress, resolveBinary, sendMessage, setMessageContext, setPluginCookie, showBrowserForm, showToast, updateToast, waitForEvent, warn, writeFile, writePluginFile };
1953
+ export { AdvisoryAction, AdvisoryAppOp, AdvisoryProposal, AdvisoryScope, AdvisorySeverity, ArchType, ArchiveLayout, ArticleInfo, BaseContext, ArchiveFormat as BinaryArchiveFormat, BinaryConfig, BinaryResolution, BinaryResolutionError, BinarySource, BrowserCloseReason, BrowserHandle, CompleteMessage, ConfigureDomainContext, Cookie, DeployContext, DeploymentInfo, DnsRecord, DnsTarget, DownloadOptions, DownloadResult, EnhanceContent, EnhanceContext, EnhanceResult, ErrorMessage, EscapeSpec, ExecuteOptions, ExecuteResult, FetchOptions, FetchResult, GenerateContext, GetOptions, GitHubSource, HookResult, LogMessage, MultipartFilePart, MultipartPostOptions, MultipartTextField, OSType, PageNode, PlatformInfo, PlatformKey, PluginCategory, PluginHook, PluginManifest, PluginMessage, PostOptions, ProcessContext, ProgressMessage, ProjectFileEntry, ProjectInfo, ResolutionSource, ResolveBinaryOptions, SiteFileInfo, SocialArticleData, SocialComment, SocialDataFile, SourceFiles, StartTaskOptions, SyndicateContext, TOAST_DISMISS_EVENT, TOAST_EVENT, TOAST_UPDATE_EVENT, TaskHandle, TauriCore, ToastAction, ToastOptions, ToastType, ToastVariant, TriggerContext, VersionCheck, clearPlatformCache, clearPluginCookies, closeBrowser, createSymlink, dismissToast, downloadAsset, emitEvent, executeBinary, fetchUrl, fileExists, getMessageContext, getPlatformInfo, getPluginCookie, getPluginEnvVar, getTauriCore, htmlToMarkdown, httpGet, httpPost, httpPostMultipart, isEventApiAvailable, isTauriAvailable, listFiles, listPluginFiles, listProjectTree, listSiteFilesWithSizes, listSocialFiles, listSourceFiles, onEvent, openBrowser, openBrowserWithHtml, openSystemBrowser, pluginFileExists, readFile, readPluginFile, readProjectFileBase64, readSiteFile, reportComplete, reportError, reportProgress, resolveBinary, returnToEditor, sendMessage, setMessageContext, setPluginCookie, showBrowserForm, showToast, startTask, updateToast, waitForEvent, writeFile, writePluginFile };
1435
1954
  //# sourceMappingURL=index.d.mts.map