@byok-sdk/core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attestation.d.ts +7 -0
- package/dist/blob.d.ts +42 -1
- package/dist/device-assertion.d.ts +224 -0
- package/dist/errors.d.ts +4 -0
- package/dist/in-memory/index.d.ts +1 -0
- package/dist/in-memory/skill-pack.d.ts +25 -0
- package/dist/index.d.ts +11 -6
- package/dist/index.js +525 -41
- package/dist/index.js.map +1 -1
- package/dist/mailbox.d.ts +14 -2
- package/dist/pairing.d.ts +38 -0
- package/dist/ports-contract.d.ts +18 -1
- package/dist/presence.d.ts +8 -0
- package/dist/skill-pack.d.ts +251 -0
- package/dist/stores.d.ts +10 -1
- package/package.json +1 -1
package/dist/mailbox.d.ts
CHANGED
|
@@ -35,16 +35,28 @@ export interface MailboxMessage {
|
|
|
35
35
|
readonly state: MailboxMessageState;
|
|
36
36
|
readonly appendedAt: string;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/** Opaque bytes produced after the mailbox has atomically reserved their delivery sequence. */
|
|
39
|
+
export interface MailboxBody {
|
|
40
40
|
readonly body: string;
|
|
41
41
|
readonly bodyHash: ContentHash;
|
|
42
42
|
readonly byteSize: bigint;
|
|
43
|
+
}
|
|
44
|
+
export interface MailboxAppendInput {
|
|
45
|
+
readonly deviceId: string;
|
|
43
46
|
/**
|
|
44
47
|
* Producer-supplied idempotency key. A second append with the same
|
|
45
48
|
* `messageId` returns the existing row instead of enqueuing a duplicate.
|
|
46
49
|
*/
|
|
47
50
|
readonly messageId: string;
|
|
51
|
+
/**
|
|
52
|
+
* Builds the opaque body around the sequence reserved by this append.
|
|
53
|
+
*
|
|
54
|
+
* The store invokes this only for a new row and commits the returned bytes
|
|
55
|
+
* at exactly that `seq`. Allocation, materialization, and insertion are one
|
|
56
|
+
* per-device serialized operation; otherwise concurrent offers could commit
|
|
57
|
+
* out of order and let an ack skip a late lower sequence.
|
|
58
|
+
*/
|
|
59
|
+
readonly materialize: (seq: number) => MailboxBody | Promise<MailboxBody>;
|
|
48
60
|
}
|
|
49
61
|
export interface MailboxReadQuery {
|
|
50
62
|
readonly deviceId: string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nonce-signing domain separation for device token renewal (docs/protocol.md §6.2).
|
|
3
|
+
*
|
|
4
|
+
* S1 (GAP-004) put a domain tag in front of every challenge nonce a device
|
|
5
|
+
* signs: the device key is a long-lived identity key that other planes (S6
|
|
6
|
+
* device proof) also sign structured messages with, so without a tag a
|
|
7
|
+
* signature produced for one purpose would be a valid signature for another.
|
|
8
|
+
*
|
|
9
|
+
* The tag started life as three separate literals — one in the daemon, one on
|
|
10
|
+
* the hosted surface, one on the reference server — each with a comment saying
|
|
11
|
+
* it was byte-identical to the others. That is a drift hazard written down, not
|
|
12
|
+
* a design: three copies agree only until someone edits one. This module is the
|
|
13
|
+
* single authority, and it lives in core for the same reason
|
|
14
|
+
* {@link DEVICE_PROOF_DOMAIN_PREFIX} does — core is the one package all three
|
|
15
|
+
* ends already depend on, it is Node-free and Workers-safe, and it holds no
|
|
16
|
+
* crypto of its own. What travels is *bytes to sign*; who signs them, and with
|
|
17
|
+
* which primitive, stays with each end.
|
|
18
|
+
*
|
|
19
|
+
* There is deliberately no dual mode and no grace window: a raw, unprefixed
|
|
20
|
+
* nonce signature is simply invalid, and a device on an older encoding re-pairs.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The domain-separation prefix a device signs along with a challenge nonce.
|
|
24
|
+
*
|
|
25
|
+
* Byte-frozen: `62 79 6f 6b 2d 6e 6f 6e 63 65 2d 76 31 0a` (14 bytes, UTF-8).
|
|
26
|
+
* Changing it invalidates every deployed device's token-renewal path, so it is
|
|
27
|
+
* a wire constant, not a tunable.
|
|
28
|
+
*/
|
|
29
|
+
export declare const NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
|
|
30
|
+
/**
|
|
31
|
+
* The exact bytes a device signs for a challenge nonce, and the exact bytes a
|
|
32
|
+
* verifier reconstructs: {@link NONCE_SIGNING_DOMAIN} followed by `nonce`,
|
|
33
|
+
* UTF-8 encoded.
|
|
34
|
+
*
|
|
35
|
+
* `TextEncoder` is a platform global on Node and Workers alike, which is what
|
|
36
|
+
* lets the one authority sit in a package that imports no `node:` builtin.
|
|
37
|
+
*/
|
|
38
|
+
export declare function nonceSigningBytes(nonce: string): Uint8Array;
|
package/dist/ports-contract.d.ts
CHANGED
|
@@ -20,7 +20,24 @@
|
|
|
20
20
|
* Adding a port method means editing this table, which is the point: a port
|
|
21
21
|
* grows by contract, not by whichever composition needed something.
|
|
22
22
|
*/
|
|
23
|
-
import type
|
|
23
|
+
import { type CoreStoreName } from './stores';
|
|
24
|
+
/**
|
|
25
|
+
* Ports that are core ports but NOT members of the composition contract.
|
|
26
|
+
*
|
|
27
|
+
* Now empty, and that is the whole point of Phase 2. Phase 1 of
|
|
28
|
+
* `skill-pack-delivery-channel` held `skillPacks` here as a self-declared
|
|
29
|
+
* temporary bridge: adding it to `CoreStores` before its Postgres
|
|
30
|
+
* implementation existed would have obliged every composition to implement it
|
|
31
|
+
* in the same slice, and the alternatives — an optional port member (a
|
|
32
|
+
* compatibility fallback, forbidden) or a port outside the contract table
|
|
33
|
+
* entirely (exempt from the tenant-first scan, the one rule most worth having)
|
|
34
|
+
* — were both worse. Phase 2 delivered that implementation and moved
|
|
35
|
+
* `skillPacks` into `CORE_STORE_NAMES`, so this list goes back to empty exactly
|
|
36
|
+
* as the Phase 1 note promised. The empty set stays named rather than deleted:
|
|
37
|
+
* it is the testable statement "every core port is a composition member", which
|
|
38
|
+
* `CORE_STORE_NAMES` alone cannot make.
|
|
39
|
+
*/
|
|
40
|
+
export declare const CORE_NON_COMPOSITION_PORT_NAMES: readonly [];
|
|
24
41
|
export declare const CORE_PORT_METHODS: Readonly<Record<CoreStoreName, readonly string[]>>;
|
|
25
42
|
/** The interface each port name is declared as, for the source-side scan. */
|
|
26
43
|
export declare const CORE_PORT_INTERFACES: Readonly<Record<CoreStoreName, string>>;
|
package/dist/presence.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface PresenceHint {
|
|
|
27
27
|
readonly level: PresenceLevel;
|
|
28
28
|
/** Free-form host label, bounded by the composition. Never parsed by core. */
|
|
29
29
|
readonly detail?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Logical MCP toolset IDs this daemon reported as configured. Discovery
|
|
32
|
+
* only: local task acceptance remains the fail-closed authority. Omission
|
|
33
|
+
* means legacy/unknown; an empty array means known-none.
|
|
34
|
+
*/
|
|
35
|
+
readonly configuredToolsets?: readonly string[];
|
|
30
36
|
readonly observedAt: string;
|
|
31
37
|
readonly expiresAt: string;
|
|
32
38
|
}
|
|
@@ -34,6 +40,8 @@ export interface PresenceHintInput {
|
|
|
34
40
|
readonly deviceId: string;
|
|
35
41
|
readonly level: PresenceLevel;
|
|
36
42
|
readonly detail?: string;
|
|
43
|
+
/** Validated logical IDs only; executable connector definitions never belong here. */
|
|
44
|
+
readonly configuredToolsets?: readonly string[];
|
|
37
45
|
/** Hint lifetime. §12.7.5 suggests 60-120s for presence. */
|
|
38
46
|
readonly ttlMs: number;
|
|
39
47
|
/** Minimum time between accepted publications for this device. `0` explicitly disables throttling. */
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill packs: declarative content a SaaS deployment distributes to a paired
|
|
3
|
+
* device (plan `skill-pack-delivery-channel`, Phase 1).
|
|
4
|
+
*
|
|
5
|
+
* A pack is an `agentskills.io`-shaped `SKILL.md` plus its static companion
|
|
6
|
+
* files. That is the WHOLE of it, and the narrowness is the design:
|
|
7
|
+
*
|
|
8
|
+
* 1. **No executable surface, by schema.** {@link SkillPackManifestSchema} is a
|
|
9
|
+
* `strictObject`, so an unknown key is a rejection rather than a field
|
|
10
|
+
* someone's tooling might later start honoring, and
|
|
11
|
+
* {@link SKILL_PACK_FORBIDDEN_FIELDS} names the specific ones — `exec`,
|
|
12
|
+
* `env`, `credential`, `hooks`, `allowed-tools` — that get their own
|
|
13
|
+
* rejection message. The credential-isolation rule this SDK is built around
|
|
14
|
+
* (§9.1) cannot be enforced downstream if the manifest can carry a command
|
|
15
|
+
* to run or an environment to run it in, so it is enforced here, once.
|
|
16
|
+
* 2. **Every declared limit has an evaluation point in this file.** A size cap
|
|
17
|
+
* that nothing measures, a path rule that nothing checks, and a content hash
|
|
18
|
+
* that nothing verifies are the same failure: a security claim with no
|
|
19
|
+
* enforcement behind it. {@link checkSkillPackManifest} evaluates the
|
|
20
|
+
* manifest-level limits and {@link checkSkillPackFileContent} evaluates the
|
|
21
|
+
* byte-level ones, both returning a named rejection instead of a boolean, so
|
|
22
|
+
* a caller can say which limit fired.
|
|
23
|
+
* 3. **Hashing lives outside core.** Core has no `node:` import and no crypto
|
|
24
|
+
* (the same rule `blob.ts` and `attestation.ts` follow), so this module owns
|
|
25
|
+
* the canonical bytes a hash is taken over ({@link skillPackContentHashInput})
|
|
26
|
+
* and the comparison, never the digest itself. The installer supplies what it
|
|
27
|
+
* observed; core decides whether that is acceptable.
|
|
28
|
+
*
|
|
29
|
+
* Distribution is hosted HTTP (`@byok-sdk/cloud`'s `GET /byok/skill-packs`),
|
|
30
|
+
* declared through ADR-010 as `skills.pack`. Nothing here touches the frozen v1
|
|
31
|
+
* wire envelope, and there is deliberately no new message type: a pack is
|
|
32
|
+
* content a device PULLS after reading a declaration, not a message a server
|
|
33
|
+
* pushes into a task stream.
|
|
34
|
+
*/
|
|
35
|
+
import { z } from 'zod';
|
|
36
|
+
import { type ContentHash } from './blob';
|
|
37
|
+
import type { TenantId } from './tenant';
|
|
38
|
+
export declare const SKILL_PACK_MANIFEST_SCHEMA_ID = "byok-skill-pack-v1";
|
|
39
|
+
/**
|
|
40
|
+
* Pack names: lowercase, starting alphanumeric — the same shape
|
|
41
|
+
* `OBJECT_KEY_PREFIX_PATTERN` accepts per segment, for the same reason. A pack
|
|
42
|
+
* name becomes a directory name on a real device filesystem, so the accepted
|
|
43
|
+
* set is the one that reaches disk spelled exactly as declared: no case folding
|
|
44
|
+
* to collide over, no separator, no leading dot, nothing needing escaping.
|
|
45
|
+
*/
|
|
46
|
+
export declare const SKILL_PACK_NAME_PATTERN: RegExp;
|
|
47
|
+
export declare const SKILL_PACK_NAME_MAX_LENGTH = 64;
|
|
48
|
+
export declare const SKILL_PACK_DESCRIPTION_MAX_LENGTH = 1024;
|
|
49
|
+
/** `MAJOR.MINOR.PATCH`. A pack version orders two publications of one name; it is not a range language. */
|
|
50
|
+
export declare const SKILL_PACK_VERSION_PATTERN: RegExp;
|
|
51
|
+
/**
|
|
52
|
+
* Relative POSIX paths only: slash-joined segments of alphanumerics, `.`, `_`
|
|
53
|
+
* and `-`, each STARTING with an alphanumeric.
|
|
54
|
+
*
|
|
55
|
+
* That single "starts with an alphanumeric" rule is what closes the traversal
|
|
56
|
+
* class rather than a blocklist of spellings: `..` cannot match (a segment may
|
|
57
|
+
* not begin with `.`), an absolute path cannot match (no leading slash), an
|
|
58
|
+
* empty segment cannot match (`a//b`), a Windows drive or UNC path cannot match
|
|
59
|
+
* (no `:`, no `\`), a home-relative path cannot match (no `~`), and a dotfile
|
|
60
|
+
* cannot match either. A blocklist would have to anticipate every encoding of
|
|
61
|
+
* the same idea; this allowlist has nothing to anticipate.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SKILL_PACK_FILE_PATH_PATTERN: RegExp;
|
|
64
|
+
export declare const SKILL_PACK_FILE_PATH_MAX_LENGTH = 200;
|
|
65
|
+
/** The entry file every pack must carry, spelled as `agentskills.io` spells it. */
|
|
66
|
+
export declare const SKILL_PACK_ENTRY_PATH = "SKILL.md";
|
|
67
|
+
/** Per-file ceiling, measured in BYTES over the observed content — never in characters. */
|
|
68
|
+
export declare const SKILL_PACK_FILE_MAX_BYTES = 262144;
|
|
69
|
+
/** Whole-pack ceiling, over the sum of the observed file sizes. */
|
|
70
|
+
export declare const SKILL_PACK_MAX_BYTES = 1048576;
|
|
71
|
+
export declare const SKILL_PACK_MAX_FILES = 64;
|
|
72
|
+
/**
|
|
73
|
+
* Manifest keys that must never exist, listed so the prohibition is testable
|
|
74
|
+
* rather than implied.
|
|
75
|
+
*
|
|
76
|
+
* `strictObject` already rejects every unknown key, so this list adds no new
|
|
77
|
+
* authority — it adds a NAMED rejection for the subset that matters: an
|
|
78
|
+
* operator who sees `unrecognized key: "env"` learns nothing about why, and a
|
|
79
|
+
* reviewer reading the schema cannot tell whether the omission was a decision
|
|
80
|
+
* or an oversight. Pinned from both directions by
|
|
81
|
+
* `__tests__/skill-pack.test.ts`.
|
|
82
|
+
*/
|
|
83
|
+
export declare const SKILL_PACK_FORBIDDEN_FIELDS: readonly ['exec', 'command', 'entrypoint', 'run', 'script', 'shell', 'env', 'environment', 'credential', 'credentials', 'secret', 'secrets', 'token', 'apiKey', 'api_key', 'allowedTools', 'allowed-tools', 'hooks', 'preinstall', 'postinstall'];
|
|
84
|
+
/** One file in a pack: where it goes, what it must hash to, and how big it is. */
|
|
85
|
+
export declare const SkillPackFileSchema: z.ZodObject<{
|
|
86
|
+
path: z.ZodString;
|
|
87
|
+
contentHash: z.ZodPipe<z.ZodString, z.ZodTransform<ContentHash, string>>;
|
|
88
|
+
byteSize: z.ZodNumber;
|
|
89
|
+
}, z.core.$strict>;
|
|
90
|
+
export type SkillPackFile = z.infer<typeof SkillPackFileSchema>;
|
|
91
|
+
/**
|
|
92
|
+
* The published description of one pack.
|
|
93
|
+
*
|
|
94
|
+
* `contentHash` addresses the manifest AS A WHOLE — see
|
|
95
|
+
* {@link skillPackContentHashInput} — which is what makes an install
|
|
96
|
+
* content-addressed: two publications that differ in any field a device can
|
|
97
|
+
* observe land in two directories, and re-installing an unchanged pack is a
|
|
98
|
+
* no-op rather than a partial overwrite.
|
|
99
|
+
*/
|
|
100
|
+
export declare const SkillPackManifestSchema: z.ZodObject<{
|
|
101
|
+
schema: z.ZodLiteral<"byok-skill-pack-v1">;
|
|
102
|
+
name: z.ZodString;
|
|
103
|
+
version: z.ZodString;
|
|
104
|
+
description: z.ZodString;
|
|
105
|
+
files: z.ZodArray<z.ZodObject<{
|
|
106
|
+
path: z.ZodString;
|
|
107
|
+
contentHash: z.ZodPipe<z.ZodString, z.ZodTransform<ContentHash, string>>;
|
|
108
|
+
byteSize: z.ZodNumber;
|
|
109
|
+
}, z.core.$strict>>;
|
|
110
|
+
contentHash: z.ZodPipe<z.ZodString, z.ZodTransform<ContentHash, string>>;
|
|
111
|
+
}, z.core.$strict>;
|
|
112
|
+
export type SkillPackManifest = z.infer<typeof SkillPackManifestSchema>;
|
|
113
|
+
/**
|
|
114
|
+
* Parses a manifest fail-closed.
|
|
115
|
+
*
|
|
116
|
+
* @throws {ByokCoreError} code `skill_pack_manifest_invalid`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function parseSkillPackManifest(input: unknown): SkillPackManifest;
|
|
119
|
+
/**
|
|
120
|
+
* The exact bytes `manifest.contentHash` is the sha256 of.
|
|
121
|
+
*
|
|
122
|
+
* Written out here, in core, because both ends have to agree on it and neither
|
|
123
|
+
* end may derive its own version: the publisher hashes this string to mint the
|
|
124
|
+
* address, the installer hashes it again to verify what it fetched, and a
|
|
125
|
+
* disagreement between the two would make every install either falsely
|
|
126
|
+
* accepted or unconditionally rejected. Newline-delimited and terminated so no
|
|
127
|
+
* field's content can be shifted into the next field's position, and the file
|
|
128
|
+
* rows are sorted by path so two publishers that enumerate a directory in
|
|
129
|
+
* different orders still mint the same address.
|
|
130
|
+
*/
|
|
131
|
+
export declare function skillPackContentHashInput(manifest: {
|
|
132
|
+
readonly name: string;
|
|
133
|
+
readonly version: string;
|
|
134
|
+
readonly description: string;
|
|
135
|
+
readonly files: readonly SkillPackFile[];
|
|
136
|
+
}): string;
|
|
137
|
+
/** Every way a pack can be refused. One name per declared limit — see {@link SkillPackCheck}. */
|
|
138
|
+
export declare const SKILL_PACK_REJECTIONS: readonly ['path-unsafe', 'duplicate-path', 'entry-missing', 'file-count-over-cap', 'file-over-cap', 'pack-over-cap', 'size-mismatch', 'hash-mismatch', 'name-mismatch'];
|
|
139
|
+
export type SkillPackRejection = (typeof SKILL_PACK_REJECTIONS)[number];
|
|
140
|
+
/**
|
|
141
|
+
* Outcome of a check. `bytes` is the measured total on the accept path so a
|
|
142
|
+
* caller can record what it admitted; a rejection always names WHICH limit
|
|
143
|
+
* fired, because "the pack was rejected" is not an operable message.
|
|
144
|
+
*/
|
|
145
|
+
export type SkillPackCheck = {
|
|
146
|
+
readonly ok: true;
|
|
147
|
+
readonly bytes: number;
|
|
148
|
+
} | {
|
|
149
|
+
readonly ok: false;
|
|
150
|
+
readonly reason: SkillPackRejection;
|
|
151
|
+
readonly detail: string;
|
|
152
|
+
};
|
|
153
|
+
/** Non-throwing path-safety predicate — the one authority on what a pack may name. */
|
|
154
|
+
export declare function isSkillPackPathSafe(path: string): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* The manifest-level limits, evaluated.
|
|
157
|
+
*
|
|
158
|
+
* Runs over a manifest that has already parsed, so the per-field caps zod owns
|
|
159
|
+
* are not re-litigated here. What is left is everything zod cannot see: the
|
|
160
|
+
* relationships BETWEEN files (a duplicate path, a missing entry file, a total
|
|
161
|
+
* that clears every per-file cap and still blows the pack cap).
|
|
162
|
+
*/
|
|
163
|
+
export declare function checkSkillPackManifest(manifest: SkillPackManifest): SkillPackCheck;
|
|
164
|
+
/** What an installer actually measured for one file, as opposed to what the manifest declared. */
|
|
165
|
+
export interface ObservedSkillPackFile {
|
|
166
|
+
readonly byteSize: number;
|
|
167
|
+
readonly contentHash: ContentHash;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The byte-level limits, evaluated against what was actually fetched.
|
|
171
|
+
*
|
|
172
|
+
* The asymmetry with {@link checkSkillPackManifest} is the point: the declared
|
|
173
|
+
* size is a claim by the publisher and the observed size is a fact about the
|
|
174
|
+
* bytes on the device, so the cap is enforced on the OBSERVED value. A check
|
|
175
|
+
* that only ever measured the declaration would pass a 4 KiB manifest row
|
|
176
|
+
* attached to a 40 MiB response.
|
|
177
|
+
*/
|
|
178
|
+
export declare function checkSkillPackFileContent(declared: SkillPackFile, observed: ObservedSkillPackFile): SkillPackCheck;
|
|
179
|
+
/**
|
|
180
|
+
* `SKILL.md` frontmatter, in the `agentskills.io` shape: exactly `name` and
|
|
181
|
+
* `description`, both required.
|
|
182
|
+
*
|
|
183
|
+
* The allowlist is the whole contract. `allowed-tools`, `hooks` and friends are
|
|
184
|
+
* real fields in other skill dialects and every one of them widens what a pack
|
|
185
|
+
* can ask a runtime to do; a parser that merely IGNORED them would still let a
|
|
186
|
+
* pack ship them to a host that does not.
|
|
187
|
+
*/
|
|
188
|
+
export interface SkillFrontmatter {
|
|
189
|
+
readonly name: string;
|
|
190
|
+
readonly description: string;
|
|
191
|
+
}
|
|
192
|
+
/** The only frontmatter keys a pack may declare. */
|
|
193
|
+
export declare const SKILL_FRONTMATTER_FIELDS: readonly ['name', 'description'];
|
|
194
|
+
/**
|
|
195
|
+
* Parses and validates a `SKILL.md`'s frontmatter block.
|
|
196
|
+
*
|
|
197
|
+
* Deliberately not a YAML engine: this is a closed two-key grammar, and the
|
|
198
|
+
* whole reason to hand-write it is that a general parser would happily accept
|
|
199
|
+
* anchors, aliases, nested maps, multi-document streams and block scalars —
|
|
200
|
+
* every one of which is a way to express something this format has decided not
|
|
201
|
+
* to have. Anything outside the grammar is a rejection, never a skipped line.
|
|
202
|
+
*
|
|
203
|
+
* @throws {ByokCoreError} code `skill_pack_frontmatter_invalid`.
|
|
204
|
+
*/
|
|
205
|
+
export declare function parseSkillFrontmatter(text: string): SkillFrontmatter;
|
|
206
|
+
/**
|
|
207
|
+
* The entry file's own declaration must agree with the manifest's.
|
|
208
|
+
*
|
|
209
|
+
* Two authorities naming the same pack is not a redundancy to tolerate — a
|
|
210
|
+
* device that installed under one name and handed the runtime a skill calling
|
|
211
|
+
* itself another would be projecting content nobody asked for.
|
|
212
|
+
*/
|
|
213
|
+
export declare function checkSkillPackEntry(manifest: SkillPackManifest, entryText: string): SkillPackCheck;
|
|
214
|
+
/** One file's bytes, as the distribution surface hands them over. */
|
|
215
|
+
export interface SkillPackFileContent {
|
|
216
|
+
readonly path: string;
|
|
217
|
+
readonly contentHash: ContentHash;
|
|
218
|
+
readonly byteSize: number;
|
|
219
|
+
/** UTF-8 text. A pack carries Markdown, YAML and static text assets — never binaries, never archives. */
|
|
220
|
+
readonly content: string;
|
|
221
|
+
}
|
|
222
|
+
/** What a publisher hands the store: the validated manifest plus the bytes it addresses. */
|
|
223
|
+
export interface SkillPackPublishInput {
|
|
224
|
+
readonly manifest: SkillPackManifest;
|
|
225
|
+
readonly files: readonly {
|
|
226
|
+
readonly path: string;
|
|
227
|
+
readonly content: string;
|
|
228
|
+
}[];
|
|
229
|
+
}
|
|
230
|
+
export interface SkillPackListQuery {
|
|
231
|
+
readonly limit?: number;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Skill pack port. Tenant-first, async, content plus manifest.
|
|
235
|
+
*
|
|
236
|
+
* Registered in `ports-contract.ts` as a core PORT, and deliberately not (yet)
|
|
237
|
+
* a member of `CoreStores`: a composition contract that named it would oblige
|
|
238
|
+
* every existing composition to implement it in the same slice, and the
|
|
239
|
+
* Postgres implementation is Phase 2 of this plan. See `ports-contract.ts` for
|
|
240
|
+
* the full note on that split.
|
|
241
|
+
*
|
|
242
|
+
* Raises: `skill_pack_manifest_invalid` (a publish whose bytes disagree with
|
|
243
|
+
* the manifest it was handed).
|
|
244
|
+
*/
|
|
245
|
+
export interface SkillPackStore {
|
|
246
|
+
/** Idempotent per (tenant, name, contentHash). Publishing a changed pack under the same name replaces it. */
|
|
247
|
+
publish(tenant: TenantId, input: SkillPackPublishInput): Promise<SkillPackManifest>;
|
|
248
|
+
get(tenant: TenantId, name: string): Promise<SkillPackManifest | undefined>;
|
|
249
|
+
list(tenant: TenantId, query: SkillPackListQuery): Promise<readonly SkillPackManifest[]>;
|
|
250
|
+
readFile(tenant: TenantId, name: string, path: string): Promise<SkillPackFileContent | undefined>;
|
|
251
|
+
}
|
package/dist/stores.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type { BoardStore } from './board';
|
|
|
22
22
|
import type { MailboxStore } from './mailbox';
|
|
23
23
|
import type { ObjectStore } from './blob';
|
|
24
24
|
import type { QuotaStore } from './quota';
|
|
25
|
+
import type { SkillPackStore } from './skill-pack';
|
|
25
26
|
import type { TruthStore } from './truth';
|
|
26
27
|
/**
|
|
27
28
|
* Injected time.
|
|
@@ -48,7 +49,15 @@ export interface CoreStores {
|
|
|
48
49
|
readonly activity: ActivityStore;
|
|
49
50
|
readonly objects: ObjectStore;
|
|
50
51
|
readonly quota: QuotaStore;
|
|
52
|
+
/**
|
|
53
|
+
* Skill-pack storage is a MANDATORY member: every composition creates the
|
|
54
|
+
* tables and supplies the port (Phase 2 of `skill-pack-delivery-channel`).
|
|
55
|
+
* Storage presence is not capability advertisement — the wire route/capability
|
|
56
|
+
* stays optional in `@byok-sdk/cloud` (`includeSkillPacks`), and a deployment
|
|
57
|
+
* that never declares `skills.pack` simply keeps the tables empty.
|
|
58
|
+
*/
|
|
59
|
+
readonly skillPacks: SkillPackStore;
|
|
51
60
|
}
|
|
52
61
|
/** Names of the ports in {@link CoreStores}, in contract order. */
|
|
53
|
-
export declare const CORE_STORE_NAMES: readonly ['mailbox', 'board', 'truth', 'presence', 'activity', 'objects', 'quota'];
|
|
62
|
+
export declare const CORE_STORE_NAMES: readonly ['mailbox', 'board', 'truth', 'presence', 'activity', 'objects', 'quota', 'skillPacks'];
|
|
54
63
|
export type CoreStoreName = (typeof CORE_STORE_NAMES)[number];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byok-sdk/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "BYOK SDK platform contracts: branded tenant identity, tenant-first store ports, device proof canonicalization, and the composition conformance reference",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|