openmates 0.12.0-alpha.27 → 0.12.0-alpha.28
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/{chunk-4S3EN6KA.js → chunk-TTQIZ476.js} +492 -34
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1 -1
- package/package.json +3 -2
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-AXNRPVLE.js";
|
|
5
5
|
|
|
6
6
|
// src/client.ts
|
|
7
|
-
import { randomUUID as
|
|
7
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
8
8
|
import { platform as platform2, release } from "os";
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin, stdout } from "process";
|
|
@@ -2048,6 +2048,163 @@ function buildEmbedShareUrl(origin, embedId, blob) {
|
|
|
2048
2048
|
return `${origin}/share/embed/${embedId}#key=${blob}`;
|
|
2049
2049
|
}
|
|
2050
2050
|
|
|
2051
|
+
// src/connectedAccountImport.ts
|
|
2052
|
+
import { createHash as createHash4, randomUUID as randomUUID2, webcrypto as webcrypto5 } from "crypto";
|
|
2053
|
+
var TRANSFER_PREFIX = "OMCA1.";
|
|
2054
|
+
var SUPPORTED_KDF_ITERATIONS = 1e5;
|
|
2055
|
+
async function decryptConnectedAccountCliTransferPayload(encryptedPayload, passcode) {
|
|
2056
|
+
if (!encryptedPayload.startsWith(TRANSFER_PREFIX)) {
|
|
2057
|
+
throw new Error("Connected account import payload must start with OMCA1.");
|
|
2058
|
+
}
|
|
2059
|
+
if (!passcode.trim()) {
|
|
2060
|
+
throw new Error("A passcode is required to import a connected account.");
|
|
2061
|
+
}
|
|
2062
|
+
const envelope = parseEnvelope(encryptedPayload.slice(TRANSFER_PREFIX.length));
|
|
2063
|
+
if (envelope.version !== 1 || envelope.kdf.name !== "PBKDF2-SHA256" || envelope.kdf.iterations !== SUPPORTED_KDF_ITERATIONS || envelope.cipher.name !== "AES-256-GCM") {
|
|
2064
|
+
throw new Error("Unsupported connected account import payload format.");
|
|
2065
|
+
}
|
|
2066
|
+
try {
|
|
2067
|
+
const key = await deriveTransferKey(passcode, base64UrlToBytes(envelope.kdf.salt), envelope.kdf.iterations);
|
|
2068
|
+
const plaintext = await webcrypto5.subtle.decrypt(
|
|
2069
|
+
{ name: "AES-GCM", iv: toArrayBuffer3(base64UrlToBytes(envelope.cipher.iv)) },
|
|
2070
|
+
key,
|
|
2071
|
+
toArrayBuffer3(base64UrlToBytes(envelope.cipher.text))
|
|
2072
|
+
);
|
|
2073
|
+
return validateTransferPayload(JSON.parse(new TextDecoder().decode(plaintext)));
|
|
2074
|
+
} catch (error) {
|
|
2075
|
+
if (error instanceof Error && error.message.startsWith("Unsupported")) {
|
|
2076
|
+
throw error;
|
|
2077
|
+
}
|
|
2078
|
+
throw new Error("Could not decrypt connected account import payload. Check the passcode and payload.");
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
async function buildEncryptedConnectedAccountImportRow(params) {
|
|
2082
|
+
const accountId = randomUUID2();
|
|
2083
|
+
const providerId = params.payload.provider_id;
|
|
2084
|
+
const appId = normalizeAppId(params.payload.app_id || appIdForProvider(providerId));
|
|
2085
|
+
const capabilities = normalizeCapabilities(params.payload.capabilities);
|
|
2086
|
+
const allowedActions = actionsForCapabilities(capabilities);
|
|
2087
|
+
const scopes = Array.isArray(params.payload.refresh_token_bundle.scopes) ? params.payload.refresh_token_bundle.scopes.filter((item) => typeof item === "string") : [];
|
|
2088
|
+
const label = params.payload.label.trim() || defaultProviderLabel(providerId);
|
|
2089
|
+
const accountRef = params.payload.account_ref?.trim() || accountId;
|
|
2090
|
+
return {
|
|
2091
|
+
id: accountId,
|
|
2092
|
+
hashed_user_id: sha256Hex(params.userId),
|
|
2093
|
+
encrypted_provider_type: await encryptJsonOrString(providerId, params.masterKey),
|
|
2094
|
+
provider_type_hash: sha256Hex(providerId),
|
|
2095
|
+
encrypted_account_label: await encryptJsonOrString(label, params.masterKey),
|
|
2096
|
+
encrypted_refresh_token_bundle: await encryptJsonOrString(params.payload.refresh_token_bundle, params.masterKey),
|
|
2097
|
+
encrypted_capabilities: await encryptJsonOrString(capabilities, params.masterKey),
|
|
2098
|
+
encrypted_app_permissions: await encryptJsonOrString(
|
|
2099
|
+
{
|
|
2100
|
+
app_id: appId,
|
|
2101
|
+
allowed_actions: allowedActions,
|
|
2102
|
+
scopes
|
|
2103
|
+
},
|
|
2104
|
+
params.masterKey
|
|
2105
|
+
),
|
|
2106
|
+
encrypted_account_directory_hint: await encryptJsonOrString(
|
|
2107
|
+
{
|
|
2108
|
+
account_ref: accountRef,
|
|
2109
|
+
label,
|
|
2110
|
+
capabilities,
|
|
2111
|
+
runtime_modes: Object.keys(params.payload.runtime_modes).length ? params.payload.runtime_modes : runtimeModesForActions(allowedActions)
|
|
2112
|
+
},
|
|
2113
|
+
params.masterKey
|
|
2114
|
+
)
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
function parseEnvelope(encodedEnvelope) {
|
|
2118
|
+
try {
|
|
2119
|
+
return JSON.parse(new TextDecoder().decode(base64UrlToBytes(encodedEnvelope)));
|
|
2120
|
+
} catch {
|
|
2121
|
+
throw new Error("Connected account import payload is malformed.");
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
function validateTransferPayload(value) {
|
|
2125
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2126
|
+
throw new Error("Connected account import payload is malformed.");
|
|
2127
|
+
}
|
|
2128
|
+
const payload = value;
|
|
2129
|
+
if (payload.version !== 1) throw new Error("Unsupported connected account import payload format.");
|
|
2130
|
+
if (typeof payload.provider_id !== "string" || !payload.provider_id) throw new Error("Import payload is missing provider_id.");
|
|
2131
|
+
if (typeof payload.app_id !== "string" || !payload.app_id) throw new Error("Import payload is missing app_id.");
|
|
2132
|
+
if (typeof payload.label !== "string") throw new Error("Import payload is missing label.");
|
|
2133
|
+
if (!Array.isArray(payload.capabilities)) throw new Error("Import payload is missing capabilities.");
|
|
2134
|
+
if (!payload.runtime_modes || typeof payload.runtime_modes !== "object" || Array.isArray(payload.runtime_modes)) {
|
|
2135
|
+
throw new Error("Import payload is missing runtime_modes.");
|
|
2136
|
+
}
|
|
2137
|
+
if (!payload.refresh_token_bundle || typeof payload.refresh_token_bundle !== "object" || Array.isArray(payload.refresh_token_bundle) || typeof payload.refresh_token_bundle.refresh_token !== "string" || !payload.refresh_token_bundle.refresh_token) {
|
|
2138
|
+
throw new Error("Import payload is missing refresh token material.");
|
|
2139
|
+
}
|
|
2140
|
+
return payload;
|
|
2141
|
+
}
|
|
2142
|
+
async function deriveTransferKey(passcode, salt, iterations) {
|
|
2143
|
+
const keyMaterial = await webcrypto5.subtle.importKey(
|
|
2144
|
+
"raw",
|
|
2145
|
+
new TextEncoder().encode(passcode),
|
|
2146
|
+
"PBKDF2",
|
|
2147
|
+
false,
|
|
2148
|
+
["deriveKey"]
|
|
2149
|
+
);
|
|
2150
|
+
return webcrypto5.subtle.deriveKey(
|
|
2151
|
+
{
|
|
2152
|
+
name: "PBKDF2",
|
|
2153
|
+
salt: toArrayBuffer3(salt),
|
|
2154
|
+
iterations,
|
|
2155
|
+
hash: "SHA-256"
|
|
2156
|
+
},
|
|
2157
|
+
keyMaterial,
|
|
2158
|
+
{ name: "AES-GCM", length: 256 },
|
|
2159
|
+
false,
|
|
2160
|
+
["decrypt"]
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
async function encryptJsonOrString(value, masterKey) {
|
|
2164
|
+
const plaintext = typeof value === "string" ? value : JSON.stringify(value);
|
|
2165
|
+
return encryptWithAesGcmCombined(plaintext, masterKey);
|
|
2166
|
+
}
|
|
2167
|
+
function normalizeCapabilities(value) {
|
|
2168
|
+
if (!Array.isArray(value)) return ["read"];
|
|
2169
|
+
const capabilities = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
2170
|
+
return capabilities.length ? Array.from(new Set(capabilities)) : ["read"];
|
|
2171
|
+
}
|
|
2172
|
+
function actionsForCapabilities(capabilities) {
|
|
2173
|
+
const actions = /* @__PURE__ */ new Set();
|
|
2174
|
+
for (const capability of capabilities) {
|
|
2175
|
+
if (capability === "read") actions.add("read");
|
|
2176
|
+
if (capability === "write") {
|
|
2177
|
+
actions.add("write");
|
|
2178
|
+
actions.add("update");
|
|
2179
|
+
}
|
|
2180
|
+
if (capability === "delete") actions.add("delete");
|
|
2181
|
+
}
|
|
2182
|
+
return actions.size ? Array.from(actions) : ["read"];
|
|
2183
|
+
}
|
|
2184
|
+
function runtimeModesForActions(actions) {
|
|
2185
|
+
return Object.fromEntries(actions.map((action) => [action, action === "read" ? "allow_automatically" : "always_ask"]));
|
|
2186
|
+
}
|
|
2187
|
+
function normalizeAppId(appId) {
|
|
2188
|
+
return appId === "google_calendar" ? "calendar" : appId;
|
|
2189
|
+
}
|
|
2190
|
+
function appIdForProvider(providerId) {
|
|
2191
|
+
return providerId === "google_calendar" ? "calendar" : providerId;
|
|
2192
|
+
}
|
|
2193
|
+
function defaultProviderLabel(providerId) {
|
|
2194
|
+
return providerId === "google_calendar" ? "Google Calendar" : "Connected account";
|
|
2195
|
+
}
|
|
2196
|
+
function sha256Hex(value) {
|
|
2197
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
2198
|
+
}
|
|
2199
|
+
function base64UrlToBytes(value) {
|
|
2200
|
+
return base64ToBytes(value.replace(/-/g, "+").replace(/_/g, "/"));
|
|
2201
|
+
}
|
|
2202
|
+
function toArrayBuffer3(input) {
|
|
2203
|
+
const output = new ArrayBuffer(input.byteLength);
|
|
2204
|
+
new Uint8Array(output).set(input);
|
|
2205
|
+
return output;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2051
2208
|
// src/client.ts
|
|
2052
2209
|
function normalizeUnixSeconds(value, fallback) {
|
|
2053
2210
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
@@ -2856,11 +3013,11 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
2856
3013
|
}
|
|
2857
3014
|
let anonymousId = loadAnonymousId();
|
|
2858
3015
|
if (!anonymousId) {
|
|
2859
|
-
anonymousId =
|
|
3016
|
+
anonymousId = randomUUID3();
|
|
2860
3017
|
saveAnonymousId(anonymousId);
|
|
2861
3018
|
}
|
|
2862
|
-
const chatId = `anonymous-${
|
|
2863
|
-
const messageId = `anonymous-message-${
|
|
3019
|
+
const chatId = `anonymous-${randomUUID3()}`;
|
|
3020
|
+
const messageId = `anonymous-message-${randomUUID3()}`;
|
|
2864
3021
|
const requestBody = {
|
|
2865
3022
|
anonymous_id: anonymousId,
|
|
2866
3023
|
client_chat_id: chatId,
|
|
@@ -2918,6 +3075,59 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
2918
3075
|
};
|
|
2919
3076
|
});
|
|
2920
3077
|
}
|
|
3078
|
+
async importConnectedAccountFromCliPayload(params) {
|
|
3079
|
+
this.requireSession();
|
|
3080
|
+
const payload = await decryptConnectedAccountCliTransferPayload(params.encryptedPayload, params.passcode);
|
|
3081
|
+
const validation = await this.validateConnectedAccountImportPayload(payload);
|
|
3082
|
+
const user = await this.whoAmI();
|
|
3083
|
+
const userId = typeof user.id === "string" ? user.id : typeof user.user_id === "string" ? user.user_id : "";
|
|
3084
|
+
if (!userId) {
|
|
3085
|
+
throw new Error("Could not resolve current user id for connected account import.");
|
|
3086
|
+
}
|
|
3087
|
+
const row = await buildEncryptedConnectedAccountImportRow({
|
|
3088
|
+
payload,
|
|
3089
|
+
userId,
|
|
3090
|
+
masterKey: this.getMasterKeyBytes()
|
|
3091
|
+
});
|
|
3092
|
+
const stored = await this.createConnectedAccountImportRow(row);
|
|
3093
|
+
return {
|
|
3094
|
+
id: stored.id,
|
|
3095
|
+
providerId: payload.provider_id,
|
|
3096
|
+
appId: payload.app_id,
|
|
3097
|
+
label: payload.label,
|
|
3098
|
+
validation
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
async validateConnectedAccountImportPayload(payload) {
|
|
3102
|
+
this.requireSession();
|
|
3103
|
+
const response = await this.http.post(
|
|
3104
|
+
"/v1/connected-accounts/validate-import",
|
|
3105
|
+
{
|
|
3106
|
+
provider_id: payload.provider_id,
|
|
3107
|
+
app_id: payload.app_id,
|
|
3108
|
+
capabilities: payload.capabilities,
|
|
3109
|
+
refresh_token_envelope: payload.refresh_token_bundle
|
|
3110
|
+
},
|
|
3111
|
+
this.getCliRequestHeaders()
|
|
3112
|
+
);
|
|
3113
|
+
if (!response.ok || response.data.valid !== true) {
|
|
3114
|
+
throw new Error(`Connected account validation failed (HTTP ${response.status})`);
|
|
3115
|
+
}
|
|
3116
|
+
assertNoConnectedAccountSecretLeak(response.data);
|
|
3117
|
+
return response.data;
|
|
3118
|
+
}
|
|
3119
|
+
async createConnectedAccountImportRow(row) {
|
|
3120
|
+
assertNoConnectedAccountSecretLeak(row);
|
|
3121
|
+
const response = await this.http.post(
|
|
3122
|
+
"/v1/connected-accounts",
|
|
3123
|
+
row,
|
|
3124
|
+
this.getCliRequestHeaders()
|
|
3125
|
+
);
|
|
3126
|
+
if (!response.ok || !response.data.id) {
|
|
3127
|
+
throw new Error(`Failed to store connected account import (HTTP ${response.status})`);
|
|
3128
|
+
}
|
|
3129
|
+
return response.data;
|
|
3130
|
+
}
|
|
2921
3131
|
async cancelConnectedAccountAction(params) {
|
|
2922
3132
|
const response = await this.http.post(
|
|
2923
3133
|
`/v1/connected-accounts/actions/${encodeURIComponent(params.actionId)}/cancel`,
|
|
@@ -2994,7 +3204,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
2994
3204
|
pin: pin.trim().toUpperCase(),
|
|
2995
3205
|
token
|
|
2996
3206
|
});
|
|
2997
|
-
const sessionId =
|
|
3207
|
+
const sessionId = randomUUID3();
|
|
2998
3208
|
const login = await this.http.post(
|
|
2999
3209
|
"/v1/auth/login",
|
|
3000
3210
|
{
|
|
@@ -3169,7 +3379,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3169
3379
|
}
|
|
3170
3380
|
const session = {
|
|
3171
3381
|
apiUrl: this.apiUrl,
|
|
3172
|
-
sessionId:
|
|
3382
|
+
sessionId: randomUUID3(),
|
|
3173
3383
|
wsToken: null,
|
|
3174
3384
|
cookies: this.http.getCookieMap(),
|
|
3175
3385
|
masterKeyExportedB64: material.masterKeyB64,
|
|
@@ -3416,8 +3626,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3416
3626
|
);
|
|
3417
3627
|
let msgEmbedIds = [];
|
|
3418
3628
|
if (clientMsgId && cache.embeds.length > 0) {
|
|
3419
|
-
const { createHash:
|
|
3420
|
-
const hashed =
|
|
3629
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
3630
|
+
const hashed = createHash7("sha256").update(clientMsgId).digest("hex");
|
|
3421
3631
|
msgEmbedIds = cache.embeds.filter(
|
|
3422
3632
|
(e) => e.hashed_message_id === hashed && // Only include parent embeds (no parent_embed_id).
|
|
3423
3633
|
// Child embeds inherit the parent's key and are loaded
|
|
@@ -3464,8 +3674,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3464
3674
|
);
|
|
3465
3675
|
}
|
|
3466
3676
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
3467
|
-
const { createHash:
|
|
3468
|
-
const hashedEmbedId =
|
|
3677
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
3678
|
+
const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
|
|
3469
3679
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
3470
3680
|
cache,
|
|
3471
3681
|
masterKey,
|
|
@@ -3573,7 +3783,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3573
3783
|
async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
|
|
3574
3784
|
if (visited.has(embedId)) return null;
|
|
3575
3785
|
visited.add(embedId);
|
|
3576
|
-
const { createHash:
|
|
3786
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
3577
3787
|
const masterKeyEntry = cache.embedKeys.find(
|
|
3578
3788
|
(ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
|
|
3579
3789
|
);
|
|
@@ -3590,7 +3800,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3590
3800
|
if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
|
|
3591
3801
|
const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
|
|
3592
3802
|
const owningChat = cache.chats.find((c) => {
|
|
3593
|
-
const chatHash =
|
|
3803
|
+
const chatHash = createHash7("sha256").update(String(c.details.id ?? "")).digest("hex");
|
|
3594
3804
|
return chatHash === hashedChatId;
|
|
3595
3805
|
});
|
|
3596
3806
|
if (owningChat) {
|
|
@@ -3619,7 +3829,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3619
3829
|
const parentFullId = String(
|
|
3620
3830
|
parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
|
|
3621
3831
|
);
|
|
3622
|
-
const parentHashedId =
|
|
3832
|
+
const parentHashedId = createHash7("sha256").update(parentFullId).digest("hex");
|
|
3623
3833
|
const parentKey = await this.resolveEmbedKey(
|
|
3624
3834
|
cache,
|
|
3625
3835
|
masterKey,
|
|
@@ -3734,7 +3944,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3734
3944
|
async sendMessage(params) {
|
|
3735
3945
|
let chatId;
|
|
3736
3946
|
if (!params.chatId) {
|
|
3737
|
-
chatId =
|
|
3947
|
+
chatId = randomUUID3();
|
|
3738
3948
|
} else if (params.chatId.length < 36) {
|
|
3739
3949
|
const resolved = await this.resolveFullChatId(params.chatId);
|
|
3740
3950
|
if (!resolved) {
|
|
@@ -3764,7 +3974,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3764
3974
|
}
|
|
3765
3975
|
}
|
|
3766
3976
|
const { ws, session } = await this.openWsClient();
|
|
3767
|
-
const messageId =
|
|
3977
|
+
const messageId = randomUUID3();
|
|
3768
3978
|
const createdAt = Math.floor(Date.now() / 1e3);
|
|
3769
3979
|
const isNewChat = !params.chatId;
|
|
3770
3980
|
ws.send("set_active_chat", { chat_id: chatId });
|
|
@@ -4151,7 +4361,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
4151
4361
|
);
|
|
4152
4362
|
const encryptedCategory = category ? await encryptWithAesGcmCombined(category, chatKeyBytes) : void 0;
|
|
4153
4363
|
const encryptedModelName = modelName ? await encryptWithAesGcmCombined(modelName, chatKeyBytes) : void 0;
|
|
4154
|
-
const persistedAssistantMessageId = assistantMessageId ??
|
|
4364
|
+
const persistedAssistantMessageId = assistantMessageId ?? randomUUID3();
|
|
4155
4365
|
ws.send("ai_response_completed", {
|
|
4156
4366
|
chat_id: chatId,
|
|
4157
4367
|
message: {
|
|
@@ -5294,7 +5504,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
5294
5504
|
);
|
|
5295
5505
|
}
|
|
5296
5506
|
}
|
|
5297
|
-
const entryId = params.entryId ??
|
|
5507
|
+
const entryId = params.entryId ?? randomUUID3();
|
|
5298
5508
|
const now = Math.floor(Date.now() / 1e3);
|
|
5299
5509
|
const hashedKey = hashItemKey(params.appId, params.itemType);
|
|
5300
5510
|
const plaintextPayload = {
|
|
@@ -5428,7 +5638,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
5428
5638
|
const session = this.requireSession();
|
|
5429
5639
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
5430
5640
|
const cache = await this.ensureSynced();
|
|
5431
|
-
const { createHash:
|
|
5641
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
5432
5642
|
const embed = cache.embeds.find(
|
|
5433
5643
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
5434
5644
|
);
|
|
@@ -5436,7 +5646,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
5436
5646
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
5437
5647
|
}
|
|
5438
5648
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
5439
|
-
const hashedEmbedId =
|
|
5649
|
+
const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
|
|
5440
5650
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
5441
5651
|
cache,
|
|
5442
5652
|
masterKey,
|
|
@@ -5496,8 +5706,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
5496
5706
|
if (!embed) {
|
|
5497
5707
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
5498
5708
|
}
|
|
5499
|
-
const { createHash:
|
|
5500
|
-
const hashedEmbedId =
|
|
5709
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
5710
|
+
const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
|
|
5501
5711
|
const embedKey = await this.resolveEmbedKey(
|
|
5502
5712
|
cache,
|
|
5503
5713
|
masterKey,
|
|
@@ -5591,8 +5801,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
5591
5801
|
if (!embed) {
|
|
5592
5802
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
5593
5803
|
}
|
|
5594
|
-
const { createHash:
|
|
5595
|
-
const hashedEmbedId =
|
|
5804
|
+
const { createHash: createHash7 } = await import("crypto");
|
|
5805
|
+
const hashedEmbedId = createHash7("sha256").update(embedId).digest("hex");
|
|
5596
5806
|
const embedKey = await this.resolveEmbedKey(
|
|
5597
5807
|
cache,
|
|
5598
5808
|
masterKey,
|
|
@@ -5769,7 +5979,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
5769
5979
|
makeWsClient(session) {
|
|
5770
5980
|
return new OpenMatesWsClient({
|
|
5771
5981
|
apiUrl: session.apiUrl,
|
|
5772
|
-
sessionId:
|
|
5982
|
+
sessionId: randomUUID3(),
|
|
5773
5983
|
wsToken: session.wsToken,
|
|
5774
5984
|
refreshToken: session.cookies.auth_refresh_token ?? null,
|
|
5775
5985
|
// Same User-Agent as login so OS-based device fingerprint hash matches.
|
|
@@ -6929,7 +7139,7 @@ var OutputRedactor = class {
|
|
|
6929
7139
|
import { readFileSync as readFileSync4, statSync, existsSync as existsSync4 } from "fs";
|
|
6930
7140
|
import { basename, extname, resolve as resolve2 } from "path";
|
|
6931
7141
|
import { homedir as homedir4 } from "os";
|
|
6932
|
-
import { createHash as
|
|
7142
|
+
import { createHash as createHash5 } from "crypto";
|
|
6933
7143
|
var MAX_PER_FILE_SIZE = 100 * 1024 * 1024;
|
|
6934
7144
|
var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
6935
7145
|
".pem",
|
|
@@ -7239,7 +7449,7 @@ function processCodeFile(filePath, filename, redactor) {
|
|
|
7239
7449
|
line_count: lineCount
|
|
7240
7450
|
});
|
|
7241
7451
|
const textPreview = `${filename} (${language}, ${lineCount} lines)`;
|
|
7242
|
-
const contentHash =
|
|
7452
|
+
const contentHash = createHash5("sha256").update(content).digest("hex");
|
|
7243
7453
|
const embed = {
|
|
7244
7454
|
embedId,
|
|
7245
7455
|
embedRef,
|
|
@@ -8755,7 +8965,7 @@ function formatTs(ts) {
|
|
|
8755
8965
|
|
|
8756
8966
|
// src/server.ts
|
|
8757
8967
|
import { execSync, spawn as nodeSpawn } from "child_process";
|
|
8758
|
-
import { createHash as
|
|
8968
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
|
|
8759
8969
|
import { chmodSync as chmodSync2, copyFileSync, cpSync, existsSync as existsSync5, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync5, readdirSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
8760
8970
|
import { createInterface as createInterface2 } from "readline";
|
|
8761
8971
|
import { createInterface as createPromptInterface } from "readline/promises";
|
|
@@ -9321,7 +9531,7 @@ function packagedCaddyTemplatePath(role) {
|
|
|
9321
9531
|
}
|
|
9322
9532
|
function fileHash(path) {
|
|
9323
9533
|
if (!existsSync5(path)) return null;
|
|
9324
|
-
return
|
|
9534
|
+
return createHash6("sha256").update(readFileSync5(path)).digest("hex");
|
|
9325
9535
|
}
|
|
9326
9536
|
async function loadSelfHostComposeTemplate(templateRef, role) {
|
|
9327
9537
|
const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
|
|
@@ -9597,7 +9807,7 @@ function writeChecksums(rootDir) {
|
|
|
9597
9807
|
}
|
|
9598
9808
|
if (entry.name === "checksums.sha256") continue;
|
|
9599
9809
|
const relative2 = path.slice(rootDir.length + 1);
|
|
9600
|
-
const hash =
|
|
9810
|
+
const hash = createHash6("sha256").update(readFileSync5(path)).digest("hex");
|
|
9601
9811
|
lines.push(`${hash} ${relative2}`);
|
|
9602
9812
|
}
|
|
9603
9813
|
};
|
|
@@ -9619,7 +9829,7 @@ function verifyChecksums(rootDir) {
|
|
|
9619
9829
|
}
|
|
9620
9830
|
const filePath = join3(rootDir, relative2);
|
|
9621
9831
|
if (!existsSync5(filePath)) throw new Error(`Backup archive is missing checksummed file: ${relative2}`);
|
|
9622
|
-
const actual =
|
|
9832
|
+
const actual = createHash6("sha256").update(readFileSync5(filePath)).digest("hex");
|
|
9623
9833
|
if (actual !== match[1]) throw new Error(`Backup checksum mismatch for ${relative2}.`);
|
|
9624
9834
|
}
|
|
9625
9835
|
}
|
|
@@ -24990,6 +25200,117 @@ var berlinMorningBikeForecastChat = {
|
|
|
24990
25200
|
}
|
|
24991
25201
|
};
|
|
24992
25202
|
|
|
25203
|
+
// ../ui/src/demo_chats/data/example_chats/privacy-first-product-launch-mind-map.ts
|
|
25204
|
+
var privacyFirstProductLaunchMindMapChat = {
|
|
25205
|
+
chat_id: "example-privacy-first-product-launch",
|
|
25206
|
+
slug: "privacy-first-product-launch-mind-map",
|
|
25207
|
+
title: "example_chats.privacy_first_product_launch_mind_map.title",
|
|
25208
|
+
summary: "example_chats.privacy_first_product_launch_mind_map.summary",
|
|
25209
|
+
icon: "workflow",
|
|
25210
|
+
category: "design",
|
|
25211
|
+
keywords: ["mind map", "privacy-first product launch", "product planning", "privacy UX", "launch strategy"],
|
|
25212
|
+
follow_up_suggestions: ["example_chats.privacy_first_product_launch_mind_map.follow_up_1", "example_chats.privacy_first_product_launch_mind_map.follow_up_2", "example_chats.privacy_first_product_launch_mind_map.follow_up_3", "example_chats.privacy_first_product_launch_mind_map.follow_up_4", "example_chats.privacy_first_product_launch_mind_map.follow_up_5", "example_chats.privacy_first_product_launch_mind_map.follow_up_6"],
|
|
25213
|
+
messages: [
|
|
25214
|
+
{
|
|
25215
|
+
"id": "15e39975-bc0e-4184-bf14-473d87131a6a",
|
|
25216
|
+
"role": "user",
|
|
25217
|
+
"content": "example_chats.privacy_first_product_launch_mind_map.message_1",
|
|
25218
|
+
"created_at": 1782037289
|
|
25219
|
+
},
|
|
25220
|
+
{
|
|
25221
|
+
"id": "d07c4f4f-76b2-4e71-a863-9f8baf0a359e",
|
|
25222
|
+
"role": "assistant",
|
|
25223
|
+
"content": "example_chats.privacy_first_product_launch_mind_map.message_2",
|
|
25224
|
+
"created_at": 1782037316,
|
|
25225
|
+
"user_message_id": "15e39975-bc0e-4184-bf14-473d87131a6a",
|
|
25226
|
+
"category": "design",
|
|
25227
|
+
"model_name": "Gemini 3.1 Pro"
|
|
25228
|
+
}
|
|
25229
|
+
],
|
|
25230
|
+
embeds: [
|
|
25231
|
+
{
|
|
25232
|
+
"embed_id": "f9540460-1fce-441a-9abc-f627efaf1c0f",
|
|
25233
|
+
"type": "mindmap",
|
|
25234
|
+
"content": `type: mindmap
|
|
25235
|
+
app_id: mindmaps
|
|
25236
|
+
skill_id: mindmap
|
|
25237
|
+
title: Privacy-First Product Launch Planning
|
|
25238
|
+
source_json: "{\\n \\"openmatesType\\": \\"mindmap\\",\\n \\"schemaVersion\\": 1,\\n \\"title\\": \\"Privacy-First Product Launch Planning\\",\\n \\"rootId\\": \\"root\\",\\n \\"nodes\\": [\\n {\\n \\"id\\": \\"root\\",\\n \\"label\\": \\"Privacy-First Product Launch\\",\\n \\"children\\": [\\n \\"prod-eng\\",\\n \\"legal\\",\\n \\"ux\\",\\n \\"marketing\\",\\n \\"logistics\\"\\n ]\\n },\\n {\\n \\"id\\": \\"prod-eng\\",\\n \\"label\\": \\"Product & Engineering\\",\\n \\"children\\": [\\n \\"pe-data\\",\\n \\"pe-e2e\\",\\n \\"pe-audit\\"\\n ]\\n },\\n {\\n \\"id\\": \\"pe-data\\",\\n \\"label\\": \\"Data Minimization Specs\\"\\n },\\n {\\n \\"id\\": \\"pe-e2e\\",\\n \\"label\\": \\"End-to-End Encryption\\"\\n },\\n {\\n \\"id\\": \\"pe-audit\\",\\n \\"label\\": \\"3rd-Party Security Audits\\"\\n },\\n {\\n \\"id\\": \\"legal\\",\\n \\"label\\": \\"Legal & Compliance\\",\\n \\"children\\": [\\n \\"leg-gdpr\\",\\n \\"leg-policy\\",\\n \\"leg-tos\\"\\n ]\\n },\\n {\\n \\"id\\": \\"leg-gdpr\\",\\n \\"label\\": \\"GDPR / CCPA Readiness\\"\\n },\\n {\\n \\"id\\": \\"leg-policy\\",\\n \\"label\\": \\"Plain-English Privacy Policy\\"\\n },\\n {\\n \\"id\\": \\"leg-tos\\",\\n \\"label\\": \\"Transparent Terms of Service\\"\\n },\\n {\\n \\"id\\": \\"ux\\",\\n \\"label\\": \\"User Experience (UX)\\",\\n \\"children\\": [\\n \\"ux-consent\\",\\n \\"ux-dash\\",\\n \\"ux-delete\\"\\n ]\\n },\\n {\\n \\"id\\": \\"ux-consent\\",\\n \\"label\\": \\"Clear Consent Flows (No Dark Patterns)\\"\\n },\\n {\\n \\"id\\": \\"ux-dash\\",\\n \\"label\\": \\"Centralized Privacy Dashboard\\"\\n },\\n {\\n \\"id\\": \\"ux-delete\\",\\n \\"label\\": \\"1-Click Account Deletion\\"\\n },\\n {\\n \\"id\\": \\"marketing\\",\\n \\"label\\": \\"Marketing & Messaging\\",\\n \\"children\\": [\\n \\"mkt-core\\",\\n \\"mkt-press\\",\\n \\"mkt-advocates\\"\\n ]\\n },\\n {\\n \\"id\\": \\"mkt-core\\",\\n \\"label\\": \\"Core Message: 'Your Data is Yours'\\"\\n },\\n {\\n \\"id\\": \\"mkt-press\\",\\n \\"label\\": \\"Pitching Privacy Tech Journalists\\"\\n },\\n {\\n \\"id\\": \\"mkt-advocates\\",\\n \\"label\\": \\"Partnering w/ Privacy Advocates\\"\\n },\\n {\\n \\"id\\": \\"logistics\\",\\n \\"label\\": \\"Launch Logistics\\",\\n \\"children\\": [\\n \\"log-beta\\",\\n \\"log-rollout\\"\\n ]\\n },\\n {\\n \\"id\\": \\"log-beta\\",\\n \\"label\\": \\"Closed Beta & Bug Bounty\\"\\n },\\n {\\n \\"id\\": \\"log-rollout\\",\\n \\"label\\": \\"Phased Public Rollout\\"\\n }\\n ],\\n \\"edges\\": [],\\n \\"view\\": {\\n \\"layout\\": \\"radial-tree\\",\\n \\"collapsedNodeIds\\": []\\n }\\n}"
|
|
25239
|
+
model:
|
|
25240
|
+
openmatesType: mindmap
|
|
25241
|
+
schemaVersion: 1
|
|
25242
|
+
title: Privacy-First Product Launch Planning
|
|
25243
|
+
rootId: root
|
|
25244
|
+
nodes[20]:
|
|
25245
|
+
- id: root
|
|
25246
|
+
label: Privacy-First Product Launch
|
|
25247
|
+
children[5]: prod-eng,legal,ux,marketing,logistics
|
|
25248
|
+
- id: prod-eng
|
|
25249
|
+
label: Product & Engineering
|
|
25250
|
+
children[3]: pe-data,pe-e2e,pe-audit
|
|
25251
|
+
- id: pe-data
|
|
25252
|
+
label: Data Minimization Specs
|
|
25253
|
+
- id: pe-e2e
|
|
25254
|
+
label: End-to-End Encryption
|
|
25255
|
+
- id: pe-audit
|
|
25256
|
+
label: 3rd-Party Security Audits
|
|
25257
|
+
- id: legal
|
|
25258
|
+
label: Legal & Compliance
|
|
25259
|
+
children[3]: leg-gdpr,leg-policy,leg-tos
|
|
25260
|
+
- id: leg-gdpr
|
|
25261
|
+
label: GDPR / CCPA Readiness
|
|
25262
|
+
- id: leg-policy
|
|
25263
|
+
label: Plain-English Privacy Policy
|
|
25264
|
+
- id: leg-tos
|
|
25265
|
+
label: Transparent Terms of Service
|
|
25266
|
+
- id: ux
|
|
25267
|
+
label: User Experience (UX)
|
|
25268
|
+
children[3]: ux-consent,ux-dash,ux-delete
|
|
25269
|
+
- id: ux-consent
|
|
25270
|
+
label: Clear Consent Flows (No Dark Patterns)
|
|
25271
|
+
- id: ux-dash
|
|
25272
|
+
label: Centralized Privacy Dashboard
|
|
25273
|
+
- id: ux-delete
|
|
25274
|
+
label: 1-Click Account Deletion
|
|
25275
|
+
- id: marketing
|
|
25276
|
+
label: Marketing & Messaging
|
|
25277
|
+
children[3]: mkt-core,mkt-press,mkt-advocates
|
|
25278
|
+
- id: mkt-core
|
|
25279
|
+
label: "Core Message: 'Your Data is Yours'"
|
|
25280
|
+
- id: mkt-press
|
|
25281
|
+
label: Pitching Privacy Tech Journalists
|
|
25282
|
+
- id: mkt-advocates
|
|
25283
|
+
label: Partnering w/ Privacy Advocates
|
|
25284
|
+
- id: logistics
|
|
25285
|
+
label: Launch Logistics
|
|
25286
|
+
children[2]: log-beta,log-rollout
|
|
25287
|
+
- id: log-beta
|
|
25288
|
+
label: Closed Beta & Bug Bounty
|
|
25289
|
+
- id: log-rollout
|
|
25290
|
+
label: Phased Public Rollout
|
|
25291
|
+
edges[0]:
|
|
25292
|
+
view:
|
|
25293
|
+
layout: radial-tree
|
|
25294
|
+
collapsedNodeIds[0]:
|
|
25295
|
+
embed_ref: mind-map-f95404
|
|
25296
|
+
status: finished
|
|
25297
|
+
node_count: 20
|
|
25298
|
+
edge_count: 0
|
|
25299
|
+
validation:
|
|
25300
|
+
status: valid
|
|
25301
|
+
warnings[0]:
|
|
25302
|
+
parse_error: null
|
|
25303
|
+
version_number: 1`,
|
|
25304
|
+
"parent_embed_id": null,
|
|
25305
|
+
"embed_ids": null
|
|
25306
|
+
}
|
|
25307
|
+
],
|
|
25308
|
+
metadata: {
|
|
25309
|
+
featured: true,
|
|
25310
|
+
order: 105
|
|
25311
|
+
}
|
|
25312
|
+
};
|
|
25313
|
+
|
|
24993
25314
|
// ../ui/src/demo_chats/exampleChatData.ts
|
|
24994
25315
|
var ALL_EXAMPLE_CHATS = [
|
|
24995
25316
|
giganticAirplanesChat,
|
|
@@ -25077,7 +25398,8 @@ var ALL_EXAMPLE_CHATS = [
|
|
|
25077
25398
|
rostockHeavyRainRadarChat,
|
|
25078
25399
|
classicCarReverseImageSearchChat,
|
|
25079
25400
|
usbC3v3LdoPcbSchematicChat,
|
|
25080
|
-
berlinMorningBikeForecastChat
|
|
25401
|
+
berlinMorningBikeForecastChat,
|
|
25402
|
+
privacyFirstProductLaunchMindMapChat
|
|
25081
25403
|
].sort((a, b) => a.metadata.order - b.metadata.order);
|
|
25082
25404
|
|
|
25083
25405
|
// ../ui/src/i18n/locales/en.json
|
|
@@ -27740,6 +28062,21 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
27740
28062
|
},
|
|
27741
28063
|
learn_anything: {
|
|
27742
28064
|
text: "learn anything"
|
|
28065
|
+
},
|
|
28066
|
+
plan_trips: {
|
|
28067
|
+
text: "plan trips"
|
|
28068
|
+
},
|
|
28069
|
+
create_images: {
|
|
28070
|
+
text: "create images"
|
|
28071
|
+
},
|
|
28072
|
+
follow_news: {
|
|
28073
|
+
text: "follow news"
|
|
28074
|
+
},
|
|
28075
|
+
stay_healthy: {
|
|
28076
|
+
text: "stay healthy"
|
|
28077
|
+
},
|
|
28078
|
+
write_better: {
|
|
28079
|
+
text: "write better"
|
|
27743
28080
|
}
|
|
27744
28081
|
},
|
|
27745
28082
|
new_chat: {
|
|
@@ -33210,6 +33547,38 @@ Like [Denis Villeneuve](wiki:Denis_Villeneuve)\u2019s work, *Contact* avoids the
|
|
|
33210
33547
|
text: '```json\n{"type": "app_skill_use", "embed_id": "54a8d949-0a13-463f-bc6a-958e1f746f79", "app_id": "pdf", "skill_id": "view", "query": "Please describe the visual layout of this page, including the title and the structure of any bulleted lists."}\n```\n\n_\nThe first page of sample.pdf features a clean, professional layout with the following structure:\n\n### **Visual Layout Overview**\n* **Title:** The document begins with a prominent, bold heading: **"OpenMates PDF Test Document"**.\n* **Subheading:** Immediately below the title is a line indicating the page and section: "Page 1 of 2 \u2014 Introduction".\n* **Introductory Paragraph:** A brief paragraph explains that the document is intended for testing PDF upload and AI reading features.\n* **Bullet List Structure:** \n * The list is introduced by the phrase "Key facts on this page:".\n * It consists of **three bullet points**, each marked with a standard black circular bullet (\u2022).\n * The bullets highlight the document title, a "secret word" (ALPHA), and the nature of the text on the page.\n* **Closing Text:** A final sentence notes that an AI assistant should be able to read the content and identify the secret word.\n\nWould you like me to [read the text](/#settings/apps/pdf) from page 2 or [search for specific keywords](/#settings/apps/pdf) within the document?'
|
|
33211
33548
|
}
|
|
33212
33549
|
},
|
|
33550
|
+
privacy_first_product_launch_mind_map: {
|
|
33551
|
+
title: {
|
|
33552
|
+
text: "Privacy-First Product Launch Mind Map"
|
|
33553
|
+
},
|
|
33554
|
+
summary: {
|
|
33555
|
+
text: "A structured mind map for planning a privacy-first product launch across product, legal, marketing, UX, and logistics."
|
|
33556
|
+
},
|
|
33557
|
+
message_1: {
|
|
33558
|
+
text: "Create a visual mind map artifact for planning a privacy-first product launch. Use a central idea with clear branches and sub-branches."
|
|
33559
|
+
},
|
|
33560
|
+
message_2: {
|
|
33561
|
+
text: 'Here is a visual mind map to help you organize and plan your privacy-first product launch. I\'ve broken it down into key areas: Product/Engineering, Legal, Marketing, UX, and Logistics. \n\n```json\n{"type": "mindmap", "embed_id": "f9540460-1fce-441a-9abc-f627efaf1c0f"}\n```\n\n### Design Notes for a Privacy-First Launch\nSince privacy is deeply connected to user trust, your UI and UX design choices will be just as important as your backend engineering. When you\'re designing the interface (especially the **UX** branch of this map), keep these principles in mind:\n* **Visual Transparency:** Avoid hiding data toggles in complex sub-menus. A clean, spacious privacy dashboard gives users a sense of control. \n* **Plain Language Design:** Pair your "Plain-English Privacy Policy" with simple iconography. E.g., an open padlock vs. a closed padlock.\n* **Combine AI and Your Own Craft:** If you need placeholder icons, UI mockups, or wireframes for this launch, you can sketch them out on paper or in a simple tool, upload them here, and we can use AI to refine those rough concepts together. It\u2019s a great way to keep the personal touch while moving quickly!\n\nWhich area of the launch map feels like the biggest hurdle for you right now? I\'d be happy to help you brainstorm marketing assets or sketch out some UI flows for your consent screens!'
|
|
33562
|
+
},
|
|
33563
|
+
follow_up_1: {
|
|
33564
|
+
text: "Search for upcoming privacy-focused product launch events"
|
|
33565
|
+
},
|
|
33566
|
+
follow_up_2: {
|
|
33567
|
+
text: "Explain the importance of visual transparency in UI design"
|
|
33568
|
+
},
|
|
33569
|
+
follow_up_3: {
|
|
33570
|
+
text: "Find tutorials on designing privacy dashboards"
|
|
33571
|
+
},
|
|
33572
|
+
follow_up_4: {
|
|
33573
|
+
text: "List key principles for a privacy-first product launch"
|
|
33574
|
+
},
|
|
33575
|
+
follow_up_5: {
|
|
33576
|
+
text: "Generate mockups for a clean privacy dashboard"
|
|
33577
|
+
},
|
|
33578
|
+
follow_up_6: {
|
|
33579
|
+
text: "Help me brainstorm marketing assets for a privacy-focused product"
|
|
33580
|
+
}
|
|
33581
|
+
},
|
|
33213
33582
|
privacy_website_hero_background: {
|
|
33214
33583
|
title: {
|
|
33215
33584
|
text: "Privacy Website Hero Background"
|
|
@@ -40105,6 +40474,33 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
40105
40474
|
},
|
|
40106
40475
|
finalize_error: {
|
|
40107
40476
|
text: "Could not finish updating connected account."
|
|
40477
|
+
},
|
|
40478
|
+
cli_export_title: {
|
|
40479
|
+
text: "CLI test-account import"
|
|
40480
|
+
},
|
|
40481
|
+
cli_export_warning: {
|
|
40482
|
+
text: "This is for testing only. Anyone with the generated command and passcode can add this provider account to another OpenMates account."
|
|
40483
|
+
},
|
|
40484
|
+
cli_export_passcode_placeholder: {
|
|
40485
|
+
text: "Passcode for the CLI import"
|
|
40486
|
+
},
|
|
40487
|
+
cli_export_passcode_required: {
|
|
40488
|
+
text: "Enter a passcode before generating the CLI import command."
|
|
40489
|
+
},
|
|
40490
|
+
cli_export_button: {
|
|
40491
|
+
text: "Generate CLI import command"
|
|
40492
|
+
},
|
|
40493
|
+
cli_export_command_label: {
|
|
40494
|
+
text: "Generated CLI import command"
|
|
40495
|
+
},
|
|
40496
|
+
cli_export_copied: {
|
|
40497
|
+
text: "CLI import command copied. Paste it while logged into the test account in OpenMates CLI."
|
|
40498
|
+
},
|
|
40499
|
+
cli_export_generated: {
|
|
40500
|
+
text: "CLI import command generated. Copy it and paste it while logged into the test account in OpenMates CLI."
|
|
40501
|
+
},
|
|
40502
|
+
cli_export_error: {
|
|
40503
|
+
text: "Could not generate the CLI import command."
|
|
40108
40504
|
}
|
|
40109
40505
|
},
|
|
40110
40506
|
nearby_by_default: {
|
|
@@ -43277,7 +43673,7 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
43277
43673
|
}
|
|
43278
43674
|
|
|
43279
43675
|
// src/benchmark.ts
|
|
43280
|
-
import { randomUUID as
|
|
43676
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
43281
43677
|
import { existsSync as existsSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
43282
43678
|
import { tmpdir } from "os";
|
|
43283
43679
|
import { dirname as dirname2, join as join4, resolve as resolve5 } from "path";
|
|
@@ -43577,7 +43973,7 @@ async function handleBenchmark(client, subcommand, rest, flags) {
|
|
|
43577
43973
|
const caseIds = parseCaseIds(flags.case);
|
|
43578
43974
|
const dryRun = flags["dry-run"] === true;
|
|
43579
43975
|
const output = typeof flags.output === "string" ? flags.output : void 0;
|
|
43580
|
-
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] :
|
|
43976
|
+
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] : randomUUID4();
|
|
43581
43977
|
const imagePath = typeof flags.image === "string" ? resolve5(flags.image) : defaultImageFixturePath();
|
|
43582
43978
|
if (!dryRun && flags["confirm-spend-credits"] !== true) {
|
|
43583
43979
|
throw new Error(
|
|
@@ -43987,7 +44383,7 @@ function buildLongContextHistory() {
|
|
|
43987
44383
|
}
|
|
43988
44384
|
function appendHistory(history, role, content) {
|
|
43989
44385
|
history.push({
|
|
43990
|
-
message_id:
|
|
44386
|
+
message_id: randomUUID4(),
|
|
43991
44387
|
role,
|
|
43992
44388
|
sender_name: role === "user" ? "User" : "Assistant",
|
|
43993
44389
|
content,
|
|
@@ -44326,6 +44722,10 @@ async function main() {
|
|
|
44326
44722
|
printSettingsHelp(client);
|
|
44327
44723
|
return;
|
|
44328
44724
|
}
|
|
44725
|
+
if (command === "connected-accounts") {
|
|
44726
|
+
printConnectedAccountsHelp();
|
|
44727
|
+
return;
|
|
44728
|
+
}
|
|
44329
44729
|
if (command === "learning-mode") {
|
|
44330
44730
|
printLearningModeHelp();
|
|
44331
44731
|
return;
|
|
@@ -44428,6 +44828,10 @@ async function main() {
|
|
|
44428
44828
|
await handleSettings(client, subcommand, rest, parsed.flags);
|
|
44429
44829
|
return;
|
|
44430
44830
|
}
|
|
44831
|
+
if (command === "connected-accounts") {
|
|
44832
|
+
await handleConnectedAccounts(client, subcommand, parsed.flags);
|
|
44833
|
+
return;
|
|
44834
|
+
}
|
|
44431
44835
|
if (command === "learning-mode") {
|
|
44432
44836
|
await handleLearningMode(client, subcommand, parsed.flags);
|
|
44433
44837
|
return;
|
|
@@ -46152,6 +46556,43 @@ async function promptSecret(question) {
|
|
|
46152
46556
|
stdin2.on("data", onData);
|
|
46153
46557
|
});
|
|
46154
46558
|
}
|
|
46559
|
+
async function handleConnectedAccounts(client, subcommand, flags) {
|
|
46560
|
+
if (!subcommand || subcommand === "help" || flags.help === true) {
|
|
46561
|
+
printConnectedAccountsHelp();
|
|
46562
|
+
return;
|
|
46563
|
+
}
|
|
46564
|
+
if (subcommand !== "import") {
|
|
46565
|
+
throw new Error(`Unknown connected-accounts command '${subcommand}'. Run 'openmates connected-accounts --help'.`);
|
|
46566
|
+
}
|
|
46567
|
+
if (flags.passcode !== void 0) {
|
|
46568
|
+
throw new Error("Connected account import passcodes must be entered through the hidden prompt, not a command-line flag.");
|
|
46569
|
+
}
|
|
46570
|
+
const payload = typeof flags.payload === "string" ? flags.payload.trim() : "";
|
|
46571
|
+
if (!payload) {
|
|
46572
|
+
throw new Error("Missing --payload. Paste the command generated by connected account settings.");
|
|
46573
|
+
}
|
|
46574
|
+
if (!client.hasSession()) {
|
|
46575
|
+
throw new Error("Not logged in. Run `openmates login` before importing a connected account.");
|
|
46576
|
+
}
|
|
46577
|
+
const passcode = await promptSecret("Connected account import passcode: ");
|
|
46578
|
+
const result = await client.importConnectedAccountFromCliPayload({
|
|
46579
|
+
encryptedPayload: payload,
|
|
46580
|
+
passcode
|
|
46581
|
+
});
|
|
46582
|
+
if (flags.json === true) {
|
|
46583
|
+
printJson2({
|
|
46584
|
+
id: result.id,
|
|
46585
|
+
provider_id: result.providerId,
|
|
46586
|
+
app_id: result.appId,
|
|
46587
|
+
validation: result.validation
|
|
46588
|
+
});
|
|
46589
|
+
return;
|
|
46590
|
+
}
|
|
46591
|
+
console.log("Connected account imported.");
|
|
46592
|
+
console.log(`Provider: ${result.providerId}`);
|
|
46593
|
+
console.log(`App: ${result.appId}`);
|
|
46594
|
+
console.log("Validation: harmless read succeeded");
|
|
46595
|
+
}
|
|
46155
46596
|
async function writeSecretFile(filePath, content, force = false) {
|
|
46156
46597
|
const { mkdir, writeFile, stat: stat2 } = await import("fs/promises");
|
|
46157
46598
|
const { dirname: dirname4 } = await import("path");
|
|
@@ -48891,6 +49332,7 @@ Commands:
|
|
|
48891
49332
|
openmates mentions [--help] List available @mentions
|
|
48892
49333
|
openmates embeds [--help] Embed commands (show)
|
|
48893
49334
|
openmates settings [--help] Predefined settings commands
|
|
49335
|
+
openmates connected-accounts [--help] Connected account import helpers
|
|
48894
49336
|
openmates learning-mode [--help] Account-wide Learning Mode controls
|
|
48895
49337
|
openmates inspirations [--lang <code>] [--json] Daily inspirations
|
|
48896
49338
|
openmates newchatsuggestions [--limit <n>] [--json] Personalized new chat suggestions
|
|
@@ -48906,6 +49348,22 @@ Flags:
|
|
|
48906
49348
|
--api-key <key> Optional API key override (or set OPENMATES_API_KEY)
|
|
48907
49349
|
--help Show contextual help for any command`);
|
|
48908
49350
|
}
|
|
49351
|
+
function printConnectedAccountsHelp() {
|
|
49352
|
+
console.log(`Connected account commands:
|
|
49353
|
+
openmates connected-accounts import --payload <OMCA1...> [--json]
|
|
49354
|
+
|
|
49355
|
+
Imports one passcode-protected connected account generated from web settings.
|
|
49356
|
+
The CLI prompts for the passcode interactively, validates the provider token with
|
|
49357
|
+
a harmless read, then re-encrypts the account for the currently logged-in CLI
|
|
49358
|
+
account before storing it.
|
|
49359
|
+
|
|
49360
|
+
Options:
|
|
49361
|
+
--payload <OMCA1...> Required encrypted import payload from web settings
|
|
49362
|
+
--json Output a redacted JSON summary
|
|
49363
|
+
|
|
49364
|
+
Security:
|
|
49365
|
+
Do not pass the passcode as a flag. It is always entered through a hidden prompt.`);
|
|
49366
|
+
}
|
|
48909
49367
|
function printFeedbackHelp() {
|
|
48910
49368
|
console.log(`Feedback commands:
|
|
48911
49369
|
openmates feedback assistant-response --rating <1-5> [--json]
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -209,6 +209,18 @@ interface EmbedKeyWrapper {
|
|
|
209
209
|
*/
|
|
210
210
|
type ShareDuration = 0 | 60 | 3600 | 86400 | 604800 | 1209600 | 2592000 | 7776000;
|
|
211
211
|
|
|
212
|
+
interface ConnectedAccountCliTransferPayload {
|
|
213
|
+
version: 1;
|
|
214
|
+
provider_id: string;
|
|
215
|
+
app_id: string;
|
|
216
|
+
label: string;
|
|
217
|
+
account_ref?: string;
|
|
218
|
+
capabilities: string[];
|
|
219
|
+
runtime_modes: Record<string, string>;
|
|
220
|
+
refresh_token_bundle: Record<string, unknown>;
|
|
221
|
+
created_at: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
212
224
|
interface CliSubChatRequest {
|
|
213
225
|
id?: string;
|
|
214
226
|
chat_id?: string;
|
|
@@ -249,6 +261,19 @@ interface ConnectedAccountTurnTokenRef {
|
|
|
249
261
|
action_scope?: Record<string, unknown>;
|
|
250
262
|
expires_at: number;
|
|
251
263
|
}
|
|
264
|
+
interface ConnectedAccountImportValidationResult {
|
|
265
|
+
valid: boolean;
|
|
266
|
+
provider_id: string;
|
|
267
|
+
app_id: string;
|
|
268
|
+
checked_at: number;
|
|
269
|
+
}
|
|
270
|
+
interface ConnectedAccountImportResult {
|
|
271
|
+
id: string;
|
|
272
|
+
providerId: string;
|
|
273
|
+
appId: string;
|
|
274
|
+
label: string;
|
|
275
|
+
validation: ConnectedAccountImportValidationResult;
|
|
276
|
+
}
|
|
252
277
|
type LearningModeAgeGroup = "under_10" | "10_12" | "13_15" | "16_18" | "adult";
|
|
253
278
|
interface LearningModeStatus {
|
|
254
279
|
enabled: boolean;
|
|
@@ -565,6 +590,12 @@ declare class OpenMatesClient {
|
|
|
565
590
|
messageId: string;
|
|
566
591
|
refs: ConnectedAccountTurnTokenRefInput[];
|
|
567
592
|
}): Promise<ConnectedAccountTurnTokenRef[]>;
|
|
593
|
+
importConnectedAccountFromCliPayload(params: {
|
|
594
|
+
encryptedPayload: string;
|
|
595
|
+
passcode: string;
|
|
596
|
+
}): Promise<ConnectedAccountImportResult>;
|
|
597
|
+
validateConnectedAccountImportPayload(payload: ConnectedAccountCliTransferPayload): Promise<ConnectedAccountImportValidationResult>;
|
|
598
|
+
private createConnectedAccountImportRow;
|
|
568
599
|
cancelConnectedAccountAction(params: {
|
|
569
600
|
actionId: string;
|
|
570
601
|
chatId: string;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openmates",
|
|
3
|
-
"version": "0.12.0-alpha.
|
|
3
|
+
"version": "0.12.0-alpha.28",
|
|
4
4
|
"description": "OpenMates CLI and SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"test:unit:billing": "node --test tests/billing.test.ts",
|
|
31
31
|
"test:unit:account-delete": "node --test tests/account-delete.test.ts",
|
|
32
32
|
"test:unit:cli": "node --test tests/cli.test.ts",
|
|
33
|
+
"test:unit:connected-account-import": "node --test --experimental-strip-types --loader ./tests/loader.mjs tests/connectedAccountImport.test.ts",
|
|
33
34
|
"test:unit:benchmark": "node --test --experimental-strip-types --loader ./tests/loader.mjs src/__tests__/benchmark.test.ts",
|
|
34
|
-
"test": "node --test --experimental-strip-types --loader ./tests/loader.mjs tests/crypto.test.ts tests/storage.test.ts tests/keychain.test.ts tests/mentions.test.ts tests/outputRedactor.test.ts tests/fileEmbed.test.ts tests/embedCreator.test.ts tests/shareEncryption.test.ts tests/server.test.ts tests/ws.test.ts tests/urlEmbed.test.ts src/__tests__/benchmark.test.ts && node --test --experimental-strip-types tests/embedRenderers.test.ts && node --test tests/cli.test.ts tests/billing.test.ts tests/account-delete.test.ts tests/signup.test.ts tests/security-setup.test.ts tests/e2e-provisioning.test.ts"
|
|
35
|
+
"test": "node --test --experimental-strip-types --loader ./tests/loader.mjs tests/crypto.test.ts tests/storage.test.ts tests/keychain.test.ts tests/mentions.test.ts tests/outputRedactor.test.ts tests/fileEmbed.test.ts tests/embedCreator.test.ts tests/shareEncryption.test.ts tests/server.test.ts tests/ws.test.ts tests/urlEmbed.test.ts src/__tests__/benchmark.test.ts && node --test --experimental-strip-types --loader ./tests/loader.mjs tests/embedRenderers.test.ts tests/connectedAccountImport.test.ts && node --test tests/cli.test.ts tests/billing.test.ts tests/account-delete.test.ts tests/signup.test.ts tests/security-setup.test.ts tests/e2e-provisioning.test.ts"
|
|
35
36
|
},
|
|
36
37
|
"keywords": [
|
|
37
38
|
"openmates",
|