primitive-admin 1.0.49 → 1.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +102 -2
  2. package/assets/skill/skills/primitive-platform/SKILL.md +85 -30
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +66 -1
  5. package/dist/bin/primitive.js.map +1 -1
  6. package/dist/src/commands/admins.d.ts +2 -0
  7. package/dist/src/commands/analytics.d.ts +2 -0
  8. package/dist/src/commands/apps.d.ts +2 -0
  9. package/dist/src/commands/apps.js +20 -0
  10. package/dist/src/commands/apps.js.map +1 -1
  11. package/dist/src/commands/auth.d.ts +2 -0
  12. package/dist/src/commands/blob-buckets.d.ts +2 -0
  13. package/dist/src/commands/catalog.d.ts +2 -0
  14. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  15. package/dist/src/commands/collections.d.ts +2 -0
  16. package/dist/src/commands/comparisons.d.ts +2 -0
  17. package/dist/src/commands/cron-triggers.d.ts +2 -0
  18. package/dist/src/commands/cron-triggers.js +8 -15
  19. package/dist/src/commands/cron-triggers.js.map +1 -1
  20. package/dist/src/commands/database-types.d.ts +2 -0
  21. package/dist/src/commands/databases.d.ts +2 -0
  22. package/dist/src/commands/databases.js +31 -0
  23. package/dist/src/commands/databases.js.map +1 -1
  24. package/dist/src/commands/documents.d.ts +2 -0
  25. package/dist/src/commands/email-templates.d.ts +2 -0
  26. package/dist/src/commands/env.d.ts +12 -0
  27. package/dist/src/commands/group-type-configs.d.ts +2 -0
  28. package/dist/src/commands/groups.d.ts +2 -0
  29. package/dist/src/commands/guides.d.ts +84 -0
  30. package/dist/src/commands/guides.js +201 -24
  31. package/dist/src/commands/guides.js.map +1 -1
  32. package/dist/src/commands/init.d.ts +17 -0
  33. package/dist/src/commands/init.js +63 -25
  34. package/dist/src/commands/init.js.map +1 -1
  35. package/dist/src/commands/integrations.d.ts +2 -0
  36. package/dist/src/commands/integrations.js +22 -5
  37. package/dist/src/commands/integrations.js.map +1 -1
  38. package/dist/src/commands/llm.d.ts +2 -0
  39. package/dist/src/commands/prompts.d.ts +2 -0
  40. package/dist/src/commands/rule-sets.d.ts +2 -0
  41. package/dist/src/commands/secrets.d.ts +2 -0
  42. package/dist/src/commands/skill.d.ts +2 -0
  43. package/dist/src/commands/sync.d.ts +113 -0
  44. package/dist/src/commands/sync.js +366 -12
  45. package/dist/src/commands/sync.js.map +1 -1
  46. package/dist/src/commands/tokens.d.ts +2 -0
  47. package/dist/src/commands/tokens.js +104 -1
  48. package/dist/src/commands/tokens.js.map +1 -1
  49. package/dist/src/commands/users.d.ts +2 -0
  50. package/dist/src/commands/waitlist.d.ts +2 -0
  51. package/dist/src/commands/waitlist.js +1 -1
  52. package/dist/src/commands/waitlist.js.map +1 -1
  53. package/dist/src/commands/webhooks.d.ts +2 -0
  54. package/dist/src/commands/workflows.d.ts +49 -0
  55. package/dist/src/commands/workflows.js +74 -21
  56. package/dist/src/commands/workflows.js.map +1 -1
  57. package/dist/src/lib/api-client.d.ts +1244 -0
  58. package/dist/src/lib/api-client.js +30 -0
  59. package/dist/src/lib/api-client.js.map +1 -1
  60. package/dist/src/lib/auth-flow.d.ts +8 -0
  61. package/dist/src/lib/cli-manifest.d.ts +60 -0
  62. package/dist/src/lib/cli-manifest.js +70 -0
  63. package/dist/src/lib/cli-manifest.js.map +1 -0
  64. package/dist/src/lib/config.d.ts +37 -0
  65. package/dist/src/lib/confirm-prompt.d.ts +66 -0
  66. package/dist/src/lib/confirm-prompt.js +85 -0
  67. package/dist/src/lib/confirm-prompt.js.map +1 -0
  68. package/dist/src/lib/constants.d.ts +2 -0
  69. package/dist/src/lib/crash-handlers.d.ts +20 -0
  70. package/dist/src/lib/crash-handlers.js +49 -0
  71. package/dist/src/lib/crash-handlers.js.map +1 -0
  72. package/dist/src/lib/credentials-store.d.ts +79 -0
  73. package/dist/src/lib/csv.d.ts +48 -0
  74. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  75. package/dist/src/lib/db-codegen/dbGenerator.d.ts +111 -0
  76. package/dist/src/lib/db-codegen/dbNaming.d.ts +45 -0
  77. package/dist/src/lib/db-codegen/dbTemplates.d.ts +97 -0
  78. package/dist/src/lib/db-codegen/dbTemplates.js +31 -10
  79. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  80. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +78 -0
  81. package/dist/src/lib/db-codegen/dbTsTypes.js +2 -2
  82. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -1
  83. package/dist/src/lib/env-resolver.d.ts +62 -0
  84. package/dist/src/lib/fetch.d.ts +5 -0
  85. package/dist/src/lib/init-config.d.ts +46 -0
  86. package/dist/src/lib/init-config.js +7 -0
  87. package/dist/src/lib/init-config.js.map +1 -1
  88. package/dist/src/lib/migration-nag.d.ts +49 -0
  89. package/dist/src/lib/output.d.ts +49 -0
  90. package/dist/src/lib/output.js +25 -1
  91. package/dist/src/lib/output.js.map +1 -1
  92. package/dist/src/lib/paginate.d.ts +33 -0
  93. package/dist/src/lib/project-config.d.ts +97 -0
  94. package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
  95. package/dist/src/lib/resolve-platform.d.ts +45 -0
  96. package/dist/src/lib/resolve-platform.js +43 -0
  97. package/dist/src/lib/resolve-platform.js.map +1 -0
  98. package/dist/src/lib/skill-installer.d.ts +23 -0
  99. package/dist/src/lib/snapshots.d.ts +99 -0
  100. package/dist/src/lib/snapshots.js +357 -0
  101. package/dist/src/lib/snapshots.js.map +1 -0
  102. package/dist/src/lib/sync-paths.d.ts +72 -0
  103. package/dist/src/lib/sync-paths.js +29 -1
  104. package/dist/src/lib/sync-paths.js.map +1 -1
  105. package/dist/src/lib/template.d.ts +93 -0
  106. package/dist/src/lib/token-inject.d.ts +56 -0
  107. package/dist/src/lib/token-inject.js +204 -0
  108. package/dist/src/lib/token-inject.js.map +1 -0
  109. package/dist/src/lib/toml-database-config.d.ts +132 -0
  110. package/dist/src/lib/toml-params-validator.d.ts +95 -0
  111. package/dist/src/lib/version-check.d.ts +10 -0
  112. package/dist/src/lib/workflow-fragments.d.ts +41 -0
  113. package/dist/src/lib/workflow-toml-validator.d.ts +86 -0
  114. package/dist/src/lib/workflow-toml-validator.js +31 -1
  115. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  116. package/dist/src/types/index.d.ts +513 -0
  117. package/dist/src/validators.d.ts +64 -0
  118. package/dist/src/validators.js +63 -0
  119. package/dist/src/validators.js.map +1 -0
  120. package/package.json +7 -1
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Project-scoped configuration (.primitive/config.json).
3
+ *
4
+ * This module handles loading, validating, and saving the project config file
5
+ * that lives inside the .primitive/ directory at the project root. The config
6
+ * defines named environments (dev, prod, staging, etc.) with their API URLs
7
+ * and app IDs.
8
+ *
9
+ * Design:
10
+ * - Config file: .primitive/config.json (or any ancestor directory)
11
+ * - The .primitive/ directory groups the committable config and the
12
+ * gitignored local state (credentials, sync data) in one place.
13
+ * - Format: JSON
14
+ * - Schema is versioned (currently 1). Future versions use semver-ish
15
+ * caret range compatibility: we accept any config where major version
16
+ * matches CURRENT_CONFIG_VERSION.
17
+ * - Invalid configs throw — we do NOT silently ignore malformed files.
18
+ */
19
+ /**
20
+ * Current schema version. Increment the major version when making
21
+ * backward-incompatible changes. Older configs will be rejected.
22
+ */
23
+ export declare const CURRENT_CONFIG_VERSION = 1;
24
+ /**
25
+ * The config file lives inside the .primitive/ directory, not at the
26
+ * project root. This keeps the project root clean and groups all
27
+ * Primitive state (config, credentials, sync data) under one directory.
28
+ */
29
+ export declare const PROJECT_CONFIG_DIR = ".primitive";
30
+ export declare const PROJECT_CONFIG_FILENAME = "config.json";
31
+ /** Display name used in user-facing messages. */
32
+ export declare const PROJECT_CONFIG_DISPLAY_NAME = ".primitive/config.json";
33
+ export interface ProjectEnvironment {
34
+ apiUrl: string;
35
+ appId?: string;
36
+ appName?: string;
37
+ /** Optional notes about this environment. */
38
+ description?: string;
39
+ }
40
+ export interface ProjectConfig {
41
+ /** Schema version (currently 1). */
42
+ version: number;
43
+ /**
44
+ * Default environment name to use when --env is not specified and
45
+ * PRIMITIVE_ENV is unset.
46
+ */
47
+ defaultEnvironment?: string;
48
+ /**
49
+ * Named environments keyed by short name (e.g. "dev", "prod", "staging").
50
+ */
51
+ environments: Record<string, ProjectEnvironment>;
52
+ }
53
+ export declare class ProjectConfigError extends Error {
54
+ readonly path?: string;
55
+ constructor(message: string, path?: string);
56
+ }
57
+ /**
58
+ * Walks upward from `startDir` looking for a `.primitive/config.json` file.
59
+ * Returns the absolute path to the config file, or null if not found.
60
+ *
61
+ * Allows running `primitive` commands from subdirectories of a project,
62
+ * much like `git` walks up to find `.git`.
63
+ */
64
+ export declare function findProjectConfigPath(startDir?: string): string | null;
65
+ /**
66
+ * Returns the project root directory (the parent of .primitive/ that contains
67
+ * .primitive/config.json), or null if no project config is found.
68
+ *
69
+ * When the PRIMITIVE_PROJECT_CONFIG env var override is active, the config
70
+ * file may not live inside a `.primitive/` directory (e.g. in tests), so
71
+ * we simply go up one level from the file.
72
+ */
73
+ export declare function findProjectRoot(startDir?: string): string | null;
74
+ /**
75
+ * Validates a raw parsed object against the ProjectConfig schema.
76
+ * Throws ProjectConfigError with a helpful message on any problem.
77
+ */
78
+ export declare function validateProjectConfig(raw: unknown, sourcePath?: string): ProjectConfig;
79
+ /**
80
+ * Loads and validates the project config from a specific file path.
81
+ * Throws ProjectConfigError on I/O errors, JSON parse errors, or schema
82
+ * validation failures — we deliberately do NOT swallow these, because a
83
+ * broken config should be noisy so the user can fix it.
84
+ */
85
+ export declare function loadProjectConfigFromPath(path: string): ProjectConfig;
86
+ /**
87
+ * Loads the project config by searching upward from the current directory.
88
+ * Returns null (not an error) if no .primitive/config.json is found anywhere.
89
+ * Still throws ProjectConfigError if a config IS found but is invalid.
90
+ */
91
+ export declare function loadProjectConfig(startDir?: string): ProjectConfig | null;
92
+ /**
93
+ * Writes a project config to disk as pretty-printed JSON. Callers are
94
+ * responsible for ensuring the parent directory exists — this function
95
+ * does not try to be clever about location.
96
+ */
97
+ export declare function saveProjectConfig(path: string, config: ProjectConfig): void;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shared helper for refreshing the admin access token via the CLI auth API.
3
+ *
4
+ * Why this module exists:
5
+ *
6
+ * - `ApiClient.refreshToken()` already POSTs to /admin/api/auth/refresh
7
+ * before every API call when the access token is near expiry.
8
+ * - `primitive token` (issue #772) now needs the same exchange so it can
9
+ * auto-refresh a stale token before printing it.
10
+ * - `primitive login --token-stdin` does the same POST a third time to
11
+ * bootstrap credentials from a refresh token piped from another CLI.
12
+ *
13
+ * Putting the HTTP exchange + response parsing in one place keeps the
14
+ * response-shape contract in a single spot (server returns both `jwt` and
15
+ * `accessToken` — see `src/index.ts:2349-2356`) and means future server
16
+ * changes to that endpoint require touching exactly one CLI file.
17
+ *
18
+ * Design notes:
19
+ *
20
+ * - This module **does not** depend on `api-client.ts`. `ApiClient` imports
21
+ * it, not the other way around — that prevents the avoidable module cycle
22
+ * codex flagged in the design review.
23
+ * - It defines its own `RefreshError` so callers can map failures to
24
+ * whatever error type (or exit code) makes sense for their surface.
25
+ * `ApiClient` translates these to `ApiError`; the `token` command
26
+ * translates them to an `error()` + `process.exit(1)`.
27
+ * - It **does not save** the refreshed credentials. The caller decides
28
+ * whether to persist (the `token` command does; `ApiClient.refreshToken`
29
+ * does). Keeping persistence caller-owned matches the existing
30
+ * "storage is owned by `credentials-store.ts`" convention.
31
+ * - It **does not print anything**. Output (success/failure messaging) is
32
+ * the caller's responsibility — important because the `token` command
33
+ * must keep stdout clean for piping.
34
+ */
35
+ import type { Credentials } from "../types/index.js";
36
+ /**
37
+ * Reasons a refresh attempt can fail. `unauthorized` covers any HTTP non-2xx
38
+ * response (typically a 401 from an expired/invalid refresh token).
39
+ * `network` covers transport-level failures (DNS, ECONNREFUSED, etc.).
40
+ * `missing-refresh-token` is a precondition failure — there's no token to
41
+ * send.
42
+ */
43
+ export type RefreshFailureKind = "missing-refresh-token" | "unauthorized" | "network";
44
+ export declare class RefreshError extends Error {
45
+ kind: RefreshFailureKind;
46
+ statusCode?: number;
47
+ cause?: unknown;
48
+ constructor(message: string, kind: RefreshFailureKind, statusCode?: number, cause?: unknown);
49
+ }
50
+ /**
51
+ * Exchange the stored refresh token for a fresh `accessToken`. Returns a new
52
+ * `Credentials` object — the caller is responsible for persistence via
53
+ * `saveCredentials()` (or whatever storage the caller owns).
54
+ *
55
+ * Server contract: `POST {serverUrl}/admin/api/auth/refresh` with
56
+ * { refreshToken: string }
57
+ * Response (200):
58
+ * { jwt, accessToken, refreshToken, expiresAt }
59
+ *
60
+ * We accept `accessToken` as the canonical field and fall back to `jwt` for
61
+ * forward-compat (the server currently emits both — see issue #772 codex
62
+ * cleanup #3). Login's legacy `data.token` form is dropped — no server
63
+ * version that's still in support emits that field.
64
+ */
65
+ export declare function refreshAdminCredentials(credentials: Credentials): Promise<Credentials>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Pure resolution of the target platform for `primitive init`.
3
+ *
4
+ * Background (issue #1009): `primitive init` must become platform-aware. The
5
+ * commander `-p, --platform` option no longer carries a `"web"` default, so an
6
+ * omitted flag is `undefined` and distinguishable from an explicit
7
+ * `--platform web`. Resolution follows a 4-case interactivity contract:
8
+ *
9
+ * (a) explicit `--platform` flag set -> use it (never prompt)
10
+ * (b) init-config TOML present (configPlatform may be set) -> use config
11
+ * value, else fall back to "web" (never prompt)
12
+ * (c) interactive TTY, no flag, no config -> prompt with `select`
13
+ * (d) non-TTY, no flag, no config -> fall back to "web" WITHOUT
14
+ * hanging and WITHOUT the `ERR_USE_AFTER_CLOSE` crash that
15
+ * `@inquirer/prompts` `select` throws on a closed/piped stdin
16
+ * (same hazard family hardened in #972/#999 — see `confirm-prompt.ts`).
17
+ *
18
+ * Extracting the decision into a pure function (with injectable `isTTY` /
19
+ * `promptFn`, mirroring `confirm-prompt.ts`) makes the otherwise
20
+ * terminal-dependent interactivity contract unit-testable.
21
+ */
22
+ export type Platform = "web" | "ios";
23
+ /** Prompt thunk returning the chosen platform. Injectable for testing. */
24
+ export type PlatformPromptFn = () => Promise<Platform>;
25
+ export interface ResolvePlatformOptions {
26
+ /** Value of the explicit `--platform` flag, or `undefined` when omitted. */
27
+ flag?: Platform;
28
+ /** `platform` key from a loaded `.primitive-init.toml`, if a config is present. */
29
+ configPlatform?: Platform;
30
+ /**
31
+ * Whether a non-interactive init-config file is in effect. When `true`, the
32
+ * prompt is skipped entirely (case b) regardless of TTY.
33
+ */
34
+ hasConfig: boolean;
35
+ /** Whether stdin is an interactive TTY. Inject `false` to exercise case (d). */
36
+ isTTY: boolean;
37
+ /** Prompt function used only in the interactive case (c). */
38
+ promptFn: PlatformPromptFn;
39
+ }
40
+ /**
41
+ * Resolve the target platform per the 4-case interactivity contract above.
42
+ *
43
+ * @returns the resolved platform (`"web"` or `"ios"`).
44
+ */
45
+ export declare function resolvePlatform(options: ResolvePlatformOptions): Promise<Platform>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure resolution of the target platform for `primitive init`.
3
+ *
4
+ * Background (issue #1009): `primitive init` must become platform-aware. The
5
+ * commander `-p, --platform` option no longer carries a `"web"` default, so an
6
+ * omitted flag is `undefined` and distinguishable from an explicit
7
+ * `--platform web`. Resolution follows a 4-case interactivity contract:
8
+ *
9
+ * (a) explicit `--platform` flag set -> use it (never prompt)
10
+ * (b) init-config TOML present (configPlatform may be set) -> use config
11
+ * value, else fall back to "web" (never prompt)
12
+ * (c) interactive TTY, no flag, no config -> prompt with `select`
13
+ * (d) non-TTY, no flag, no config -> fall back to "web" WITHOUT
14
+ * hanging and WITHOUT the `ERR_USE_AFTER_CLOSE` crash that
15
+ * `@inquirer/prompts` `select` throws on a closed/piped stdin
16
+ * (same hazard family hardened in #972/#999 — see `confirm-prompt.ts`).
17
+ *
18
+ * Extracting the decision into a pure function (with injectable `isTTY` /
19
+ * `promptFn`, mirroring `confirm-prompt.ts`) makes the otherwise
20
+ * terminal-dependent interactivity contract unit-testable.
21
+ */
22
+ /**
23
+ * Resolve the target platform per the 4-case interactivity contract above.
24
+ *
25
+ * @returns the resolved platform (`"web"` or `"ios"`).
26
+ */
27
+ export async function resolvePlatform(options) {
28
+ // (a) explicit flag wins, no prompt.
29
+ if (options.flag !== undefined) {
30
+ return options.flag;
31
+ }
32
+ // (b) non-interactive config present: use its platform or default to web.
33
+ if (options.hasConfig) {
34
+ return options.configPlatform ?? "web";
35
+ }
36
+ // (c) interactive TTY, no flag, no config: prompt.
37
+ if (options.isTTY) {
38
+ return options.promptFn();
39
+ }
40
+ // (d) non-TTY, no flag, no config: never prompt; fall back to web.
41
+ return "web";
42
+ }
43
+ //# sourceMappingURL=resolve-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-platform.js","sourceRoot":"","sources":["../../../src/lib/resolve-platform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAuBH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA+B;IAE/B,qCAAqC;IACrC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAED,0EAA0E;IAC1E,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IACzC,CAAC;IAED,mDAAmD;IACnD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,mEAAmE;IACnE,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Returns true if the skill is currently installed.
3
+ */
4
+ export declare function isSkillInstalled(): boolean;
5
+ /**
6
+ * Auto-update an already-installed skill if a newer version is bundled.
7
+ * Called after command execution for non-init, non-skill commands.
8
+ * If the skill is not installed, shows a non-blocking hint instead.
9
+ * Never throws — fails silently like version-check.
10
+ */
11
+ export declare function checkSkillStatus(): Promise<void>;
12
+ /**
13
+ * Explicitly install or reinstall the skill. Shows output.
14
+ */
15
+ export declare function installSkillExplicit(): boolean;
16
+ /**
17
+ * Uninstall the skill.
18
+ */
19
+ export declare function uninstallSkill(): boolean;
20
+ /**
21
+ * Show current skill installation status.
22
+ */
23
+ export declare function skillStatus(): void;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Sync snapshots — point-in-time backups of a sync directory taken before a
3
+ * destructive `sync pull` overwrites local TOML state (issue #578, Phase 1).
4
+ *
5
+ * A snapshot is a full recursive copy of the live sync tree (including
6
+ * `.primitive-sync.json`, the content-hash baseline) into a timestamped slot
7
+ * under the snapshots root:
8
+ *
9
+ * <snapshotsRoot>/<YYYY-MM-DDTHH-MM-SS>/
10
+ * <full pre-pull copy of the sync tree, incl. .primitive-sync.json>
11
+ * .snapshot-complete # integrity marker, written LAST
12
+ * .audit-id # ULID cross-link (Phase 2; absent in P1)
13
+ *
14
+ * The `.snapshot-complete` marker is written last so a partial/interrupted
15
+ * copy is detectable: `restoreSnapshot` refuses any snapshot missing it.
16
+ *
17
+ * Snapshots fail LOUD — `createSnapshot` throws if it can't write a complete
18
+ * snapshot. The caller (sync pull) aborts before touching any local file so we
19
+ * never perform a destructive pull without a recoverable backup.
20
+ *
21
+ * Restore is staged-then-swapped: the snapshot is materialized into a sibling
22
+ * temp dir, then the live `configDir` is atomically replaced, so a mid-restore
23
+ * crash never leaves a half-written tree.
24
+ */
25
+ /** Marker file written LAST inside a snapshot to prove it completed. */
26
+ export declare const SNAPSHOT_COMPLETE_MARKER = ".snapshot-complete";
27
+ /** Cross-link to the audit entry that produced the snapshot (Phase 2). */
28
+ export declare const SNAPSHOT_AUDIT_ID_MARKER = ".audit-id";
29
+ export interface SnapshotResult {
30
+ /** Snapshot id — the timestamp directory name (YYYY-MM-DDTHH-MM-SS). */
31
+ id: string;
32
+ /** Absolute path to the snapshot directory. */
33
+ path: string;
34
+ }
35
+ export interface SnapshotInfo {
36
+ /** Snapshot id — the timestamp directory name. */
37
+ id: string;
38
+ /** Absolute path to the snapshot directory. */
39
+ path: string;
40
+ /** Whether the `.snapshot-complete` marker is present. */
41
+ complete: boolean;
42
+ /** The audit entry id this snapshot is cross-linked to, if any. */
43
+ auditId: string | null;
44
+ /** Creation time (from the directory mtime). */
45
+ createdAt: Date;
46
+ }
47
+ /**
48
+ * Snapshot the current `syncDir` into a new timestamped slot under
49
+ * `snapshotsRoot`. Returns the created snapshot, or `null` when there's
50
+ * nothing to back up (missing/empty `syncDir`).
51
+ *
52
+ * Fails LOUD: throws if the copy or marker write fails. The partial snapshot
53
+ * dir is best-effort cleaned up before rethrowing so a failed snapshot never
54
+ * leaves a marker-less husk behind.
55
+ */
56
+ export declare function createSnapshot(syncDir: string, snapshotsRoot: string, opts?: {
57
+ auditId?: string;
58
+ now?: Date;
59
+ }): SnapshotResult | null;
60
+ /**
61
+ * List snapshots under `snapshotsRoot`, newest-first. Returns an empty array
62
+ * if the root doesn't exist. Each entry reports whether it's complete and any
63
+ * cross-linked audit id.
64
+ */
65
+ export declare function listSnapshots(snapshotsRoot: string): SnapshotInfo[];
66
+ /**
67
+ * Resolve a snapshot by id within `snapshotsRoot`. Accepts either the full
68
+ * timestamp directory name OR a unique prefix of at least `minPrefix`
69
+ * characters (default 8). Returns the matching snapshot, or throws a clear
70
+ * error on no-match / ambiguous-prefix.
71
+ *
72
+ * When `id` is omitted, resolves to the most recent COMPLETE snapshot.
73
+ */
74
+ export declare function resolveSnapshot(snapshotsRoot: string, id: string | undefined, minPrefix?: number): SnapshotInfo;
75
+ /**
76
+ * Restore a snapshot into `syncDir`, replacing whatever is there.
77
+ *
78
+ * Refuses to restore a snapshot missing the `.snapshot-complete` marker
79
+ * (partial/corrupted snapshot). The internal marker files (`.snapshot-complete`,
80
+ * `.audit-id`) are NOT copied into the restored tree.
81
+ *
82
+ * Staged-then-swapped: the snapshot is materialized into a sibling temp dir,
83
+ * then `syncDir` is atomically replaced, so a mid-restore crash never leaves a
84
+ * half-written tree.
85
+ *
86
+ * `opts.preserveDir` names a directory (the snapshots root) that lives INSIDE
87
+ * `syncDir` and must survive the full-tree swap — the Fork-4 legacy `--dir`
88
+ * case, where backups sit at `<syncDir>/.snapshots/`. Without preserving it,
89
+ * the swap would wipe the user's entire snapshot history on restore.
90
+ */
91
+ export declare function restoreSnapshot(snapshotPath: string, syncDir: string, opts?: {
92
+ preserveDir?: string;
93
+ }): void;
94
+ /**
95
+ * Prune snapshots older than `retentionDays` from `snapshotsRoot`. Only the
96
+ * given slot is touched (cross-slot retention is independent — each slot has
97
+ * its own snapshots root). No-op if the root doesn't exist.
98
+ */
99
+ export declare function pruneSnapshots(snapshotsRoot: string, retentionDays?: number): void;