@peers-app/peers-sdk 0.26.1 → 0.26.2
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/contracts/__tests__/remote-contract-transport.test.js +39 -0
- package/dist/contracts/__tests__/system-contracts.test.js +1 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/remote-contract-transport.d.ts +5 -2
- package/dist/contracts/remote-contract-transport.js +22 -2
- package/dist/contracts/system/app-updates-contract.d.ts +109 -0
- package/dist/contracts/system/app-updates-contract.js +110 -0
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +2 -0
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/update-settings.pvar.d.ts +26 -0
- package/dist/data/update-settings.pvar.js +34 -0
- package/dist/system-ids.d.ts +6 -0
- package/dist/system-ids.js +7 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const system_ids_1 = require("../../system-ids");
|
|
3
4
|
const index_1 = require("../index");
|
|
4
5
|
const makeRequest = (overrides = {}) => ({
|
|
5
6
|
contractId: "contract-a",
|
|
@@ -49,11 +50,49 @@ describe("remote contract endpoint", () => {
|
|
|
49
50
|
await expect(endpoint.handleCall(toolCall, {})).rejects.toThrow("disposed");
|
|
50
51
|
expect(emit).not.toHaveBeenCalled();
|
|
51
52
|
});
|
|
53
|
+
it("refuses package callers for contracts whose providers refuse packages", async () => {
|
|
54
|
+
const { transport, emit } = makeTransport();
|
|
55
|
+
const packageContext = { callerPackageId: "package-a" };
|
|
56
|
+
expect(() => (0, index_1.createRemoteContractEndpoint)(makeRequest({ contractId: system_ids_1.appUpdatesContractId, context: packageContext }), () => transport)).toThrow("packages cannot call contract");
|
|
57
|
+
const operator = (0, index_1.createRemoteContractEndpoint)(makeRequest({ contractId: system_ids_1.appUpdatesContractId }), () => transport);
|
|
58
|
+
await expect(operator.handleCall({ ...toolCall, contractId: system_ids_1.appUpdatesContractId }, {})).resolves.toBe("pong");
|
|
59
|
+
const packageTool = (0, index_1.createRemoteContractEndpoint)(makeRequest({ context: packageContext }), () => transport);
|
|
60
|
+
await expect(packageTool.handleCall(toolCall, packageContext)).resolves.toBe("pong");
|
|
61
|
+
expect(emit).toHaveBeenCalledTimes(2);
|
|
62
|
+
});
|
|
52
63
|
it("fails clearly when no transport factory is registered or the route is incomplete", () => {
|
|
53
64
|
expect(() => (0, index_1.createRemoteContractEndpoint)(makeRequest())).toThrow("no remote contract transport is registered");
|
|
54
65
|
expect(() => (0, index_1.createRemoteContractEndpoint)(makeRequest({ dataContextId: undefined }), () => makeTransport().transport)).toThrow("data context");
|
|
55
66
|
expect(() => (0, index_1.createRemoteContractEndpoint)(makeRequest({ targetDeviceId: undefined }), () => makeTransport().transport)).toThrow("target device");
|
|
56
67
|
});
|
|
68
|
+
it("refuses package runTool forwarding that could invoke App Updates indirectly", async () => {
|
|
69
|
+
const { transport, emit } = makeTransport();
|
|
70
|
+
const packageContext = { callerPackageId: "package-a" };
|
|
71
|
+
const endpoint = (0, index_1.createRemoteContractEndpoint)(makeRequest({ contractId: system_ids_1.deviceOperationsContractId, context: packageContext }), () => transport);
|
|
72
|
+
for (const toolName of ["checkForUpdates", "installUpdateAndRestart"]) {
|
|
73
|
+
const call = {
|
|
74
|
+
...toolCall,
|
|
75
|
+
contractId: system_ids_1.deviceOperationsContractId,
|
|
76
|
+
name: "runTool",
|
|
77
|
+
args: [
|
|
78
|
+
{
|
|
79
|
+
toolIdOrName: (0, index_1.deriveToolCatalogId)({
|
|
80
|
+
contractId: system_ids_1.appUpdatesContractId,
|
|
81
|
+
version: index_1.APP_UPDATES_CONTRACT_VERSION,
|
|
82
|
+
toolName,
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
await expect(endpoint.handleCall(call, packageContext)).rejects.toThrow("packages cannot call Device Operations runTool");
|
|
88
|
+
}
|
|
89
|
+
expect(emit).not.toHaveBeenCalled();
|
|
90
|
+
// Read-only tools on the same cached package endpoint still work.
|
|
91
|
+
await expect(endpoint.handleCall({ ...toolCall, contractId: system_ids_1.deviceOperationsContractId, name: "getDeviceStatus" }, packageContext)).resolves.toBe("pong");
|
|
92
|
+
const operator = (0, index_1.createRemoteContractEndpoint)(makeRequest({ contractId: system_ids_1.deviceOperationsContractId }), () => transport);
|
|
93
|
+
await expect(operator.handleCall({ ...toolCall, contractId: system_ids_1.deviceOperationsContractId, name: "runTool" }, {})).resolves.toBe("pong");
|
|
94
|
+
expect(emit).toHaveBeenCalledTimes(2);
|
|
95
|
+
});
|
|
57
96
|
});
|
|
58
97
|
describe("resolveRemoteTargetEndpoint", () => {
|
|
59
98
|
it("returns undefined for local routes and for the local device itself", () => {
|
|
@@ -15,6 +15,7 @@ describe("system contract catalog", () => {
|
|
|
15
15
|
.map((entry) => entry.contractId)
|
|
16
16
|
.filter(Boolean);
|
|
17
17
|
expect(ids).toEqual(expect.arrayContaining([
|
|
18
|
+
system_ids_1.appUpdatesContractId,
|
|
18
19
|
system_ids_1.logsContractId,
|
|
19
20
|
system_ids_1.deviceOperationsContractId,
|
|
20
21
|
system_ids_1.identityContractId,
|
|
@@ -13,6 +13,7 @@ export { getHostContractTransport, type HostContractTransportListener, registerH
|
|
|
13
13
|
export { PersistentContractRegistry } from "./persistent-registry";
|
|
14
14
|
export { ContractRegistry, type IContractProviderRegistration } from "./registry";
|
|
15
15
|
export { createRemoteContractEndpoint, getRemoteContractTransportFactory, type IRemoteContractHost, type IRemoteContractTarget, type RemoteContractTransportFactory, resolveRemoteTargetEndpoint, setRemoteContractTransportFactory, } from "./remote-contract-transport";
|
|
16
|
+
export * from "./system/app-updates-contract";
|
|
16
17
|
export { CONTRACT_CATALOG_VERSION, contractCatalogProviderPackageId, getContractCatalogDefinition, type IContractCatalogConsumer, type IContractProviderInfo, type IListContractProvidersInput, } from "./system/contract-catalog-contract";
|
|
17
18
|
export * from "./system/desktop-tools-contract";
|
|
18
19
|
export { DEVICE_OPERATIONS_CONTRACT_VERSION, DEVICE_OPERATIONS_MAX_QUERY_LENGTH, DEVICE_OPERATIONS_MAX_RESULT_BYTES, DEVICE_OPERATIONS_MAX_ROWS, DEVICE_OPERATIONS_MAX_TOOLS, type DeviceOperationCapability, deviceOperationsProviderPackageId, deviceOperationsToolIds, getDeviceOperationsContractDefinition, type IDeviceDatabaseQueryInput, type IDeviceDatabaseQueryResult, type IDeviceOperationStatus, type IDeviceToolListInput, type IDeviceToolLookupInput, type IDeviceToolLookupResult, type IDeviceToolRunInput, } from "./system/device-operations-contract";
|
package/dist/contracts/index.js
CHANGED
|
@@ -83,6 +83,7 @@ Object.defineProperty(exports, "createRemoteContractEndpoint", { enumerable: tru
|
|
|
83
83
|
Object.defineProperty(exports, "getRemoteContractTransportFactory", { enumerable: true, get: function () { return remote_contract_transport_1.getRemoteContractTransportFactory; } });
|
|
84
84
|
Object.defineProperty(exports, "resolveRemoteTargetEndpoint", { enumerable: true, get: function () { return remote_contract_transport_1.resolveRemoteTargetEndpoint; } });
|
|
85
85
|
Object.defineProperty(exports, "setRemoteContractTransportFactory", { enumerable: true, get: function () { return remote_contract_transport_1.setRemoteContractTransportFactory; } });
|
|
86
|
+
__exportStar(require("./system/app-updates-contract"), exports);
|
|
86
87
|
var contract_catalog_contract_1 = require("./system/contract-catalog-contract");
|
|
87
88
|
Object.defineProperty(exports, "CONTRACT_CATALOG_VERSION", { enumerable: true, get: function () { return contract_catalog_contract_1.CONTRACT_CATALOG_VERSION; } });
|
|
88
89
|
Object.defineProperty(exports, "contractCatalogProviderPackageId", { enumerable: true, get: function () { return contract_catalog_contract_1.contractCatalogProviderPackageId; } });
|
|
@@ -60,10 +60,13 @@ export declare function resolveRemoteTargetEndpoint(request: IContractProviderEn
|
|
|
60
60
|
* *local* caller for the route. It strips {@link IContractCall.targetDeviceId} before
|
|
61
61
|
* emitting so the receiving device sees an ordinary call addressed to itself and the
|
|
62
62
|
* message is signed with this device's own identity. Only tool runs are forwarded; the
|
|
63
|
-
* router rejects other members before reaching this endpoint.
|
|
63
|
+
* router rejects other members before reaching this endpoint. Package callers cannot
|
|
64
|
+
* reach contracts whose providers refuse packages, such as System App Updates, or the
|
|
65
|
+
* Device Operations `runTool` wrapper that could invoke them indirectly.
|
|
64
66
|
*
|
|
65
67
|
* @param request Authorized route whose `targetDeviceId` and `dataContextId` are set.
|
|
66
68
|
* @param factory Transport factory; defaults to the process-wide registration.
|
|
67
|
-
* @throws If no factory is available
|
|
69
|
+
* @throws If no factory is available, the route lacks a target device or data context, or
|
|
70
|
+
* a package targets a contract that refuses package callers.
|
|
68
71
|
*/
|
|
69
72
|
export declare function createRemoteContractEndpoint(request: IContractProviderEndpointRequest, factory?: RemoteContractTransportFactory | undefined): IContractProviderEndpoint;
|
|
@@ -4,7 +4,14 @@ exports.setRemoteContractTransportFactory = setRemoteContractTransportFactory;
|
|
|
4
4
|
exports.getRemoteContractTransportFactory = getRemoteContractTransportFactory;
|
|
5
5
|
exports.resolveRemoteTargetEndpoint = resolveRemoteTargetEndpoint;
|
|
6
6
|
exports.createRemoteContractEndpoint = createRemoteContractEndpoint;
|
|
7
|
+
const system_ids_1 = require("../system-ids");
|
|
7
8
|
const contract_transport_1 = require("./contract-transport");
|
|
9
|
+
/**
|
|
10
|
+
* Contracts whose providers refuse package callers. A forwarded call reaches the target
|
|
11
|
+
* signed by this device alone, so the target cannot tell that a package made it; the
|
|
12
|
+
* forwarding host refuses instead.
|
|
13
|
+
*/
|
|
14
|
+
const packageRefusedRemoteContractIds = new Set([system_ids_1.appUpdatesContractId]);
|
|
8
15
|
const factoriesByUserContext = new WeakMap();
|
|
9
16
|
let defaultFactory;
|
|
10
17
|
/**
|
|
@@ -58,17 +65,23 @@ function resolveRemoteTargetEndpoint(request, host) {
|
|
|
58
65
|
* *local* caller for the route. It strips {@link IContractCall.targetDeviceId} before
|
|
59
66
|
* emitting so the receiving device sees an ordinary call addressed to itself and the
|
|
60
67
|
* message is signed with this device's own identity. Only tool runs are forwarded; the
|
|
61
|
-
* router rejects other members before reaching this endpoint.
|
|
68
|
+
* router rejects other members before reaching this endpoint. Package callers cannot
|
|
69
|
+
* reach contracts whose providers refuse packages, such as System App Updates, or the
|
|
70
|
+
* Device Operations `runTool` wrapper that could invoke them indirectly.
|
|
62
71
|
*
|
|
63
72
|
* @param request Authorized route whose `targetDeviceId` and `dataContextId` are set.
|
|
64
73
|
* @param factory Transport factory; defaults to the process-wide registration.
|
|
65
|
-
* @throws If no factory is available
|
|
74
|
+
* @throws If no factory is available, the route lacks a target device or data context, or
|
|
75
|
+
* a package targets a contract that refuses package callers.
|
|
66
76
|
*/
|
|
67
77
|
function createRemoteContractEndpoint(request, factory = getRemoteContractTransportFactory()) {
|
|
68
78
|
const { targetDeviceId, dataContextId } = request;
|
|
69
79
|
if (!targetDeviceId) {
|
|
70
80
|
throw new Error("Remote contract endpoint requires a target device id");
|
|
71
81
|
}
|
|
82
|
+
if (request.context?.callerPackageId && packageRefusedRemoteContractIds.has(request.contractId)) {
|
|
83
|
+
throw new Error(`Permission denied: packages cannot call contract ${request.contractId} on another device`);
|
|
84
|
+
}
|
|
72
85
|
if (!dataContextId) {
|
|
73
86
|
throw new Error("Remote contract endpoint requires an authorized data context id");
|
|
74
87
|
}
|
|
@@ -85,6 +98,13 @@ function createRemoteContractEndpoint(request, factory = getRemoteContractTransp
|
|
|
85
98
|
if (call.member !== "tool" || call.operation !== "run") {
|
|
86
99
|
return Promise.reject(new Error(`Contract ${call.member} '${call.name}'.${call.operation} cannot be forwarded to a remote device`));
|
|
87
100
|
}
|
|
101
|
+
// runTool refuses packages locally, but the target cannot recover their identity
|
|
102
|
+
// after forwarding. Without this gate it could invoke App Updates on their behalf.
|
|
103
|
+
if (request.context.callerPackageId &&
|
|
104
|
+
request.contractId === system_ids_1.deviceOperationsContractId &&
|
|
105
|
+
call.name === "runTool") {
|
|
106
|
+
return Promise.reject(new Error("Permission denied: packages cannot call Device Operations runTool on another device"));
|
|
107
|
+
}
|
|
88
108
|
const { targetDeviceId: _stripped, ...outbound } = call;
|
|
89
109
|
return transport.emit(contract_transport_1.CONTRACT_REQUEST_CHANNEL, { ...outbound, dataContextId });
|
|
90
110
|
},
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Observable } from "../../observable";
|
|
2
|
+
import type { IContractConsumerBase } from "../contract-proxy";
|
|
3
|
+
import type { IContractDefinition } from "../types";
|
|
4
|
+
/** Version of the built-in System App Updates contract. */
|
|
5
|
+
export declare const APP_UPDATES_CONTRACT_VERSION = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Lifecycle of the host application's update check, download, and install.
|
|
8
|
+
*
|
|
9
|
+
* - `idle` — no check has completed since launch.
|
|
10
|
+
* - `checking` — the update feed is being read.
|
|
11
|
+
* - `up-to-date` — the feed has nothing newer than the running version.
|
|
12
|
+
* - `available` — a newer version exists and its download has not finished.
|
|
13
|
+
* - `downloading` — the newer version is downloading (see {@link IAppUpdateStatus.downloadPercent}).
|
|
14
|
+
* - `downloaded` — the newer version is ready to install.
|
|
15
|
+
* - `installing` — the app is shutting down to run the installer and relaunch.
|
|
16
|
+
* - `restarting` — the app is restarting without installing anything.
|
|
17
|
+
* - `error` — the last check, download, or install failed (see {@link IAppUpdateStatus.error}).
|
|
18
|
+
*/
|
|
19
|
+
export type AppUpdateState = "idle" | "checking" | "up-to-date" | "available" | "downloading" | "downloaded" | "installing" | "restarting" | "error";
|
|
20
|
+
/** Snapshot of the host application's update state, as reported by the device that runs it. */
|
|
21
|
+
export interface IAppUpdateStatus {
|
|
22
|
+
/** False when the host cannot update itself (for example an unpackaged development build). */
|
|
23
|
+
supported: boolean;
|
|
24
|
+
/** True when the host can restart itself on request (desktop apps, even unpackaged ones). */
|
|
25
|
+
canRestart: boolean;
|
|
26
|
+
/** Host operating system as reported by Node (`darwin`, `win32`, `linux`). */
|
|
27
|
+
platform: string;
|
|
28
|
+
/** Version of the running application. */
|
|
29
|
+
currentVersion: string;
|
|
30
|
+
/** Newest version the update feed reported, when a check has succeeded. */
|
|
31
|
+
latestVersion?: string;
|
|
32
|
+
/** Version whose installer has finished downloading in this session. */
|
|
33
|
+
downloadedVersion?: string;
|
|
34
|
+
/** Current lifecycle state. */
|
|
35
|
+
state: AppUpdateState;
|
|
36
|
+
/** Download progress from 0 to 100 while {@link state} is `downloading`. */
|
|
37
|
+
downloadPercent?: number;
|
|
38
|
+
/** Message from the last failed check, download, or install. */
|
|
39
|
+
error?: string;
|
|
40
|
+
/** Epoch milliseconds of the last completed feed check. */
|
|
41
|
+
lastCheckedAt?: number;
|
|
42
|
+
/** This device's "install updates when Peers restarts" setting. */
|
|
43
|
+
installOnRestart: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* True when a downloaded update will install the next time the app quits. Usually mirrors
|
|
46
|
+
* {@link installOnRestart}; the quit itself re-reads the stored setting. On macOS it is also
|
|
47
|
+
* true once the system installer has accepted the update for an Update & restart that timed
|
|
48
|
+
* out, which cannot be undone.
|
|
49
|
+
*/
|
|
50
|
+
installOnQuitArmed: boolean;
|
|
51
|
+
/** This device's "allow my other devices to update and restart this device" setting. */
|
|
52
|
+
remoteRestartAllowed: boolean;
|
|
53
|
+
/** True when installing prompts for an administrator password on this device (Linux `.deb`). */
|
|
54
|
+
installNeedsElevation?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* What `installUpdateAndRestart` started. The call returns as soon as the work is underway;
|
|
58
|
+
* read {@link IAppUpdateStatus} to follow it.
|
|
59
|
+
*
|
|
60
|
+
* - `installing` — a downloaded update is installing; the app will relaunch on the new version.
|
|
61
|
+
* - `downloading` — the update is still downloading; it installs and relaunches when done.
|
|
62
|
+
* - `checking` — no update was known; the device checks first, then installs or simply restarts.
|
|
63
|
+
* - `restarting` — the app is restarting without an update.
|
|
64
|
+
*/
|
|
65
|
+
export type AppUpdateInstallAction = "installing" | "downloading" | "checking" | "restarting";
|
|
66
|
+
/** Result of `installUpdateAndRestart`. */
|
|
67
|
+
export interface IAppUpdateInstallResult {
|
|
68
|
+
/** What the device started. */
|
|
69
|
+
action: AppUpdateInstallAction;
|
|
70
|
+
/** Status at the moment the work started. */
|
|
71
|
+
status: IAppUpdateStatus;
|
|
72
|
+
}
|
|
73
|
+
/** Input naming the device a relayed App Updates call targets. */
|
|
74
|
+
export interface IAppUpdatesDeviceInput {
|
|
75
|
+
/** Another device of this user, or of a user who trusts this user as `Self`. */
|
|
76
|
+
deviceId: string;
|
|
77
|
+
}
|
|
78
|
+
/** Typed consumer proxy for System App Updates v1. */
|
|
79
|
+
export interface IAppUpdatesConsumer extends IContractConsumerBase {
|
|
80
|
+
tools: {
|
|
81
|
+
/** Return this device's current update status. */
|
|
82
|
+
getUpdateStatus(): Promise<IAppUpdateStatus>;
|
|
83
|
+
/** Read the update feed now and return the resulting status. */
|
|
84
|
+
checkForUpdates(): Promise<IAppUpdateStatus>;
|
|
85
|
+
/** Install an available update and relaunch, or restart when there is nothing to install. */
|
|
86
|
+
installUpdateAndRestart(): Promise<IAppUpdateInstallResult>;
|
|
87
|
+
/** Relay `getUpdateStatus` to another device. Local callers only. */
|
|
88
|
+
getDeviceUpdateStatus(input: IAppUpdatesDeviceInput): Promise<IAppUpdateStatus>;
|
|
89
|
+
/** Relay `installUpdateAndRestart` to another device. Local callers only. */
|
|
90
|
+
installUpdateAndRestartOnDevice(input: IAppUpdatesDeviceInput): Promise<IAppUpdateInstallResult>;
|
|
91
|
+
};
|
|
92
|
+
observables: {
|
|
93
|
+
/** Live update status; available over the local host transport only. */
|
|
94
|
+
status: Observable<IAppUpdateStatus>;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Return the System App Updates contract shape.
|
|
99
|
+
*
|
|
100
|
+
* Tools carry no `remoteAccessLevel`, so only callers this device trusts as `Self` (the
|
|
101
|
+
* user's own devices and users explicitly granted Self) can reach them remotely. The
|
|
102
|
+
* provider additionally honours the device's own remote-restart switch.
|
|
103
|
+
*
|
|
104
|
+
* The `*Device*` tools let a local UI or CLI reach another device: the host calls the target
|
|
105
|
+
* over a direct connection (or, for the user's own devices, the mesh) and the target applies
|
|
106
|
+
* its own trust check and switch. They refuse remote callers, so a device never relays.
|
|
107
|
+
* Observables cross the local host transport only.
|
|
108
|
+
*/
|
|
109
|
+
export declare function getAppUpdatesContractDefinition(): IContractDefinition;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APP_UPDATES_CONTRACT_VERSION = void 0;
|
|
4
|
+
exports.getAppUpdatesContractDefinition = getAppUpdatesContractDefinition;
|
|
5
|
+
const system_ids_1 = require("../../system-ids");
|
|
6
|
+
const field_type_1 = require("../../types/field-type");
|
|
7
|
+
const system_contracts_1 = require("../system-contracts");
|
|
8
|
+
/** Version of the built-in System App Updates contract. */
|
|
9
|
+
exports.APP_UPDATES_CONTRACT_VERSION = 1;
|
|
10
|
+
const field = (name, type, description, opts = {}) => ({ name, type, description, ...opts });
|
|
11
|
+
const statusFields = [
|
|
12
|
+
field("supported", field_type_1.FieldType.boolean, "Whether this host can update itself."),
|
|
13
|
+
field("canRestart", field_type_1.FieldType.boolean, "Whether this host can restart itself on request."),
|
|
14
|
+
field("platform", field_type_1.FieldType.string, "Host operating system."),
|
|
15
|
+
field("currentVersion", field_type_1.FieldType.string, "Version of the running application."),
|
|
16
|
+
field("latestVersion", field_type_1.FieldType.string, "Newest version the update feed reported.", {
|
|
17
|
+
optional: true,
|
|
18
|
+
}),
|
|
19
|
+
field("downloadedVersion", field_type_1.FieldType.string, "Version ready to install.", { optional: true }),
|
|
20
|
+
field("state", field_type_1.FieldType.string, "Update lifecycle state."),
|
|
21
|
+
field("downloadPercent", field_type_1.FieldType.number, "Download progress from 0 to 100.", {
|
|
22
|
+
optional: true,
|
|
23
|
+
}),
|
|
24
|
+
field("error", field_type_1.FieldType.string, "Last update failure message.", { optional: true }),
|
|
25
|
+
field("lastCheckedAt", field_type_1.FieldType.number, "Epoch ms of the last completed check.", {
|
|
26
|
+
optional: true,
|
|
27
|
+
}),
|
|
28
|
+
field("installOnRestart", field_type_1.FieldType.boolean, "Install updates when the app restarts."),
|
|
29
|
+
field("installOnQuitArmed", field_type_1.FieldType.boolean, "A downloaded update will install on quit."),
|
|
30
|
+
field("remoteRestartAllowed", field_type_1.FieldType.boolean, "Whether trusted devices may update and restart this device."),
|
|
31
|
+
field("installNeedsElevation", field_type_1.FieldType.boolean, "Installing prompts for a password.", {
|
|
32
|
+
optional: true,
|
|
33
|
+
}),
|
|
34
|
+
];
|
|
35
|
+
let definition;
|
|
36
|
+
const installResultFields = [
|
|
37
|
+
field("action", field_type_1.FieldType.string, "installing, downloading, checking, or restarting."),
|
|
38
|
+
field("status", field_type_1.FieldType.object, "Update status when the work started."),
|
|
39
|
+
];
|
|
40
|
+
const deviceIdField = field("deviceId", field_type_1.FieldType.id, "Device to call: another of this user's devices, or one whose owner trusts this user as Self.");
|
|
41
|
+
/**
|
|
42
|
+
* Return the System App Updates contract shape.
|
|
43
|
+
*
|
|
44
|
+
* Tools carry no `remoteAccessLevel`, so only callers this device trusts as `Self` (the
|
|
45
|
+
* user's own devices and users explicitly granted Self) can reach them remotely. The
|
|
46
|
+
* provider additionally honours the device's own remote-restart switch.
|
|
47
|
+
*
|
|
48
|
+
* The `*Device*` tools let a local UI or CLI reach another device: the host calls the target
|
|
49
|
+
* over a direct connection (or, for the user's own devices, the mesh) and the target applies
|
|
50
|
+
* its own trust check and switch. They refuse remote callers, so a device never relays.
|
|
51
|
+
* Observables cross the local host transport only.
|
|
52
|
+
*/
|
|
53
|
+
function getAppUpdatesContractDefinition() {
|
|
54
|
+
definition ??= {
|
|
55
|
+
contractId: system_ids_1.appUpdatesContractId,
|
|
56
|
+
version: exports.APP_UPDATES_CONTRACT_VERSION,
|
|
57
|
+
devTag: "dev",
|
|
58
|
+
name: "System App Updates",
|
|
59
|
+
description: "Desktop application update status, on-demand checks, and update-and-restart.",
|
|
60
|
+
tables: [],
|
|
61
|
+
tools: [
|
|
62
|
+
{
|
|
63
|
+
name: "getUpdateStatus",
|
|
64
|
+
usageDescription: "Return this device's application version and update status.",
|
|
65
|
+
inputFields: [],
|
|
66
|
+
outputFields: statusFields,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "checkForUpdates",
|
|
70
|
+
usageDescription: "Check the update feed now and return the resulting update status.",
|
|
71
|
+
inputFields: [],
|
|
72
|
+
outputFields: statusFields,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "installUpdateAndRestart",
|
|
76
|
+
usageDescription: "Install an available update and relaunch, or restart the app when there is nothing to install.",
|
|
77
|
+
inputFields: [],
|
|
78
|
+
outputFields: installResultFields,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "getDeviceUpdateStatus",
|
|
82
|
+
usageDescription: "Return another device's application version and update status.",
|
|
83
|
+
inputFields: [deviceIdField],
|
|
84
|
+
outputFields: statusFields,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "installUpdateAndRestartOnDevice",
|
|
88
|
+
usageDescription: "Ask another device to install its available update and relaunch, or restart when it has nothing to install.",
|
|
89
|
+
inputFields: [deviceIdField],
|
|
90
|
+
outputFields: installResultFields,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
observables: [
|
|
94
|
+
{
|
|
95
|
+
name: "status",
|
|
96
|
+
description: "Live application update status.",
|
|
97
|
+
valueType: field_type_1.FieldType.object,
|
|
98
|
+
writable: false,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
events: [],
|
|
102
|
+
};
|
|
103
|
+
return definition;
|
|
104
|
+
}
|
|
105
|
+
(0, system_contracts_1.registerSystemContract)({
|
|
106
|
+
providerPackageId: system_ids_1.peersSystemPackageId,
|
|
107
|
+
contractId: system_ids_1.appUpdatesContractId,
|
|
108
|
+
version: exports.APP_UPDATES_CONTRACT_VERSION,
|
|
109
|
+
getDefinition: getAppUpdatesContractDefinition,
|
|
110
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Built-in system contracts. Importing this barrel runs each module's
|
|
3
3
|
* {@link registerSystemContract} side effect so the catalog is populated.
|
|
4
4
|
*/
|
|
5
|
+
import "./app-updates-contract";
|
|
5
6
|
import "./contract-catalog-contract";
|
|
6
7
|
import "./device-operations-contract";
|
|
7
8
|
import "./http-contract";
|
|
@@ -11,6 +12,7 @@ import "./logs-contract";
|
|
|
11
12
|
import "./network-contracts";
|
|
12
13
|
import "./scheduler-contract";
|
|
13
14
|
import "./tool-access-control-contract";
|
|
15
|
+
export * from "./app-updates-contract";
|
|
14
16
|
export { DEVICE_OPERATIONS_CONTRACT_VERSION, DEVICE_OPERATIONS_MAX_QUERY_LENGTH, DEVICE_OPERATIONS_MAX_RESULT_BYTES, DEVICE_OPERATIONS_MAX_ROWS, DEVICE_OPERATIONS_MAX_TOOLS, type DeviceOperationCapability, deviceOperationsProviderPackageId, deviceOperationsToolIds, getDeviceOperationsContractDefinition, type IDeviceDatabaseQueryInput, type IDeviceDatabaseQueryResult, type IDeviceOperationStatus, type IDeviceToolListInput, type IDeviceToolLookupInput, type IDeviceToolLookupResult, type IDeviceToolRunInput, } from "./device-operations-contract";
|
|
15
17
|
export { getIdentityContractDefinition, IDENTITY_CONTRACT_VERSION, type IIdentityConsumer, type IIdentityKeyInfo, type IIdentityRegistryKey, type IIdentityRotateKeyInput, type IIdentityRotateKeyResult, type IIdentitySessionInfo, } from "./identity-contract";
|
|
16
18
|
export { getInvitesContractDefinition, type IAcceptInviteResult, type ICreatedInvite, type IInvitePreview, type IInvitesConsumer, INVITES_CONTRACT_VERSION, } from "./invites-contract";
|
|
@@ -19,6 +19,7 @@ exports.TOOL_ACCESS_CONTROL_CONTRACT_VERSION = exports.getToolAccessControlContr
|
|
|
19
19
|
* Built-in system contracts. Importing this barrel runs each module's
|
|
20
20
|
* {@link registerSystemContract} side effect so the catalog is populated.
|
|
21
21
|
*/
|
|
22
|
+
require("./app-updates-contract");
|
|
22
23
|
require("./contract-catalog-contract");
|
|
23
24
|
require("./device-operations-contract");
|
|
24
25
|
require("./http-contract");
|
|
@@ -28,6 +29,7 @@ require("./logs-contract");
|
|
|
28
29
|
require("./network-contracts");
|
|
29
30
|
require("./scheduler-contract");
|
|
30
31
|
require("./tool-access-control-contract");
|
|
32
|
+
__exportStar(require("./app-updates-contract"), exports);
|
|
31
33
|
var device_operations_contract_1 = require("./device-operations-contract");
|
|
32
34
|
Object.defineProperty(exports, "DEVICE_OPERATIONS_CONTRACT_VERSION", { enumerable: true, get: function () { return device_operations_contract_1.DEVICE_OPERATIONS_CONTRACT_VERSION; } });
|
|
33
35
|
Object.defineProperty(exports, "DEVICE_OPERATIONS_MAX_QUERY_LENGTH", { enumerable: true, get: function () { return device_operations_contract_1.DEVICE_OPERATIONS_MAX_QUERY_LENGTH; } });
|
package/dist/data/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from "./tool-access";
|
|
|
30
30
|
export * from "./tool-access-approvals";
|
|
31
31
|
export * from "./tool-tests";
|
|
32
32
|
export * from "./tools";
|
|
33
|
+
export * from "./update-settings.pvar";
|
|
33
34
|
export * from "./user-permissions";
|
|
34
35
|
export * from "./user-trust-levels";
|
|
35
36
|
export * from "./users";
|
package/dist/data/index.js
CHANGED
|
@@ -46,6 +46,7 @@ __exportStar(require("./tool-access"), exports);
|
|
|
46
46
|
__exportStar(require("./tool-access-approvals"), exports);
|
|
47
47
|
__exportStar(require("./tool-tests"), exports);
|
|
48
48
|
__exportStar(require("./tools"), exports);
|
|
49
|
+
__exportStar(require("./update-settings.pvar"), exports);
|
|
49
50
|
__exportStar(require("./user-permissions"), exports);
|
|
50
51
|
__exportStar(require("./user-trust-levels"), exports);
|
|
51
52
|
__exportStar(require("./users"), exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-scoped settings for desktop application updates. Each device keeps its own values;
|
|
3
|
+
* they never sync to the user's other devices, so another device cannot change them.
|
|
4
|
+
*
|
|
5
|
+
* Like the other `*.pvar` modules, this creates pvars at load, so the data barrel must
|
|
6
|
+
* evaluate it after `persistent-vars` has initialized; its name sorts it there.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Install a downloaded application update automatically the next time Peers quits or
|
|
10
|
+
* restarts on this device.
|
|
11
|
+
*
|
|
12
|
+
* Defaults to true. When false, updates still download in the background but install only
|
|
13
|
+
* when someone chooses **Update & restart**.
|
|
14
|
+
*
|
|
15
|
+
* The desktop app reads this row by name each time it quits, rather than trusting the
|
|
16
|
+
* in-memory value, so keep the name stable.
|
|
17
|
+
*/
|
|
18
|
+
export declare const installAppUpdatesOnRestart: import("./persistent-vars").PersistentVar<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Allow callers this device trusts as `Self` (the user's other devices, and users explicitly
|
|
21
|
+
* granted Self trust) to run **Update & restart** on this device remotely.
|
|
22
|
+
*
|
|
23
|
+
* Defaults to true. This is a consent switch, not a security boundary: Self-trusted callers
|
|
24
|
+
* already have full remote access to this device's system contracts.
|
|
25
|
+
*/
|
|
26
|
+
export declare const allowRemoteAppUpdateRestart: import("./persistent-vars").PersistentVar<boolean>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Device-scoped settings for desktop application updates. Each device keeps its own values;
|
|
4
|
+
* they never sync to the user's other devices, so another device cannot change them.
|
|
5
|
+
*
|
|
6
|
+
* Like the other `*.pvar` modules, this creates pvars at load, so the data barrel must
|
|
7
|
+
* evaluate it after `persistent-vars` has initialized; its name sorts it there.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.allowRemoteAppUpdateRestart = exports.installAppUpdatesOnRestart = void 0;
|
|
11
|
+
const persistent_vars_1 = require("./persistent-vars");
|
|
12
|
+
/**
|
|
13
|
+
* Install a downloaded application update automatically the next time Peers quits or
|
|
14
|
+
* restarts on this device.
|
|
15
|
+
*
|
|
16
|
+
* Defaults to true. When false, updates still download in the background but install only
|
|
17
|
+
* when someone chooses **Update & restart**.
|
|
18
|
+
*
|
|
19
|
+
* The desktop app reads this row by name each time it quits, rather than trusting the
|
|
20
|
+
* in-memory value, so keep the name stable.
|
|
21
|
+
*/
|
|
22
|
+
exports.installAppUpdatesOnRestart = (0, persistent_vars_1.deviceVar)("installAppUpdatesOnRestart", {
|
|
23
|
+
defaultValue: true,
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Allow callers this device trusts as `Self` (the user's other devices, and users explicitly
|
|
27
|
+
* granted Self trust) to run **Update & restart** on this device remotely.
|
|
28
|
+
*
|
|
29
|
+
* Defaults to true. This is a consent switch, not a security boundary: Self-trusted callers
|
|
30
|
+
* already have full remote access to this device's system contracts.
|
|
31
|
+
*/
|
|
32
|
+
exports.allowRemoteAppUpdateRestart = (0, persistent_vars_1.deviceVar)("allowRemoteAppUpdateRestart", {
|
|
33
|
+
defaultValue: true,
|
|
34
|
+
});
|
package/dist/system-ids.d.ts
CHANGED
|
@@ -65,6 +65,12 @@ export declare const deviceOperationsContractId = "00mriijkhq94msfe9m2j6quwp";
|
|
|
65
65
|
export declare const networkDiagnosticsContractId = "00ms1yb50w29oi116uwwrg7rl";
|
|
66
66
|
/** Contract id for explicit local network and synchronization control operations. */
|
|
67
67
|
export declare const networkControlContractId = "00ms1yb50yj9w5qbytl69qfht";
|
|
68
|
+
/**
|
|
69
|
+
* Contract id for **System App Updates**: desktop host update status, on-demand checks, and
|
|
70
|
+
* update-and-restart. Remotely callable by Self-trusted devices; hosts without an updater
|
|
71
|
+
* provide status and relay tools, while Electron also implements local updates.
|
|
72
|
+
*/
|
|
73
|
+
export declare const appUpdatesContractId = "00mvczoafequsatpfjaj3r4g8";
|
|
68
74
|
/**
|
|
69
75
|
* Contract id for the local-only tool-access approval control surface.
|
|
70
76
|
* Not available over device-to-device connections.
|
package/dist/system-ids.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.desktopToolsContractId = exports.hostToolsContractId = exports.toolCatalogContractId = exports.schedulerToolIds = exports.schedulerContractId = exports.contractCatalogToolIds = exports.contractCatalogContractId = exports.httpToolIds = exports.httpContractId = exports.localFoldersToolIds = exports.localFoldersContractId = exports.identityToolIds = exports.identityContractId = exports.invitesToolIds = exports.invitesContractId = exports.toolAccessControlToolIds = exports.toolAccessControlContractId = exports.networkControlContractId = exports.networkDiagnosticsContractId = exports.deviceOperationsContractId = exports.logsContractId = exports.peersSystemPackageId = exports.peersCoreRevokedPublishPublicKeys = exports.peersCorePublishPublicKey = exports.peersCorePackageRepoUrl = exports.peersCorePackageId = exports.createGroupToolId = exports.queryDatabaseToolId = exports.runWorkflowToolId = exports.defaultSendMessageToolId = exports.defaultAssistantRunnerToolId = exports.adminAssistantId = exports.openCodeAssistantId = exports.defaultAssistantId = exports.peersRootUserId = void 0;
|
|
3
|
+
exports.desktopToolsContractId = exports.hostToolsContractId = exports.toolCatalogContractId = exports.schedulerToolIds = exports.schedulerContractId = exports.contractCatalogToolIds = exports.contractCatalogContractId = exports.httpToolIds = exports.httpContractId = exports.localFoldersToolIds = exports.localFoldersContractId = exports.identityToolIds = exports.identityContractId = exports.invitesToolIds = exports.invitesContractId = exports.toolAccessControlToolIds = exports.toolAccessControlContractId = exports.appUpdatesContractId = exports.networkControlContractId = exports.networkDiagnosticsContractId = exports.deviceOperationsContractId = exports.logsContractId = exports.peersSystemPackageId = exports.peersCoreRevokedPublishPublicKeys = exports.peersCorePublishPublicKey = exports.peersCorePackageRepoUrl = exports.peersCorePackageId = exports.createGroupToolId = exports.queryDatabaseToolId = exports.runWorkflowToolId = exports.defaultSendMessageToolId = exports.defaultAssistantRunnerToolId = exports.adminAssistantId = exports.openCodeAssistantId = exports.defaultAssistantId = exports.peersRootUserId = void 0;
|
|
4
4
|
/** Stable user ID that owns built-in Peers system records. */
|
|
5
5
|
exports.peersRootUserId = "000peers0user000000000001";
|
|
6
6
|
/** Default assistant used when no user-selected assistant is available. */
|
|
@@ -72,6 +72,12 @@ exports.deviceOperationsContractId = "00mriijkhq94msfe9m2j6quwp";
|
|
|
72
72
|
exports.networkDiagnosticsContractId = "00ms1yb50w29oi116uwwrg7rl";
|
|
73
73
|
/** Contract id for explicit local network and synchronization control operations. */
|
|
74
74
|
exports.networkControlContractId = "00ms1yb50yj9w5qbytl69qfht";
|
|
75
|
+
/**
|
|
76
|
+
* Contract id for **System App Updates**: desktop host update status, on-demand checks, and
|
|
77
|
+
* update-and-restart. Remotely callable by Self-trusted devices; hosts without an updater
|
|
78
|
+
* provide status and relay tools, while Electron also implements local updates.
|
|
79
|
+
*/
|
|
80
|
+
exports.appUpdatesContractId = "00mvczoafequsatpfjaj3r4g8";
|
|
75
81
|
/**
|
|
76
82
|
* Contract id for the local-only tool-access approval control surface.
|
|
77
83
|
* Not available over device-to-device connections.
|