@peers-app/peers-sdk 0.28.6 → 0.28.8
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/README.md +2 -2
- package/dist/contracts/builder.d.ts +0 -6
- package/dist/contracts/builder.js +0 -11
- package/dist/contracts/contract-proxy.d.ts +7 -6
- package/dist/contracts/extract.d.ts +1 -1
- package/dist/contracts/system/contract-catalog-contract.d.ts +6 -0
- package/dist/contracts/system/contract-catalog-contract.js +1 -0
- package/dist/contracts/system/packages-contract.d.ts +19 -6
- package/dist/contracts/system/packages-contract.js +15 -2
- package/dist/data/package-version-resolver.js +1 -19
- package/dist/data/package-versions.d.ts +4 -0
- package/dist/data/package-versions.js +8 -0
- package/dist/data/packages.utils.d.ts +4 -0
- package/dist/data/packages.utils.js +5 -1
- package/dist/data/tools.d.ts +0 -10
- package/dist/define/context.d.ts +5 -7
- package/dist/define/observables.js +1 -5
- package/dist/package-build/cli.js +4 -3
- package/dist/package-build/contract-add.d.ts +4 -2
- package/dist/package-build/contract-add.js +3 -11
- package/dist/package-build/contract-source.d.ts +7 -4
- package/dist/package-build/contract-source.js +26 -13
- package/dist/package-build/index.d.ts +3 -0
- package/dist/package-build/index.js +11 -0
- package/dist/package-installer/package-cloner.js +1 -0
- package/dist/package-installer/package-creator.js +2 -0
- package/dist/package-installer/package-installer.d.ts +3 -1
- package/dist/package-installer/package-installer.js +37 -8
- package/dist/package-installer/package-sdk-version.d.ts +5 -0
- package/dist/package-installer/package-sdk-version.js +50 -0
- package/dist/package-installer/types.d.ts +11 -4
- package/dist/package-loader/isolated-artifact.d.ts +16 -29
- package/dist/package-loader/isolated-artifact.js +20 -33
- package/dist/package-loader/package-loader.d.ts +5 -5
- package/dist/package-loader/package-loader.js +14 -43
- package/dist/package-loader/package-runtime-state.d.ts +1 -2
- package/dist/sdk-version.d.ts +2 -0
- package/dist/sdk-version.js +5 -0
- package/dist/types/peers-package.d.ts +1 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ NaCl-based key management, message signing and verification, box encryption, and
|
|
|
32
32
|
|
|
33
33
|
### Tools Framework
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Declare AI-callable tools with `definePackage()` and provide their implementations through contract resolvers. Use `IContractToolImplementation` for provider implementations.
|
|
36
36
|
|
|
37
37
|
### Identity and Groups
|
|
38
38
|
|
|
@@ -58,7 +58,7 @@ The fastest way to build a Peers package is to start from the template:
|
|
|
58
58
|
| `DataQuery` / `DataFilter` | Mongo-style query filters translated to SQL |
|
|
59
59
|
| `observable()` / `computed()` | Reactive state primitives |
|
|
60
60
|
| `deviceVar` / `userVar` / `groupVar` | Persistent variables — observables backed by the database |
|
|
61
|
-
| `ITool` / `
|
|
61
|
+
| `ITool` / `IContractToolImplementation` | AI-callable tool definitions |
|
|
62
62
|
| `IPeersUI` / `IPeersUIRoute` | Screen and route declarations for package UIs |
|
|
63
63
|
| `newid()` | Generate 25-character time-sortable peer IDs |
|
|
64
64
|
| `newKeys()` / `hydrateKeys()` | Cryptographic key pair generation and hydration |
|
|
@@ -18,7 +18,6 @@ export declare class ContractBuilder {
|
|
|
18
18
|
private _events;
|
|
19
19
|
private _alsoImplements;
|
|
20
20
|
private _exposes;
|
|
21
|
-
private warnedAboutToolInstances;
|
|
22
21
|
/** Full table definitions for this contract (used by the install flow). */
|
|
23
22
|
private _tableDefinitions;
|
|
24
23
|
constructor(contractId: string, version: number, name: string, description?: string | undefined);
|
|
@@ -35,11 +34,6 @@ export declare class ContractBuilder {
|
|
|
35
34
|
*/
|
|
36
35
|
set events(events: IContractEvent[]);
|
|
37
36
|
get events(): IContractEvent[];
|
|
38
|
-
/**
|
|
39
|
-
* Discard executable assignments from bundles compiled against an older SDK.
|
|
40
|
-
* @deprecated Implement tools with registerContractResolver instead.
|
|
41
|
-
*/
|
|
42
|
-
set toolInstances(_instances: never);
|
|
43
37
|
/** Full table definitions for runtime table registration. */
|
|
44
38
|
set tableDefinitions(defs: ITableDefinition[]);
|
|
45
39
|
get tableDefinitions(): ITableDefinition[];
|
|
@@ -26,7 +26,6 @@ class ContractBuilder {
|
|
|
26
26
|
_events = [];
|
|
27
27
|
_alsoImplements = [];
|
|
28
28
|
_exposes = [];
|
|
29
|
-
warnedAboutToolInstances = false;
|
|
30
29
|
/** Full table definitions for this contract (used by the install flow). */
|
|
31
30
|
_tableDefinitions = [];
|
|
32
31
|
constructor(contractId, version, name, description) {
|
|
@@ -64,16 +63,6 @@ class ContractBuilder {
|
|
|
64
63
|
get events() {
|
|
65
64
|
return this._events;
|
|
66
65
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Discard executable assignments from bundles compiled against an older SDK.
|
|
69
|
-
* @deprecated Implement tools with registerContractResolver instead.
|
|
70
|
-
*/
|
|
71
|
-
set toolInstances(_instances) {
|
|
72
|
-
if (this.warnedAboutToolInstances)
|
|
73
|
-
return;
|
|
74
|
-
this.warnedAboutToolInstances = true;
|
|
75
|
-
console.warn(`Contract ${this.name} (${this.contractId}@${this.version}) assigned toolInstances, which no longer run; implement these tools with registerContractResolver (docs/Packages/contracts.md#migrating-from-toolinstances)`);
|
|
76
|
-
}
|
|
77
66
|
/** Full table definitions for runtime table registration. */
|
|
78
67
|
set tableDefinitions(defs) {
|
|
79
68
|
this._tableDefinitions = defs;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
1
2
|
import type { DataContext } from "../context/data-context";
|
|
2
3
|
import type { Table } from "../data/orm/table";
|
|
3
|
-
import type { ITool
|
|
4
|
+
import type { ITool } from "../data/tools";
|
|
4
5
|
import type { Event, ISubscriptionResult } from "../events";
|
|
5
6
|
import { type Observable } from "../observable";
|
|
6
7
|
import { type IIsolatedCallPrincipal } from "../package-loader/isolated-runtime";
|
|
@@ -359,15 +360,15 @@ export declare function createRemoteContractConsumer(contract: IContractDefiniti
|
|
|
359
360
|
dataContextId?: string;
|
|
360
361
|
}): IRemoteContractConsumer<IContractConsumer>;
|
|
361
362
|
/**
|
|
362
|
-
* Provider-local executable metadata
|
|
363
|
-
*
|
|
363
|
+
* Provider-local executable metadata for contract tools.
|
|
364
|
+
* Implementations need no catalog ID, persisted row, or executable registration.
|
|
364
365
|
*/
|
|
365
366
|
export interface IContractToolImplementation {
|
|
366
367
|
/** Provider metadata; the contract member name supplies identity, without a Tools row. */
|
|
367
368
|
tool: Pick<ITool, "name" | "usageDescription"> & Partial<Pick<ITool, "toolId" | "detailedDescription" | "inputSchema" | "outputSchema" | "accessLevel" | "authorityPropagation" | "remoteAccessLevel">>;
|
|
368
|
-
toolFn:
|
|
369
|
-
inputSchema?:
|
|
370
|
-
outputSchema?:
|
|
369
|
+
toolFn: (...args: any[]) => any;
|
|
370
|
+
inputSchema?: z.ZodObject<any>;
|
|
371
|
+
outputSchema?: z.ZodObject<any>;
|
|
371
372
|
}
|
|
372
373
|
/** Context-bound live members of a provider; executable ownership stays with the contract. */
|
|
373
374
|
export interface IContractResolution {
|
|
@@ -18,7 +18,7 @@ export interface IExtractableTableDef {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Minimal tool-instance shape accepted by extractToolShape.
|
|
21
|
-
* Mirrors the subset of
|
|
21
|
+
* Mirrors the subset of IContractToolImplementation we need.
|
|
22
22
|
*/
|
|
23
23
|
export interface IExtractableToolInstance {
|
|
24
24
|
/** Host-only workflow policy carried into the contract definition. */
|
|
@@ -71,6 +71,12 @@ export interface IContractDefinitionWithPolicy {
|
|
|
71
71
|
definition: IContractDefinition;
|
|
72
72
|
/** Each tool's policy, where the Runtime knows it. */
|
|
73
73
|
tools?: IContractToolPolicy[];
|
|
74
|
+
/** Active provider's table settings, when known; absent on older hosts. */
|
|
75
|
+
tables?: {
|
|
76
|
+
name: string;
|
|
77
|
+
versionNumber?: number;
|
|
78
|
+
localOnly?: boolean;
|
|
79
|
+
}[];
|
|
74
80
|
}
|
|
75
81
|
/**
|
|
76
82
|
* Consumer shape for the System Contract Catalog.
|
|
@@ -47,6 +47,7 @@ function getContractCatalogDefinition() {
|
|
|
47
47
|
outputFields: [
|
|
48
48
|
field("definition", field_type_1.FieldType.object, "The registered definition, in Peers IR."),
|
|
49
49
|
field("tools", field_type_1.FieldType.object, "Each tool's name, accessLevel, and authorityPropagation on the active provider.", { isArray: true, optional: true }),
|
|
50
|
+
field("tables", field_type_1.FieldType.object, "Each table's name, versionNumber, and localOnly setting on the active provider.", { isArray: true, optional: true }),
|
|
50
51
|
],
|
|
51
52
|
},
|
|
52
53
|
],
|
|
@@ -42,6 +42,8 @@ export interface IPackageObservedRuntime {
|
|
|
42
42
|
export interface IPackageRequestedVersion {
|
|
43
43
|
/** Group-wide active version (`Packages.activePackageVersionId`). */
|
|
44
44
|
groupActivePackageVersionId?: string;
|
|
45
|
+
/** The group selected a version whose row is not on this device yet. */
|
|
46
|
+
groupVersionMissing?: boolean;
|
|
45
47
|
/** This device's override, when set. */
|
|
46
48
|
deviceActivePackageVersionId?: string;
|
|
47
49
|
/** The version this device should run: the device override, else the group version. */
|
|
@@ -101,8 +103,8 @@ export interface IPackageOperationResult {
|
|
|
101
103
|
/** True when `createPackage` scaffolded a new package instead of importing an existing folder. */
|
|
102
104
|
created?: boolean;
|
|
103
105
|
/**
|
|
104
|
-
* Non-fatal problem the caller should show
|
|
105
|
-
*
|
|
106
|
+
* Non-fatal problem the caller should show, for example a missing package template.
|
|
107
|
+
* Older hosts also report released-number conflicts here instead of refusing reload.
|
|
106
108
|
*/
|
|
107
109
|
warning?: string;
|
|
108
110
|
/** The package after the operation. */
|
|
@@ -120,7 +122,7 @@ export interface IPackageReloadOutcome {
|
|
|
120
122
|
changed?: boolean;
|
|
121
123
|
/** Failure message when `ok` is false. */
|
|
122
124
|
error?: string;
|
|
123
|
-
/** Non-fatal problem
|
|
125
|
+
/** Non-fatal problem; older hosts may warn here about a reused released number. */
|
|
124
126
|
warning?: string;
|
|
125
127
|
}
|
|
126
128
|
/** Result of `reloadAllPackages`. */
|
|
@@ -141,11 +143,19 @@ export interface IPromotePackageVersionResult extends IPackageOperationResult {
|
|
|
141
143
|
/** True when this device holds the package signing key and signed the promoted version. */
|
|
142
144
|
signed: boolean;
|
|
143
145
|
/**
|
|
144
|
-
* True when
|
|
145
|
-
*
|
|
146
|
+
* True when activation ran, either explicitly with activateForGroup or automatically
|
|
147
|
+
* under the group follow policy. See groupRunsPromotedVersion for the group outcome.
|
|
146
148
|
*/
|
|
147
149
|
activated: boolean;
|
|
150
|
+
/** Group selection after promotion, when one is set. */
|
|
151
|
+
groupActivePackageVersionId?: string;
|
|
152
|
+
/** True even when the group already selected this id before it was re-tagged. */
|
|
153
|
+
groupRunsPromotedVersion?: boolean;
|
|
154
|
+
/** What the group now runs, why selection stayed unchanged, and any next command. */
|
|
155
|
+
activationNote?: string;
|
|
148
156
|
}
|
|
157
|
+
/** Explain a group version missing locally and how to select an available release. */
|
|
158
|
+
export declare function packageGroupVersionWarning(packageId: string, requested: Pick<IPackageRequestedVersion, "groupActivePackageVersionId" | "groupVersionMissing">): string | undefined;
|
|
149
159
|
/**
|
|
150
160
|
* Release channels a version may be promoted to from its current tag: `dev` to `beta` or
|
|
151
161
|
* `stable`, and `beta` to `stable`. Missing or empty tags on legacy versions mean `dev`,
|
|
@@ -207,7 +217,7 @@ export interface IPackagesConsumer extends IContractConsumerBase {
|
|
|
207
217
|
packageVersionId?: string;
|
|
208
218
|
scope?: "device" | "group";
|
|
209
219
|
}): Promise<IPackageOperationResult>;
|
|
210
|
-
/**
|
|
220
|
+
/** Read local bundles, then force-reload the selected version even when unchanged. */
|
|
211
221
|
reloadPackage(input: {
|
|
212
222
|
packageId: string;
|
|
213
223
|
}): Promise<IPackageOperationResult>;
|
|
@@ -228,10 +238,13 @@ export interface IPackagesConsumer extends IContractConsumerBase {
|
|
|
228
238
|
* Promote a stored version to `beta` or `stable`: re-tag, re-sign with the package author
|
|
229
239
|
* key when this device has it, record the promotion in the version's history, and
|
|
230
240
|
* activate it when this device was running it and the group follows the new tag.
|
|
241
|
+
* activateForGroup explicitly selects it for the group and follows that selection here.
|
|
231
242
|
*/
|
|
232
243
|
promotePackageVersion(input: {
|
|
233
244
|
packageVersionId: string;
|
|
234
245
|
versionTag: string;
|
|
246
|
+
/** Also select the promoted version for the group and follow it here (Admin). */
|
|
247
|
+
activateForGroup?: boolean;
|
|
235
248
|
}): Promise<IPromotePackageVersionResult>;
|
|
236
249
|
/** Install the default packages into the bound data context. */
|
|
237
250
|
installDefaultPackages(): Promise<{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PACKAGES_CONTRACT_VERSION = void 0;
|
|
4
|
+
exports.packageGroupVersionWarning = packageGroupVersionWarning;
|
|
4
5
|
exports.packageVersionPromotionTargets = packageVersionPromotionTargets;
|
|
5
6
|
exports.isPackageRepositoryUrl = isPackageRepositoryUrl;
|
|
6
7
|
exports.getPackagesContractDefinition = getPackagesContractDefinition;
|
|
@@ -9,6 +10,12 @@ const field_type_1 = require("../../types/field-type");
|
|
|
9
10
|
const system_contracts_1 = require("../system-contracts");
|
|
10
11
|
/** Version of the built-in System Packages contract. */
|
|
11
12
|
exports.PACKAGES_CONTRACT_VERSION = 1;
|
|
13
|
+
/** Explain a group version missing locally and how to select an available release. */
|
|
14
|
+
function packageGroupVersionWarning(packageId, requested) {
|
|
15
|
+
if (!requested.groupVersionMissing || !requested.groupActivePackageVersionId)
|
|
16
|
+
return undefined;
|
|
17
|
+
return `The group's selected version ${requested.groupActivePackageVersionId} isn't on this device; it may not have synced yet. To select an available beta or stable version for the group, run: peers packages activate ${packageId} <packageVersionId> --group`;
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
20
|
* Release channels a version may be promoted to from its current tag: `dev` to `beta` or
|
|
14
21
|
* `stable`, and `beta` to `stable`. Missing or empty tags on legacy versions mean `dev`,
|
|
@@ -138,7 +145,7 @@ function getPackagesContractDefinition() {
|
|
|
138
145
|
},
|
|
139
146
|
{
|
|
140
147
|
name: "reloadPackage",
|
|
141
|
-
usageDescription: "
|
|
148
|
+
usageDescription: "Read local bundles, then force-reload the selected version even when unchanged. Changed builds at a released number are refused.",
|
|
142
149
|
inputFields: [packageIdField],
|
|
143
150
|
outputFields: operationResultFields,
|
|
144
151
|
},
|
|
@@ -170,10 +177,11 @@ function getPackagesContractDefinition() {
|
|
|
170
177
|
},
|
|
171
178
|
{
|
|
172
179
|
name: "promotePackageVersion",
|
|
173
|
-
usageDescription: "Promote a stored package version to beta or stable (dev to beta or stable, beta to stable). Re-signs it with the package author key when this device has it, records the promotion in its history, and activates it when this device was running it and the group follows the new tag.",
|
|
180
|
+
usageDescription: "Promote a stored package version to beta or stable (dev to beta or stable, beta to stable). Re-signs it with the package author key when this device has it, records the promotion in its history, and activates it when this device was running it and the group follows the new tag. Optionally selects it for the group with activateForGroup. Reports the group selection and any next command.",
|
|
174
181
|
inputFields: [
|
|
175
182
|
field("packageVersionId", field_type_1.FieldType.id, "Version to promote."),
|
|
176
183
|
field("versionTag", field_type_1.FieldType.string, "New release channel: beta or stable."),
|
|
184
|
+
field("activateForGroup", field_type_1.FieldType.boolean, "Also select this version for the group and follow it here (Admin).", { optional: true }),
|
|
177
185
|
],
|
|
178
186
|
outputFields: [
|
|
179
187
|
...operationResultFields,
|
|
@@ -181,6 +189,11 @@ function getPackagesContractDefinition() {
|
|
|
181
189
|
field("version", field_type_1.FieldType.string, "Its version number."),
|
|
182
190
|
field("versionTag", field_type_1.FieldType.string, "Its new release channel."),
|
|
183
191
|
field("signed", field_type_1.FieldType.boolean, "True when this device signed the promoted version."),
|
|
192
|
+
field("groupActivePackageVersionId", field_type_1.FieldType.id, "Group selection after promotion.", {
|
|
193
|
+
optional: true,
|
|
194
|
+
}),
|
|
195
|
+
field("groupRunsPromotedVersion", field_type_1.FieldType.boolean, "Whether the group now selects the promoted version, including an already-selected id.", { optional: true }),
|
|
196
|
+
field("activationNote", field_type_1.FieldType.string, "What the group now runs and any next command.", { optional: true }),
|
|
184
197
|
field("activated", field_type_1.FieldType.boolean, "True when the promotion also activated the version for the group or this device."),
|
|
185
198
|
],
|
|
186
199
|
},
|
|
@@ -18,14 +18,12 @@ exports.hasDeviceFollowOverride = hasDeviceFollowOverride;
|
|
|
18
18
|
exports.readStoredPackageLocalPath = readStoredPackageLocalPath;
|
|
19
19
|
const user_context_singleton_1 = require("../context/user-context-singleton");
|
|
20
20
|
const keys_1 = require("../keys");
|
|
21
|
-
const assistants_1 = require("./assistants");
|
|
22
21
|
const file_types_1 = require("./files/file.types");
|
|
23
22
|
const group_member_roles_1 = require("./group-member-roles");
|
|
24
23
|
const group_permissions_1 = require("./group-permissions");
|
|
25
24
|
const package_versions_1 = require("./package-versions");
|
|
26
25
|
const packages_1 = require("./packages");
|
|
27
26
|
const persistent_vars_1 = require("./persistent-vars");
|
|
28
|
-
const workflows_1 = require("./workflows");
|
|
29
27
|
/**
|
|
30
28
|
* Reactive, cached observable holding device-local preferences for a single
|
|
31
29
|
* package. Backed by a `groupDeviceVar` so the UI can subscribe via
|
|
@@ -344,9 +342,7 @@ async function resolveDevicePackageVersion(pkg, dataContext, opts) {
|
|
|
344
342
|
}
|
|
345
343
|
return { loaded: false, upgraded: false };
|
|
346
344
|
}
|
|
347
|
-
// Load succeeded
|
|
348
|
-
// either as a group-level active version or as device-local state.
|
|
349
|
-
await installPackageContents(dataContext, pkg, instance);
|
|
345
|
+
// Load succeeded; persist activation as a group default or device-local state.
|
|
350
346
|
// When the device is following group settings (no device-level follow
|
|
351
347
|
// override) and the upgraded version is non-dev, also advance the group's
|
|
352
348
|
// activePackageVersionId so other devices stay in sync. Only admin+ users
|
|
@@ -436,20 +432,6 @@ async function loadOlderCompatiblePackageVersion(pkg, dataContext, unavailableVe
|
|
|
436
432
|
}
|
|
437
433
|
return undefined;
|
|
438
434
|
}
|
|
439
|
-
/**
|
|
440
|
-
* Save a loaded package's assistants and workflows into the data context.
|
|
441
|
-
* Contract-installed packages are skipped because their content is already
|
|
442
|
-
* managed by the contract loader.
|
|
443
|
-
*/
|
|
444
|
-
async function installPackageContents(dataContext, pkg, instance) {
|
|
445
|
-
if (dataContext.packageLoader.isContractInstalled(pkg.packageId))
|
|
446
|
-
return;
|
|
447
|
-
const saves = [
|
|
448
|
-
...(instance.assistants?.map((a) => (0, assistants_1.Assistants)(dataContext).save(a)) ?? []),
|
|
449
|
-
...(instance.workflows?.map((w) => (0, workflows_1.Workflows)(dataContext).save(w)) ?? []),
|
|
450
|
-
];
|
|
451
|
-
await Promise.all(saves);
|
|
452
|
-
}
|
|
453
435
|
/**
|
|
454
436
|
* Returns true when the device has an explicit follow-policy override
|
|
455
437
|
* (`followRange`, `followTags`, or `pinned`). Used by the UI to drive the
|
|
@@ -115,6 +115,10 @@ declare const schema: z.ZodObject<{
|
|
|
115
115
|
}>;
|
|
116
116
|
/** A signed package build and its bundle hashes, tag, and lifecycle history. */
|
|
117
117
|
export type IPackageVersion = z.infer<typeof schema>;
|
|
118
|
+
/** Find a beta or stable build with the same package and version number, excluding a supplied id. */
|
|
119
|
+
export declare function releasedVersionWithNumber(versions: readonly IPackageVersion[], candidate: Pick<IPackageVersion, "packageId" | "version"> & {
|
|
120
|
+
packageVersionId?: string;
|
|
121
|
+
}): IPackageVersion | undefined;
|
|
118
122
|
/** Package version table with signature and group promotion permission checks. */
|
|
119
123
|
export declare class PackageVersionsTable extends Table<IPackageVersion> {
|
|
120
124
|
static isPassthrough: boolean;
|
|
@@ -35,6 +35,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.PackageVersionsTable = void 0;
|
|
38
|
+
exports.releasedVersionWithNumber = releasedVersionWithNumber;
|
|
38
39
|
exports.PackageVersions = PackageVersions;
|
|
39
40
|
exports.computePackageVersionHash = computePackageVersionHash;
|
|
40
41
|
exports.isVersionInRange = isVersionInRange;
|
|
@@ -100,6 +101,13 @@ const metaData = {
|
|
|
100
101
|
fields: (0, types_1.schemaToFields)(schema),
|
|
101
102
|
indexes: [{ fields: ["packageId"] }],
|
|
102
103
|
};
|
|
104
|
+
/** Find a beta or stable build with the same package and version number, excluding a supplied id. */
|
|
105
|
+
function releasedVersionWithNumber(versions, candidate) {
|
|
106
|
+
return versions.find((other) => other.packageId === candidate.packageId &&
|
|
107
|
+
other.packageVersionId !== candidate.packageVersionId &&
|
|
108
|
+
other.version === candidate.version &&
|
|
109
|
+
(other.versionTag === "beta" || other.versionTag === "stable"));
|
|
110
|
+
}
|
|
103
111
|
// Symbol.for keeps signer registration shared by separately installed SDK copies.
|
|
104
112
|
const signingKey = Symbol.for("@peers-app/peers-sdk/package-version-signing");
|
|
105
113
|
const signingGlobals = globalThis;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { DataContext } from "../context/data-context";
|
|
2
2
|
import { type IPackage } from "./packages";
|
|
3
|
+
/**
|
|
4
|
+
* Copy a package and its active version's bundles into another context.
|
|
5
|
+
* Restores an uninstalled package; uninstall keeps version and file rows.
|
|
6
|
+
*/
|
|
3
7
|
export declare function copyPackageToAnotherDataContext(packageId: string, fromDataContext: DataContext, toDataContext: DataContext): Promise<IPackage>;
|
|
@@ -15,6 +15,10 @@ async function copyBundleFile(fileId, fromFiles, toFiles, label) {
|
|
|
15
15
|
console.warn(`${label} file ${fileId} not found in source context`);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Copy a package and its active version's bundles into another context.
|
|
20
|
+
* Restores an uninstalled package; uninstall keeps version and file rows.
|
|
21
|
+
*/
|
|
18
22
|
async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDataContext) {
|
|
19
23
|
const fromPackages = (0, packages_1.Packages)(fromDataContext);
|
|
20
24
|
const toPackages = (0, packages_1.Packages)(toDataContext);
|
|
@@ -35,5 +39,5 @@ async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDat
|
|
|
35
39
|
await toPvTable.signAndSave(pv, { saveAsSnapshot: true });
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
return toPackages.signAndSave(pkg);
|
|
42
|
+
return toPackages.signAndSave(pkg, { restoreIfDeleted: true, saveAsSnapshot: true });
|
|
39
43
|
}
|
package/dist/data/tools.d.ts
CHANGED
|
@@ -172,16 +172,6 @@ export declare const toolSchema: z.ZodObject<{
|
|
|
172
172
|
}>;
|
|
173
173
|
/** A tool definition stored in the Tools table. */
|
|
174
174
|
export type ITool = z.infer<typeof toolSchema>;
|
|
175
|
-
/**
|
|
176
|
-
* Historical tool instance shape used by older packages.
|
|
177
|
-
* @deprecated Use IContractToolImplementation with a contract resolver.
|
|
178
|
-
*/
|
|
179
|
-
export interface IToolInstance {
|
|
180
|
-
tool: ITool;
|
|
181
|
-
toolFn: (...args: any[]) => any;
|
|
182
|
-
inputSchema?: z.ZodObject<any>;
|
|
183
|
-
outputSchema?: z.ZodObject<any>;
|
|
184
|
-
}
|
|
185
175
|
/**
|
|
186
176
|
* Accessor for the Tools table.
|
|
187
177
|
*
|
package/dist/define/context.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface IPackageProviderOptions {
|
|
|
14
14
|
providerPackageId?: string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* The host functions the Runtime gives one
|
|
17
|
+
* The host functions the Runtime gives one guest API 3 handler call.
|
|
18
18
|
*
|
|
19
19
|
* Every tool handler gets its own `ctx`, and host calls made through it act for that call's
|
|
20
20
|
* caller, even while other calls run. The table and contract handles in this module take a
|
|
@@ -75,15 +75,13 @@ export interface IPackageContext {
|
|
|
75
75
|
watchOwnedTable(tableName: string, handler: (event: IDataChangedEvent<unknown>) => unknown): Promise<PackageUnsubscribe>;
|
|
76
76
|
/**
|
|
77
77
|
* Set the value of an in-memory observable this package provides (one declared without a
|
|
78
|
-
* `persistentVar`). The host checks it against the observable's `valueType
|
|
79
|
-
*
|
|
78
|
+
* `persistentVar`). The host checks it against the observable's `valueType` and pushes it
|
|
79
|
+
* to subscribers. Allowed at any time while the worker runs.
|
|
80
80
|
*/
|
|
81
81
|
publishOwnedObservable(contractId: string, version: number, observableName: string, value: unknown): Promise<unknown>;
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* returns what it stored. A later call replaces the handler. Guest API 3 instead routes
|
|
86
|
-
* writes through the contract member handler under the caller's authority.
|
|
83
|
+
* Compatibility no-op retained for built guest API 3 bundles. Consumer writes are
|
|
84
|
+
* served through the contract member handler under the caller's authority.
|
|
87
85
|
*/
|
|
88
86
|
onOwnedObservableSet(contractId: string, version: number, observableName: string, handler: (value: unknown) => unknown): void;
|
|
89
87
|
}
|
|
@@ -162,8 +162,7 @@ async function serveObservable(observable, targets, ctx) {
|
|
|
162
162
|
});
|
|
163
163
|
serving.set(observable, () => subscription.dispose());
|
|
164
164
|
if (definition.writable) {
|
|
165
|
-
//
|
|
166
|
-
// API 2 uses onOwnedObservableSet without a caller frame. Both parse before writing.
|
|
165
|
+
// The member handler runs this under the caller's authority and parses before writing.
|
|
167
166
|
const write = async (value) => {
|
|
168
167
|
const next = schema ? schema.parse((0, empty_fields_1.withoutEmptySchemaFields)(schema, value)) : value;
|
|
169
168
|
applying = true;
|
|
@@ -177,9 +176,6 @@ async function serveObservable(observable, targets, ctx) {
|
|
|
177
176
|
return publish(source());
|
|
178
177
|
};
|
|
179
178
|
writers.set(observable, write);
|
|
180
|
-
for (const target of targets) {
|
|
181
|
-
ctx.onOwnedObservableSet(target.contractId, target.version, name, write);
|
|
182
|
-
}
|
|
183
179
|
}
|
|
184
180
|
// A successful start guarantees readers have a published value. Later background failures
|
|
185
181
|
// are logged, but a failed initial publish must fail startup rather than silently serve null.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.main = main;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const path = require("node:path");
|
|
6
|
+
const sdk_version_1 = require("../sdk-version");
|
|
6
7
|
const build_package_1 = require("./build-package");
|
|
7
8
|
const contract_add_1 = require("./contract-add");
|
|
8
9
|
const freeze_contracts_1 = require("./freeze-contracts");
|
|
@@ -11,7 +12,7 @@ const USAGE = `Usage: peers-package <command> [options]
|
|
|
11
12
|
Commands:
|
|
12
13
|
manifest Write dist/manifest.json from the package's declarations (src/package.ts).
|
|
13
14
|
build Write the manifest and dist/package.bundle.js, the artifact the Runtime
|
|
14
|
-
installs.
|
|
15
|
+
installs. Prints the SDK version used to build it.
|
|
15
16
|
contract add <source> Write src/contracts/<name>.v<version>.ts, a declaration of a contract this
|
|
16
17
|
package implements or calls, generated from <source>: a declaration module
|
|
17
18
|
and its export (path/to/file.ts#Name), a provider's manifest.json or
|
|
@@ -96,13 +97,13 @@ async function main(argv) {
|
|
|
96
97
|
console.error(`Build failed: ${result.message}`);
|
|
97
98
|
return;
|
|
98
99
|
}
|
|
99
|
-
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
100
|
+
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; SDK ${sdk_version_1.SDK_VERSION}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
100
101
|
});
|
|
101
102
|
console.log("Watching for changes");
|
|
102
103
|
return new Promise(() => undefined);
|
|
103
104
|
}
|
|
104
105
|
const result = await (0, build_package_1.buildPackage)(options);
|
|
105
|
-
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
106
|
+
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; SDK ${sdk_version_1.SDK_VERSION}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
106
107
|
return 0;
|
|
107
108
|
}
|
|
108
109
|
/** `peers-package contract add <source> [options]` or `peers-package contract freeze [options]`. */
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { type IContractSource } from "./contract-source";
|
|
1
2
|
/** What {@link addContract} takes. */
|
|
2
3
|
export interface IContractAddOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Where the contract comes from: a declaration module and its export
|
|
5
6
|
* (`../voice-hub/src/contracts/voice-skill.ts#VoiceSkill`), a provider's `manifest.json` or
|
|
6
|
-
* `package.bundle.js`,
|
|
7
|
+
* `package.bundle.js`, the JSON `getContractDefinition` returned, or an in-memory
|
|
8
|
+
* contract source (for example from `contractSourceFromDefinition`).
|
|
7
9
|
*/
|
|
8
|
-
source: string;
|
|
10
|
+
source: string | IContractSource;
|
|
9
11
|
/** `contractId@version`, for a manifest that provides more than one contract. */
|
|
10
12
|
contract?: string;
|
|
11
13
|
/** Package root. Defaults to the current directory. */
|
|
@@ -19,9 +19,10 @@ const contract_source_1 = require("./contract-source");
|
|
|
19
19
|
*/
|
|
20
20
|
async function addContract(options) {
|
|
21
21
|
const root = path.resolve(options.root ?? process.cwd());
|
|
22
|
-
const contract =
|
|
22
|
+
const contract = typeof options.source === "string"
|
|
23
|
+
? await readContractSource(root, { ...options, source: options.source })
|
|
24
|
+
: options.source;
|
|
23
25
|
const generated = (0, contract_source_1.generateContractDeclaration)(contract, {
|
|
24
|
-
from: sourceLabel(root, options.source),
|
|
25
26
|
...(options.name ? { exportName: options.name } : {}),
|
|
26
27
|
});
|
|
27
28
|
const target = path.join(path.resolve(root, options.outDir ?? "src/contracts"), generated.fileName);
|
|
@@ -41,15 +42,6 @@ function splitSource(source) {
|
|
|
41
42
|
? { file: source.slice(0, hash), exportName: source.slice(hash + 1) }
|
|
42
43
|
: { file: source };
|
|
43
44
|
}
|
|
44
|
-
/**
|
|
45
|
-
* The source as the file's header names it: relative to the package root, with `/`, so the
|
|
46
|
-
* generated file (and `--check`) doesn't depend on where the command ran or on local paths.
|
|
47
|
-
*/
|
|
48
|
-
function sourceLabel(root, source) {
|
|
49
|
-
const { file, exportName } = splitSource(source);
|
|
50
|
-
const relative = path.relative(root, path.resolve(file)).split(path.sep).join("/");
|
|
51
|
-
return exportName === undefined ? relative : `${relative}#${exportName}`;
|
|
52
|
-
}
|
|
53
45
|
async function readContractSource(root, options) {
|
|
54
46
|
const { file, exportName } = splitSource(options.source);
|
|
55
47
|
if (exportName !== undefined) {
|
|
@@ -49,11 +49,14 @@ export declare function parseContractSelector(selector: string): {
|
|
|
49
49
|
* propagation. Refinements (a regex, a minimum) and defaults made by code aren't in the IR, so
|
|
50
50
|
* generated schemas check only what the IR describes. Objects keep keys their fields don't list.
|
|
51
51
|
*
|
|
52
|
+
* Table storage settings are preserved: implementing the declaration uses its tables.
|
|
53
|
+
* Default settings (schema version 1 and synced storage) are omitted.
|
|
54
|
+
*
|
|
52
55
|
* @param input - The contract and its tables.
|
|
53
|
-
* @param options -
|
|
54
|
-
* instead of the one made from the contract's name.
|
|
56
|
+
* @param options - An export name to use instead of the one made from the contract's name.
|
|
55
57
|
*/
|
|
56
|
-
export declare function generateContractDeclaration(input: IContractSource, options
|
|
57
|
-
|
|
58
|
+
export declare function generateContractDeclaration(input: IContractSource, options?: {
|
|
59
|
+
/** Ignored for compatibility with older callers; generated headers never name a source. */
|
|
60
|
+
from?: string;
|
|
58
61
|
exportName?: string;
|
|
59
62
|
}): IGeneratedContractDeclaration;
|
|
@@ -67,6 +67,7 @@ function contractSourceFromDefinition(input) {
|
|
|
67
67
|
throw new Error("Expected getContractDefinition's result: { definition, tools }");
|
|
68
68
|
}
|
|
69
69
|
const policy = new Map((input.tools ?? []).map((tool) => [tool.name, tool]));
|
|
70
|
+
const tablePolicy = new Map((input.tables ?? []).map((table) => [table.name, table]));
|
|
70
71
|
const tools = definition.tools.map((tool) => {
|
|
71
72
|
const toolPolicy = policy.get(tool.name);
|
|
72
73
|
if (toolPolicy?.accessLevel === undefined) {
|
|
@@ -101,12 +102,17 @@ function contractSourceFromDefinition(input) {
|
|
|
101
102
|
...(definition.observables?.length ? { observables: definition.observables } : {}),
|
|
102
103
|
...(definition.events?.length ? { events: definition.events } : {}),
|
|
103
104
|
},
|
|
104
|
-
tables: (definition.tables ?? []).map((table) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
tables: (definition.tables ?? []).map((table) => {
|
|
106
|
+
const settings = tablePolicy.get(table.name);
|
|
107
|
+
return {
|
|
108
|
+
name: table.name,
|
|
109
|
+
description: table.description,
|
|
110
|
+
primaryKeyName: table.primaryKeyName,
|
|
111
|
+
fields: table.fields,
|
|
112
|
+
...(settings?.versionNumber === undefined ? {} : { versionNumber: settings.versionNumber }),
|
|
113
|
+
...(settings?.localOnly ? { localOnly: true } : {}),
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
110
116
|
};
|
|
111
117
|
}
|
|
112
118
|
/**
|
|
@@ -131,11 +137,13 @@ function parseContractSelector(selector) {
|
|
|
131
137
|
* propagation. Refinements (a regex, a minimum) and defaults made by code aren't in the IR, so
|
|
132
138
|
* generated schemas check only what the IR describes. Objects keep keys their fields don't list.
|
|
133
139
|
*
|
|
140
|
+
* Table storage settings are preserved: implementing the declaration uses its tables.
|
|
141
|
+
* Default settings (schema version 1 and synced storage) are omitted.
|
|
142
|
+
*
|
|
134
143
|
* @param input - The contract and its tables.
|
|
135
|
-
* @param options -
|
|
136
|
-
* instead of the one made from the contract's name.
|
|
144
|
+
* @param options - An export name to use instead of the one made from the contract's name.
|
|
137
145
|
*/
|
|
138
|
-
function generateContractDeclaration(input, options) {
|
|
146
|
+
function generateContractDeclaration(input, options = {}) {
|
|
139
147
|
const { contract, tables } = input;
|
|
140
148
|
assertVersion(contract.version, "Contract version");
|
|
141
149
|
const exportName = options.exportName ?? pascalCase(contract.name);
|
|
@@ -145,7 +153,7 @@ function generateContractDeclaration(input, options) {
|
|
|
145
153
|
const imports = new Set(["declareContract"]);
|
|
146
154
|
const code = new Code(imports);
|
|
147
155
|
const lines = [];
|
|
148
|
-
lines.push(`// Generated
|
|
156
|
+
lines.push(`// Generated contract declaration. Regenerate with \`peers contracts add ${docText(contract.contractId)}@${contract.version}\``, "// or `peers-package contract add <source>`; do not edit by hand.", "");
|
|
149
157
|
const eventNames = new Map();
|
|
150
158
|
for (const event of contract.events ?? []) {
|
|
151
159
|
const name = uniqueName(`${camelCase(exportName)}${pascalCase(event.name)}`, new Set([...RESERVED_NAMES, exportName, ...eventNames.values()]));
|
|
@@ -223,7 +231,7 @@ class Code {
|
|
|
223
231
|
["description", JSON.stringify(table.description ?? "")],
|
|
224
232
|
["primaryKeyName", JSON.stringify(table.primaryKeyName)],
|
|
225
233
|
["schema", this.objectSchema(table.fields, depth + 1)],
|
|
226
|
-
...(table.versionNumber === undefined
|
|
234
|
+
...(table.versionNumber === undefined || table.versionNumber === 1
|
|
227
235
|
? []
|
|
228
236
|
: [["versionNumber", String(table.versionNumber)]]),
|
|
229
237
|
...(table.localOnly ? [["localOnly", "true"]] : []),
|
|
@@ -237,7 +245,7 @@ class Code {
|
|
|
237
245
|
["name", JSON.stringify(tool.name)],
|
|
238
246
|
["usageDescription", JSON.stringify(tool.usageDescription)],
|
|
239
247
|
["accessLevel", this.accessLevel(level)],
|
|
240
|
-
...(tool.authorityPropagation
|
|
248
|
+
...(tool.authorityPropagation && tool.authorityPropagation !== "cap"
|
|
241
249
|
? [["authorityPropagation", JSON.stringify(tool.authorityPropagation)]]
|
|
242
250
|
: []),
|
|
243
251
|
...(tool.remoteAccessLevel === undefined
|
|
@@ -323,7 +331,10 @@ class Code {
|
|
|
323
331
|
return `generatedField(${schema}, ${this.shapeSource(metadata)}${valueType === undefined ? "" : `, ${this.fieldType(valueType)}`})`;
|
|
324
332
|
}
|
|
325
333
|
shapeSource(shape) {
|
|
326
|
-
const entries = Object.entries(shape)
|
|
334
|
+
const entries = Object.entries(shape)
|
|
335
|
+
.filter(([, value]) => value !== undefined)
|
|
336
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
337
|
+
.map(([key, value]) => {
|
|
327
338
|
const source = key === "type"
|
|
328
339
|
? this.fieldType(value)
|
|
329
340
|
: key === "fields"
|
|
@@ -422,6 +433,8 @@ function dataSource(value) {
|
|
|
422
433
|
return `[${value.map(dataSource).join(", ")}]`;
|
|
423
434
|
if (value && typeof value === "object") {
|
|
424
435
|
return `{ ${Object.entries(value)
|
|
436
|
+
.filter(([, entry]) => entry !== undefined)
|
|
437
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
425
438
|
.map(([key, entry]) => `${propertyKey(key)}: ${dataSource(entry)}`)
|
|
426
439
|
.join(", ")} }`;
|
|
427
440
|
}
|