@peers-app/peers-sdk 0.21.7 → 0.23.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/context/user-context-singleton.d.ts +42 -0
- package/dist/context/user-context-singleton.js +45 -0
- package/dist/contracts/__tests__/contract-proxy.test.js +51 -1
- package/dist/contracts/__tests__/contract-resolvers.test.js +65 -0
- package/dist/contracts/__tests__/playground.test.js +5 -2
- package/dist/contracts/__tests__/system-contracts.test.js +3 -0
- package/dist/contracts/contract-proxy.d.ts +27 -0
- package/dist/contracts/contract-proxy.js +37 -1
- package/dist/contracts/contract-resolvers.d.ts +10 -0
- package/dist/contracts/contract-resolvers.js +21 -3
- package/dist/contracts/index.d.ts +3 -2
- package/dist/contracts/index.js +5 -2
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +5 -1
- package/dist/contracts/system/tool-access-control-contract.d.ts +35 -0
- package/dist/contracts/system/tool-access-control-contract.js +70 -0
- package/dist/data/access-level.d.ts +72 -0
- package/dist/data/access-level.js +109 -0
- package/dist/data/access-level.test.d.ts +1 -0
- package/dist/data/access-level.test.js +53 -0
- package/dist/data/assistants.d.ts +24 -0
- package/dist/data/assistants.js +21 -0
- package/dist/data/group-member-roles.d.ts +9 -0
- package/dist/data/group-member-roles.js +9 -0
- package/dist/data/group-permissions.d.ts +55 -1
- package/dist/data/group-permissions.js +76 -6
- package/dist/data/index.d.ts +3 -1
- package/dist/data/index.js +3 -1
- package/dist/data/orm/sql.data-source.d.ts +10 -0
- package/dist/data/orm/sql.data-source.js +48 -30
- package/dist/data/orm/sql.data-source.test.js +13 -0
- package/dist/data/package-version-resolver.test.js +2 -0
- package/dist/data/tool-access-approvals.d.ts +126 -0
- package/dist/data/tool-access-approvals.js +86 -0
- package/dist/data/tool-access.d.ts +162 -0
- package/dist/data/tool-access.js +235 -0
- package/dist/data/tool-access.test.d.ts +1 -0
- package/dist/data/tool-access.test.js +159 -0
- package/dist/data/tools.d.ts +15 -0
- package/dist/data/tools.js +11 -0
- package/dist/data/workflow-logs.d.ts +5 -5
- package/dist/data/workflow-runs.d.ts +2 -2
- package/dist/device-pairing/types.d.ts +6 -6
- package/dist/package-installer/package-creator.d.ts +31 -0
- package/dist/package-installer/package-creator.js +140 -40
- package/dist/package-installer/package-creator.test.js +113 -11
- package/dist/rpc-types.d.ts +0 -17
- package/dist/rpc-types.js +0 -15
- package/dist/system-ids.d.ts +13 -0
- package/dist/system-ids.js +14 -1
- package/package.json +1 -1
- package/dist/data/voice-messages.d.ts +0 -33
- package/dist/data/voice-messages.js +0 -33
|
@@ -29,8 +29,8 @@ export declare const workflowLogSchema: z.ZodObject<{
|
|
|
29
29
|
resultObject: z.ZodOptional<z.ZodUnion<[z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, z.ZodArray<z.ZodAny, "many">]>>;
|
|
30
30
|
isError: z.ZodOptional<z.ZodBoolean>;
|
|
31
31
|
}, "strip", z.ZodTypeAny, {
|
|
32
|
-
workflowLogId: string;
|
|
33
32
|
workflowRunId: string;
|
|
33
|
+
workflowLogId: string;
|
|
34
34
|
contextId: string;
|
|
35
35
|
logDT: Date;
|
|
36
36
|
logText: string;
|
|
@@ -47,13 +47,13 @@ export declare const workflowLogSchema: z.ZodObject<{
|
|
|
47
47
|
resultObject?: any[] | z.objectOutputType<{}, z.ZodAny, "strip"> | undefined;
|
|
48
48
|
isError?: boolean | undefined;
|
|
49
49
|
}, {
|
|
50
|
-
workflowLogId: string;
|
|
51
50
|
workflowRunId: string;
|
|
51
|
+
workflowLogId: string;
|
|
52
52
|
contextId: string;
|
|
53
53
|
logText: string;
|
|
54
54
|
toolId?: string | undefined;
|
|
55
|
-
logDT?: Date | undefined;
|
|
56
55
|
result?: string | undefined;
|
|
56
|
+
logDT?: Date | undefined;
|
|
57
57
|
instruction?: {
|
|
58
58
|
markdown?: string | undefined;
|
|
59
59
|
onError?: string | undefined;
|
|
@@ -67,8 +67,8 @@ export declare const workflowLogSchema: z.ZodObject<{
|
|
|
67
67
|
}>;
|
|
68
68
|
export type IWorkflowLog = z.infer<typeof workflowLogSchema>;
|
|
69
69
|
export declare function WorkflowLogs(dataContext?: DataContext): import("..").Table<{
|
|
70
|
-
workflowLogId: string;
|
|
71
70
|
workflowRunId: string;
|
|
71
|
+
workflowLogId: string;
|
|
72
72
|
contextId: string;
|
|
73
73
|
logDT: Date;
|
|
74
74
|
logText: string;
|
|
@@ -86,8 +86,8 @@ export declare function WorkflowLogs(dataContext?: DataContext): import("..").Ta
|
|
|
86
86
|
isError?: boolean | undefined;
|
|
87
87
|
}>;
|
|
88
88
|
export declare const getLogger: (workflowRunId: string, contextId: string) => (logData: string | Omit<Partial<IWorkflowLog>, "workflowRunId" | "workflowLogId">) => Promise<{
|
|
89
|
-
workflowLogId: string;
|
|
90
89
|
workflowRunId: string;
|
|
90
|
+
workflowLogId: string;
|
|
91
91
|
contextId: string;
|
|
92
92
|
logDT: Date;
|
|
93
93
|
logText: string;
|
|
@@ -33,6 +33,7 @@ export declare const workflowRunSchema: z.ZodObject<{
|
|
|
33
33
|
defaultAssistantId: z.ZodEffects<z.ZodString, string, string>;
|
|
34
34
|
}, "strip", z.ZodTypeAny, {
|
|
35
35
|
createdAt: Date;
|
|
36
|
+
workflowRunId: string;
|
|
36
37
|
defaultAssistantId: string;
|
|
37
38
|
instructions: {
|
|
38
39
|
markdown?: string | undefined;
|
|
@@ -40,7 +41,6 @@ export declare const workflowRunSchema: z.ZodObject<{
|
|
|
40
41
|
directCallToolId?: string | undefined;
|
|
41
42
|
subWorkflowId?: string | undefined;
|
|
42
43
|
}[];
|
|
43
|
-
workflowRunId: string;
|
|
44
44
|
parentMessageId: string;
|
|
45
45
|
currentInstructionIndex: number;
|
|
46
46
|
instructionResults: any[];
|
|
@@ -54,8 +54,8 @@ export declare const workflowRunSchema: z.ZodObject<{
|
|
|
54
54
|
completedAt?: Date | undefined;
|
|
55
55
|
parentWorkflowRunId?: string | undefined;
|
|
56
56
|
}, {
|
|
57
|
-
defaultAssistantId: string;
|
|
58
57
|
workflowRunId: string;
|
|
58
|
+
defaultAssistantId: string;
|
|
59
59
|
parentMessageId: string;
|
|
60
60
|
createdAt?: Date | undefined;
|
|
61
61
|
workflowId?: string | undefined;
|
|
@@ -715,14 +715,14 @@ export declare const pairingApprovalContentsSchema: z.ZodObject<{
|
|
|
715
715
|
role: "source" | "destination";
|
|
716
716
|
version: 1;
|
|
717
717
|
expiresAt: number;
|
|
718
|
-
transcriptHash: string;
|
|
719
718
|
approvalId: string;
|
|
719
|
+
transcriptHash: string;
|
|
720
720
|
}, {
|
|
721
721
|
role: "source" | "destination";
|
|
722
722
|
version: 1;
|
|
723
723
|
expiresAt: number;
|
|
724
|
-
transcriptHash: string;
|
|
725
724
|
approvalId: string;
|
|
725
|
+
transcriptHash: string;
|
|
726
726
|
}>;
|
|
727
727
|
/** Contents of a signed pairing approval. */
|
|
728
728
|
export type PairingApprovalContents = z.infer<typeof pairingApprovalContentsSchema>;
|
|
@@ -738,14 +738,14 @@ export declare const signedPairingApprovalSchema: z.ZodObject<{
|
|
|
738
738
|
role: "source" | "destination";
|
|
739
739
|
version: 1;
|
|
740
740
|
expiresAt: number;
|
|
741
|
-
transcriptHash: string;
|
|
742
741
|
approvalId: string;
|
|
742
|
+
transcriptHash: string;
|
|
743
743
|
}, {
|
|
744
744
|
role: "source" | "destination";
|
|
745
745
|
version: 1;
|
|
746
746
|
expiresAt: number;
|
|
747
|
-
transcriptHash: string;
|
|
748
747
|
approvalId: string;
|
|
748
|
+
transcriptHash: string;
|
|
749
749
|
}>;
|
|
750
750
|
signature: z.ZodString;
|
|
751
751
|
publicKey: z.ZodString;
|
|
@@ -756,8 +756,8 @@ export declare const signedPairingApprovalSchema: z.ZodObject<{
|
|
|
756
756
|
role: "source" | "destination";
|
|
757
757
|
version: 1;
|
|
758
758
|
expiresAt: number;
|
|
759
|
-
transcriptHash: string;
|
|
760
759
|
approvalId: string;
|
|
760
|
+
transcriptHash: string;
|
|
761
761
|
};
|
|
762
762
|
}, {
|
|
763
763
|
publicKey: string;
|
|
@@ -766,8 +766,8 @@ export declare const signedPairingApprovalSchema: z.ZodObject<{
|
|
|
766
766
|
role: "source" | "destination";
|
|
767
767
|
version: 1;
|
|
768
768
|
expiresAt: number;
|
|
769
|
-
transcriptHash: string;
|
|
770
769
|
approvalId: string;
|
|
770
|
+
transcriptHash: string;
|
|
771
771
|
};
|
|
772
772
|
}>;
|
|
773
773
|
/** Signed approval bound to one transcript and endpoint identity. */
|
|
@@ -12,11 +12,42 @@ export declare function nameToFolderSlug(name: string): string;
|
|
|
12
12
|
* @returns The path to the cached template directory.
|
|
13
13
|
*/
|
|
14
14
|
export declare function ensureTemplateCache(deps: IPackageInstallerDeps, templateRepo: string): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve (or create) the Ed25519 publish public key for a package.
|
|
17
|
+
*
|
|
18
|
+
* Idempotent: never rotates an established key. Reuses `existingPublishPublicKey`
|
|
19
|
+
* when present, derives the public key from an existing secret pvar when possible,
|
|
20
|
+
* and only generates a new keypair when both are missing.
|
|
21
|
+
*
|
|
22
|
+
* @throws When a readable secret key does not match an established `publishPublicKey`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensurePackageSigningKey(opts: {
|
|
25
|
+
personalContext?: DataContext;
|
|
26
|
+
packageId: string;
|
|
27
|
+
packageName: string;
|
|
28
|
+
existingPublishPublicKey?: string;
|
|
29
|
+
}): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Import an existing on-disk Peers package directory into the current data context.
|
|
32
|
+
*
|
|
33
|
+
* Validates the manifest packageId, creates/updates the Packages record, persists
|
|
34
|
+
* `packageLocalPath_*`, provisions a signing key when missing, then installs the
|
|
35
|
+
* local dev bundles.
|
|
36
|
+
*/
|
|
37
|
+
export declare function importExistingPackage(dataContext: DataContext, deps: IPackageInstallerDeps, opts: {
|
|
38
|
+
location: string;
|
|
39
|
+
personalContext?: DataContext;
|
|
40
|
+
/** Display name fallback when the manifest name is a slug. */
|
|
41
|
+
displayName?: string;
|
|
42
|
+
}): Promise<ICreatePackageResult>;
|
|
15
43
|
/**
|
|
16
44
|
* Create a new Peers package from the package template.
|
|
17
45
|
*
|
|
18
46
|
* Clones the template repo, patches IDs and metadata, runs npm install + build,
|
|
19
47
|
* initializes a fresh git repo, then delegates to {@link installPackageFromBundles}
|
|
20
48
|
* to register the dev bundle in the database.
|
|
49
|
+
*
|
|
50
|
+
* When `location` already exists and contains a valid Peers package manifest,
|
|
51
|
+
* imports that directory instead (see {@link importExistingPackage}).
|
|
21
52
|
*/
|
|
22
53
|
export declare function createPackage(dataContext: DataContext, deps: IPackageInstallerDeps, opts: ICreatePackageOpts): Promise<ICreatePackageResult>;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nameToFolderSlug = nameToFolderSlug;
|
|
4
4
|
exports.ensureTemplateCache = ensureTemplateCache;
|
|
5
|
+
exports.ensurePackageSigningKey = ensurePackageSigningKey;
|
|
6
|
+
exports.importExistingPackage = importExistingPackage;
|
|
5
7
|
exports.createPackage = createPackage;
|
|
6
8
|
const packages_1 = require("../data/packages");
|
|
7
9
|
const persistent_vars_1 = require("../data/persistent-vars");
|
|
@@ -42,29 +44,148 @@ async function ensureTemplateCache(deps, templateRepo) {
|
|
|
42
44
|
}
|
|
43
45
|
return cachePath;
|
|
44
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve (or create) the Ed25519 publish public key for a package.
|
|
49
|
+
*
|
|
50
|
+
* Idempotent: never rotates an established key. Reuses `existingPublishPublicKey`
|
|
51
|
+
* when present, derives the public key from an existing secret pvar when possible,
|
|
52
|
+
* and only generates a new keypair when both are missing.
|
|
53
|
+
*
|
|
54
|
+
* @throws When a readable secret key does not match an established `publishPublicKey`.
|
|
55
|
+
*/
|
|
56
|
+
async function ensurePackageSigningKey(opts) {
|
|
57
|
+
const { personalContext, packageId, packageName, existingPublishPublicKey } = opts;
|
|
58
|
+
const establishedKey = existingPublishPublicKey?.trim() || "";
|
|
59
|
+
if (personalContext) {
|
|
60
|
+
const keyName = `${package_author_signing_1.PACKAGE_SIGNING_KEY_PREFIX}${packageId}`;
|
|
61
|
+
const existingKey = await (0, persistent_vars_1.PersistentVars)(personalContext).findOne({ name: keyName });
|
|
62
|
+
const rawSecret = existingKey?.value?.value;
|
|
63
|
+
if (typeof rawSecret === "string" && rawSecret.length > 0) {
|
|
64
|
+
try {
|
|
65
|
+
const derivedPublicKey = (0, keys_1.hydrateKeys)(rawSecret).publicKey;
|
|
66
|
+
if (establishedKey && derivedPublicKey !== establishedKey) {
|
|
67
|
+
throw new Error(`Package signing key mismatch for "${packageName}" (${packageId}): ` +
|
|
68
|
+
"stored secret does not match Packages.publishPublicKey. " +
|
|
69
|
+
"Refusing to rotate the established key.");
|
|
70
|
+
}
|
|
71
|
+
return establishedKey || derivedPublicKey;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof Error && err.message.includes("signing key mismatch")) {
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
// Secret is likely encrypted at rest — cannot derive here without the user key.
|
|
78
|
+
if (establishedKey) {
|
|
79
|
+
return establishedKey;
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Package "${packageName}" (${packageId}) has a signing secret pvar but no ` +
|
|
82
|
+
"publishPublicKey, and the secret could not be hydrated. " +
|
|
83
|
+
"Restore publishPublicKey or replace the unreadable key before importing.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (establishedKey) {
|
|
88
|
+
return establishedKey;
|
|
89
|
+
}
|
|
90
|
+
if (!personalContext) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
const keys = (0, keys_1.newKeys)();
|
|
94
|
+
const keyName = `${package_author_signing_1.PACKAGE_SIGNING_KEY_PREFIX}${packageId}`;
|
|
95
|
+
await (0, persistent_vars_1.PersistentVars)(personalContext).save({
|
|
96
|
+
persistentVarId: (0, utils_1.deterministicPvarId)(keyName),
|
|
97
|
+
name: keyName,
|
|
98
|
+
scope: "user",
|
|
99
|
+
value: { value: keys.secretKey },
|
|
100
|
+
isSecret: true,
|
|
101
|
+
description: `Signing key for package "${packageName}"`,
|
|
102
|
+
modifiedAt: Date.now(),
|
|
103
|
+
});
|
|
104
|
+
return keys.publicKey;
|
|
105
|
+
}
|
|
106
|
+
async function registerPackageRecord(dataContext, opts) {
|
|
107
|
+
const { packageId, name, description, publishPublicKey, remoteRepo, existing } = opts;
|
|
108
|
+
await (0, packages_1.Packages)(dataContext).signAndSave({
|
|
109
|
+
packageId,
|
|
110
|
+
name,
|
|
111
|
+
description: description || existing?.description || "",
|
|
112
|
+
createdBy: existing?.createdBy || system_ids_1.peersRootUserId,
|
|
113
|
+
disabled: existing?.disabled ?? false,
|
|
114
|
+
remoteRepo: remoteRepo || existing?.remoteRepo,
|
|
115
|
+
activePackageVersionId: existing?.activePackageVersionId,
|
|
116
|
+
versionFollowRange: existing?.versionFollowRange,
|
|
117
|
+
followVersionTags: existing?.followVersionTags,
|
|
118
|
+
updateUrl: existing?.updateUrl,
|
|
119
|
+
publishPublicKey: publishPublicKey || existing?.publishPublicKey || "",
|
|
120
|
+
signature: existing?.signature || "",
|
|
121
|
+
}, { restoreIfDeleted: true, saveAsSnapshot: true });
|
|
122
|
+
}
|
|
123
|
+
async function persistPackageLocalPath(dataContext, deps, packageId, location) {
|
|
124
|
+
const pvar = (0, persistent_vars_1.groupDeviceVar)(`packageLocalPath_${packageId}`, {
|
|
125
|
+
defaultValue: deps.packagesRootDir,
|
|
126
|
+
dataContext,
|
|
127
|
+
});
|
|
128
|
+
await pvar.loadingPromise;
|
|
129
|
+
pvar(location);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Import an existing on-disk Peers package directory into the current data context.
|
|
133
|
+
*
|
|
134
|
+
* Validates the manifest packageId, creates/updates the Packages record, persists
|
|
135
|
+
* `packageLocalPath_*`, provisions a signing key when missing, then installs the
|
|
136
|
+
* local dev bundles.
|
|
137
|
+
*/
|
|
138
|
+
async function importExistingPackage(dataContext, deps, opts) {
|
|
139
|
+
const info = await (0, package_installer_1.getPackageInfo)(deps, opts.location);
|
|
140
|
+
if (!info.packageId) {
|
|
141
|
+
throw new Error(`Directory already exists at ${opts.location} but does not contain a valid Peers package`);
|
|
142
|
+
}
|
|
143
|
+
const packageId = info.packageId;
|
|
144
|
+
const existing = await (0, packages_1.Packages)(dataContext).get(packageId);
|
|
145
|
+
const packageName = opts.displayName || info.name || packageId;
|
|
146
|
+
const publishPublicKey = await ensurePackageSigningKey({
|
|
147
|
+
personalContext: opts.personalContext,
|
|
148
|
+
packageId,
|
|
149
|
+
packageName,
|
|
150
|
+
existingPublishPublicKey: existing?.publishPublicKey,
|
|
151
|
+
});
|
|
152
|
+
await registerPackageRecord(dataContext, {
|
|
153
|
+
packageId,
|
|
154
|
+
name: packageName,
|
|
155
|
+
description: info.description || "",
|
|
156
|
+
publishPublicKey,
|
|
157
|
+
remoteRepo: info.remoteRepo,
|
|
158
|
+
existing,
|
|
159
|
+
});
|
|
160
|
+
await persistPackageLocalPath(dataContext, deps, packageId, opts.location);
|
|
161
|
+
const result = await (0, package_installer_1.installPackageFromBundles)(dataContext, deps, packageId, {
|
|
162
|
+
localPath: opts.location,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
packageId,
|
|
166
|
+
localPath: opts.location,
|
|
167
|
+
packageVersion: result.packageVersion,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
45
170
|
/**
|
|
46
171
|
* Create a new Peers package from the package template.
|
|
47
172
|
*
|
|
48
173
|
* Clones the template repo, patches IDs and metadata, runs npm install + build,
|
|
49
174
|
* initializes a fresh git repo, then delegates to {@link installPackageFromBundles}
|
|
50
175
|
* to register the dev bundle in the database.
|
|
176
|
+
*
|
|
177
|
+
* When `location` already exists and contains a valid Peers package manifest,
|
|
178
|
+
* imports that directory instead (see {@link importExistingPackage}).
|
|
51
179
|
*/
|
|
52
180
|
async function createPackage(dataContext, deps, opts) {
|
|
53
181
|
const location = opts.location ?? deps.resolvePath(deps.packagesRootDir, nameToFolderSlug(opts.name));
|
|
54
182
|
// If directory already exists, treat as an import rather than a fresh create
|
|
55
183
|
if (await deps.fs.exists(location)) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
packageId: info.packageId,
|
|
63
|
-
localPath: location,
|
|
64
|
-
packageVersion: result.packageVersion,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
throw new Error(`Directory already exists at ${location} but does not contain a valid Peers package`);
|
|
184
|
+
return importExistingPackage(dataContext, deps, {
|
|
185
|
+
location,
|
|
186
|
+
personalContext: opts.personalContext,
|
|
187
|
+
displayName: opts.name,
|
|
188
|
+
});
|
|
68
189
|
}
|
|
69
190
|
// Generate fresh IDs
|
|
70
191
|
const packageId = (0, utils_1.newid)();
|
|
@@ -110,39 +231,18 @@ async function createPackage(dataContext, deps, opts) {
|
|
|
110
231
|
await deps.shell.exec("git init", { cwd: location });
|
|
111
232
|
await deps.shell.exec("git add .", { cwd: location });
|
|
112
233
|
await deps.shell.exec('git commit -m "Initial commit"', { cwd: location });
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
await (0, persistent_vars_1.PersistentVars)(opts.personalContext).save({
|
|
120
|
-
persistentVarId: (0, utils_1.deterministicPvarId)(keyName),
|
|
121
|
-
name: keyName,
|
|
122
|
-
scope: "user",
|
|
123
|
-
value: { value: keys.secretKey },
|
|
124
|
-
isSecret: true,
|
|
125
|
-
description: `Signing key for package "${opts.name}"`,
|
|
126
|
-
modifiedAt: Date.now(),
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
// Register the package record
|
|
130
|
-
await (0, packages_1.Packages)(dataContext).signAndSave({
|
|
234
|
+
const publishPublicKey = await ensurePackageSigningKey({
|
|
235
|
+
personalContext: opts.personalContext,
|
|
236
|
+
packageId,
|
|
237
|
+
packageName: opts.name,
|
|
238
|
+
});
|
|
239
|
+
await registerPackageRecord(dataContext, {
|
|
131
240
|
packageId,
|
|
132
241
|
name: opts.name,
|
|
133
242
|
description: "",
|
|
134
|
-
createdBy: system_ids_1.peersRootUserId,
|
|
135
|
-
disabled: false,
|
|
136
243
|
publishPublicKey,
|
|
137
|
-
signature: "",
|
|
138
|
-
}, { restoreIfDeleted: true, saveAsSnapshot: true });
|
|
139
|
-
// Set the packageLocalPath device var
|
|
140
|
-
const pvar = (0, persistent_vars_1.groupDeviceVar)(`packageLocalPath_${packageId}`, {
|
|
141
|
-
defaultValue: deps.packagesRootDir,
|
|
142
|
-
dataContext,
|
|
143
244
|
});
|
|
144
|
-
await
|
|
145
|
-
pvar(location);
|
|
245
|
+
await persistPackageLocalPath(dataContext, deps, packageId, location);
|
|
146
246
|
// Install the dev bundle
|
|
147
247
|
const result = await (0, package_installer_1.installPackageFromBundles)(dataContext, deps, packageId, {
|
|
148
248
|
localPath: location,
|
|
@@ -3,17 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const package_creator_1 = require("./package-creator");
|
|
4
4
|
// --- Mocks for SDK internals ---
|
|
5
5
|
const mockSignAndSavePkg = jest.fn();
|
|
6
|
+
const mockGetPkg = jest.fn();
|
|
6
7
|
jest.mock("../data/packages", () => ({
|
|
7
8
|
Packages: () => ({
|
|
8
9
|
signAndSave: mockSignAndSavePkg,
|
|
10
|
+
get: mockGetPkg,
|
|
9
11
|
}),
|
|
10
12
|
}));
|
|
11
13
|
const mockGroupDeviceVar = jest.fn();
|
|
12
14
|
const mockPersistentVarsSave = jest.fn();
|
|
15
|
+
const mockPersistentVarsFindOne = jest.fn();
|
|
13
16
|
jest.mock("../data/persistent-vars", () => ({
|
|
14
17
|
groupDeviceVar: (...args) => mockGroupDeviceVar(...args),
|
|
15
18
|
PersistentVars: () => ({
|
|
16
19
|
save: mockPersistentVarsSave,
|
|
20
|
+
findOne: mockPersistentVarsFindOne,
|
|
17
21
|
}),
|
|
18
22
|
}));
|
|
19
23
|
const mockNewKeys = jest.fn();
|
|
@@ -70,6 +74,13 @@ function mockDataContext() {
|
|
|
70
74
|
// --- Tests ---
|
|
71
75
|
beforeEach(() => {
|
|
72
76
|
jest.clearAllMocks();
|
|
77
|
+
mockGetPkg.mockResolvedValue(undefined);
|
|
78
|
+
mockPersistentVarsFindOne.mockResolvedValue(undefined);
|
|
79
|
+
mockNewKeys.mockReturnValue({
|
|
80
|
+
publicKey: "test-public-key-abc",
|
|
81
|
+
secretKey: "test-secret-key-xyz",
|
|
82
|
+
publicBoxKey: "test-box-key",
|
|
83
|
+
});
|
|
73
84
|
});
|
|
74
85
|
describe("nameToFolderSlug", () => {
|
|
75
86
|
it("converts a simple name", () => {
|
|
@@ -170,16 +181,18 @@ describe("createPackage", () => {
|
|
|
170
181
|
expect(result.localPath).toBe("/packages/my-widget");
|
|
171
182
|
expect(result.packageVersion).toBe(mockPv);
|
|
172
183
|
});
|
|
173
|
-
it("
|
|
184
|
+
it("registers metadata, path, and signing key when importing an existing package directory", async () => {
|
|
174
185
|
const deps = makeMockDeps({});
|
|
175
186
|
const dc = mockDataContext();
|
|
176
|
-
|
|
187
|
+
const personalCtx = mockDataContext();
|
|
177
188
|
deps.fs.exists.mockResolvedValue(true);
|
|
178
|
-
// getPackageInfo returns a valid packageId
|
|
179
189
|
mockGetPackageInfo.mockResolvedValue({
|
|
180
190
|
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
181
191
|
name: "existing-pkg",
|
|
192
|
+
description: "Imported package",
|
|
182
193
|
});
|
|
194
|
+
const pvarSetter = jest.fn();
|
|
195
|
+
mockGroupDeviceVar.mockReturnValue(pvarSetter);
|
|
183
196
|
const mockPv = { packageVersionId: "pv002", packageId: "00mnkoyo7mizav6tseubjzr3a" };
|
|
184
197
|
mockInstallPackageFromBundles.mockResolvedValue({
|
|
185
198
|
packageVersion: mockPv,
|
|
@@ -187,16 +200,67 @@ describe("createPackage", () => {
|
|
|
187
200
|
unchanged: false,
|
|
188
201
|
});
|
|
189
202
|
const result = await (0, package_creator_1.createPackage)(dc, deps, {
|
|
190
|
-
name: "
|
|
203
|
+
name: "Existing Pkg",
|
|
191
204
|
location: "/custom/path",
|
|
205
|
+
personalContext: personalCtx,
|
|
192
206
|
});
|
|
193
|
-
// Should NOT clone
|
|
194
207
|
expect(deps.shell.exec).not.toHaveBeenCalled();
|
|
195
|
-
|
|
208
|
+
expect(mockGetPkg).toHaveBeenCalledWith("00mnkoyo7mizav6tseubjzr3a");
|
|
209
|
+
expect(mockNewKeys).toHaveBeenCalled();
|
|
210
|
+
expect(mockPersistentVarsSave).toHaveBeenCalledWith(expect.objectContaining({
|
|
211
|
+
name: "packageSigningKey_00mnkoyo7mizav6tseubjzr3a",
|
|
212
|
+
isSecret: true,
|
|
213
|
+
value: { value: "test-secret-key-xyz" },
|
|
214
|
+
}));
|
|
215
|
+
expect(mockSignAndSavePkg).toHaveBeenCalledWith(expect.objectContaining({
|
|
216
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
217
|
+
name: "Existing Pkg",
|
|
218
|
+
description: "Imported package",
|
|
219
|
+
publishPublicKey: "test-public-key-abc",
|
|
220
|
+
}), { restoreIfDeleted: true, saveAsSnapshot: true });
|
|
221
|
+
expect(mockGroupDeviceVar).toHaveBeenCalledWith("packageLocalPath_00mnkoyo7mizav6tseubjzr3a", expect.any(Object));
|
|
222
|
+
expect(pvarSetter).toHaveBeenCalledWith("/custom/path");
|
|
196
223
|
expect(mockInstallPackageFromBundles).toHaveBeenCalledWith(dc, deps, "00mnkoyo7mizav6tseubjzr3a", { localPath: "/custom/path" });
|
|
197
224
|
expect(result.packageId).toBe("00mnkoyo7mizav6tseubjzr3a");
|
|
198
225
|
expect(result.localPath).toBe("/custom/path");
|
|
199
226
|
});
|
|
227
|
+
it("reuses an existing publishPublicKey and does not rotate the signing key on re-import", async () => {
|
|
228
|
+
const deps = makeMockDeps({});
|
|
229
|
+
const dc = mockDataContext();
|
|
230
|
+
const personalCtx = mockDataContext();
|
|
231
|
+
deps.fs.exists.mockResolvedValue(true);
|
|
232
|
+
mockGetPackageInfo.mockResolvedValue({
|
|
233
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
234
|
+
name: "existing-pkg",
|
|
235
|
+
});
|
|
236
|
+
mockGetPkg.mockResolvedValue({
|
|
237
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
238
|
+
name: "existing-pkg",
|
|
239
|
+
description: "Old description",
|
|
240
|
+
createdBy: "00m87fbwu96jzf8qnwllly0fd",
|
|
241
|
+
publishPublicKey: "already-established-public-key",
|
|
242
|
+
signature: "sig",
|
|
243
|
+
disabled: false,
|
|
244
|
+
});
|
|
245
|
+
const pvarSetter = jest.fn();
|
|
246
|
+
mockGroupDeviceVar.mockReturnValue(pvarSetter);
|
|
247
|
+
mockInstallPackageFromBundles.mockResolvedValue({
|
|
248
|
+
packageVersion: { packageVersionId: "pv002", packageId: "00mnkoyo7mizav6tseubjzr3a" },
|
|
249
|
+
loaded: undefined,
|
|
250
|
+
unchanged: false,
|
|
251
|
+
});
|
|
252
|
+
await (0, package_creator_1.createPackage)(dc, deps, {
|
|
253
|
+
name: "existing-pkg",
|
|
254
|
+
location: "/custom/path",
|
|
255
|
+
personalContext: personalCtx,
|
|
256
|
+
});
|
|
257
|
+
expect(mockNewKeys).not.toHaveBeenCalled();
|
|
258
|
+
expect(mockPersistentVarsSave).not.toHaveBeenCalled();
|
|
259
|
+
expect(mockSignAndSavePkg).toHaveBeenCalledWith(expect.objectContaining({
|
|
260
|
+
publishPublicKey: "already-established-public-key",
|
|
261
|
+
createdBy: "00m87fbwu96jzf8qnwllly0fd",
|
|
262
|
+
}), expect.any(Object));
|
|
263
|
+
});
|
|
200
264
|
it("throws when directory exists but has no valid package", async () => {
|
|
201
265
|
const deps = makeMockDeps({});
|
|
202
266
|
const dc = mockDataContext();
|
|
@@ -268,11 +332,6 @@ describe("createPackage", () => {
|
|
|
268
332
|
loaded: undefined,
|
|
269
333
|
unchanged: false,
|
|
270
334
|
});
|
|
271
|
-
mockNewKeys.mockReturnValue({
|
|
272
|
-
publicKey: "test-public-key-abc",
|
|
273
|
-
secretKey: "test-secret-key-xyz",
|
|
274
|
-
publicBoxKey: "test-box-key",
|
|
275
|
-
});
|
|
276
335
|
await (0, package_creator_1.createPackage)(dc, deps, {
|
|
277
336
|
name: "Signed Pkg",
|
|
278
337
|
personalContext: personalCtx,
|
|
@@ -319,6 +378,49 @@ describe("createPackage", () => {
|
|
|
319
378
|
}), expect.any(Object));
|
|
320
379
|
});
|
|
321
380
|
});
|
|
381
|
+
describe("ensurePackageSigningKey", () => {
|
|
382
|
+
it("generates a key once when none exists", async () => {
|
|
383
|
+
const personalCtx = mockDataContext();
|
|
384
|
+
const publicKey = await (0, package_creator_1.ensurePackageSigningKey)({
|
|
385
|
+
personalContext: personalCtx,
|
|
386
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
387
|
+
packageName: "Test Pkg",
|
|
388
|
+
});
|
|
389
|
+
expect(publicKey).toBe("test-public-key-abc");
|
|
390
|
+
expect(mockNewKeys).toHaveBeenCalledTimes(1);
|
|
391
|
+
expect(mockPersistentVarsSave).toHaveBeenCalledTimes(1);
|
|
392
|
+
});
|
|
393
|
+
it("reuses an established publishPublicKey without generating a new key", async () => {
|
|
394
|
+
const personalCtx = mockDataContext();
|
|
395
|
+
const publicKey = await (0, package_creator_1.ensurePackageSigningKey)({
|
|
396
|
+
personalContext: personalCtx,
|
|
397
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
398
|
+
packageName: "Test Pkg",
|
|
399
|
+
existingPublishPublicKey: "established-key",
|
|
400
|
+
});
|
|
401
|
+
expect(publicKey).toBe("established-key");
|
|
402
|
+
expect(mockNewKeys).not.toHaveBeenCalled();
|
|
403
|
+
expect(mockPersistentVarsSave).not.toHaveBeenCalled();
|
|
404
|
+
});
|
|
405
|
+
it("throws on an unrecoverable public/private key mismatch", async () => {
|
|
406
|
+
const { newKeys: realNewKeys } = jest.requireActual("../keys");
|
|
407
|
+
const realKeys = realNewKeys();
|
|
408
|
+
const otherKeys = realNewKeys();
|
|
409
|
+
const personalCtx = mockDataContext();
|
|
410
|
+
mockPersistentVarsFindOne.mockResolvedValue({
|
|
411
|
+
name: "packageSigningKey_00mnkoyo7mizav6tseubjzr3a",
|
|
412
|
+
value: { value: realKeys.secretKey },
|
|
413
|
+
isSecret: true,
|
|
414
|
+
});
|
|
415
|
+
await expect((0, package_creator_1.ensurePackageSigningKey)({
|
|
416
|
+
personalContext: personalCtx,
|
|
417
|
+
packageId: "00mnkoyo7mizav6tseubjzr3a",
|
|
418
|
+
packageName: "Test Pkg",
|
|
419
|
+
existingPublishPublicKey: otherKeys.publicKey,
|
|
420
|
+
})).rejects.toThrow("signing key mismatch");
|
|
421
|
+
expect(mockNewKeys).not.toHaveBeenCalled();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
322
424
|
describe("ensureTemplateCache", () => {
|
|
323
425
|
it("clones template when cache directory does not exist", async () => {
|
|
324
426
|
const deps = makeMockDeps({});
|
package/dist/rpc-types.d.ts
CHANGED
|
@@ -174,23 +174,6 @@ export declare const rpcServerCalls: {
|
|
|
174
174
|
success: boolean;
|
|
175
175
|
error?: string;
|
|
176
176
|
}>;
|
|
177
|
-
voiceGetState: () => Promise<{
|
|
178
|
-
state: "disabled" | "idle" | "listening" | "recording" | "processing" | "speaking";
|
|
179
|
-
keyError?: "refused" | "invalid" | "limit" | "throttled";
|
|
180
|
-
}>;
|
|
181
|
-
voiceStartRecording: () => Promise<void>;
|
|
182
|
-
voiceStopRecording: () => Promise<void>;
|
|
183
|
-
voiceCancelRecording: () => Promise<void>;
|
|
184
|
-
voiceSetTargetChannel: (channelId: string) => Promise<void>;
|
|
185
|
-
voiceGetAudioDevices: () => Promise<string[]>;
|
|
186
|
-
voiceTestTTS: (text: string) => Promise<void>;
|
|
187
|
-
voiceNotifyPlaybackComplete: () => Promise<void>;
|
|
188
|
-
voiceStopPlayback: () => Promise<void>;
|
|
189
|
-
voiceGetThreadId: () => Promise<string | null>;
|
|
190
|
-
voiceSetThreadId: (threadId: string | null) => Promise<void>;
|
|
191
|
-
voiceNotifyTextActivity: () => Promise<void>;
|
|
192
|
-
voiceDisable: () => Promise<void>;
|
|
193
|
-
voiceEnable: () => Promise<void>;
|
|
194
177
|
flushDatabases: () => Promise<void>;
|
|
195
178
|
subscribeEvents: (names: string[]) => Promise<void>;
|
|
196
179
|
unsubscribeEvents: (names: string[]) => Promise<void>;
|
package/dist/rpc-types.js
CHANGED
|
@@ -84,21 +84,6 @@ exports.rpcServerCalls = {
|
|
|
84
84
|
uiClick: rpcStub("uiClick"),
|
|
85
85
|
uiSet: rpcStub("uiSet"),
|
|
86
86
|
uiScroll: rpcStub("uiScroll"),
|
|
87
|
-
// Voice control (settings managed via voiceSettings pvar, keys via secret pvars)
|
|
88
|
-
voiceGetState: rpcStub("voiceGetState"),
|
|
89
|
-
voiceStartRecording: rpcStub("voiceStartRecording"),
|
|
90
|
-
voiceStopRecording: rpcStub("voiceStopRecording"),
|
|
91
|
-
voiceCancelRecording: rpcStub("voiceCancelRecording"),
|
|
92
|
-
voiceSetTargetChannel: rpcStub("voiceSetTargetChannel"),
|
|
93
|
-
voiceGetAudioDevices: rpcStub("voiceGetAudioDevices"),
|
|
94
|
-
voiceTestTTS: rpcStub("voiceTestTTS"),
|
|
95
|
-
voiceNotifyPlaybackComplete: rpcStub("voiceNotifyPlaybackComplete"),
|
|
96
|
-
voiceStopPlayback: rpcStub("voiceStopPlayback"),
|
|
97
|
-
voiceGetThreadId: rpcStub("voiceGetThreadId"),
|
|
98
|
-
voiceSetThreadId: rpcStub("voiceSetThreadId"),
|
|
99
|
-
voiceNotifyTextActivity: rpcStub("voiceNotifyTextActivity"),
|
|
100
|
-
voiceDisable: rpcStub("voiceDisable"),
|
|
101
|
-
voiceEnable: rpcStub("voiceEnable"),
|
|
102
87
|
// Flush all in-memory database snapshots to durable storage (IndexedDB in PWA).
|
|
103
88
|
// No-op on Electron where better-sqlite3 writes are synchronous to disk.
|
|
104
89
|
flushDatabases: (async () => { }),
|
package/dist/system-ids.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare const peersRootUserId = "000peers0user000000000001";
|
|
|
4
4
|
export declare const defaultAssistantId = "000peers0bot00000000shell";
|
|
5
5
|
/** Stable ID of the bundled OpenCode assistant. */
|
|
6
6
|
export declare const openCodeAssistantId = "00mlwl2km2opencode0000001";
|
|
7
|
+
/** Stable ID of the built-in Admin assistant. */
|
|
8
|
+
export declare const adminAssistantId = "000peers0bot00000000admin";
|
|
7
9
|
/** Stable ID of the default assistant-runner tool. */
|
|
8
10
|
export declare const defaultAssistantRunnerToolId = "000peers0tool00000runner1";
|
|
9
11
|
/** Stable ID of the built-in send-message tool. */
|
|
@@ -36,3 +38,14 @@ export declare const deviceOperationsContractId = "00mriijkhq94msfe9m2j6quwp";
|
|
|
36
38
|
export declare const networkDiagnosticsContractId = "00ms1yb50w29oi116uwwrg7rl";
|
|
37
39
|
/** Contract id for explicit local network and synchronization control operations. */
|
|
38
40
|
export declare const networkControlContractId = "00ms1yb50yj9w5qbytl69qfht";
|
|
41
|
+
/**
|
|
42
|
+
* Contract id for the local-only tool-access approval control surface.
|
|
43
|
+
* Not available over device-to-device connections.
|
|
44
|
+
*/
|
|
45
|
+
export declare const toolAccessControlContractId = "000peers0toolaccessctl0001";
|
|
46
|
+
/** Stable IDs for tools in System Tool Access Control v1. */
|
|
47
|
+
export declare const toolAccessControlToolIds: {
|
|
48
|
+
readonly approve: "000peers0tacapprove000001";
|
|
49
|
+
readonly reject: "000peers0tacreject0000001";
|
|
50
|
+
readonly listPending: "000peers0taclist000000001";
|
|
51
|
+
};
|