stitchkit 0.103.8 → 0.103.9
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 +13 -0
- package/dist/agent-runtime/sandbox-process.d.ts +2 -2
- package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
- package/dist/durability/contract.d.ts +3 -2
- package/dist/durability/contract.d.ts.map +1 -1
- package/dist/durability/effect.d.ts.map +1 -1
- package/dist/durability/engine.d.ts.map +1 -1
- package/dist/durability/json.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/harness.js +2 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +29 -81
- package/dist/entrypoints/agent-runtime.js +2 -2
- package/dist/entrypoints/cli.js +1 -1
- package/dist/entrypoints/files.d.ts +2 -0
- package/dist/entrypoints/files.d.ts.map +1 -1
- package/dist/entrypoints/files.js +6 -2
- package/dist/entrypoints/primitives.js +77 -57
- package/dist/entrypoints/process.d.ts +4 -0
- package/dist/entrypoints/process.d.ts.map +1 -0
- package/dist/entrypoints/process.js +13 -0
- package/dist/entrypoints/tools.js +12 -3
- package/dist/files/boundary.d.ts +19 -1
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files/file-io.d.ts +5 -1
- package/dist/files/file-io.d.ts.map +1 -1
- package/dist/files/read.d.ts +13 -0
- package/dist/files/read.d.ts.map +1 -0
- package/dist/index-12m2v2a9.js +98 -0
- package/dist/index-8a9ksm91.js +95 -0
- package/dist/{index-90ppdx3q.js → index-9jb61j5a.js} +1 -1
- package/dist/{index-5vyd0fkb.js → index-ary5s7k0.js} +40 -61
- package/dist/index-c1jmtxa0.js +74 -0
- package/dist/index-eeqmg7qm.js +202 -0
- package/dist/index-mbnnw3f2.js +25 -0
- package/dist/{index-v4a2fjtk.js → index-sybedc8b.js} +124 -55
- package/dist/internal/abort-race.d.ts +3 -0
- package/dist/internal/abort-race.d.ts.map +1 -0
- package/dist/internal/atomic-file.d.ts +20 -2
- package/dist/internal/atomic-file.d.ts.map +1 -1
- package/dist/internal/atomic-publication.d.ts +19 -0
- package/dist/internal/atomic-publication.d.ts.map +1 -0
- package/dist/internal/json-data.d.ts +8 -0
- package/dist/internal/json-data.d.ts.map +1 -0
- package/dist/primitives/canonical-json.d.ts +11 -0
- package/dist/primitives/canonical-json.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/process/command-owner.d.ts +13 -0
- package/dist/process/command-owner.d.ts.map +1 -0
- package/dist/process/command.d.ts +3 -0
- package/dist/process/command.d.ts.map +1 -0
- package/dist/process/contract.d.ts +35 -0
- package/dist/process/contract.d.ts.map +1 -0
- package/dist/process/group.d.ts +6 -0
- package/dist/process/group.d.ts.map +1 -0
- package/dist/tools/transfer/managed-file-error.d.ts.map +1 -1
- package/llms/files.txt +8 -0
- package/llms/primitives.txt +31 -1
- package/llms/process.txt +167 -0
- package/llms/stitchkit.txt +1 -0
- package/llms/tools.txt +18 -0
- package/llms-full.txt +216 -1
- package/llms.txt +6 -4
- package/package.json +5 -1
- package/dist/index-aan6hqb9.js +0 -65
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const NativeCommandOptionsSchema: z.ZodObject<{
|
|
3
|
+
executable: z.ZodString;
|
|
4
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
5
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
6
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
7
|
+
envPolicy: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
ambient: "ambient";
|
|
9
|
+
"declared-only": "declared-only";
|
|
10
|
+
}>>;
|
|
11
|
+
signal: z.ZodOptional<z.ZodCustom<AbortSignal, AbortSignal>>;
|
|
12
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
maxOutputBytes: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
capture: z.ZodDefault<z.ZodBoolean>;
|
|
15
|
+
stdin: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>>;
|
|
16
|
+
maxStdinBytes: z.ZodDefault<z.ZodNumber>;
|
|
17
|
+
onOutput: z.ZodOptional<z.ZodCustom<(bytes: Uint8Array, channel: 'stdout' | 'stderr', signal: AbortSignal) => void | Promise<void>, (bytes: Uint8Array, channel: 'stdout' | 'stderr', signal: AbortSignal) => void | Promise<void>>>;
|
|
18
|
+
killGraceMs: z.ZodDefault<z.ZodNumber>;
|
|
19
|
+
cleanupTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type NativeCommandOptions = z.input<typeof NativeCommandOptionsSchema>;
|
|
22
|
+
export type ParsedNativeCommandOptions = z.output<typeof NativeCommandOptionsSchema>;
|
|
23
|
+
declare const NativeCommandResultSchema: z.ZodObject<{
|
|
24
|
+
exitCode: z.ZodNullable<z.ZodNumber>;
|
|
25
|
+
signal: z.ZodNullable<z.ZodString>;
|
|
26
|
+
stdout: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
|
|
27
|
+
stderr: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
export type NativeCommandResult = z.infer<typeof NativeCommandResultSchema>;
|
|
30
|
+
export declare class NativeCommandError extends Error {
|
|
31
|
+
readonly code: 'COMMAND_LIMIT' | 'COMMAND_UNAVAILABLE' | 'COMMAND_CLEANUP';
|
|
32
|
+
constructor(code: 'COMMAND_LIMIT' | 'COMMAND_UNAVAILABLE' | 'COMMAND_CLEANUP', message: string, options?: ErrorOptions);
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/process/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;gDAoBtB,UAAU,WACR,QAAQ,GAAG,QAAQ,UACpB,WAAW,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;kBAgB7B,CAAC;AACL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,0BAA0B,CAAC,CAAC;AACrF,QAAA,MAAM,yBAAyB;;;;;iBAK7B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,IAAI,EAAE,eAAe,GAAG,qBAAqB,GAAG,iBAAiB;IADnF,YACkB,IAAI,EAAE,eAAe,GAAG,qBAAqB,GAAG,iBAAiB,EACjF,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,EAIvB;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { NativeCommandError } from './contract.js';
|
|
2
|
+
export declare function signalGroup(pid: number | undefined, signal: NodeJS.Signals): void;
|
|
3
|
+
export declare function stopCommandGroup(pid: number | undefined, graceMs: number): Promise<void>;
|
|
4
|
+
export declare function commandCleanupError(cause: unknown): NativeCommandError;
|
|
5
|
+
export declare function waitForCommandClose<T>(closed: Promise<T>, timeoutMs: number): Promise<T>;
|
|
6
|
+
//# sourceMappingURL=group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../src/process/group.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAOjF;AAYD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAItE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAExF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed-file-error.d.ts","sourceRoot":"","sources":["../../../src/tools/transfer/managed-file-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"managed-file-error.d.ts","sourceRoot":"","sources":["../../../src/tools/transfer/managed-file-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AAmBtE,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAyBnE;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAE/D"}
|
package/llms/files.txt
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
Contains: API reference: `stitchkit/files` (docs/api/reference.md)
|
|
7
7
|
|
|
8
|
+
Also read:
|
|
9
|
+
- see `llms/process.txt` for the Native IO guide
|
|
10
|
+
|
|
8
11
|
==============================================================================
|
|
9
12
|
# API reference: `stitchkit/files` (docs/api/reference.md)
|
|
10
13
|
==============================================================================
|
|
@@ -35,3 +38,8 @@ available from `stitchkit/contract`.
|
|
|
35
38
|
| `CHUNK_SPOOL_ERROR_CODES` / `isChunkSpoolErrorCode` | const / function | every `ChunkSpoolErrorCode` as a frozen list, and the guard for one — map them onto the app's own error codes, or a spool refusal reaches its client as an unknown 500 |
|
|
36
39
|
|
|
37
40
|
---
|
|
41
|
+
|
|
42
|
+
| Export | Kind | Purpose |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `AtomicFilePublicationError` | class | published=true with cleanup/directory-sync/directory-close phase |
|
|
45
|
+
| `FileObservation` | type | observed descriptor dev/ino/size/nlink/mtimeMs/ctimeMs |
|
package/llms/primitives.txt
CHANGED
|
@@ -15,7 +15,7 @@ description: Declare lifecycle, access, exact values, audit, delivery and export
|
|
|
15
15
|
type: guide
|
|
16
16
|
status: active
|
|
17
17
|
created: 2026-09-01
|
|
18
|
-
updated: 2026-
|
|
18
|
+
updated: 2026-10-01 21:03 +07:00
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# Generic application primitives
|
|
@@ -108,6 +108,31 @@ of the application's existing async operation. The input, scope, metadata and to
|
|
|
108
108
|
change. The application creates bytes and owns operation persistence; the managed-file boundary
|
|
109
109
|
streams them.
|
|
110
110
|
|
|
111
|
+
## Canonical JSON
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { canonicalJson } from 'stitchkit/primitives'
|
|
115
|
+
|
|
116
|
+
const bytes = canonicalJson({ '2': 'two', '10': 'ten', optional: undefined, nested: [1, null] }, {
|
|
117
|
+
maxDepth: 20, maxNodes: 10_000, maxBytes: 64 * 1024,
|
|
118
|
+
})
|
|
119
|
+
// {"10":"ten","2":"two","nested":[1,null]}
|
|
120
|
+
// Native crypto or browser WebCrypto hashes these same UTF-8 bytes.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`canonicalJson` использует единый serializer исторических digest: object keys в UTF-16 code-unit
|
|
124
|
+
order, включая integer-like keys; array order сохраняется. Optional object members с undefined
|
|
125
|
+
опускаются. Top-level/array undefined, sparse arrays, functions/symbols/BigInt, non-finite numbers,
|
|
126
|
+
negative zero, cycles, user prototypes, accessors, non-enumerable/symbol members отказаны до
|
|
127
|
+
serialization. Приложение валидирует свою schema первым; canonical boundary не заменяет её.
|
|
128
|
+
|
|
129
|
+
Defaults: depth 100 (maximum 100), nodes 100 000, UTF-8 bytes 1 MiB. Превышение или не-JSON data —
|
|
130
|
+
TypeError; неверные options — Zod validation error. Это browser-safe leaf без Node/Bun/crypto,
|
|
131
|
+
agent-runtime/testing или optional peers. Native и browser используют один algorithm; hashing
|
|
132
|
+
остаётся platform adapter. Переносите только serializer recursion, сохраняя proof schemas и
|
|
133
|
+
verification boundary. Существующие внутренние historical normalization/digest fixtures не меняются;
|
|
134
|
+
новый public boundary сознательно отказывает неоднозначным values вместо фиктивной совместимости.
|
|
135
|
+
|
|
111
136
|
==============================================================================
|
|
112
137
|
# API reference: `stitchkit/primitives` (docs/api/reference.md)
|
|
113
138
|
==============================================================================
|
|
@@ -156,3 +181,8 @@ schedules, transports and file generation remain application-owned.
|
|
|
156
181
|
| `SourceText` / `SourceRisk` | _types_ | migration scanner input and exact path/line evidence |
|
|
157
182
|
|
|
158
183
|
---
|
|
184
|
+
|
|
185
|
+
| Export | Kind | Purpose |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `canonicalJson` | function | bounded plain JSON, UTF-16 key sort, optional object members omitted |
|
|
188
|
+
| `CanonicalJsonOptions` | type | inferred depth/node/UTF-8 bytes limits |
|
package/llms/process.txt
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# stitchkit/process — stitchkit docs slice
|
|
2
|
+
|
|
3
|
+
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
|
+
> `llms.txt` in this package.
|
|
5
|
+
|
|
6
|
+
Contains: Guide: Native IO (docs/guide/native-io.md) · API reference: `stitchkit/process` (docs/api/reference.md)
|
|
7
|
+
|
|
8
|
+
==============================================================================
|
|
9
|
+
# Guide: Native IO (docs/guide/native-io.md)
|
|
10
|
+
==============================================================================
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
title: Конечные команды и файловые гарантии
|
|
14
|
+
description: Native IO для Bun и Node, с явно выбранной durability, безопасными bounds и caller lifetime.
|
|
15
|
+
type: guide
|
|
16
|
+
status: active
|
|
17
|
+
created: 2026-10-01 20:44 +07:00
|
|
18
|
+
updated: 2026-10-01 21:31 +07:00
|
|
19
|
+
participants:
|
|
20
|
+
- role: authored
|
|
21
|
+
harness: Codex
|
|
22
|
+
model: GPT-6
|
|
23
|
+
at: 2026-10-01 20:44 +07:00
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Native IO
|
|
27
|
+
|
|
28
|
+
Примеры используют публичные imports и одинаково исполняются в Bun и Node ≥22.
|
|
29
|
+
|
|
30
|
+
## Запись файла
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { writeFileAtomic, AtomicFilePublicationError } from 'stitchkit/files'
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await writeFileAtomic('/srv/app/receipt.json', JSON.stringify({ id: 'receipt-1' }), {
|
|
37
|
+
replace: false,
|
|
38
|
+
mode: 0o600,
|
|
39
|
+
durability: 'directory',
|
|
40
|
+
})
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof AtomicFilePublicationError) {
|
|
43
|
+
// target уже виден. Проверьте receipt, прежде чем решать о повторе.
|
|
44
|
+
console.error(error.phase, error.cause)
|
|
45
|
+
} else throw error
|
|
46
|
+
}
|
|
47
|
+
await writeFileAtomic('/srv/app/state.json', '{"version":2}', {
|
|
48
|
+
replace: true, durability: 'directory',
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
По умолчанию `replace: true`, `durability: 'file'`, `mode: 0o600`: existing вызовы сохраняют
|
|
53
|
+
file-fsync и atomic replacement. `durability: 'none'` отключает sync; `'directory'` выполняет
|
|
54
|
+
полную запись → exact descriptor chmod → file fsync → rename (replace) либо link (create) →
|
|
55
|
+
удаление staging link → parent fsync. `writeFileAtomicSync` имеет те же options и порядок.
|
|
56
|
+
Create отказывает с native `EEXIST` при существующем файле, symlink или concurrent winner.
|
|
57
|
+
Никогда не переводите этот отказ в replace автоматически.
|
|
58
|
+
|
|
59
|
+
`AtomicFilePublicationError.published === true` отличает видимость от неподтверждённого
|
|
60
|
+
acknowledgement. `phase` — `cleanup`, `directory-sync` или `directory-close`; первичная причина
|
|
61
|
+
сохранена в `cause`. Ошибка до publish оставляет прежний target. Cleanup вторичен к первичной ошибке.
|
|
62
|
+
При create кратко существует второй hardlink; strict single-link reader вправе отказать в этом окне.
|
|
63
|
+
|
|
64
|
+
Directory fsync — native POSIX/filesystem capability. Windows явно отказывает; иной filesystem
|
|
65
|
+
может отказать при open/sync. Проверка на Linux не подтверждает поведение macOS, сетевой FS или
|
|
66
|
+
сохранность при отключении питания. Состояние диска определяется гарантиями самой FS/ОС.
|
|
67
|
+
`createManagedFileBoundary.write(..., { durable: true })` использует тот же publish owner и parent sync;
|
|
68
|
+
его create по умолчанию остаётся create, а `replace: true` явно разрешает replacement.
|
|
69
|
+
|
|
70
|
+
## Строгое чтение
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { createManagedFileBoundary } from 'stitchkit/files'
|
|
74
|
+
|
|
75
|
+
const files = await createManagedFileBoundary({ root: '/srv/app', maxReadBytes: 64 * 1024 })
|
|
76
|
+
const source = await files.read('receipt.json', {
|
|
77
|
+
rejectSymlinks: true, singleLink: true, stable: true, observe: true,
|
|
78
|
+
})
|
|
79
|
+
console.log(source.observation) // dev, ino, size, nlink, mtimeMs, ctimeMs
|
|
80
|
+
const receipt = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(source.bytes))
|
|
81
|
+
// schema.parse(receipt) и проверка digest принадлежат приложению.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Без новых options ответ сохраняет `{ ref, bytes }`, допускает trusted in-root symlink и hardlink.
|
|
85
|
+
`rejectSymlinks` запрещает requested leaf, `singleLink` проверяет `nlink` открытого descriptor,
|
|
86
|
+
`stable` сравнивает dev/ino/size/mtime/ctime/nlink вокруг byte read; `observe` добавляет metadata.
|
|
87
|
+
`FILE_NOT_FOUND`, `FILE_NOT_REGULAR`, `FILE_UNSAFE_LINK`, `FILE_TOO_LARGE`, `FILE_CHANGED`,
|
|
88
|
+
`FILE_UNSUPPORTED` и `FILE_IO_ERROR` различаются. Abort сохраняет caller reason.
|
|
89
|
+
Nonblocking open позволяет отказать FIFO без ожидания writer. Bytes ограничены во время чтения,
|
|
90
|
+
включая рост файла, не только initial stat. Chunk allocation ≤ maxBytes+1+64 KiB, flatten ≤ maxBytes;
|
|
91
|
+
FD закрывается при success, отказе, abort и IO error.
|
|
92
|
+
Ошибка close сохраняет первичную ошибку чтения; после успешного чтения ошибка close возвращается caller.
|
|
93
|
+
|
|
94
|
+
Root и ancestors принадлежат trusted actor. Эти options не дают descriptor-relative containment
|
|
95
|
+
при hostile concurrent ancestor replacement. Отдельного переключателя «считать ancestors безопасными»
|
|
96
|
+
нет: portable owner не предоставляет такую capability. Metadata stability не доказывает immutable
|
|
97
|
+
snapshot против произвольного hostile writer; используйте application lock/immutable publication.
|
|
98
|
+
|
|
99
|
+
## Команды
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { runNativeCommand } from 'stitchkit/process'
|
|
103
|
+
|
|
104
|
+
const captured = await runNativeCommand({
|
|
105
|
+
executable: '/usr/bin/git', args: ['rev-parse', 'HEAD'], cwd: '/srv/app',
|
|
106
|
+
envPolicy: 'declared-only', env: { PATH: '/usr/bin:/bin' },
|
|
107
|
+
capture: true, maxOutputBytes: 4096, timeoutMs: 5000,
|
|
108
|
+
})
|
|
109
|
+
console.log(captured.exitCode, new TextDecoder().decode(captured.stdout))
|
|
110
|
+
|
|
111
|
+
const lifetime = new AbortController()
|
|
112
|
+
await runNativeCommand({
|
|
113
|
+
executable: '/usr/bin/git', args: ['status'], signal: lifetime.signal,
|
|
114
|
+
onOutput: async (bytes, channel, outputSignal) => {
|
|
115
|
+
outputSignal.throwIfAborted()
|
|
116
|
+
await output.write(bytes, channel, outputSignal) // application-owned bounded sink
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Команда требует caller signal или finite `timeoutMs` (1–2147483647 ms: native timer range; большее значение отказывается, не превращается в 1 ms). Streaming имеет caller lifetime, без скрытого
|
|
122
|
+
60-second timeout или 1-MiB output limit. `capture` по умолчанию false; true требует
|
|
123
|
+
`maxOutputBytes` для суммарных stdout+stderr. Лимит считает исходные bytes; API возвращает
|
|
124
|
+
`Uint8Array`, не преобразует invalid UTF-8. Result содержит наблюдаемый `exitCode: number | null`
|
|
125
|
+
и `signal: string | null`; signal termination не превращается в выдуманный exit code. `maxOutputBytes` может ограничивать и streaming.
|
|
126
|
+
`stdin` — bytes с `maxStdinBytes` (default 1 MiB). `envPolicy` default `declared-only`: отсутствие env
|
|
127
|
+
означает пустой environment; `ambient` явно разрешает наследование. Cwd/executable policy остаётся
|
|
128
|
+
приложению; этот primitive запускает native executable и сам не создаёт shell.
|
|
129
|
+
|
|
130
|
+
Вывод читается с backpressure отдельно для stdout/stderr: один выполняющийся sink на канал плюс
|
|
131
|
+
bounded native Readable buffer; накопления всей истории при streaming нет. Callback получает
|
|
132
|
+
lifetime signal. Abort освобождает ожидание даже игнорирующего signal callback, но не способен
|
|
133
|
+
отменить сторонние действия, которые сам callback продолжит после abort. Sink обязан отменять свои
|
|
134
|
+
IO, не сохранять бесконечную историю и не изменять shared descriptor flags. После abort новых
|
|
135
|
+
callbacks owner не запускает. Normal completion ждёт всех bytes и sinks.
|
|
136
|
+
|
|
137
|
+
Cleanup: POSIX process group, TERM, `killGraceMs` (default 100, maximum 10 000), затем KILL;
|
|
138
|
+
`cleanupTimeoutMs` (default 2000, maximum 30 000) ограничивает ожидание close после сигналов.
|
|
139
|
+
Сохраняется caller/sink причина; failure cleanup — `NativeCommandError` с `COMMAND_CLEANUP` и
|
|
140
|
+
AggregateError cause. Даже вышедший parent не исключает helper, удерживающий pipe. Windows
|
|
141
|
+
отказывает с `COMMAND_UNAVAILABLE`; потомок, самостоятельно покинувший группу через setsid,
|
|
142
|
+
лежит за границей этой capability. Это one-shot execution, не PTY, supervisor или daemon host.
|
|
143
|
+
|
|
144
|
+
## Механическая адаптация
|
|
145
|
+
|
|
146
|
+
Atomic file wrapper заменяется одним вызовом `writeFileAtomic` с выбранными options. Bounded
|
|
147
|
+
regular-file read заменяется `files.read`; JSON/schema, receipt conflicts, journal rotation и
|
|
148
|
+
application locks сохраняются. Spawn/drain/capture/cancellation wrapper заменяется `runNativeCommand`;
|
|
149
|
+
shell policy, command admission, логирование, redaction, bytes→text decoder и operator sink сохраняются.
|
|
150
|
+
Длительные signal-only команды остаются streaming; bounded tar/capture получают свои явные limits.
|
|
151
|
+
Импорт leaf не делает установку всего npm package маленькой; выбирайте dependency после измерения
|
|
152
|
+
packed размера и dependency closure.
|
|
153
|
+
|
|
154
|
+
==============================================================================
|
|
155
|
+
# API reference: `stitchkit/process` (docs/api/reference.md)
|
|
156
|
+
==============================================================================
|
|
157
|
+
|
|
158
|
+
## `stitchkit/process`
|
|
159
|
+
|
|
160
|
+
Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/native-io.md).
|
|
161
|
+
|
|
162
|
+
| Export | Kind | Purpose |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
|
|
165
|
+
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink and finite cleanup limits |
|
|
166
|
+
| `NativeCommandResult` | type | observed nullable exit code/signal and captured stdout/stderr bytes |
|
|
167
|
+
| `NativeCommandError` | class | COMMAND_LIMIT, COMMAND_UNAVAILABLE or COMMAND_CLEANUP with internal cause |
|
package/llms/stitchkit.txt
CHANGED
|
@@ -68,6 +68,7 @@ own, recorded as an ADR.
|
|
|
68
68
|
| `stitchkit/tools/connections` | server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
|
|
69
69
|
| `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
|
|
70
70
|
| `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
71
|
+
| `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
|
|
71
72
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
72
73
|
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
|
|
73
74
|
| `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
|
package/llms/tools.txt
CHANGED
|
@@ -409,6 +409,10 @@ where both exist the runtime's wins, because the run is recorded in its ledger.
|
|
|
409
409
|
|
|
410
410
|
#### An effect in another system — `effect`, at most once
|
|
411
411
|
|
|
412
|
+
Standalone callers must hold an application-owned exclusive execution lease for the conversation/run
|
|
413
|
+
through the whole effect call. Sharing a ledger or separate ledger adapters is not an atomic claim;
|
|
414
|
+
the runtime supplies its run lease. Process-local coalescing only covers the same ledger object.
|
|
415
|
+
|
|
412
416
|
`step` records its result **after** the body, so a body cut short by a crash
|
|
413
417
|
runs again. For sending a message or starting a turn at a provider, a repeat is
|
|
414
418
|
worse than a loss. The engine's `effect` records the intent **before** it runs:
|
|
@@ -431,6 +435,20 @@ throws, or a `reconcile` that throws or overruns `reconcileTimeoutMs` (default
|
|
|
431
435
|
call. The proof is JSON of at most 64 KiB — the identity the recipient assigned,
|
|
432
436
|
not a payload. → [ADR 0200](../decisions/0200-an-effect-is-recorded-before-it-runs.md)
|
|
433
437
|
|
|
438
|
+
Caller abort during reconcile releases the wait with `StepAbortedError`, leaves the intent and ignores
|
|
439
|
+
a late result. A coalesced caller can cancel its own await without cancelling the shared owner;
|
|
440
|
+
the first caller owns reconciliation handlers/deadline. `reconcileTimeoutMs` cannot exceed the native
|
|
441
|
+
timer range (2147483647 ms). It does not turn cancellation into proof of absence. Neither `null` nor an absence digest
|
|
442
|
+
proves that an earlier recipient request cannot complete later. Safe retries require recipient-side
|
|
443
|
+
idempotency or a verified fence. See [ADR 0224](../decisions/0224-native-primitives-have-one-owner.md).
|
|
444
|
+
|
|
445
|
+
Ledger reads are paged at 10 000 and incremental in one engine; a fresh engine replays the conversation
|
|
446
|
+
and retains its durable keys. This is not a bounded outstanding index/direct terminal receipt store.
|
|
447
|
+
For application delivery, `defineDomainEventDelivery` in `stitchkit/primitives` composes a caller-owned
|
|
448
|
+
atomic outbox claim with delivered/retryable/terminal/unknown states, bounded dispatch count (default
|
|
449
|
+
100). Storage retention, per-key lookup, recipient proof and transactions remain in that adapter;
|
|
450
|
+
it is not an automatic cutover for a different reconciliation protocol.
|
|
451
|
+
|
|
434
452
|
### Typed MCP call metadata
|
|
435
453
|
|
|
436
454
|
Contract handlers, runtime-tool handlers/factories, lifecycle and tool hooks all
|
package/llms-full.txt
CHANGED
|
@@ -59,6 +59,7 @@ own, recorded as an ADR.
|
|
|
59
59
|
| `stitchkit/tools/connections` | server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
|
|
60
60
|
| `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
|
|
61
61
|
| `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
62
|
+
| `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
|
|
62
63
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
63
64
|
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
|
|
64
65
|
| `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
|
|
@@ -3872,6 +3873,10 @@ where both exist the runtime's wins, because the run is recorded in its ledger.
|
|
|
3872
3873
|
|
|
3873
3874
|
#### An effect in another system — `effect`, at most once
|
|
3874
3875
|
|
|
3876
|
+
Standalone callers must hold an application-owned exclusive execution lease for the conversation/run
|
|
3877
|
+
through the whole effect call. Sharing a ledger or separate ledger adapters is not an atomic claim;
|
|
3878
|
+
the runtime supplies its run lease. Process-local coalescing only covers the same ledger object.
|
|
3879
|
+
|
|
3875
3880
|
`step` records its result **after** the body, so a body cut short by a crash
|
|
3876
3881
|
runs again. For sending a message or starting a turn at a provider, a repeat is
|
|
3877
3882
|
worse than a loss. The engine's `effect` records the intent **before** it runs:
|
|
@@ -3894,6 +3899,20 @@ throws, or a `reconcile` that throws or overruns `reconcileTimeoutMs` (default
|
|
|
3894
3899
|
call. The proof is JSON of at most 64 KiB — the identity the recipient assigned,
|
|
3895
3900
|
not a payload. → [ADR 0200](../decisions/0200-an-effect-is-recorded-before-it-runs.md)
|
|
3896
3901
|
|
|
3902
|
+
Caller abort during reconcile releases the wait with `StepAbortedError`, leaves the intent and ignores
|
|
3903
|
+
a late result. A coalesced caller can cancel its own await without cancelling the shared owner;
|
|
3904
|
+
the first caller owns reconciliation handlers/deadline. `reconcileTimeoutMs` cannot exceed the native
|
|
3905
|
+
timer range (2147483647 ms). It does not turn cancellation into proof of absence. Neither `null` nor an absence digest
|
|
3906
|
+
proves that an earlier recipient request cannot complete later. Safe retries require recipient-side
|
|
3907
|
+
idempotency or a verified fence. See [ADR 0224](../decisions/0224-native-primitives-have-one-owner.md).
|
|
3908
|
+
|
|
3909
|
+
Ledger reads are paged at 10 000 and incremental in one engine; a fresh engine replays the conversation
|
|
3910
|
+
and retains its durable keys. This is not a bounded outstanding index/direct terminal receipt store.
|
|
3911
|
+
For application delivery, `defineDomainEventDelivery` in `stitchkit/primitives` composes a caller-owned
|
|
3912
|
+
atomic outbox claim with delivered/retryable/terminal/unknown states, bounded dispatch count (default
|
|
3913
|
+
100). Storage retention, per-key lookup, recipient proof and transactions remain in that adapter;
|
|
3914
|
+
it is not an automatic cutover for a different reconciliation protocol.
|
|
3915
|
+
|
|
3897
3916
|
### Typed MCP call metadata
|
|
3898
3917
|
|
|
3899
3918
|
Contract handlers, runtime-tool handlers/factories, lifecycle and tool hooks all
|
|
@@ -8002,6 +8021,153 @@ workspace-relative or canonical `/workspace/...` paths. Unsupported policies mus
|
|
|
8002
8021
|
be refused explicitly by the backend.
|
|
8003
8022
|
|
|
8004
8023
|
|
|
8024
|
+
==============================================================================
|
|
8025
|
+
# Guide: Native IO (docs/guide/native-io.md)
|
|
8026
|
+
==============================================================================
|
|
8027
|
+
|
|
8028
|
+
---
|
|
8029
|
+
title: Конечные команды и файловые гарантии
|
|
8030
|
+
description: Native IO для Bun и Node, с явно выбранной durability, безопасными bounds и caller lifetime.
|
|
8031
|
+
type: guide
|
|
8032
|
+
status: active
|
|
8033
|
+
created: 2026-10-01 20:44 +07:00
|
|
8034
|
+
updated: 2026-10-01 21:31 +07:00
|
|
8035
|
+
participants:
|
|
8036
|
+
- role: authored
|
|
8037
|
+
harness: Codex
|
|
8038
|
+
model: GPT-6
|
|
8039
|
+
at: 2026-10-01 20:44 +07:00
|
|
8040
|
+
---
|
|
8041
|
+
|
|
8042
|
+
# Native IO
|
|
8043
|
+
|
|
8044
|
+
Примеры используют публичные imports и одинаково исполняются в Bun и Node ≥22.
|
|
8045
|
+
|
|
8046
|
+
## Запись файла
|
|
8047
|
+
|
|
8048
|
+
```ts
|
|
8049
|
+
import { writeFileAtomic, AtomicFilePublicationError } from 'stitchkit/files'
|
|
8050
|
+
|
|
8051
|
+
try {
|
|
8052
|
+
await writeFileAtomic('/srv/app/receipt.json', JSON.stringify({ id: 'receipt-1' }), {
|
|
8053
|
+
replace: false,
|
|
8054
|
+
mode: 0o600,
|
|
8055
|
+
durability: 'directory',
|
|
8056
|
+
})
|
|
8057
|
+
} catch (error) {
|
|
8058
|
+
if (error instanceof AtomicFilePublicationError) {
|
|
8059
|
+
// target уже виден. Проверьте receipt, прежде чем решать о повторе.
|
|
8060
|
+
console.error(error.phase, error.cause)
|
|
8061
|
+
} else throw error
|
|
8062
|
+
}
|
|
8063
|
+
await writeFileAtomic('/srv/app/state.json', '{"version":2}', {
|
|
8064
|
+
replace: true, durability: 'directory',
|
|
8065
|
+
})
|
|
8066
|
+
```
|
|
8067
|
+
|
|
8068
|
+
По умолчанию `replace: true`, `durability: 'file'`, `mode: 0o600`: existing вызовы сохраняют
|
|
8069
|
+
file-fsync и atomic replacement. `durability: 'none'` отключает sync; `'directory'` выполняет
|
|
8070
|
+
полную запись → exact descriptor chmod → file fsync → rename (replace) либо link (create) →
|
|
8071
|
+
удаление staging link → parent fsync. `writeFileAtomicSync` имеет те же options и порядок.
|
|
8072
|
+
Create отказывает с native `EEXIST` при существующем файле, symlink или concurrent winner.
|
|
8073
|
+
Никогда не переводите этот отказ в replace автоматически.
|
|
8074
|
+
|
|
8075
|
+
`AtomicFilePublicationError.published === true` отличает видимость от неподтверждённого
|
|
8076
|
+
acknowledgement. `phase` — `cleanup`, `directory-sync` или `directory-close`; первичная причина
|
|
8077
|
+
сохранена в `cause`. Ошибка до publish оставляет прежний target. Cleanup вторичен к первичной ошибке.
|
|
8078
|
+
При create кратко существует второй hardlink; strict single-link reader вправе отказать в этом окне.
|
|
8079
|
+
|
|
8080
|
+
Directory fsync — native POSIX/filesystem capability. Windows явно отказывает; иной filesystem
|
|
8081
|
+
может отказать при open/sync. Проверка на Linux не подтверждает поведение macOS, сетевой FS или
|
|
8082
|
+
сохранность при отключении питания. Состояние диска определяется гарантиями самой FS/ОС.
|
|
8083
|
+
`createManagedFileBoundary.write(..., { durable: true })` использует тот же publish owner и parent sync;
|
|
8084
|
+
его create по умолчанию остаётся create, а `replace: true` явно разрешает replacement.
|
|
8085
|
+
|
|
8086
|
+
## Строгое чтение
|
|
8087
|
+
|
|
8088
|
+
```ts
|
|
8089
|
+
import { createManagedFileBoundary } from 'stitchkit/files'
|
|
8090
|
+
|
|
8091
|
+
const files = await createManagedFileBoundary({ root: '/srv/app', maxReadBytes: 64 * 1024 })
|
|
8092
|
+
const source = await files.read('receipt.json', {
|
|
8093
|
+
rejectSymlinks: true, singleLink: true, stable: true, observe: true,
|
|
8094
|
+
})
|
|
8095
|
+
console.log(source.observation) // dev, ino, size, nlink, mtimeMs, ctimeMs
|
|
8096
|
+
const receipt = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(source.bytes))
|
|
8097
|
+
// schema.parse(receipt) и проверка digest принадлежат приложению.
|
|
8098
|
+
```
|
|
8099
|
+
|
|
8100
|
+
Без новых options ответ сохраняет `{ ref, bytes }`, допускает trusted in-root symlink и hardlink.
|
|
8101
|
+
`rejectSymlinks` запрещает requested leaf, `singleLink` проверяет `nlink` открытого descriptor,
|
|
8102
|
+
`stable` сравнивает dev/ino/size/mtime/ctime/nlink вокруг byte read; `observe` добавляет metadata.
|
|
8103
|
+
`FILE_NOT_FOUND`, `FILE_NOT_REGULAR`, `FILE_UNSAFE_LINK`, `FILE_TOO_LARGE`, `FILE_CHANGED`,
|
|
8104
|
+
`FILE_UNSUPPORTED` и `FILE_IO_ERROR` различаются. Abort сохраняет caller reason.
|
|
8105
|
+
Nonblocking open позволяет отказать FIFO без ожидания writer. Bytes ограничены во время чтения,
|
|
8106
|
+
включая рост файла, не только initial stat. Chunk allocation ≤ maxBytes+1+64 KiB, flatten ≤ maxBytes;
|
|
8107
|
+
FD закрывается при success, отказе, abort и IO error.
|
|
8108
|
+
Ошибка close сохраняет первичную ошибку чтения; после успешного чтения ошибка close возвращается caller.
|
|
8109
|
+
|
|
8110
|
+
Root и ancestors принадлежат trusted actor. Эти options не дают descriptor-relative containment
|
|
8111
|
+
при hostile concurrent ancestor replacement. Отдельного переключателя «считать ancestors безопасными»
|
|
8112
|
+
нет: portable owner не предоставляет такую capability. Metadata stability не доказывает immutable
|
|
8113
|
+
snapshot против произвольного hostile writer; используйте application lock/immutable publication.
|
|
8114
|
+
|
|
8115
|
+
## Команды
|
|
8116
|
+
|
|
8117
|
+
```ts
|
|
8118
|
+
import { runNativeCommand } from 'stitchkit/process'
|
|
8119
|
+
|
|
8120
|
+
const captured = await runNativeCommand({
|
|
8121
|
+
executable: '/usr/bin/git', args: ['rev-parse', 'HEAD'], cwd: '/srv/app',
|
|
8122
|
+
envPolicy: 'declared-only', env: { PATH: '/usr/bin:/bin' },
|
|
8123
|
+
capture: true, maxOutputBytes: 4096, timeoutMs: 5000,
|
|
8124
|
+
})
|
|
8125
|
+
console.log(captured.exitCode, new TextDecoder().decode(captured.stdout))
|
|
8126
|
+
|
|
8127
|
+
const lifetime = new AbortController()
|
|
8128
|
+
await runNativeCommand({
|
|
8129
|
+
executable: '/usr/bin/git', args: ['status'], signal: lifetime.signal,
|
|
8130
|
+
onOutput: async (bytes, channel, outputSignal) => {
|
|
8131
|
+
outputSignal.throwIfAborted()
|
|
8132
|
+
await output.write(bytes, channel, outputSignal) // application-owned bounded sink
|
|
8133
|
+
},
|
|
8134
|
+
})
|
|
8135
|
+
```
|
|
8136
|
+
|
|
8137
|
+
Команда требует caller signal или finite `timeoutMs` (1–2147483647 ms: native timer range; большее значение отказывается, не превращается в 1 ms). Streaming имеет caller lifetime, без скрытого
|
|
8138
|
+
60-second timeout или 1-MiB output limit. `capture` по умолчанию false; true требует
|
|
8139
|
+
`maxOutputBytes` для суммарных stdout+stderr. Лимит считает исходные bytes; API возвращает
|
|
8140
|
+
`Uint8Array`, не преобразует invalid UTF-8. Result содержит наблюдаемый `exitCode: number | null`
|
|
8141
|
+
и `signal: string | null`; signal termination не превращается в выдуманный exit code. `maxOutputBytes` может ограничивать и streaming.
|
|
8142
|
+
`stdin` — bytes с `maxStdinBytes` (default 1 MiB). `envPolicy` default `declared-only`: отсутствие env
|
|
8143
|
+
означает пустой environment; `ambient` явно разрешает наследование. Cwd/executable policy остаётся
|
|
8144
|
+
приложению; этот primitive запускает native executable и сам не создаёт shell.
|
|
8145
|
+
|
|
8146
|
+
Вывод читается с backpressure отдельно для stdout/stderr: один выполняющийся sink на канал плюс
|
|
8147
|
+
bounded native Readable buffer; накопления всей истории при streaming нет. Callback получает
|
|
8148
|
+
lifetime signal. Abort освобождает ожидание даже игнорирующего signal callback, но не способен
|
|
8149
|
+
отменить сторонние действия, которые сам callback продолжит после abort. Sink обязан отменять свои
|
|
8150
|
+
IO, не сохранять бесконечную историю и не изменять shared descriptor flags. После abort новых
|
|
8151
|
+
callbacks owner не запускает. Normal completion ждёт всех bytes и sinks.
|
|
8152
|
+
|
|
8153
|
+
Cleanup: POSIX process group, TERM, `killGraceMs` (default 100, maximum 10 000), затем KILL;
|
|
8154
|
+
`cleanupTimeoutMs` (default 2000, maximum 30 000) ограничивает ожидание close после сигналов.
|
|
8155
|
+
Сохраняется caller/sink причина; failure cleanup — `NativeCommandError` с `COMMAND_CLEANUP` и
|
|
8156
|
+
AggregateError cause. Даже вышедший parent не исключает helper, удерживающий pipe. Windows
|
|
8157
|
+
отказывает с `COMMAND_UNAVAILABLE`; потомок, самостоятельно покинувший группу через setsid,
|
|
8158
|
+
лежит за границей этой capability. Это one-shot execution, не PTY, supervisor или daemon host.
|
|
8159
|
+
|
|
8160
|
+
## Механическая адаптация
|
|
8161
|
+
|
|
8162
|
+
Atomic file wrapper заменяется одним вызовом `writeFileAtomic` с выбранными options. Bounded
|
|
8163
|
+
regular-file read заменяется `files.read`; JSON/schema, receipt conflicts, journal rotation и
|
|
8164
|
+
application locks сохраняются. Spawn/drain/capture/cancellation wrapper заменяется `runNativeCommand`;
|
|
8165
|
+
shell policy, command admission, логирование, redaction, bytes→text decoder и operator sink сохраняются.
|
|
8166
|
+
Длительные signal-only команды остаются streaming; bounded tar/capture получают свои явные limits.
|
|
8167
|
+
Импорт leaf не делает установку всего npm package маленькой; выбирайте dependency после измерения
|
|
8168
|
+
packed размера и dependency closure.
|
|
8169
|
+
|
|
8170
|
+
|
|
8005
8171
|
==============================================================================
|
|
8006
8172
|
# Guide: Generic application primitives (docs/guide/primitives.md)
|
|
8007
8173
|
==============================================================================
|
|
@@ -8012,7 +8178,7 @@ description: Declare lifecycle, access, exact values, audit, delivery and export
|
|
|
8012
8178
|
type: guide
|
|
8013
8179
|
status: active
|
|
8014
8180
|
created: 2026-09-01
|
|
8015
|
-
updated: 2026-
|
|
8181
|
+
updated: 2026-10-01 21:03 +07:00
|
|
8016
8182
|
---
|
|
8017
8183
|
|
|
8018
8184
|
# Generic application primitives
|
|
@@ -8105,6 +8271,31 @@ of the application's existing async operation. The input, scope, metadata and to
|
|
|
8105
8271
|
change. The application creates bytes and owns operation persistence; the managed-file boundary
|
|
8106
8272
|
streams them.
|
|
8107
8273
|
|
|
8274
|
+
## Canonical JSON
|
|
8275
|
+
|
|
8276
|
+
```ts
|
|
8277
|
+
import { canonicalJson } from 'stitchkit/primitives'
|
|
8278
|
+
|
|
8279
|
+
const bytes = canonicalJson({ '2': 'two', '10': 'ten', optional: undefined, nested: [1, null] }, {
|
|
8280
|
+
maxDepth: 20, maxNodes: 10_000, maxBytes: 64 * 1024,
|
|
8281
|
+
})
|
|
8282
|
+
// {"10":"ten","2":"two","nested":[1,null]}
|
|
8283
|
+
// Native crypto or browser WebCrypto hashes these same UTF-8 bytes.
|
|
8284
|
+
```
|
|
8285
|
+
|
|
8286
|
+
`canonicalJson` использует единый serializer исторических digest: object keys в UTF-16 code-unit
|
|
8287
|
+
order, включая integer-like keys; array order сохраняется. Optional object members с undefined
|
|
8288
|
+
опускаются. Top-level/array undefined, sparse arrays, functions/symbols/BigInt, non-finite numbers,
|
|
8289
|
+
negative zero, cycles, user prototypes, accessors, non-enumerable/symbol members отказаны до
|
|
8290
|
+
serialization. Приложение валидирует свою schema первым; canonical boundary не заменяет её.
|
|
8291
|
+
|
|
8292
|
+
Defaults: depth 100 (maximum 100), nodes 100 000, UTF-8 bytes 1 MiB. Превышение или не-JSON data —
|
|
8293
|
+
TypeError; неверные options — Zod validation error. Это browser-safe leaf без Node/Bun/crypto,
|
|
8294
|
+
agent-runtime/testing или optional peers. Native и browser используют один algorithm; hashing
|
|
8295
|
+
остаётся platform adapter. Переносите только serializer recursion, сохраняя proof schemas и
|
|
8296
|
+
verification boundary. Существующие внутренние historical normalization/digest fixtures не меняются;
|
|
8297
|
+
новый public boundary сознательно отказывает неоднозначным values вместо фиктивной совместимости.
|
|
8298
|
+
|
|
8108
8299
|
|
|
8109
8300
|
==============================================================================
|
|
8110
8301
|
# Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
@@ -18804,6 +18995,12 @@ schedules, transports and file generation remain application-owned.
|
|
|
18804
18995
|
|
|
18805
18996
|
---
|
|
18806
18997
|
|
|
18998
|
+
| Export | Kind | Purpose |
|
|
18999
|
+
|---|---|---|
|
|
19000
|
+
| `canonicalJson` | function | bounded plain JSON, UTF-16 key sort, optional object members omitted |
|
|
19001
|
+
| `CanonicalJsonOptions` | type | inferred depth/node/UTF-8 bytes limits |
|
|
19002
|
+
|
|
19003
|
+
|
|
18807
19004
|
## `stitchkit/server`
|
|
18808
19005
|
|
|
18809
19006
|
Server-only. Builds and runs the HTTP server, and carries the server primitives.
|
|
@@ -20380,6 +20577,12 @@ available from `stitchkit/contract`.
|
|
|
20380
20577
|
|
|
20381
20578
|
---
|
|
20382
20579
|
|
|
20580
|
+
| Export | Kind | Purpose |
|
|
20581
|
+
|---|---|---|
|
|
20582
|
+
| `AtomicFilePublicationError` | class | published=true with cleanup/directory-sync/directory-close phase |
|
|
20583
|
+
| `FileObservation` | type | observed descriptor dev/ino/size/nlink/mtimeMs/ctimeMs |
|
|
20584
|
+
|
|
20585
|
+
|
|
20383
20586
|
## `stitchkit/telegram`
|
|
20384
20587
|
|
|
20385
20588
|
Peer-free server-only Telegram platform primitives — the Bot API's own
|
|
@@ -20907,3 +21110,15 @@ AI-only peer boundary. Existing custom tool callbacks need no new import.
|
|
|
20907
21110
|
| Export | Kind | Summary |
|
|
20908
21111
|
|--------|------|---------|
|
|
20909
21112
|
| `createAgentHarnessTools` / `AgentHarnessToolsConfig` | function / _type_ | async per-run full mount configuration (`services` plus all `AgentMountConfig` options); composes application lifecycle then runtime fence exactly once |
|
|
21113
|
+
|
|
21114
|
+
|
|
21115
|
+
## `stitchkit/process`
|
|
21116
|
+
|
|
21117
|
+
Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/native-io.md).
|
|
21118
|
+
|
|
21119
|
+
| Export | Kind | Purpose |
|
|
21120
|
+
|---|---|---|
|
|
21121
|
+
| `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
|
|
21122
|
+
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink and finite cleanup limits |
|
|
21123
|
+
| `NativeCommandResult` | type | observed nullable exit code/signal and captured stdout/stderr bytes |
|
|
21124
|
+
| `NativeCommandError` | class | COMMAND_LIMIT, COMMAND_UNAVAILABLE or COMMAND_CLEANUP with internal cause |
|