@celestea/studio 2.7.1

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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app.d.ts +111 -0
  3. package/dist/app.js +219 -0
  4. package/dist/auth/api-token.d.ts +76 -0
  5. package/dist/auth/api-token.js +172 -0
  6. package/dist/auth/htpasswd.d.ts +22 -0
  7. package/dist/auth/htpasswd.js +61 -0
  8. package/dist/auth/index.d.ts +14 -0
  9. package/dist/auth/index.js +14 -0
  10. package/dist/auth/page.d.ts +19 -0
  11. package/dist/auth/page.js +73 -0
  12. package/dist/auth/rate-limit.d.ts +27 -0
  13. package/dist/auth/rate-limit.js +47 -0
  14. package/dist/auth/token.d.ts +49 -0
  15. package/dist/auth/token.js +112 -0
  16. package/dist/config.d.ts +78 -0
  17. package/dist/config.js +72 -0
  18. package/dist/deployment.d.ts +92 -0
  19. package/dist/deployment.js +154 -0
  20. package/dist/fake-runtime-adapter.d.ts +37 -0
  21. package/dist/fake-runtime-adapter.js +276 -0
  22. package/dist/handlers/auth.d.ts +24 -0
  23. package/dist/handlers/auth.js +155 -0
  24. package/dist/handlers/common.d.ts +76 -0
  25. package/dist/handlers/common.js +127 -0
  26. package/dist/handlers/config-shape.d.ts +76 -0
  27. package/dist/handlers/config-shape.js +227 -0
  28. package/dist/handlers/config.d.ts +26 -0
  29. package/dist/handlers/config.js +161 -0
  30. package/dist/handlers/context-shape.d.ts +33 -0
  31. package/dist/handlers/context-shape.js +59 -0
  32. package/dist/handlers/dialog.d.ts +22 -0
  33. package/dist/handlers/dialog.js +292 -0
  34. package/dist/handlers/exec.d.ts +22 -0
  35. package/dist/handlers/exec.js +131 -0
  36. package/dist/handlers/fs-read.d.ts +63 -0
  37. package/dist/handlers/fs-read.js +159 -0
  38. package/dist/handlers/fs.d.ts +57 -0
  39. package/dist/handlers/fs.js +153 -0
  40. package/dist/handlers/grants-shape.d.ts +45 -0
  41. package/dist/handlers/grants-shape.js +130 -0
  42. package/dist/handlers/grants.d.ts +21 -0
  43. package/dist/handlers/grants.js +267 -0
  44. package/dist/handlers/health.d.ts +37 -0
  45. package/dist/handlers/health.js +139 -0
  46. package/dist/handlers/index.d.ts +40 -0
  47. package/dist/handlers/index.js +85 -0
  48. package/dist/handlers/permissions.d.ts +9 -0
  49. package/dist/handlers/permissions.js +183 -0
  50. package/dist/handlers/plugins.d.ts +18 -0
  51. package/dist/handlers/plugins.js +23 -0
  52. package/dist/handlers/prompts.d.ts +12 -0
  53. package/dist/handlers/prompts.js +120 -0
  54. package/dist/handlers/providers.d.ts +12 -0
  55. package/dist/handlers/providers.js +178 -0
  56. package/dist/handlers/questions.d.ts +23 -0
  57. package/dist/handlers/questions.js +115 -0
  58. package/dist/handlers/session-model.d.ts +38 -0
  59. package/dist/handlers/session-model.js +105 -0
  60. package/dist/handlers/session-move.d.ts +22 -0
  61. package/dist/handlers/session-move.js +188 -0
  62. package/dist/handlers/session-tools.d.ts +19 -0
  63. package/dist/handlers/session-tools.js +75 -0
  64. package/dist/handlers/sessions.d.ts +32 -0
  65. package/dist/handlers/sessions.js +242 -0
  66. package/dist/handlers/usage.d.ts +26 -0
  67. package/dist/handlers/usage.js +77 -0
  68. package/dist/handlers/worker.d.ts +12 -0
  69. package/dist/handlers/worker.js +82 -0
  70. package/dist/handlers/workspaces.d.ts +12 -0
  71. package/dist/handlers/workspaces.js +88 -0
  72. package/dist/harness.test-util.d.ts +101 -0
  73. package/dist/harness.test-util.js +176 -0
  74. package/dist/index.d.ts +33 -0
  75. package/dist/index.js +33 -0
  76. package/dist/main.d.ts +13 -0
  77. package/dist/main.js +35 -0
  78. package/dist/plugins.d.ts +105 -0
  79. package/dist/plugins.js +97 -0
  80. package/dist/question-registry.d.ts +124 -0
  81. package/dist/question-registry.js +212 -0
  82. package/dist/question-rows.d.ts +32 -0
  83. package/dist/question-rows.js +43 -0
  84. package/dist/replay/compare.d.ts +47 -0
  85. package/dist/replay/compare.js +89 -0
  86. package/dist/replay/e2e-replay.d.ts +46 -0
  87. package/dist/replay/e2e-replay.js +91 -0
  88. package/dist/replay/expect-compact.d.ts +31 -0
  89. package/dist/replay/expect-compact.js +92 -0
  90. package/dist/replay/fixtures.d.ts +52 -0
  91. package/dist/replay/fixtures.js +53 -0
  92. package/dist/replay/host.d.ts +32 -0
  93. package/dist/replay/host.js +62 -0
  94. package/dist/replay/index.d.ts +16 -0
  95. package/dist/replay/index.js +16 -0
  96. package/dist/replay/probes.d.ts +55 -0
  97. package/dist/replay/probes.js +225 -0
  98. package/dist/replay/report.d.ts +13 -0
  99. package/dist/replay/report.js +103 -0
  100. package/dist/replay/session-e2e.d.ts +44 -0
  101. package/dist/replay/session-e2e.js +91 -0
  102. package/dist/routes.d.ts +47 -0
  103. package/dist/routes.js +59 -0
  104. package/dist/runtime/attachments-llm.d.ts +13 -0
  105. package/dist/runtime/attachments-llm.js +37 -0
  106. package/dist/runtime/boot-recovery.d.ts +32 -0
  107. package/dist/runtime/boot-recovery.js +71 -0
  108. package/dist/runtime/context-snapshot.d.ts +59 -0
  109. package/dist/runtime/context-snapshot.js +113 -0
  110. package/dist/runtime/engine-grants.d.ts +121 -0
  111. package/dist/runtime/engine-grants.js +338 -0
  112. package/dist/runtime/engine-permissions.d.ts +29 -0
  113. package/dist/runtime/engine-permissions.js +92 -0
  114. package/dist/runtime/engine-plugins.d.ts +189 -0
  115. package/dist/runtime/engine-plugins.js +292 -0
  116. package/dist/runtime/engine-profile.d.ts +28 -0
  117. package/dist/runtime/engine-profile.js +88 -0
  118. package/dist/runtime/engine-session.d.ts +9 -0
  119. package/dist/runtime/engine-session.js +9 -0
  120. package/dist/runtime/fallback-contract.d.ts +32 -0
  121. package/dist/runtime/fallback-contract.js +10 -0
  122. package/dist/runtime/fallback-host.d.ts +134 -0
  123. package/dist/runtime/fallback-host.js +299 -0
  124. package/dist/runtime/host-autowake.d.ts +79 -0
  125. package/dist/runtime/host-autowake.js +86 -0
  126. package/dist/runtime/image-downgrade.d.ts +49 -0
  127. package/dist/runtime/image-downgrade.js +90 -0
  128. package/dist/runtime/inbox-message.d.ts +11 -0
  129. package/dist/runtime/inbox-message.js +20 -0
  130. package/dist/runtime/index.d.ts +17 -0
  131. package/dist/runtime/index.js +17 -0
  132. package/dist/runtime/ledger-view.d.ts +40 -0
  133. package/dist/runtime/ledger-view.js +56 -0
  134. package/dist/runtime/llm-assembly.d.ts +66 -0
  135. package/dist/runtime/llm-assembly.js +114 -0
  136. package/dist/runtime/offline-llm.d.ts +71 -0
  137. package/dist/runtime/offline-llm.js +132 -0
  138. package/dist/runtime/provider-target.d.ts +10 -0
  139. package/dist/runtime/provider-target.js +10 -0
  140. package/dist/runtime/question-host.d.ts +33 -0
  141. package/dist/runtime/question-host.js +41 -0
  142. package/dist/runtime/question-view.d.ts +48 -0
  143. package/dist/runtime/question-view.js +76 -0
  144. package/dist/runtime/real-runtime-adapter.d.ts +95 -0
  145. package/dist/runtime/real-runtime-adapter.js +553 -0
  146. package/dist/runtime/recovery-audit.d.ts +81 -0
  147. package/dist/runtime/recovery-audit.js +95 -0
  148. package/dist/runtime/recovery-view.d.ts +36 -0
  149. package/dist/runtime/recovery-view.js +63 -0
  150. package/dist/runtime/session-compose.d.ts +235 -0
  151. package/dist/runtime/session-compose.js +350 -0
  152. package/dist/runtime/session-grants.d.ts +36 -0
  153. package/dist/runtime/session-grants.js +69 -0
  154. package/dist/runtime/session-lifecycle.d.ts +40 -0
  155. package/dist/runtime/session-lifecycle.js +86 -0
  156. package/dist/runtime/session-publisher.d.ts +40 -0
  157. package/dist/runtime/session-publisher.js +48 -0
  158. package/dist/runtime/session-release.d.ts +53 -0
  159. package/dist/runtime/session-release.js +80 -0
  160. package/dist/runtime/test-util.d.ts +78 -0
  161. package/dist/runtime/test-util.js +162 -0
  162. package/dist/runtime/watchdog-view.d.ts +33 -0
  163. package/dist/runtime/watchdog-view.js +45 -0
  164. package/dist/runtime/worker-bridge.d.ts +76 -0
  165. package/dist/runtime/worker-bridge.js +156 -0
  166. package/dist/runtime/worker-live.d.ts +22 -0
  167. package/dist/runtime/worker-live.js +25 -0
  168. package/dist/runtime/worker-recovery.d.ts +35 -0
  169. package/dist/runtime/worker-recovery.js +64 -0
  170. package/dist/runtime/worker-table.d.ts +58 -0
  171. package/dist/runtime/worker-table.js +70 -0
  172. package/dist/runtime-adapter.d.ts +421 -0
  173. package/dist/runtime-adapter.js +76 -0
  174. package/dist/serial-queue.d.ts +16 -0
  175. package/dist/serial-queue.js +22 -0
  176. package/dist/server.d.ts +44 -0
  177. package/dist/server.js +119 -0
  178. package/dist/settings.d.ts +23 -0
  179. package/dist/settings.js +31 -0
  180. package/dist/sse.d.ts +67 -0
  181. package/dist/sse.js +186 -0
  182. package/dist/static.d.ts +20 -0
  183. package/dist/static.js +120 -0
  184. package/dist/store/builtin-sections.d.ts +52 -0
  185. package/dist/store/builtin-sections.js +118 -0
  186. package/dist/store/celestea-home.d.ts +12 -0
  187. package/dist/store/celestea-home.js +11 -0
  188. package/dist/store/fs-json.d.ts +51 -0
  189. package/dist/store/fs-json.js +119 -0
  190. package/dist/store/grants-audit.d.ts +84 -0
  191. package/dist/store/grants-audit.js +109 -0
  192. package/dist/store/grants-service.d.ts +44 -0
  193. package/dist/store/grants-service.js +41 -0
  194. package/dist/store/grants-tokens.d.ts +62 -0
  195. package/dist/store/grants-tokens.js +105 -0
  196. package/dist/store/grants.d.ts +113 -0
  197. package/dist/store/grants.js +238 -0
  198. package/dist/store/index.d.ts +36 -0
  199. package/dist/store/index.js +36 -0
  200. package/dist/store/mode.d.ts +35 -0
  201. package/dist/store/mode.js +41 -0
  202. package/dist/store/permissions.d.ts +61 -0
  203. package/dist/store/permissions.js +139 -0
  204. package/dist/store/prompts-compose.d.ts +56 -0
  205. package/dist/store/prompts-compose.js +62 -0
  206. package/dist/store/prompts-template.d.ts +26 -0
  207. package/dist/store/prompts-template.js +99 -0
  208. package/dist/store/prompts.d.ts +142 -0
  209. package/dist/store/prompts.js +273 -0
  210. package/dist/store/provider-probe.d.ts +81 -0
  211. package/dist/store/provider-probe.js +154 -0
  212. package/dist/store/providers.d.ts +97 -0
  213. package/dist/store/providers.js +219 -0
  214. package/dist/store/result.d.ts +29 -0
  215. package/dist/store/result.js +30 -0
  216. package/dist/store/session-id.d.ts +119 -0
  217. package/dist/store/session-id.js +220 -0
  218. package/dist/store/session-meta.d.ts +41 -0
  219. package/dist/store/session-meta.js +66 -0
  220. package/dist/store/session-ops.d.ts +109 -0
  221. package/dist/store/session-ops.js +264 -0
  222. package/dist/store/session-tools.d.ts +42 -0
  223. package/dist/store/session-tools.js +69 -0
  224. package/dist/store/sessions.d.ts +162 -0
  225. package/dist/store/sessions.js +307 -0
  226. package/dist/store/validate.d.ts +13 -0
  227. package/dist/store/validate.js +38 -0
  228. package/dist/store/workspaces.d.ts +67 -0
  229. package/dist/store/workspaces.js +228 -0
  230. package/dist/user-questions.d.ts +58 -0
  231. package/dist/user-questions.js +157 -0
  232. package/dist/version.d.ts +14 -0
  233. package/dist/version.js +52 -0
  234. package/package.json +38 -0
  235. package/webdist/assets/index-Bngs7gUm.js +98 -0
  236. package/webdist/assets/index-Bu6ci_rN.css +1 -0
  237. package/webdist/assets/katex-CCbS2qAY.js +257 -0
  238. package/webdist/assets/mhchem-CnUN8HwY.js +1 -0
  239. package/webdist/build-meta.json +7 -0
  240. package/webdist/index.html +261 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Confirm tokens + abuse limits for the grant endpoints (W516 §5.5, §6.4).
3
+ *
4
+ * A grant can only be POSTed with a one-shot token that a BROWSER interaction
5
+ * obtained: `GET /api/sessions/{id}/grants/confirm-token` requires same-origin
6
+ * evidence, so the session's own `http_request` tool cannot mint one even when
7
+ * it can reach `127.0.0.1` (a tool call carries no browser `Sec-Fetch-*`
8
+ * headers). The token is bound to `(session, cap, scope_hash)`, lives 60s and
9
+ * is burned on first use.
10
+ *
11
+ * The limiter is the anti-confirmation-fatigue half: at most 3 grant requests
12
+ * per session per minute (429), and 3 consecutive rejections put that session in
13
+ * a 5-minute cooldown (409 + remaining seconds).
14
+ */
15
+ export const CONFIRM_HEADER = "x-celestea-grant-confirm";
16
+ export const SEC_FETCH_SITE = "sec-fetch-site";
17
+ export const SEC_FETCH_MODE = "sec-fetch-mode";
18
+ export const ORIGIN_HEADER = "origin";
19
+ export const CONFIRM_TTL_SEC = 60;
20
+ export const RATE_LIMIT_MAX = 3;
21
+ export const RATE_LIMIT_WINDOW_MS = 60_000;
22
+ export const DENIAL_THRESHOLD = 3;
23
+ export const DENIAL_COOLDOWN_MS = 5 * 60_000;
24
+ /** One-shot `(session, cap, scope_hash)` tokens with a 60s TTL. */
25
+ export class GrantTokenStore {
26
+ now;
27
+ tokens = new Map();
28
+ constructor(now = Date.now) {
29
+ this.now = now;
30
+ }
31
+ issue(session, cap, scopeHash) {
32
+ this.gc();
33
+ const token = randomToken();
34
+ const expiresAt = Math.floor(this.now() / 1000) + CONFIRM_TTL_SEC;
35
+ this.tokens.set(token, { token, session, cap, scopeHash, expiresAt, used: false });
36
+ return { token, expiresAt };
37
+ }
38
+ /**
39
+ * Burn the token. `used` is reported separately from `invalid` so the caller
40
+ * can answer `409 confirmation token already used` (a replay) instead of 403.
41
+ */
42
+ consume(session, cap, scopeHash, token) {
43
+ this.gc();
44
+ const found = this.tokens.get(token);
45
+ if (found === undefined)
46
+ return "invalid";
47
+ if (found.session !== session || found.cap !== cap || found.scopeHash !== scopeHash)
48
+ return "invalid";
49
+ if (found.used)
50
+ return "used";
51
+ found.used = true;
52
+ return "ok";
53
+ }
54
+ /** Drop only EXPIRED tokens: a burned one must stay recognizable as `used`. */
55
+ gc() {
56
+ const deadline = Math.floor(this.now() / 1000);
57
+ for (const [key, value] of this.tokens)
58
+ if (value.expiresAt <= deadline)
59
+ this.tokens.delete(key);
60
+ }
61
+ }
62
+ /** Per-session grant-request rate limit + consecutive-denial cooldown. */
63
+ export class GrantRateLimiter {
64
+ now;
65
+ hits = new Map();
66
+ denials = new Map();
67
+ cooldown = new Map();
68
+ constructor(now = Date.now) {
69
+ this.now = now;
70
+ }
71
+ /** Called once per POST attempt, before validation. */
72
+ allow(session) {
73
+ const now = this.now();
74
+ const until = this.cooldown.get(session) ?? 0;
75
+ if (now < until)
76
+ return { ok: false, status: 409, retryAfterSec: Math.ceil((until - now) / 1000) };
77
+ const recent = (this.hits.get(session) ?? []).filter((t) => now - t < RATE_LIMIT_WINDOW_MS);
78
+ if (recent.length >= RATE_LIMIT_MAX) {
79
+ const oldest = recent[recent.length - RATE_LIMIT_MAX] ?? now;
80
+ return { ok: false, status: 429, retryAfterSec: Math.max(1, Math.ceil((oldest + RATE_LIMIT_WINDOW_MS - now) / 1000)) };
81
+ }
82
+ recent.push(now);
83
+ this.hits.set(session, recent);
84
+ return { ok: true };
85
+ }
86
+ /** A rejected request counts toward the cooldown; a grant resets it. */
87
+ recordDenial(session) {
88
+ const count = (this.denials.get(session) ?? 0) + 1;
89
+ if (count >= DENIAL_THRESHOLD) {
90
+ this.denials.set(session, 0);
91
+ this.cooldown.set(session, this.now() + DENIAL_COOLDOWN_MS);
92
+ return;
93
+ }
94
+ this.denials.set(session, count);
95
+ }
96
+ recordSuccess(session) {
97
+ this.denials.set(session, 0);
98
+ this.cooldown.delete(session);
99
+ }
100
+ }
101
+ function randomToken() {
102
+ const bytes = new Uint8Array(24);
103
+ globalThis.crypto.getRandomValues(bytes);
104
+ return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
105
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `<session dir>/grants.json` — the session's grant file (W516 §2.1, §5.4).
3
+ *
4
+ * The file is the ONLY source of a session's widenings and it is deliberately
5
+ * hostile-input-shaped: it lives next to `session.json` inside a workspace, so
6
+ * anything that can write the workspace can write it (including a session's own
7
+ * tools) — therefore every field is whitelisted, every value is validated, and
8
+ * a corrupt file means "no grants at all" rather than "repair it".
9
+ *
10
+ * Durability: `<path>.tmp` → rename (`fs-json.ts`) with mode 0600, like
11
+ * `providers.json`. Redaction: the `note` field is redacted before it is
12
+ * written, and a `roots`/`hosts`/`tools` value that LOOKS like a credential is
13
+ * rejected (400 at the endpoint, dropped entry when hand-edited) — no response,
14
+ * audit line or UI ever echoes such a value back.
15
+ *
16
+ * `grants.json` is NOT a session registry file: it is absent by default (that is
17
+ * the normal, least-privilege state) and it is not part of
18
+ * `contracts/data-files/index.json`.
19
+ */
20
+ export declare const GRANTS_FILE = "grants.json";
21
+ /** Env var enabling the `unsandboxed` cap (§2.2: hidden in the UI by default). */
22
+ export declare const ENV_GRANTS_UNSANDBOXED = "CELESTEA_GRANTS_ALLOW_UNSANDBOXED";
23
+ /** Default `ttl_sec` the UI pre-fills (§2.3). */
24
+ export declare const DEFAULT_TTL_SEC = 1800;
25
+ /** Cap on scope values (`roots`/`hosts`/`tools` entries). */
26
+ export declare const MAX_SCOPE_VALUE_CHARS = 200;
27
+ /** Cap on the number of entries per scope list. */
28
+ export declare const MAX_SCOPE_ENTRIES = 32;
29
+ export type GrantCap = "network" | "read_roots" | "write_roots" | "net_hosts" | "tool_extra" | "unsandboxed";
30
+ export declare const GRANT_CAPS: readonly GrantCap[];
31
+ /**
32
+ * W819-8: caps that are KNOWN and still read back (GRANT_CAPS) but are NOT
33
+ * offered any more. tool_extra is documented as reserved for future
34
+ * browser/net tools and no tool-exposure point ever consumed it, so offering
35
+ * it as a capability the deployment could honour was false. Stored entries
36
+ * keep parsing, echoing and warning; they just cannot be newly granted.
37
+ */
38
+ export declare const RESERVED_GRANT_CAPS: readonly GrantCap[];
39
+ /** Per-cap TTL ceiling, enforced by the SERVER (§2.3). */
40
+ export declare const MAX_TTL_SEC: Readonly<Record<GrantCap, number>>;
41
+ /** Scope of one grant entry: at most one of these lists is meaningful per cap. */
42
+ export interface GrantScope {
43
+ roots?: string[];
44
+ hosts?: string[];
45
+ tools?: string[];
46
+ }
47
+ export interface GrantRecord {
48
+ id: string;
49
+ cap: GrantCap;
50
+ scope: GrantScope;
51
+ granted_at: number;
52
+ granted_by: string;
53
+ /** Absolute unix seconds; null = no expiry. */
54
+ expires_at: number | null;
55
+ /** null = unlimited; 1 = single use (forced for `unsandboxed`). */
56
+ uses_left: number | null;
57
+ note: string;
58
+ }
59
+ export interface GrantsFile {
60
+ version: number;
61
+ /** Self-description `<workspace>/<session>`; a mismatch voids the whole file. */
62
+ session: string;
63
+ updated_at: number;
64
+ grants: GrantRecord[];
65
+ }
66
+ export type GrantsRead = {
67
+ exists: boolean;
68
+ file?: GrantsFile;
69
+ error?: string;
70
+ };
71
+ /** The env-side secret set (`collectKnownSecrets`) used by [looksLikeCredential]. */
72
+ export declare function knownSecretsOf(env?: NodeJS.ProcessEnv): string[];
73
+ /** Shape-only credential screen (W516 §5.4) — never echoes the value. */
74
+ export declare function looksLikeCredential(value: string, known: readonly string[]): boolean;
75
+ export declare function isGrantCap(value: unknown): value is GrantCap;
76
+ /** Known AND currently offerable (reserved caps are deliberately excluded). */
77
+ export declare function isOfferedGrantCap(value: unknown): value is GrantCap;
78
+ export declare function maxTtlOf(cap: GrantCap): number;
79
+ /** Random, revocable-audit-stable id (`g-` + 8 hex, §2.1). */
80
+ export declare function newGrantId(): string;
81
+ export declare function emptyGrantsFile(session: string, now: number): GrantsFile;
82
+ /** Canonical scope JSON — the `scope_hash` input shared with the UI (§6.4). */
83
+ export declare function canonicalScopeJson(cap: string, scope: GrantScope): string;
84
+ /** sha256 hex of [canonicalScopeJson] — the `scope_hash` of §6.4. */
85
+ export declare function canonicalScopeHash(cap: string, scope: GrantScope): string;
86
+ /**
87
+ * Validate + normalize one scope. Unknown keys are dropped (whitelist), values
88
+ * are credential-screened, and a wrong type is a 400-shaped error string.
89
+ */
90
+ export declare function validateScope(cap: GrantCap, raw: unknown, known: readonly string[]): {
91
+ ok: true;
92
+ scope: GrantScope;
93
+ } | {
94
+ ok: false;
95
+ error: string;
96
+ };
97
+ /**
98
+ * Read + validate the file. `exists:false` = the normal least-privilege state.
99
+ * Any structural problem (bad JSON, unknown version, self-description mismatch,
100
+ * `grants` not an array) returns `{exists:true, error}` and NO file: the caller
101
+ * turns that into an empty grant set plus a `grants_unreadable` audit line.
102
+ */
103
+ export declare function readGrantsFile(dir: string, expectedSession: string): GrantsRead;
104
+ /**
105
+ * Atomic 0600 write of the whitelisted, redacted file. `now` drives the lazy
106
+ * GC: expired entries are dropped here, on the next write (§2.3 — no timers).
107
+ */
108
+ export declare function writeGrantsFile(dir: string, file: GrantsFile, opts: {
109
+ env?: NodeJS.ProcessEnv;
110
+ now: number;
111
+ }): void;
112
+ /** Expiry is evaluated at READ time (`now >= expires_at`, §2.3). */
113
+ export declare function isExpired(grant: Pick<GrantRecord, "expires_at">, now: number): boolean;
@@ -0,0 +1,238 @@
1
+ /**
2
+ * `<session dir>/grants.json` — the session's grant file (W516 §2.1, §5.4).
3
+ *
4
+ * The file is the ONLY source of a session's widenings and it is deliberately
5
+ * hostile-input-shaped: it lives next to `session.json` inside a workspace, so
6
+ * anything that can write the workspace can write it (including a session's own
7
+ * tools) — therefore every field is whitelisted, every value is validated, and
8
+ * a corrupt file means "no grants at all" rather than "repair it".
9
+ *
10
+ * Durability: `<path>.tmp` → rename (`fs-json.ts`) with mode 0600, like
11
+ * `providers.json`. Redaction: the `note` field is redacted before it is
12
+ * written, and a `roots`/`hosts`/`tools` value that LOOKS like a credential is
13
+ * rejected (400 at the endpoint, dropped entry when hand-edited) — no response,
14
+ * audit line or UI ever echoes such a value back.
15
+ *
16
+ * `grants.json` is NOT a session registry file: it is absent by default (that is
17
+ * the normal, least-privilege state) and it is not part of
18
+ * `contracts/data-files/index.json`.
19
+ */
20
+ import { createHash } from "node:crypto";
21
+ import { join } from "node:path";
22
+ import { collectKnownSecrets, createRedactor } from "@celestea/core";
23
+ import { readJsonIfExists, writeJsonAtomic } from "./fs-json.js";
24
+ export const GRANTS_FILE = "grants.json";
25
+ /** Env var enabling the `unsandboxed` cap (§2.2: hidden in the UI by default). */
26
+ export const ENV_GRANTS_UNSANDBOXED = "CELESTEA_GRANTS_ALLOW_UNSANDBOXED";
27
+ /** Default `ttl_sec` the UI pre-fills (§2.3). */
28
+ export const DEFAULT_TTL_SEC = 1800;
29
+ /** Cap on scope values (`roots`/`hosts`/`tools` entries). */
30
+ export const MAX_SCOPE_VALUE_CHARS = 200;
31
+ /** Cap on the number of entries per scope list. */
32
+ export const MAX_SCOPE_ENTRIES = 32;
33
+ export const GRANT_CAPS = ["network", "read_roots", "write_roots", "net_hosts", "tool_extra", "unsandboxed"];
34
+ /**
35
+ * W819-8: caps that are KNOWN and still read back (GRANT_CAPS) but are NOT
36
+ * offered any more. tool_extra is documented as reserved for future
37
+ * browser/net tools and no tool-exposure point ever consumed it, so offering
38
+ * it as a capability the deployment could honour was false. Stored entries
39
+ * keep parsing, echoing and warning; they just cannot be newly granted.
40
+ */
41
+ export const RESERVED_GRANT_CAPS = ["tool_extra"];
42
+ /** Per-cap TTL ceiling, enforced by the SERVER (§2.3). */
43
+ export const MAX_TTL_SEC = {
44
+ network: 3600,
45
+ read_roots: 86400,
46
+ write_roots: 86400,
47
+ net_hosts: 86400,
48
+ tool_extra: 86400,
49
+ unsandboxed: 900,
50
+ };
51
+ /** The env-side secret set (`collectKnownSecrets`) used by [looksLikeCredential]. */
52
+ export function knownSecretsOf(env = process.env) {
53
+ return collectKnownSecrets({ env });
54
+ }
55
+ /** Shape-only credential screen (W516 §5.4) — never echoes the value. */
56
+ export function looksLikeCredential(value, known) {
57
+ if (value.trim() !== value || /[\r\n]/.test(value))
58
+ return true;
59
+ if (createRedactor(known).redact(value) !== value)
60
+ return true;
61
+ return false;
62
+ }
63
+ export function isGrantCap(value) {
64
+ return typeof value === "string" && GRANT_CAPS.includes(value);
65
+ }
66
+ /** Known AND currently offerable (reserved caps are deliberately excluded). */
67
+ export function isOfferedGrantCap(value) {
68
+ return isGrantCap(value) && !RESERVED_GRANT_CAPS.includes(value);
69
+ }
70
+ export function maxTtlOf(cap) {
71
+ return MAX_TTL_SEC[cap];
72
+ }
73
+ /** Random, revocable-audit-stable id (`g-` + 8 hex, §2.1). */
74
+ export function newGrantId() {
75
+ const hex = Math.floor(Math.random() * 0xffff_ffff)
76
+ .toString(16)
77
+ .padStart(8, "0");
78
+ return `g-${hex}`;
79
+ }
80
+ export function emptyGrantsFile(session, now) {
81
+ return { version: 1, session, updated_at: now, grants: [] };
82
+ }
83
+ /** Canonical scope JSON — the `scope_hash` input shared with the UI (§6.4). */
84
+ export function canonicalScopeJson(cap, scope) {
85
+ const keys = Object.keys(scope).sort();
86
+ const out = {};
87
+ for (const key of keys) {
88
+ const value = scope[key];
89
+ if (value === undefined)
90
+ continue;
91
+ out[key] = [...value].sort();
92
+ }
93
+ return JSON.stringify({ cap, scope: out });
94
+ }
95
+ /** sha256 hex of [canonicalScopeJson] — the `scope_hash` of §6.4. */
96
+ export function canonicalScopeHash(cap, scope) {
97
+ return createHash("sha256").update(canonicalScopeJson(cap, scope)).digest("hex");
98
+ }
99
+ /**
100
+ * Validate + normalize one scope. Unknown keys are dropped (whitelist), values
101
+ * are credential-screened, and a wrong type is a 400-shaped error string.
102
+ */
103
+ export function validateScope(cap, raw, known) {
104
+ const source = raw === undefined || raw === null ? {} : raw;
105
+ if (typeof source !== "object" || Array.isArray(source))
106
+ return { ok: false, error: "scope must be an object" };
107
+ const rec = source;
108
+ const key = SCOPE_KEY[cap];
109
+ if (key === null)
110
+ return { ok: true, scope: {} };
111
+ const values = rec[key];
112
+ if (values === undefined || values === null)
113
+ return { ok: false, error: `scope.${key} is required` };
114
+ if (!Array.isArray(values))
115
+ return { ok: false, error: `scope.${key} must be an array of strings` };
116
+ if (values.length === 0)
117
+ return { ok: false, error: `scope.${key} must not be empty` };
118
+ if (values.length > MAX_SCOPE_ENTRIES)
119
+ return { ok: false, error: `scope.${key} holds more than ${MAX_SCOPE_ENTRIES} entries` };
120
+ const out = [];
121
+ for (const value of values) {
122
+ if (typeof value !== "string" || value.trim() === "")
123
+ return { ok: false, error: `scope.${key} entries must be non-empty strings` };
124
+ if (value.length > MAX_SCOPE_VALUE_CHARS)
125
+ return { ok: false, error: `scope.${key} entry is longer than ${MAX_SCOPE_VALUE_CHARS} chars` };
126
+ if (looksLikeCredential(value, known))
127
+ return { ok: false, error: "value looks like a credential" };
128
+ out.push(value.trim());
129
+ }
130
+ return { ok: true, scope: { [key]: [...new Set(out)] } };
131
+ }
132
+ const SCOPE_KEY = {
133
+ network: null,
134
+ unsandboxed: null,
135
+ read_roots: "roots",
136
+ write_roots: "roots",
137
+ net_hosts: "hosts",
138
+ tool_extra: "tools",
139
+ };
140
+ /**
141
+ * Read + validate the file. `exists:false` = the normal least-privilege state.
142
+ * Any structural problem (bad JSON, unknown version, self-description mismatch,
143
+ * `grants` not an array) returns `{exists:true, error}` and NO file: the caller
144
+ * turns that into an empty grant set plus a `grants_unreadable` audit line.
145
+ */
146
+ export function readGrantsFile(dir, expectedSession) {
147
+ const out = readJsonIfExists(join(dir, GRANTS_FILE));
148
+ if (!out.exists)
149
+ return { exists: false };
150
+ if (out.error !== undefined)
151
+ return { exists: true, error: `unparsable grants.json: ${out.error}` };
152
+ const value = out.value;
153
+ if (typeof value !== "object" || value === null || Array.isArray(value))
154
+ return { exists: true, error: "grants.json is not an object" };
155
+ const rec = value;
156
+ if (rec["version"] !== 1)
157
+ return { exists: true, error: `unknown grants.json version '${String(rec["version"])}'` };
158
+ if (rec["session"] !== expectedSession)
159
+ return { exists: true, error: `grants.json belongs to '${String(rec["session"])}'` };
160
+ if (!Array.isArray(rec["grants"]))
161
+ return { exists: true, error: "grants.json has no grants array" };
162
+ const grants = [];
163
+ rec["grants"].forEach((entry, index) => {
164
+ const parsed = parseGrantEntry(entry, index);
165
+ if (parsed !== null)
166
+ grants.push(parsed);
167
+ });
168
+ const updated = typeof rec["updated_at"] === "number" ? rec["updated_at"] : 0;
169
+ return { exists: true, file: { version: 1, session: expectedSession, updated_at: updated, grants } };
170
+ }
171
+ /** Whitelist one entry; `null` = unusable row (dropped, never guessed at). */
172
+ function parseGrantEntry(raw, index) {
173
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
174
+ return null;
175
+ const rec = raw;
176
+ if (!isGrantCap(rec["cap"]))
177
+ return null;
178
+ const id = typeof rec["id"] === "string" && rec["id"] !== "" ? rec["id"] : `g-invalid-${index}`;
179
+ const scope = isGrantCap(rec["cap"]) ? normalizeStoredScope(rec["cap"], rec["scope"]) : {};
180
+ return {
181
+ id,
182
+ cap: rec["cap"],
183
+ scope,
184
+ granted_at: numberOr(rec["granted_at"], 0),
185
+ granted_by: typeof rec["granted_by"] === "string" ? rec["granted_by"] : "",
186
+ expires_at: numberOrNull(rec["expires_at"]),
187
+ uses_left: numberOrNull(rec["uses_left"]),
188
+ note: typeof rec["note"] === "string" ? rec["note"] : "",
189
+ };
190
+ }
191
+ function normalizeStoredScope(cap, raw) {
192
+ const key = SCOPE_KEY[cap];
193
+ if (key === null)
194
+ return {};
195
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
196
+ return {};
197
+ const values = raw[key];
198
+ if (!Array.isArray(values))
199
+ return {};
200
+ return { [key]: values.filter((v) => typeof v === "string") };
201
+ }
202
+ function numberOr(value, fallback) {
203
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
204
+ }
205
+ function numberOrNull(value) {
206
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
207
+ }
208
+ /**
209
+ * Atomic 0600 write of the whitelisted, redacted file. `now` drives the lazy
210
+ * GC: expired entries are dropped here, on the next write (§2.3 — no timers).
211
+ */
212
+ export function writeGrantsFile(dir, file, opts) {
213
+ const redactor = createRedactor(knownSecretsOf(opts.env ?? process.env));
214
+ const body = {
215
+ version: 1,
216
+ session: file.session,
217
+ updated_at: opts.now,
218
+ grants: file.grants.filter((g) => !isExpired(g, opts.now)).map((g) => sanitizeGrant(g, redactor.redact(g.note))),
219
+ };
220
+ writeJsonAtomic(join(dir, GRANTS_FILE), body, { mode: 0o600 });
221
+ }
222
+ /** Field whitelist: unknown fields are DROPPED, never passed through (§5.4). */
223
+ function sanitizeGrant(grant, note) {
224
+ return {
225
+ id: grant.id,
226
+ cap: grant.cap,
227
+ scope: normalizeStoredScope(grant.cap, grant.scope),
228
+ granted_at: grant.granted_at,
229
+ granted_by: grant.granted_by,
230
+ expires_at: grant.expires_at,
231
+ uses_left: grant.uses_left,
232
+ note,
233
+ };
234
+ }
235
+ /** Expiry is evaluated at READ time (`now >= expires_at`, §2.3). */
236
+ export function isExpired(grant, now) {
237
+ return grant.expires_at !== null && now >= grant.expires_at;
238
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Studio data stores — the public surface of this directory.
3
+ *
4
+ * Module map:
5
+ * fs-json.ts atomic JSON read/write + dir helpers (durability map)
6
+ * result.ts StoreResult / StoreFailure + `{e}` formatting
7
+ * session-id.ts sanitize / parse `<ws>/<session>` / name validation
8
+ * mode.ts session mode vocabulary (standard | execution, W729)
9
+ * session-meta.ts `<dir>/session.json` (model + prompt binding + mode)
10
+ * validate.ts model / prompt-id / url / effort validation
11
+ * builtin-sections.ts frozen 10-row builtin prompt registry (data only)
12
+ * prompts-template.ts `{{var}}` scan / validate / render
13
+ * celestea-home.ts CELESTEA_HOME data-root re-export (W880)
14
+ * prompts.ts prompts.json + <CELESTEA_HOME>/.../prompts.json registry
15
+ * prompts-compose.ts build_gen assembly + active-prompt chain
16
+ * workspaces.ts workspaces.json v2 registry (+ folder rename)
17
+ * sessions.ts session scan / resolve / create / transcript
18
+ * session-ops.ts rename / branch / archive / trash moves
19
+ * providers.ts providers.json (0600) + public_view redaction
20
+ * provider-probe.ts /models probe incl. keyless same-origin borrow
21
+ */
22
+ export * from "./result.js";
23
+ export * from "./fs-json.js";
24
+ export * from "./session-id.js";
25
+ export * from "./mode.js";
26
+ export * from "./session-meta.js";
27
+ export * from "./validate.js";
28
+ export * from "./builtin-sections.js";
29
+ export * from "./prompts-template.js";
30
+ export * from "./prompts.js";
31
+ export * from "./prompts-compose.js";
32
+ export * from "./workspaces.js";
33
+ export * from "./sessions.js";
34
+ export * from "./session-ops.js";
35
+ export * from "./providers.js";
36
+ export * from "./provider-probe.js";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Studio data stores — the public surface of this directory.
3
+ *
4
+ * Module map:
5
+ * fs-json.ts atomic JSON read/write + dir helpers (durability map)
6
+ * result.ts StoreResult / StoreFailure + `{e}` formatting
7
+ * session-id.ts sanitize / parse `<ws>/<session>` / name validation
8
+ * mode.ts session mode vocabulary (standard | execution, W729)
9
+ * session-meta.ts `<dir>/session.json` (model + prompt binding + mode)
10
+ * validate.ts model / prompt-id / url / effort validation
11
+ * builtin-sections.ts frozen 10-row builtin prompt registry (data only)
12
+ * prompts-template.ts `{{var}}` scan / validate / render
13
+ * celestea-home.ts CELESTEA_HOME data-root re-export (W880)
14
+ * prompts.ts prompts.json + <CELESTEA_HOME>/.../prompts.json registry
15
+ * prompts-compose.ts build_gen assembly + active-prompt chain
16
+ * workspaces.ts workspaces.json v2 registry (+ folder rename)
17
+ * sessions.ts session scan / resolve / create / transcript
18
+ * session-ops.ts rename / branch / archive / trash moves
19
+ * providers.ts providers.json (0600) + public_view redaction
20
+ * provider-probe.ts /models probe incl. keyless same-origin borrow
21
+ */
22
+ export * from "./result.js";
23
+ export * from "./fs-json.js";
24
+ export * from "./session-id.js";
25
+ export * from "./mode.js";
26
+ export * from "./session-meta.js";
27
+ export * from "./validate.js";
28
+ export * from "./builtin-sections.js";
29
+ export * from "./prompts-template.js";
30
+ export * from "./prompts.js";
31
+ export * from "./prompts-compose.js";
32
+ export * from "./workspaces.js";
33
+ export * from "./sessions.js";
34
+ export * from "./session-ops.js";
35
+ export * from "./providers.js";
36
+ export * from "./provider-probe.js";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Session modes — the P0 vocabulary of "how a session works"
3
+ * (`docs/modes-standard-vs-execution.md` §1.1, D1/D2).
4
+ *
5
+ * `standard` is exactly today's behaviour (call the tools directly); `execution`
6
+ * is the same engine with the *behaviour contract* turned toward one
7
+ * `run_code` program per dependent sequence. P0 changes ONLY the assembled
8
+ * `tool_access` section — the tool face is identical in both modes (§1.2, D5).
9
+ *
10
+ * K3: the whole mode table lives here as module-level constants (no literal list
11
+ * is re-written at a call site), and this file is plain data + pure parsers, so
12
+ * every consumer (session meta, session rows, prompts, handlers) shares ONE
13
+ * definition of the vocabulary.
14
+ *
15
+ * This is NOT the DSH host's agent preset (D4/§3.3): `mode` is the only mode
16
+ * semantics the Studio engine knows; the host preset name is derived OUTSIDE
17
+ * this repository, at the spawn boundary, and must never appear here.
18
+ */
19
+ /** The two literals, in frozen order. */
20
+ export declare const SESSION_MODES: readonly ["standard", "execution"];
21
+ /** The mode of a session; a session that never declared one reads as `standard`. */
22
+ export type SessionMode = (typeof SESSION_MODES)[number];
23
+ /** Absent/blank/unknown mode reads as this — and it is never written to disk. */
24
+ export declare const DEFAULT_SESSION_MODE: SessionMode;
25
+ /** A mode literal, or `null` when `raw` is not one (never throws). */
26
+ export declare function parseMode(raw: unknown): SessionMode | null;
27
+ /**
28
+ * Tolerant read: anything that is not a literal (absent, blank, hand-edited
29
+ * junk) degrades to [DEFAULT_SESSION_MODE], exactly like a missing key (K8/U9).
30
+ */
31
+ export declare function effectiveMode(raw: unknown): SessionMode;
32
+ /** Write-path validation: `null` = acceptable, else the frozen 400 text (M2). */
33
+ export declare function validateMode(raw: string): string | null;
34
+ /** True when the value is a declared literal (the `contracts/` enum, §4.1). */
35
+ export declare function isSessionMode(raw: unknown): raw is SessionMode;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Session modes — the P0 vocabulary of "how a session works"
3
+ * (`docs/modes-standard-vs-execution.md` §1.1, D1/D2).
4
+ *
5
+ * `standard` is exactly today's behaviour (call the tools directly); `execution`
6
+ * is the same engine with the *behaviour contract* turned toward one
7
+ * `run_code` program per dependent sequence. P0 changes ONLY the assembled
8
+ * `tool_access` section — the tool face is identical in both modes (§1.2, D5).
9
+ *
10
+ * K3: the whole mode table lives here as module-level constants (no literal list
11
+ * is re-written at a call site), and this file is plain data + pure parsers, so
12
+ * every consumer (session meta, session rows, prompts, handlers) shares ONE
13
+ * definition of the vocabulary.
14
+ *
15
+ * This is NOT the DSH host's agent preset (D4/§3.3): `mode` is the only mode
16
+ * semantics the Studio engine knows; the host preset name is derived OUTSIDE
17
+ * this repository, at the spawn boundary, and must never appear here.
18
+ */
19
+ /** The two literals, in frozen order. */
20
+ export const SESSION_MODES = ["standard", "execution"];
21
+ /** Absent/blank/unknown mode reads as this — and it is never written to disk. */
22
+ export const DEFAULT_SESSION_MODE = "standard";
23
+ /** A mode literal, or `null` when `raw` is not one (never throws). */
24
+ export function parseMode(raw) {
25
+ return typeof raw === "string" && SESSION_MODES.includes(raw) ? raw : null;
26
+ }
27
+ /**
28
+ * Tolerant read: anything that is not a literal (absent, blank, hand-edited
29
+ * junk) degrades to [DEFAULT_SESSION_MODE], exactly like a missing key (K8/U9).
30
+ */
31
+ export function effectiveMode(raw) {
32
+ return parseMode(raw) ?? DEFAULT_SESSION_MODE;
33
+ }
34
+ /** Write-path validation: `null` = acceptable, else the frozen 400 text (M2). */
35
+ export function validateMode(raw) {
36
+ return parseMode(raw) === null ? `invalid mode: ${raw}` : null;
37
+ }
38
+ /** True when the value is a declared literal (the `contracts/` enum, §4.1). */
39
+ export function isSessionMode(raw) {
40
+ return parseMode(raw) !== null;
41
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Permission presets (W9) - the built-in constants and the two files.
3
+ *
4
+ * Model (design approved by the architect):
5
+ * - built-in presets read-only / write-read / full-access are CODE constants;
6
+ * - custom presets live at <data dir>/permissions.json (atomic 0600);
7
+ * - a session's chosen preset lives at <session dir>/permission.json, a file
8
+ * PARALLEL to grants.json (whose v1 shape is deliberately untouched).
9
+ *
10
+ * Same discipline as grants.json: whitelist + validate every field, and a
11
+ * corrupt file means "fall back to the default", never "repair it". A permission
12
+ * is a BASELINE: engine-grants.ts intersects grants into it and a grant can
13
+ * never widen past it.
14
+ */
15
+ export declare const PERMISSIONS_FILE = "permissions.json";
16
+ export declare const SESSION_PERMISSION_FILE = "permission.json";
17
+ export declare const ENV_PERMISSION_DEFAULT = "CELESTEA_PERMISSION_DEFAULT";
18
+ export declare const ENV_PERMISSION_MAX = "CELESTEA_PERMISSION_MAX";
19
+ export declare const DEFAULT_PERMISSION_ID = "full-access";
20
+ export declare const MAX_PRESET_ROOTS = 32;
21
+ export declare const MAX_PRESET_TOOLS = 64;
22
+ export declare const MAX_PRESET_LABEL_CHARS = 80;
23
+ export declare const BUILTIN_PRESET_IDS: readonly ["read-only", "write-read", "full-access"];
24
+ export type BuiltinPresetId = (typeof BUILTIN_PRESET_IDS)[number];
25
+ export interface PermissionPreset {
26
+ id: string;
27
+ label: string;
28
+ network: boolean;
29
+ workspaceWritable: boolean;
30
+ /** The deployment CELESTEA_TOOL_ROOTS (repo/harness/tmp) become writable. */
31
+ toolRootsWritable: boolean;
32
+ /** Explicit absolute extra write roots, besides workspace + tool roots. */
33
+ writeRoots: string[];
34
+ /**
35
+ * W864: the whole filesystem is readable AND writable ("/" as the one root).
36
+ * Path-only: network / unsandboxed / toolDeny are untouched by it.
37
+ */
38
+ allPaths: boolean;
39
+ unsandboxed: boolean;
40
+ /** Tools removed from the session face (baseline filter, before tool_extra). */
41
+ toolDeny: string[];
42
+ }
43
+ export declare const BUILTIN_PRESETS: readonly PermissionPreset[];
44
+ export declare function isBuiltinPresetId(id: string): id is BuiltinPresetId;
45
+ export declare function builtinPreset(id: string): PermissionPreset | null;
46
+ export declare function defaultPermissionId(env?: NodeJS.ProcessEnv): string;
47
+ export declare function maxPermissionId(env?: NodeJS.ProcessEnv): string;
48
+ export interface PermissionsRead {
49
+ presets: PermissionPreset[];
50
+ warnings: string[];
51
+ }
52
+ export declare function readPermissionsFile(dataDir: string): PermissionsRead;
53
+ export declare function writePermissionsFile(dataDir: string, presets: readonly PermissionPreset[], now: number): void;
54
+ export declare function parsePreset(raw: unknown): PermissionPreset | null;
55
+ export interface SessionPermissionRead {
56
+ exists: boolean;
57
+ preset?: string;
58
+ error?: string;
59
+ }
60
+ export declare function readSessionPermission(dir: string, expectedSession: string): SessionPermissionRead;
61
+ export declare function writeSessionPermission(dir: string, session: string, preset: string, now: number): void;