@sigrun/sigrun 0.1.6 → 0.2.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.
- package/CHANGELOG.md +192 -0
- package/README.md +445 -42
- package/dist/blocks/build-stamp.json +13 -0
- package/dist/blocks/builders.d.ts +57 -0
- package/dist/blocks/images.d.ts +26 -0
- package/dist/blocks/index.d.ts +20 -0
- package/dist/blocks/limits.d.ts +25 -0
- package/dist/blocks/placements.d.ts +26 -0
- package/dist/blocks/schema.d.ts +38 -0
- package/dist/blocks/schema.js +9 -2
- package/dist/blocks/validate.d.ts +73 -0
- package/dist/blocks/validate.js +57 -8
- package/dist/cli.js +26 -2
- package/dist/config.js +67 -1
- package/dist/extension.d.ts +230 -22
- package/dist/harness/ambient.js +39 -185
- package/dist/harness/blocks/entries.js +47 -0
- package/dist/harness/blocks/state.js +58 -0
- package/dist/harness/blocks/tools.js +274 -0
- package/dist/harness/boot.js +259 -20
- package/dist/harness/builtins.js +15 -11
- package/dist/harness/event-bus.js +105 -10
- package/dist/harness/identity/accounts.js +186 -0
- package/dist/harness/identity/groups.js +14 -0
- package/dist/harness/identity/index.js +13 -6
- package/dist/harness/identity/json-store.js +15 -4
- package/dist/harness/identity/setup-code-check.js +9 -0
- package/dist/harness/identity/tokens.js +28 -5
- package/dist/harness/identity/users.js +111 -12
- package/dist/harness/interactions.js +292 -136
- package/dist/harness/loader.js +271 -17
- package/dist/harness/packages.js +1 -1
- package/dist/harness/reserved-sections.js +21 -0
- package/dist/harness/seam-host.js +92 -6
- package/dist/harness/sections/context-files.js +1 -1
- package/dist/harness/settings.js +63 -2
- package/dist/harness/stores.js +241 -4
- package/dist/harness/user-services.js +391 -0
- package/dist/harness/user-tools.js +187 -0
- package/dist/harness/workspace.js +61 -57
- package/dist/jobs/fire.js +10 -16
- package/dist/sandbox/availability.js +211 -0
- package/dist/sandbox/command-observer.js +59 -0
- package/dist/sandbox/grants.js +159 -0
- package/dist/sandbox/helper.js +29 -0
- package/dist/sandbox/isolation.js +29 -0
- package/dist/sandbox/launch.js +31 -0
- package/dist/sandbox/os-account.js +49 -0
- package/dist/sandbox/policy.js +274 -0
- package/dist/sandbox/pool.js +548 -0
- package/dist/sandbox/protocol.js +167 -0
- package/dist/sandbox/sandboxed-env.js +245 -0
- package/dist/sandbox/unavailable-env.js +49 -0
- package/dist/sandbox/worker.js +261 -0
- package/dist/serve.js +213 -25
- package/dist/server/auth/access-principal.js +16 -0
- package/dist/server/auth/pipeline.js +99 -36
- package/dist/server/blocks/answers.js +94 -0
- package/dist/server/blocks/chat-surfaces.js +393 -0
- package/dist/server/blocks/notes.js +216 -0
- package/dist/server/blocks/summaries.js +328 -0
- package/dist/server/commands.js +5 -0
- package/dist/server/content-parts-lazy.js +13 -0
- package/dist/server/conversation-handler.js +2 -5
- package/dist/server/conversation-ops.js +138 -37
- package/dist/server/helpers.js +18 -6
- package/dist/server/host-bridge.js +40 -20
- package/dist/server/images/cache.js +213 -0
- package/dist/server/images/convert.js +13 -0
- package/dist/server/images/fetch.js +302 -0
- package/dist/server/images/ranges.js +42 -0
- package/dist/server/images/rewrite.js +94 -0
- package/dist/server/images/sign.js +84 -0
- package/dist/server/images/sniff.js +62 -0
- package/dist/server/index.js +152 -4
- package/dist/server/login/flows.js +126 -0
- package/dist/server/login/http.js +31 -0
- package/dist/server/login/resolve.js +133 -0
- package/dist/server/login/setup-claim.js +55 -0
- package/dist/server/login/usernames.js +15 -0
- package/dist/server/notifications.js +21 -4
- package/dist/server/ownership.js +13 -0
- package/dist/server/routes/admin.js +58 -0
- package/dist/server/routes/auth.js +88 -2
- package/dist/server/routes/conversations.js +3 -6
- package/dist/server/routes/events.js +28 -3
- package/dist/server/routes/extensions.js +37 -5
- package/dist/server/routes/images.js +274 -0
- package/dist/server/routes/interactions.js +57 -68
- package/dist/server/routes/jobs.js +2 -8
- package/dist/server/routes/login.js +223 -0
- package/dist/server/routes/me.js +90 -0
- package/dist/server/routes/meta.js +19 -0
- package/dist/server/routes/onboarding.js +94 -84
- package/dist/server/routes/settings-sections.js +190 -0
- package/dist/server/routes/settings.js +16 -4
- package/dist/server/routes/submissions.js +1 -1
- package/dist/server/routes/surfaces.js +175 -23
- package/dist/server/routes/users.js +116 -20
- package/dist/server/schema-fixture.js +213 -0
- package/dist/server/session-meta.js +3 -0
- package/dist/server/shutdown.js +11 -4
- package/dist/server/static.js +18 -0
- package/dist/server/surfaces/dedup.js +9 -2
- package/dist/server/surfaces/service.js +181 -13
- package/dist/server/surfaces/settings-surfaces.js +597 -0
- package/dist/server/turns/submit-turn.js +26 -0
- package/dist/server/views.js +16 -0
- package/dist/settings/core-sections.js +532 -0
- package/dist/settings/pending.js +181 -0
- package/dist/settings/registry.js +453 -0
- package/dist/skills/index.js +6 -0
- package/node_modules/@sigrun/shared/CHANGELOG.md +11 -0
- package/node_modules/@sigrun/shared/dist/access.d.ts +4 -2
- package/node_modules/@sigrun/shared/dist/access.js +11 -4
- package/node_modules/@sigrun/shared/dist/ambient.d.ts +17 -74
- package/node_modules/@sigrun/shared/dist/ambient.js +8 -6
- package/node_modules/@sigrun/shared/dist/blocks/text.js +16 -2
- package/node_modules/@sigrun/shared/dist/blocks/transport.d.ts +4 -0
- package/node_modules/@sigrun/shared/dist/blocks/types.d.ts +21 -4
- package/node_modules/@sigrun/shared/dist/interactions.d.ts +22 -48
- package/node_modules/@sigrun/shared/dist/interactions.js +9 -7
- package/node_modules/@sigrun/shared/dist/settings.d.ts +27 -2
- package/node_modules/@sigrun/shared/dist/themes.d.ts +2 -3
- package/node_modules/@sigrun/shared/dist/themes.js +2 -3
- package/node_modules/@sigrun/shared/dist/turns.d.ts +14 -0
- package/node_modules/@sigrun/shared/dist/turns.js +53 -3
- package/node_modules/@sigrun/shared/dist/views.d.ts +202 -12
- package/node_modules/@sigrun/shared/dist/views.js +16 -0
- package/node_modules/@sigrun/shared/package.json +1 -1
- package/package.json +13 -9
- package/webui/dist/assets/index-SO2W4jjk.js +55 -0
- package/webui/dist/assets/index-yqkL6BSS.css +1 -0
- package/webui/dist/index.html +2 -2
- package/CONTEXT.md +0 -163
- package/webui/dist/assets/index-CiYInlLx.css +0 -1
- package/webui/dist/assets/index-JDfOQV9z.js +0 -52
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed builders (spec §14.1): one function per block and field,
|
|
3
|
+
* plus `bind(pointer)` and `tree(root)`. Builders return plain JSON —
|
|
4
|
+
* there is no runtime besides them.
|
|
5
|
+
*
|
|
6
|
+
* Each builder takes its block's props object and returns the block
|
|
7
|
+
* with the `type` literal stamped in. The literal is spread LAST, so a
|
|
8
|
+
* stray `type` inside a JavaScript caller's props object can never
|
|
9
|
+
* override it; TypeScript callers cannot pass one at all (`Props` omits
|
|
10
|
+
* it). The tree BLOCK builder is exported as `treeBlock`, keeping the
|
|
11
|
+
* `tree` name for the version-stamping root wrapper.
|
|
12
|
+
*
|
|
13
|
+
* `tree(root)` stamps {@link BLOCK_SET_VERSION} from the shared wire
|
|
14
|
+
* package — the version the SERVER validates against, which is why
|
|
15
|
+
* extensions import these builders from `@sigrun/sigrun/blocks` (the
|
|
16
|
+
* host-linked copy, spec §14.1) instead of bundling their own.
|
|
17
|
+
*/
|
|
18
|
+
import type { Binding, Block, CalloutBlock, Card, ChartBlock, ChoiceField, CollectionBlock, ColumnsBlock, DateField, DaterangeField, DatetimeField, DetailsBlock, DiffBlock, FieldsBlock, FileField, FormBlock, GridBlock, ImageBlock, ListField, LogBlock, LongtextField, MarkdownBlock, MeterBlock, NumberField, PageBlock, SectionBlock, SecretField, StackBlock, StatBlock, TextField, ToggleField, Tree, TreeBlock } from "@sigrun/shared/blocks";
|
|
19
|
+
/** One block/field's props: the wire shape minus the `type` literal. */
|
|
20
|
+
type Props<T> = Omit<T, "type">;
|
|
21
|
+
/** `bind("/cards")` → `{ $: "/cards" }` — a pointer into the surface's data model (§4.2). */
|
|
22
|
+
export declare function bind(pointer: string): Binding;
|
|
23
|
+
/** Wrap a root block with the server's block-set version (§4.4). */
|
|
24
|
+
export declare function tree(root: Block): Tree;
|
|
25
|
+
export declare function page(props: Props<PageBlock>): PageBlock;
|
|
26
|
+
export declare function stack(props: Props<StackBlock>): StackBlock;
|
|
27
|
+
export declare function columns(props: Props<ColumnsBlock>): ColumnsBlock;
|
|
28
|
+
export declare function grid(props: Props<GridBlock>): GridBlock;
|
|
29
|
+
export declare function section(props: Props<SectionBlock>): SectionBlock;
|
|
30
|
+
export declare function markdown(props: Props<MarkdownBlock>): MarkdownBlock;
|
|
31
|
+
export declare function fields(props: Props<FieldsBlock>): FieldsBlock;
|
|
32
|
+
export declare function stat(props: Props<StatBlock>): StatBlock;
|
|
33
|
+
export declare function callout(props: Props<CalloutBlock>): CalloutBlock;
|
|
34
|
+
export declare function image(props: Props<ImageBlock>): ImageBlock;
|
|
35
|
+
export declare function chart(props: Props<ChartBlock>): ChartBlock;
|
|
36
|
+
export declare function meter(props: Props<MeterBlock>): MeterBlock;
|
|
37
|
+
export declare function log(props: Props<LogBlock>): LogBlock;
|
|
38
|
+
/** The tree BLOCK builder (§5.3) — `tree` is the root wrapper. */
|
|
39
|
+
export declare function treeBlock(props: Props<TreeBlock>): TreeBlock;
|
|
40
|
+
export declare function diff(props: Props<DiffBlock>): DiffBlock;
|
|
41
|
+
export declare function details(props: Props<DetailsBlock>): DetailsBlock;
|
|
42
|
+
export declare function collection(props: Props<CollectionBlock>): CollectionBlock;
|
|
43
|
+
/** The collection item template (§5.5.1) — a slot bundle, not a block. */
|
|
44
|
+
export declare function card(props: Card): Card;
|
|
45
|
+
export declare function form(props: Props<FormBlock>): FormBlock;
|
|
46
|
+
export declare function text(props: Props<TextField>): TextField;
|
|
47
|
+
export declare function longtext(props: Props<LongtextField>): LongtextField;
|
|
48
|
+
export declare function number(props: Props<NumberField>): NumberField;
|
|
49
|
+
export declare function toggle(props: Props<ToggleField>): ToggleField;
|
|
50
|
+
export declare function choice(props: Props<ChoiceField>): ChoiceField;
|
|
51
|
+
export declare function date(props: Props<DateField>): DateField;
|
|
52
|
+
export declare function datetime(props: Props<DatetimeField>): DatetimeField;
|
|
53
|
+
export declare function daterange(props: Props<DaterangeField>): DaterangeField;
|
|
54
|
+
export declare function secret(props: Props<SecretField>): SecretField;
|
|
55
|
+
export declare function file(props: Props<FileField>): FileField;
|
|
56
|
+
export declare function list(props: Props<ListField>): ListField;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image source rules (spec §11.1): `asset:` paths, `data:` URLs of the
|
|
3
|
+
* four accepted MIME types (capped at 192 KiB of base64 text), and
|
|
4
|
+
* `https:` URLs. SVG and AVIF are never accepted (script payload; no
|
|
5
|
+
* TUI decoder), and `http:` URLs are rejected. The proxy that rewrites
|
|
6
|
+
* and fetches is U7; this module only validates.
|
|
7
|
+
*/
|
|
8
|
+
/** A source that passed the §11.1 rules. */
|
|
9
|
+
export type ImageSourceOk = {
|
|
10
|
+
ok: true;
|
|
11
|
+
kind: "asset" | "data" | "https";
|
|
12
|
+
};
|
|
13
|
+
/** A source the §11.1 rules reject. */
|
|
14
|
+
export type ImageSourceErr = {
|
|
15
|
+
ok: false;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
/** The result of checking one `src` value. */
|
|
19
|
+
export type ImageSourceResult = ImageSourceOk | ImageSourceErr;
|
|
20
|
+
/**
|
|
21
|
+
* Check one image `src` against the §11.1 source rules. Bindings
|
|
22
|
+
* (`{$: …}` objects) are not checked here — their resolved string is,
|
|
23
|
+
* where the data model holds one (the data-size limit already covers
|
|
24
|
+
* the byte cost).
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkImageSource(src: string): ImageSourceResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@sigrun/sigrun/blocks` — the extension authoring surface for the
|
|
3
|
+
* block set (spec §14.1): the typed builders, the wire types, and
|
|
4
|
+
* `validateTree` (§14.2) so an extension can unit-test its trees
|
|
5
|
+
* against the same validation the server runs.
|
|
6
|
+
*
|
|
7
|
+
* Extensions import this subpath, never a bundled copy: the package is
|
|
8
|
+
* a `peerDependency: "*"` (see docs/extending.md) and
|
|
9
|
+
* `@sigrun/sigrun` is a HOST RUNTIME MODULE (src/harness/packages.ts)
|
|
10
|
+
* — the host links its own running copy into every extension packages
|
|
11
|
+
* dir, so `tree(...)` always stamps the server's
|
|
12
|
+
* {@link BLOCK_SET_VERSION}. The `exports` map points here at
|
|
13
|
+
* `dist/blocks/index.js` (never `src/`), which is what the built
|
|
14
|
+
* server ships.
|
|
15
|
+
*/
|
|
16
|
+
export * from "./builders.js";
|
|
17
|
+
export { validateTree, validateData, VALID_PLACEMENTS, PLACEMENT_RULES, LIMITS, } from "./validate.js";
|
|
18
|
+
export type { PlacementLimits, PlacementRule, ValidateDataResult, ValidateErr, ValidateOk, ValidateOptions, ValidateResult, ValidationError, } from "./validate.js";
|
|
19
|
+
export { BLOCK_SET_VERSION } from "@sigrun/shared/blocks";
|
|
20
|
+
export type * from "@sigrun/shared/blocks";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-placement size limits of spec §4.5: max blocks per tree, and
|
|
3
|
+
* max UTF-8 bytes of the serialized JSON for the tree and the data.
|
|
4
|
+
*
|
|
5
|
+
* Over a limit the server rejects with `413 surface_too_large` naming
|
|
6
|
+
* the limit; `validateTree`/`validateData` surface the same error
|
|
7
|
+
* strings. A collection counts as one block however many items it
|
|
8
|
+
* binds, and a `data:` image URL counts toward whichever of the tree or
|
|
9
|
+
* the data holds it (the sizes are measured over the serialized JSON,
|
|
10
|
+
* so both hold by construction).
|
|
11
|
+
*/
|
|
12
|
+
import type { Placement } from "@sigrun/shared/blocks";
|
|
13
|
+
/** One row of the §4.5 limits table. */
|
|
14
|
+
export interface PlacementLimits {
|
|
15
|
+
/** Max blocks in one tree (a collection counts as one). */
|
|
16
|
+
readonly blocks: number;
|
|
17
|
+
/** Max UTF-8 bytes of the serialized tree JSON. */
|
|
18
|
+
readonly treeBytes: number;
|
|
19
|
+
/** Max UTF-8 bytes of the serialized data JSON. */
|
|
20
|
+
readonly dataBytes: number;
|
|
21
|
+
}
|
|
22
|
+
/** Spec §4.5, one row per placement. */
|
|
23
|
+
export declare const LIMITS: Readonly<Record<Placement, PlacementLimits>>;
|
|
24
|
+
/** UTF-8 byte length of a value's serialized JSON (spec §4.5 sizing). */
|
|
25
|
+
export declare function jsonBytes(value: unknown): number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The placement rules of spec §5.7, as data: which blocks, which
|
|
3
|
+
* collection displays and which form fields each placement allows,
|
|
4
|
+
* plus the root-block and `page.sidebar` constraints the table carries.
|
|
5
|
+
*
|
|
6
|
+
* `validateTree` walks a tree against these rules; the tests pin the
|
|
7
|
+
* table's literal cells so this data cannot drift from the spec.
|
|
8
|
+
*/
|
|
9
|
+
/** One row of the §5.7 table. */
|
|
10
|
+
export interface PlacementRule {
|
|
11
|
+
/** Blocks allowed anywhere in the tree. */
|
|
12
|
+
readonly blocks: ReadonlySet<string>;
|
|
13
|
+
/** The only legal root block type, when the placement pins it. */
|
|
14
|
+
readonly rootBlock?: "page" | "form";
|
|
15
|
+
/** Whether a root `page` may carry `sidebar` (panel: no). */
|
|
16
|
+
readonly pageSidebar: boolean;
|
|
17
|
+
/** Collection displays allowed (`undefined`: all five). */
|
|
18
|
+
readonly collectionDisplays?: readonly string[];
|
|
19
|
+
/** Field types allowed in `form` blocks. */
|
|
20
|
+
readonly fields: ReadonlySet<string>;
|
|
21
|
+
}
|
|
22
|
+
/** The placement names, kept local so this module has no imports. */
|
|
23
|
+
type PlacementName = "view" | "panel" | "settings" | "chat" | "interaction" | "ambient";
|
|
24
|
+
/** Spec §5.7, one row per placement. */
|
|
25
|
+
export declare const PLACEMENT_RULES: Readonly<Record<PlacementName, PlacementRule>>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typebox wire schemas for every block and field (spec §5/§6.1),
|
|
3
|
+
* each compiled once into its own `Validator` (spec §14.2 validation).
|
|
4
|
+
*
|
|
5
|
+
* Design notes:
|
|
6
|
+
* - Schemas are the wire contract, so they deliberately do NOT set
|
|
7
|
+
* `additionalProperties: false`: unknown props pass through and
|
|
8
|
+
* channels ignore them (the block-set version governs what exists).
|
|
9
|
+
* - `Tone`, `IconName` and `Format` are enums/patterns built from the
|
|
10
|
+
* shared constants; an unknown icon name fails here, at the icon
|
|
11
|
+
* prop's own path.
|
|
12
|
+
* - Bindable props are `Union([literal, Binding])` throughout; the
|
|
13
|
+
* literal-ness of STRUCTURAL props (`display`, `groups`, `reorder`,
|
|
14
|
+
* `mode`, field `name`s, `columns`, handler names) is enforced by the
|
|
15
|
+
* second walk in `validate.ts`, not the schema, so the error can say
|
|
16
|
+
* "must be a literal" instead of an anyOf mismatch.
|
|
17
|
+
* - Child slots (`children`, `sidebar`, `main`, `detail`,
|
|
18
|
+
* `columns[].block`, `collection.item`, `form` children, a field's
|
|
19
|
+
* `list.item`, `page.switcher`, a bulk action's `form.root`) are
|
|
20
|
+
* deliberately loose here (any object): `validate.ts` walks the tree
|
|
21
|
+
* and validates every child node against ITS compiled validator, so
|
|
22
|
+
* one broken block is one precise error instead of a union mismatch
|
|
23
|
+
* per sibling branch.
|
|
24
|
+
* - `Compile` (typebox/compile) builds each validator; its errors are
|
|
25
|
+
* read from `instancePath`, relative to the node the validator ran
|
|
26
|
+
* against.
|
|
27
|
+
*/
|
|
28
|
+
import type { Validator } from "typebox/compile";
|
|
29
|
+
/** One compiled validator per block type, keyed by its `type` literal. */
|
|
30
|
+
export declare const BLOCK_VALIDATORS: ReadonlyMap<string, Validator>;
|
|
31
|
+
/** One compiled validator per field type, keyed by its `type` literal. */
|
|
32
|
+
export declare const FIELD_VALIDATORS: ReadonlyMap<string, Validator>;
|
|
33
|
+
/** The compiled validator for `Action` objects (spec §6.1). */
|
|
34
|
+
export declare const ACTION_VALIDATOR: Validator;
|
|
35
|
+
/** The compiled validator for action buttons (spec §6.1). */
|
|
36
|
+
export declare const ACTION_BUTTON_VALIDATOR: Validator;
|
|
37
|
+
/** The compiled validator for a collection's card template (spec §5.5.1). */
|
|
38
|
+
export declare const CARD_VALIDATOR: Validator;
|
package/dist/blocks/schema.js
CHANGED
|
@@ -307,7 +307,10 @@ const MeterBlock = Type.Object({
|
|
|
307
307
|
const LogBlock = Type.Object({
|
|
308
308
|
...BlockBase,
|
|
309
309
|
type: Type.Literal("log"),
|
|
310
|
-
lines
|
|
310
|
+
// Literal lines are legal (ambient placement requires them — the
|
|
311
|
+
// ambient envelope carries no data document); placements that resolve
|
|
312
|
+
// bindings keep the binding form.
|
|
313
|
+
lines: Type.Union([Type.Ref("Binding"), Type.Array(Type.Ref("JsonValue"))]),
|
|
311
314
|
follow: Type.Optional(Type.Boolean()),
|
|
312
315
|
wrap: Type.Optional(Type.Boolean()),
|
|
313
316
|
tones: Type.Optional(Type.Record(Type.String(), Type.Ref("Tone"))),
|
|
@@ -374,7 +377,10 @@ const BulkAction = Type.Object({
|
|
|
374
377
|
const CollectionBlock = Type.Object({
|
|
375
378
|
...BlockBase,
|
|
376
379
|
type: Type.Literal("collection"),
|
|
377
|
-
|
|
380
|
+
// Literal rows are legal (ambient placement requires them — the
|
|
381
|
+
// ambient envelope carries no data document); placements that resolve
|
|
382
|
+
// bindings keep the binding form.
|
|
383
|
+
items: Type.Union([Type.Ref("Binding"), Type.Array(Type.Ref("JsonValue"))]),
|
|
378
384
|
item: AnyObject,
|
|
379
385
|
display: Type.Unsafe({
|
|
380
386
|
type: "string",
|
|
@@ -445,6 +451,7 @@ const NumberField = Type.Object({
|
|
|
445
451
|
const ToggleField = Type.Object({
|
|
446
452
|
...FieldBase,
|
|
447
453
|
type: Type.Literal("toggle"),
|
|
454
|
+
default: Type.Optional(Type.Boolean()),
|
|
448
455
|
});
|
|
449
456
|
const ChoiceField = Type.Object({
|
|
450
457
|
...FieldBase,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `validateTree` (spec §14.2): every error with its JSON path and a
|
|
3
|
+
* plain message.
|
|
4
|
+
*
|
|
5
|
+
* Two passes:
|
|
6
|
+
* 1. The compiled typebox validators (`schema.ts`) check each node's
|
|
7
|
+
* shape: the walk descends the tree, finds every block, field,
|
|
8
|
+
* action, button and card template, and runs the matching compiled
|
|
9
|
+
* validator against it. Instance paths (relative to the node) join
|
|
10
|
+
* the walk's path, so one broken block is one precise error
|
|
11
|
+
* (`root.main[2].text`), never a union mismatch per sibling.
|
|
12
|
+
* 2. The same walk checks what schemas cannot express: placements
|
|
13
|
+
* (§5.7), the literal-ness of structural props (§4.2), handler names
|
|
14
|
+
* (§6.1), relative bindings outside item templates (§4.2), ids
|
|
15
|
+
* (§4.1), `turn` outside chat (§6.2), `open` schemes (§6.1), image
|
|
16
|
+
* sources (§11.1), limits (§4.5), and the ambient one-line rule
|
|
17
|
+
* (§9.2).
|
|
18
|
+
*/
|
|
19
|
+
import { type Placement, type Tree } from "@sigrun/shared/blocks";
|
|
20
|
+
export { PLACEMENT_RULES } from "./placements.js";
|
|
21
|
+
export { LIMITS } from "./limits.js";
|
|
22
|
+
export type { PlacementRule } from "./placements.js";
|
|
23
|
+
export type { PlacementLimits } from "./limits.js";
|
|
24
|
+
/** The placements the block set defines (spec §3). */
|
|
25
|
+
export declare const VALID_PLACEMENTS: readonly Placement[];
|
|
26
|
+
/** One validation error: where and what (spec §14.2). */
|
|
27
|
+
export interface ValidationError {
|
|
28
|
+
path: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
/** Options for `validateTree`. */
|
|
32
|
+
export interface ValidateOptions {
|
|
33
|
+
placement: Placement;
|
|
34
|
+
/**
|
|
35
|
+
* The surface's `actions` map keys (spec §6.1); when given, every
|
|
36
|
+
* handler-name prop must be in it. When omitted (chat), handler
|
|
37
|
+
* names are free labels.
|
|
38
|
+
*/
|
|
39
|
+
handlers?: ReadonlySet<string>;
|
|
40
|
+
/** The server's block-set version; a mismatched tree is rejected (§4.4). */
|
|
41
|
+
serverV?: number;
|
|
42
|
+
/** For ambient trees: which `place` renders this body (§9.2 one-line rule). */
|
|
43
|
+
ambientPlace?: "status" | "footer" | "panel";
|
|
44
|
+
}
|
|
45
|
+
/** The ok result of `validateTree`: the tree, narrowed to the wire type. */
|
|
46
|
+
export interface ValidateOk {
|
|
47
|
+
ok: true;
|
|
48
|
+
tree: Tree;
|
|
49
|
+
}
|
|
50
|
+
/** The failure result: every error found, each with its path. */
|
|
51
|
+
export interface ValidateErr {
|
|
52
|
+
ok: false;
|
|
53
|
+
errors: ValidationError[];
|
|
54
|
+
}
|
|
55
|
+
/** `validateTree`'s result. */
|
|
56
|
+
export type ValidateResult = ValidateOk | ValidateErr;
|
|
57
|
+
/** The result of `validateData`. */
|
|
58
|
+
export type ValidateDataResult = {
|
|
59
|
+
ok: true;
|
|
60
|
+
} | {
|
|
61
|
+
ok: false;
|
|
62
|
+
errors: ValidationError[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Validate a block tree against a placement (spec §14.2). Returns every
|
|
66
|
+
* error with its JSON path; on success, the narrowed tree.
|
|
67
|
+
*/
|
|
68
|
+
export declare function validateTree(tree: unknown, opts: ValidateOptions): ValidateResult;
|
|
69
|
+
/**
|
|
70
|
+
* Validate a surface's data object: a JSON object within the placement's
|
|
71
|
+
* data-size limit (spec §4.5).
|
|
72
|
+
*/
|
|
73
|
+
export declare function validateData(data: unknown, placement: Placement): ValidateDataResult;
|
package/dist/blocks/validate.js
CHANGED
|
@@ -142,9 +142,22 @@ function checkActionButtons(ctx, buttons, path) {
|
|
|
142
142
|
checkAction(ctx, button.action, joinPath(bPath, "action"));
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Bindings outside a card template (§4.2): relative `./` bindings are
|
|
147
|
+
* legal only inside an item template. In the AMBIENT placement, every
|
|
148
|
+
* binding (absolute or relative) outside an item template is rejected —
|
|
149
|
+
* the ambient envelope carries no data document, so a bound prop has
|
|
150
|
+
* nothing to resolve against and renders blank (the 2026-10-24 ruling's
|
|
151
|
+
* items rule, extended to every bindable prop).
|
|
152
|
+
*/
|
|
146
153
|
function checkBinding(ctx, binding, path) {
|
|
147
|
-
if (
|
|
154
|
+
if (ctx.itemTemplate)
|
|
155
|
+
return;
|
|
156
|
+
if (ctx.placement === "ambient") {
|
|
157
|
+
push(ctx, joinPath(path, "$"), `ambient surfaces carry literal content only (binding "${binding.$}" outside a collection item template)`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (binding.$.startsWith("./")) {
|
|
148
161
|
push(ctx, joinPath(path, "$"), `relative binding "${binding.$}" is only valid inside a collection item template`);
|
|
149
162
|
}
|
|
150
163
|
}
|
|
@@ -152,10 +165,12 @@ function checkBinding(ctx, binding, path) {
|
|
|
152
165
|
* Any relative `./` binding on this node (§4.2): every prop value is
|
|
153
166
|
* scanned — the binding-only props (`items`, `lines`, `nodes`, `rows`,
|
|
154
167
|
* `series[].data`, `submitLabel`, `copy.text`) bind just like the
|
|
155
|
-
* bindable literals, so a hardcoded prop list would miss them.
|
|
156
|
-
* `/` bindings stay legal everywhere
|
|
157
|
-
*
|
|
158
|
-
*
|
|
168
|
+
* bindable literals, so a hardcoded prop list would miss them. Outside
|
|
169
|
+
* ambient, absolute `/` bindings stay legal everywhere and relative
|
|
170
|
+
* ones only inside item templates; in ambient, every binding outside an
|
|
171
|
+
* item template fails (checkBinding). `skip` names props whose own walk
|
|
172
|
+
* already reported (the structural literals above carry their more
|
|
173
|
+
* precise message).
|
|
159
174
|
*/
|
|
160
175
|
function checkNodeRelativeBindings(ctx, node, path, skip) {
|
|
161
176
|
if (ctx.itemTemplate)
|
|
@@ -282,6 +297,35 @@ function checkBlock(ctx, node, path, rootDepth) {
|
|
|
282
297
|
checkSchema(ctx, validator, node, path);
|
|
283
298
|
// collection-specific rules
|
|
284
299
|
if (type === "collection") {
|
|
300
|
+
// Controller ruling (2026-10-24): bindings are OUT of ambient
|
|
301
|
+
// placement — the ambient envelope carries no data document, so a
|
|
302
|
+
// bound `items` has nothing to resolve against. Literal rows only.
|
|
303
|
+
// (Reversible: extending the envelope with a data slot remains open.)
|
|
304
|
+
if (ctx.placement === "ambient" && isBinding(node.items)) {
|
|
305
|
+
// checkBinding's ambient branch fires for this prop too; the
|
|
306
|
+
// more specific collection wording wins, so skip `items` below.
|
|
307
|
+
push(ctx, joinPath(path, "items"), "ambient collections carry literal rows (items must not be a binding)");
|
|
308
|
+
}
|
|
309
|
+
// Ambient: bindables nested inside structural props are still
|
|
310
|
+
// bound nowhere (the no-data-document ruling). `groups`/`columns`
|
|
311
|
+
// sit in the structural skip set, so the generic binding walk
|
|
312
|
+
// never reaches their `title`/`label` bindables — walk them here.
|
|
313
|
+
if (ctx.placement === "ambient") {
|
|
314
|
+
const nestedBindables = [
|
|
315
|
+
["groups", "title"],
|
|
316
|
+
["columns", "label"],
|
|
317
|
+
];
|
|
318
|
+
for (const [listProp, leafProp] of nestedBindables) {
|
|
319
|
+
const list = node[listProp];
|
|
320
|
+
if (!Array.isArray(list))
|
|
321
|
+
continue;
|
|
322
|
+
list.forEach((entry, i) => {
|
|
323
|
+
if (isRec(entry) && isBinding(entry[leafProp])) {
|
|
324
|
+
checkBinding(ctx, entry[leafProp], joinPath(joinPath(joinPath(path, listProp), String(i)), leafProp));
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
285
329
|
const display = node.display;
|
|
286
330
|
if (typeof display === "string" &&
|
|
287
331
|
rules.collectionDisplays &&
|
|
@@ -350,9 +394,14 @@ function checkBlock(ctx, node, path, rootDepth) {
|
|
|
350
394
|
if (!check.ok)
|
|
351
395
|
push(ctx, joinPath(path, "src"), check.message);
|
|
352
396
|
}
|
|
353
|
-
// bindable props: every relative `./` binding on the node (§4.2)
|
|
397
|
+
// bindable props: every relative `./` binding on the node (§4.2).
|
|
398
|
+
// `items` joins the skip set in ambient so the collection-specific
|
|
399
|
+
// message above stays the single error at its path.
|
|
354
400
|
const structural = STRUCTURAL_LITERALS[type] ?? [];
|
|
355
|
-
|
|
401
|
+
const skip = new Set(structural);
|
|
402
|
+
if (type === "collection" && ctx.placement === "ambient")
|
|
403
|
+
skip.add("items");
|
|
404
|
+
checkNodeRelativeBindings(ctx, node, path, skip);
|
|
356
405
|
// onActivate carries an Action (§6.1): same rules as button actions
|
|
357
406
|
if (node.onActivate !== undefined) {
|
|
358
407
|
checkAction(ctx, node.onActivate, joinPath(path, "onActivate"));
|
package/dist/cli.js
CHANGED
|
@@ -49,6 +49,12 @@ import { Writable } from "node:stream";
|
|
|
49
49
|
import { resolveStoreRoots, writeFileAtomic } from "./harness/stores.js";
|
|
50
50
|
import { openIdentityStores } from "./harness/identity/index.js";
|
|
51
51
|
import { pathToFileURL } from "node:url";
|
|
52
|
+
import { effectiveMethods, loadConfig } from "./config.js";
|
|
53
|
+
import { probeIsolation, runHelperProbe, selfTestHelper } from "./sandbox/availability.js";
|
|
54
|
+
import { SettingsStore } from "./harness/settings.js";
|
|
55
|
+
import { resolveHelper } from "./sandbox/helper.js";
|
|
56
|
+
import { selfPackageRoot } from "./harness/packages.js";
|
|
57
|
+
import { createAdmissionGuard } from "./harness/identity/users.js";
|
|
52
58
|
import { serve } from "./serve.js";
|
|
53
59
|
import { ACCENT_NAMES, PALETTE_NAMES, isAccentName, isPaletteName, } from "@sigrun/shared/themes";
|
|
54
60
|
export const USAGE_TEXT = `usage: sigrun [--user <name>]
|
|
@@ -643,7 +649,7 @@ async function runProviderCommand(parsed, env, io, fetchImpl, readSecret) {
|
|
|
643
649
|
* secret exists only at create time);
|
|
644
650
|
* - `token revoke` fails loud on an unknown id.
|
|
645
651
|
*/
|
|
646
|
-
async function runIdentityCommand(parsed, env, io) {
|
|
652
|
+
async function runIdentityCommand(parsed, env, io, isolationProbe) {
|
|
647
653
|
const stores = openIdentityStores(resolveStoreRoots(env));
|
|
648
654
|
if (parsed.command === "token") {
|
|
649
655
|
switch (parsed.sub) {
|
|
@@ -679,6 +685,24 @@ async function runIdentityCommand(parsed, env, io) {
|
|
|
679
685
|
}
|
|
680
686
|
switch (parsed.sub) {
|
|
681
687
|
case "add": {
|
|
688
|
+
const config = loadConfig(env, new SettingsStore({ dir: defaultConfigDir(env) }).read());
|
|
689
|
+
const coreRoot = selfPackageRoot();
|
|
690
|
+
const isolation = await isolationProbe({
|
|
691
|
+
platform: process.platform,
|
|
692
|
+
helper: coreRoot === undefined ? undefined : resolveHelper(env, coreRoot),
|
|
693
|
+
runProbe: runHelperProbe,
|
|
694
|
+
selfTest: selfTestHelper,
|
|
695
|
+
});
|
|
696
|
+
const methods = effectiveMethods(config.auth, "127.0.0.1");
|
|
697
|
+
stores.users.configureAdmission({
|
|
698
|
+
firstWorkspace: config.workspace.path,
|
|
699
|
+
admit: createAdmissionGuard({
|
|
700
|
+
isolation,
|
|
701
|
+
mode: config.isolation.mode,
|
|
702
|
+
methods,
|
|
703
|
+
proxySecretHeader: config.auth.proxy.secretHeader,
|
|
704
|
+
}),
|
|
705
|
+
});
|
|
682
706
|
await stores.users.add({
|
|
683
707
|
user: parsed.user,
|
|
684
708
|
admin: parsed.admin ?? false,
|
|
@@ -850,7 +874,7 @@ export async function cliMain(argv, env = process.env, deps, io = {
|
|
|
850
874
|
case "token":
|
|
851
875
|
case "user": {
|
|
852
876
|
// The identity commands (Task 7): stores directly, server or not.
|
|
853
|
-
return await runIdentityCommand(parsed, env, io);
|
|
877
|
+
return await runIdentityCommand(parsed, env, io, realDeps.probeIsolation ?? probeIsolation);
|
|
854
878
|
}
|
|
855
879
|
case "provider": {
|
|
856
880
|
// The provider commands (release 0.1 Task 2b): API clients over
|
package/dist/config.js
CHANGED
|
@@ -148,6 +148,7 @@ export const DEFAULT_PROVIDER_REFRESH_MINUTES = 15;
|
|
|
148
148
|
* defaulted — they exist only when env provides them.
|
|
149
149
|
*/
|
|
150
150
|
export const DEFAULT_AUTH = {
|
|
151
|
+
sessionDays: 30,
|
|
151
152
|
methods: undefined,
|
|
152
153
|
proxy: {
|
|
153
154
|
trustedProxies: [],
|
|
@@ -323,6 +324,40 @@ function parseDev(env) {
|
|
|
323
324
|
return false;
|
|
324
325
|
throw new Error(`SIGRUN_DEV must be "true" or "false" (or 1/0), got ${JSON.stringify(raw)}`);
|
|
325
326
|
}
|
|
327
|
+
/** Parse `SIGRUN_ISOLATION`; unset/empty lets the file/default tier apply. */
|
|
328
|
+
function parseIsolationMode(env) {
|
|
329
|
+
const raw = env.SIGRUN_ISOLATION;
|
|
330
|
+
if (raw === undefined || raw === "")
|
|
331
|
+
return undefined;
|
|
332
|
+
const mode = raw.trim().toLowerCase();
|
|
333
|
+
if (mode === "auto" || mode === "always" || mode === "off")
|
|
334
|
+
return mode;
|
|
335
|
+
throw new Error(`SIGRUN_ISOLATION must be "auto", "always", or "off", got ${JSON.stringify(raw)}`);
|
|
336
|
+
}
|
|
337
|
+
function parseIsolation(env, server) {
|
|
338
|
+
return {
|
|
339
|
+
mode: parseIsolationMode(env) ?? server?.mode ?? "auto",
|
|
340
|
+
osUsers: server?.osUsers ?? false,
|
|
341
|
+
workerIdleMinutes: server?.workerIdleMinutes ?? 10,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Parse `SIGRUN_SUPERVISED` (UI-blocks U4 Task 2, spec §13.3): the
|
|
346
|
+
* `parseDev` grammar exactly ("true"/"false"/1/0, strict otherwise —
|
|
347
|
+
* the env contract is the deploy's promise that a restart policy exists,
|
|
348
|
+
* so a fuzzy parse would lie about the failure mode).
|
|
349
|
+
*/
|
|
350
|
+
function parseSupervised(env) {
|
|
351
|
+
const raw = env.SIGRUN_SUPERVISED;
|
|
352
|
+
if (raw === undefined || raw === "")
|
|
353
|
+
return undefined;
|
|
354
|
+
const lowered = raw.trim().toLowerCase();
|
|
355
|
+
if (lowered === "true" || lowered === "1")
|
|
356
|
+
return true;
|
|
357
|
+
if (lowered === "false" || lowered === "0")
|
|
358
|
+
return false;
|
|
359
|
+
throw new Error(`SIGRUN_SUPERVISED must be "true" or "false" (or 1/0), got ${JSON.stringify(raw)}`);
|
|
360
|
+
}
|
|
326
361
|
/**
|
|
327
362
|
* Parse `SIGRUN_AUTH`: a csv of `token`/`proxy` (order preserved,
|
|
328
363
|
* duplicates dropped), `none` ALONE as the empty list (authentication
|
|
@@ -398,6 +433,7 @@ function parseAuth(env, server) {
|
|
|
398
433
|
const token = env.SIGRUN_API_TOKEN?.trim() || undefined;
|
|
399
434
|
const secret = env.SIGRUN_PROXY_SECRET?.trim() || undefined;
|
|
400
435
|
return {
|
|
436
|
+
sessionDays: server?.sessionDays ?? DEFAULT_AUTH.sessionDays,
|
|
401
437
|
methods,
|
|
402
438
|
proxy: {
|
|
403
439
|
trustedProxies,
|
|
@@ -730,11 +766,15 @@ export function resolvedServerSection(config) {
|
|
|
730
766
|
proxySection !== undefined ||
|
|
731
767
|
config.auth.adminGroups.length > 0 ||
|
|
732
768
|
config.auth.allowedHosts !== undefined ||
|
|
733
|
-
config.auth.allowedOrigins !== undefined
|
|
769
|
+
config.auth.allowedOrigins !== undefined ||
|
|
770
|
+
config.auth.sessionDays !== DEFAULT_AUTH.sessionDays
|
|
734
771
|
? {
|
|
735
772
|
...(config.auth.methods === undefined
|
|
736
773
|
? {}
|
|
737
774
|
: { methods: [...config.auth.methods] }),
|
|
775
|
+
...(config.auth.sessionDays === DEFAULT_AUTH.sessionDays
|
|
776
|
+
? {}
|
|
777
|
+
: { sessionDays: config.auth.sessionDays }),
|
|
738
778
|
...(proxySection === undefined ? {} : { proxy: proxySection }),
|
|
739
779
|
...(config.auth.adminGroups.length > 0
|
|
740
780
|
? { adminGroups: [...config.auth.adminGroups] }
|
|
@@ -765,6 +805,7 @@ export function resolvedServerSection(config) {
|
|
|
765
805
|
...(webui === undefined ? {} : { webui }),
|
|
766
806
|
compaction: { ...config.compaction },
|
|
767
807
|
memorySeam: { ...config.memorySeam },
|
|
808
|
+
isolation: { ...config.isolation },
|
|
768
809
|
providers: { ...config.providers },
|
|
769
810
|
...(auth === undefined ? {} : { auth }),
|
|
770
811
|
...(metrics === undefined ? {} : { metrics }),
|
|
@@ -803,6 +844,7 @@ export function resolvedServerSection(config) {
|
|
|
803
844
|
* | `SIGRUN_IDLE_THRESHOLD` | `memorySeam.idleThresholdMs` |
|
|
804
845
|
* | `SIGRUN_WORKER_DEADLINE` | `memorySeam.workerDeadlineMs` |
|
|
805
846
|
* | `SIGRUN_QUEUE_MAX` | `memorySeam.queueMax` |
|
|
847
|
+
* | `SIGRUN_ISOLATION` | `isolation.mode` |
|
|
806
848
|
* | `SIGRUN_PROVIDER_REFRESH_MINUTES` | `providers.refreshIntervalMinutes` |
|
|
807
849
|
* | `SIGRUN_BACKUP_*` | `backup` |
|
|
808
850
|
* | `SIGRUN_MAX_UPLOAD_BYTES` | `maxUploadBytes` |
|
|
@@ -859,6 +901,9 @@ export function envServerSection(env) {
|
|
|
859
901
|
});
|
|
860
902
|
if (memorySeam !== undefined)
|
|
861
903
|
section.memorySeam = memorySeam;
|
|
904
|
+
const isolationMode = parseIsolationMode(env);
|
|
905
|
+
if (isolationMode !== undefined)
|
|
906
|
+
section.isolation = { mode: isolationMode };
|
|
862
907
|
const refreshIntervalMinutes = parseNonNegativeIntEnv(env, PROVIDER_REFRESH_MINUTES_ENV);
|
|
863
908
|
if (refreshIntervalMinutes !== undefined) {
|
|
864
909
|
section.providers = { refreshIntervalMinutes };
|
|
@@ -877,6 +922,12 @@ export function envServerSection(env) {
|
|
|
877
922
|
const dev = parseDev(env);
|
|
878
923
|
if (dev !== undefined)
|
|
879
924
|
section.dev = dev;
|
|
925
|
+
// The supervised flag (§13.3) — the same bootstrap rule as dev:
|
|
926
|
+
// projected only when the env var is SET (a defaulted flag is never
|
|
927
|
+
// persisted; the deploy's restart posture is env intent, not state).
|
|
928
|
+
const supervised = parseSupervised(env);
|
|
929
|
+
if (supervised !== undefined)
|
|
930
|
+
section.supervised = supervised;
|
|
880
931
|
return section;
|
|
881
932
|
}
|
|
882
933
|
/**
|
|
@@ -950,8 +1001,18 @@ export function effectiveServerSection(env, fileServer, config) {
|
|
|
950
1001
|
"memorySeam",
|
|
951
1002
|
"queueMax",
|
|
952
1003
|
]);
|
|
1004
|
+
emit("auth.sessionDays", config.auth.sessionDays, ["auth", "sessionDays"]);
|
|
1005
|
+
emit("isolation.mode", config.isolation.mode, ["isolation", "mode"]);
|
|
1006
|
+
emit("isolation.osUsers", config.isolation.osUsers, ["isolation", "osUsers"]);
|
|
1007
|
+
emit("isolation.workerIdleMinutes", config.isolation.workerIdleMinutes, [
|
|
1008
|
+
"isolation",
|
|
1009
|
+
"workerIdleMinutes",
|
|
1010
|
+
]);
|
|
953
1011
|
emit("providers.refreshIntervalMinutes", config.providers.refreshIntervalMinutes, ["providers", "refreshIntervalMinutes"]);
|
|
954
1012
|
emit("dev", config.dev, ["dev"]);
|
|
1013
|
+
// U4 §13.3: the supervised posture rides the effective block too —
|
|
1014
|
+
// the settings surface's restart affordance reads one place.
|
|
1015
|
+
emit("supervised", config.supervised, ["supervised"]);
|
|
955
1016
|
if (config.backup?.target === "path") {
|
|
956
1017
|
emit("backup.path", config.backup.path, ["backup", "path"]);
|
|
957
1018
|
emit("backup.staleAfterHours", config.backupStaleAfterHours, ["backup", "staleAfterHours"]);
|
|
@@ -1093,6 +1154,7 @@ export function loadConfig(env, settingsFile) {
|
|
|
1093
1154
|
backupStaleAfterHours: server?.backup?.staleAfterHours ?? DEFAULT_BACKUP_STALE_AFTER_HOURS,
|
|
1094
1155
|
compaction: parseCompaction(env, server?.compaction),
|
|
1095
1156
|
memorySeam: parseMemorySeam(env, server?.memorySeam),
|
|
1157
|
+
isolation: parseIsolation(env, server?.isolation),
|
|
1096
1158
|
providers: {
|
|
1097
1159
|
refreshIntervalMinutes: parseRefreshIntervalMinutes(env, server?.providers?.refreshIntervalMinutes),
|
|
1098
1160
|
},
|
|
@@ -1101,6 +1163,10 @@ export function loadConfig(env, settingsFile) {
|
|
|
1101
1163
|
// The development flag (§14.2/§14.4): strict env parse over the
|
|
1102
1164
|
// file's `server.dev`, default false.
|
|
1103
1165
|
dev: parseDev(env) ?? server?.dev ?? false,
|
|
1166
|
+
// The supervised posture (§13.3): env over file over false. Only
|
|
1167
|
+
// the chart and compose set the env; a file-set flag serves a
|
|
1168
|
+
// hand-rolled systemd/supervisord deploy (same restart promise).
|
|
1169
|
+
supervised: parseSupervised(env) ?? server?.supervised ?? false,
|
|
1104
1170
|
// The jobs default timezone (§8.2): env over file over the process
|
|
1105
1171
|
// TZ, validated loud (a bad name would otherwise crash the scheduler
|
|
1106
1172
|
// at evaluation time instead of failing boot).
|