openmates 0.18.0-alpha.0 → 0.18.0-alpha.10
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-RLOO6QKC.js → chunk-ISGTN5LQ.js} +909 -521
- package/dist/{cli-BMrmfKIF.d.ts → cli-CZoCLxkk.d.ts} +4 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +5 -2
|
@@ -1015,6 +1015,10 @@ var OpenMatesHttpClient = class {
|
|
|
1015
1015
|
getCookieMap() {
|
|
1016
1016
|
return Object.fromEntries(this.cookies.entries());
|
|
1017
1017
|
}
|
|
1018
|
+
replaceCookies(cookies) {
|
|
1019
|
+
this.cookies.clear();
|
|
1020
|
+
for (const [name, value] of Object.entries(cookies)) this.cookies.set(name, value);
|
|
1021
|
+
}
|
|
1018
1022
|
async get(path2, headers = {}) {
|
|
1019
1023
|
return this.request("GET", path2, void 0, headers);
|
|
1020
1024
|
}
|
|
@@ -1194,8 +1198,10 @@ import {
|
|
|
1194
1198
|
readdirSync,
|
|
1195
1199
|
readFileSync as readFileSync2,
|
|
1196
1200
|
rmSync,
|
|
1201
|
+
renameSync,
|
|
1197
1202
|
writeFileSync
|
|
1198
1203
|
} from "fs";
|
|
1204
|
+
import lockfile from "proper-lockfile";
|
|
1199
1205
|
import { createHash as createHash4 } from "crypto";
|
|
1200
1206
|
import { homedir as homedir2 } from "os";
|
|
1201
1207
|
import { isAbsolute, join, resolve } from "path";
|
|
@@ -1483,7 +1489,55 @@ function saveAnonymousId(anonymousId) {
|
|
|
1483
1489
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
1484
1490
|
});
|
|
1485
1491
|
}
|
|
1486
|
-
|
|
1492
|
+
var SESSION_LOCK_STALE_MS = 3e4;
|
|
1493
|
+
var SESSION_LOCK_POLL_MS = 50;
|
|
1494
|
+
async function withSessionRefreshLock(operation) {
|
|
1495
|
+
const path2 = join(ensureStateDir(), "session.json");
|
|
1496
|
+
const release2 = await lockfile.lock(`${path2}.refresh`, {
|
|
1497
|
+
realpath: false,
|
|
1498
|
+
stale: SESSION_LOCK_STALE_MS,
|
|
1499
|
+
retries: { retries: 600, factor: 1, minTimeout: SESSION_LOCK_POLL_MS, maxTimeout: SESSION_LOCK_POLL_MS }
|
|
1500
|
+
});
|
|
1501
|
+
try {
|
|
1502
|
+
return await operation();
|
|
1503
|
+
} finally {
|
|
1504
|
+
await release2();
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
function saveSession(session, options = {}) {
|
|
1508
|
+
const path2 = join(ensureStateDir(), "session.json");
|
|
1509
|
+
let release2;
|
|
1510
|
+
const waitCell = new Int32Array(new SharedArrayBuffer(4));
|
|
1511
|
+
for (let attempt = 0; ; attempt++) {
|
|
1512
|
+
try {
|
|
1513
|
+
release2 = lockfile.lockSync(`${path2}.write`, { realpath: false, stale: SESSION_LOCK_STALE_MS });
|
|
1514
|
+
break;
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
if (error.code !== "ELOCKED" || attempt >= 100) throw error;
|
|
1517
|
+
Atomics.wait(waitCell, 0, 0, SESSION_LOCK_POLL_MS);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
try {
|
|
1521
|
+
const current = readJsonFile(path2);
|
|
1522
|
+
if (current && !options.replace) {
|
|
1523
|
+
if (current.sessionId !== session.sessionId || current.hashedEmail !== session.hashedEmail || current.apiUrl !== session.apiUrl) {
|
|
1524
|
+
throw new Error("Login session changed in another process. Retry the command using the current profile.");
|
|
1525
|
+
}
|
|
1526
|
+
if (options.expectedRefreshToken !== void 0) {
|
|
1527
|
+
if (current.cookies.auth_refresh_token !== options.expectedRefreshToken) {
|
|
1528
|
+
throw new Error("Login credentials changed during refresh. Retry with the current profile.");
|
|
1529
|
+
}
|
|
1530
|
+
} else {
|
|
1531
|
+
session.cookies = { ...current.cookies };
|
|
1532
|
+
session.wsToken = current.wsToken;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
writeSession(session);
|
|
1536
|
+
} finally {
|
|
1537
|
+
release2();
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
function writeSession(session) {
|
|
1487
1541
|
const filePath = join(ensureStateDir(), "session.json");
|
|
1488
1542
|
const result = storeMasterKey(session.masterKeyExportedB64, resolveKeyStorageId(session.hashedEmail));
|
|
1489
1543
|
const onDisk = {
|
|
@@ -1516,7 +1570,13 @@ function saveSession(session) {
|
|
|
1516
1570
|
onDisk.emailEncryptionKeyB64 = session.emailEncryptionKeyB64;
|
|
1517
1571
|
}
|
|
1518
1572
|
}
|
|
1519
|
-
|
|
1573
|
+
const temporary = `${filePath}.${process.pid}.tmp`;
|
|
1574
|
+
try {
|
|
1575
|
+
writeJsonFile(temporary, onDisk);
|
|
1576
|
+
renameSync(temporary, filePath);
|
|
1577
|
+
} finally {
|
|
1578
|
+
rmSync(temporary, { force: true });
|
|
1579
|
+
}
|
|
1520
1580
|
if (result.type !== "plaintext") {
|
|
1521
1581
|
process.stderr.write("Decrypting data...\n");
|
|
1522
1582
|
}
|
|
@@ -7509,7 +7569,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
7509
7569
|
apiUrl;
|
|
7510
7570
|
session;
|
|
7511
7571
|
http;
|
|
7572
|
+
explicitSession;
|
|
7512
7573
|
constructor(options = {}) {
|
|
7574
|
+
this.explicitSession = options.session !== void 0;
|
|
7513
7575
|
const diskSession = options.session ?? this.getValidSessionFromDisk();
|
|
7514
7576
|
this.apiUrl = (options.apiUrl ?? process.env.OPENMATES_API_URL ?? diskSession?.apiUrl ?? loadDefaultServerApiUrl() ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
7515
7577
|
this.session = diskSession;
|
|
@@ -8375,7 +8437,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8375
8437
|
if (!userId) {
|
|
8376
8438
|
throw new Error("Could not resolve current user id for local connected-account connector.");
|
|
8377
8439
|
}
|
|
8378
|
-
const { createHash:
|
|
8440
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
8379
8441
|
const accountId = randomUUID6();
|
|
8380
8442
|
const masterKey = this.getMasterKeyBytes();
|
|
8381
8443
|
const encryptLocalConnectorValue = async (value) => {
|
|
@@ -8384,9 +8446,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8384
8446
|
};
|
|
8385
8447
|
const payload = {
|
|
8386
8448
|
id: accountId,
|
|
8387
|
-
hashed_user_id:
|
|
8449
|
+
hashed_user_id: createHash16("sha256").update(userId).digest("hex"),
|
|
8388
8450
|
encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
|
|
8389
|
-
provider_type_hash:
|
|
8451
|
+
provider_type_hash: createHash16("sha256").update(input.provider_id).digest("hex"),
|
|
8390
8452
|
encrypted_account_label: await encryptLocalConnectorValue(input.label),
|
|
8391
8453
|
encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
|
|
8392
8454
|
encrypted_app_permissions: await encryptLocalConnectorValue({
|
|
@@ -8570,31 +8632,34 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8570
8632
|
};
|
|
8571
8633
|
await this.hydrateEmailEncryptionKey(session);
|
|
8572
8634
|
this.session = session;
|
|
8573
|
-
saveSession(session);
|
|
8635
|
+
saveSession(session, { replace: true });
|
|
8574
8636
|
}
|
|
8575
8637
|
async whoAmI() {
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8638
|
+
return this.withFreshStoredSession(async () => {
|
|
8639
|
+
const session = this.requireSession();
|
|
8640
|
+
const previousRefreshToken = session.cookies.auth_refresh_token;
|
|
8641
|
+
const response = await this.http.post(
|
|
8642
|
+
"/v1/auth/session",
|
|
8643
|
+
{ session_id: session.sessionId },
|
|
8644
|
+
this.getCliRequestHeaders()
|
|
8645
|
+
);
|
|
8646
|
+
if (!response.ok || !response.data.success) {
|
|
8647
|
+
if (response.status === 502 || response.status === 503 || response.status === 504) {
|
|
8648
|
+
throw new Error(
|
|
8649
|
+
`Session validation temporarily unavailable (HTTP ${response.status}). The API may be restarting; retry shortly.`
|
|
8650
|
+
);
|
|
8651
|
+
}
|
|
8584
8652
|
throw new Error(
|
|
8585
|
-
`Session validation
|
|
8653
|
+
`Session validation failed (HTTP ${response.status}): ${response.data.message ?? "invalid session"}${response.data.re_auth_reason ? ` (${response.data.re_auth_reason})` : ""}. Please run \`${this.loginRecoveryCommand()}\`.`
|
|
8586
8654
|
);
|
|
8587
8655
|
}
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
}
|
|
8595
|
-
session.cookies = this.http.getCookieMap();
|
|
8596
|
-
saveSession(session);
|
|
8597
|
-
return response.data.user ?? {};
|
|
8656
|
+
if (response.data.ws_token) {
|
|
8657
|
+
session.wsToken = response.data.ws_token;
|
|
8658
|
+
}
|
|
8659
|
+
session.cookies = this.http.getCookieMap();
|
|
8660
|
+
saveSession(session, { expectedRefreshToken: previousRefreshToken });
|
|
8661
|
+
return response.data.user ?? {};
|
|
8662
|
+
});
|
|
8598
8663
|
}
|
|
8599
8664
|
async getTopicPreferences() {
|
|
8600
8665
|
const user = await this.whoAmI();
|
|
@@ -8734,7 +8799,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8734
8799
|
authorizerDeviceName: null,
|
|
8735
8800
|
autoLogoutMinutes: null
|
|
8736
8801
|
};
|
|
8737
|
-
saveSession(session);
|
|
8802
|
+
saveSession(session, { replace: true });
|
|
8738
8803
|
this.session = session;
|
|
8739
8804
|
return {
|
|
8740
8805
|
success: true,
|
|
@@ -9882,8 +9947,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
9882
9947
|
);
|
|
9883
9948
|
}
|
|
9884
9949
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
9885
|
-
const { createHash:
|
|
9886
|
-
const hashedEmbedId =
|
|
9950
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
9951
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
9887
9952
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
9888
9953
|
cache,
|
|
9889
9954
|
masterKey,
|
|
@@ -9991,7 +10056,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
9991
10056
|
async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
|
|
9992
10057
|
if (visited.has(embedId)) return null;
|
|
9993
10058
|
visited.add(embedId);
|
|
9994
|
-
const { createHash:
|
|
10059
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
9995
10060
|
const masterKeyEntry = cache.embedKeys.find(
|
|
9996
10061
|
(ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
|
|
9997
10062
|
);
|
|
@@ -10008,7 +10073,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
10008
10073
|
if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
|
|
10009
10074
|
const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
|
|
10010
10075
|
const owningChat = cache.chats.find((c) => {
|
|
10011
|
-
const chatHash =
|
|
10076
|
+
const chatHash = createHash16("sha256").update(String(c.details.id ?? "")).digest("hex");
|
|
10012
10077
|
return chatHash === hashedChatId;
|
|
10013
10078
|
});
|
|
10014
10079
|
if (owningChat) {
|
|
@@ -10037,7 +10102,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
10037
10102
|
const parentFullId = String(
|
|
10038
10103
|
parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
|
|
10039
10104
|
);
|
|
10040
|
-
const parentHashedId =
|
|
10105
|
+
const parentHashedId = createHash16("sha256").update(parentFullId).digest("hex");
|
|
10041
10106
|
const parentKey = await this.resolveEmbedKey(
|
|
10042
10107
|
cache,
|
|
10043
10108
|
masterKey,
|
|
@@ -12776,6 +12841,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
12776
12841
|
if (Number.isSafeInteger(context.limit) && context.limit !== void 0 && context.limit > 0) {
|
|
12777
12842
|
params.set("limit", String(context.limit));
|
|
12778
12843
|
}
|
|
12844
|
+
if (context.newestFirst) params.set("newest_first", "true");
|
|
12779
12845
|
const query = params.toString();
|
|
12780
12846
|
const response = await this.http.get(
|
|
12781
12847
|
`/v1/user-tasks/${encodeURIComponent(taskId)}/activity${query ? `?${query}` : ""}`,
|
|
@@ -13974,8 +14040,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
13974
14040
|
const session = this.requireSession();
|
|
13975
14041
|
const currentCookies = this.http.getCookieMap();
|
|
13976
14042
|
if (JSON.stringify(session.cookies) !== JSON.stringify(currentCookies)) {
|
|
14043
|
+
const previousRefreshToken = session.cookies.auth_refresh_token;
|
|
13977
14044
|
session.cookies = currentCookies;
|
|
13978
|
-
saveSession(session);
|
|
14045
|
+
saveSession(session, { expectedRefreshToken: previousRefreshToken });
|
|
13979
14046
|
}
|
|
13980
14047
|
return session;
|
|
13981
14048
|
}
|
|
@@ -14070,7 +14137,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
14070
14137
|
const session = this.requireSession();
|
|
14071
14138
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
14072
14139
|
const cache = await this.ensureSynced();
|
|
14073
|
-
const { createHash:
|
|
14140
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14074
14141
|
const embed = cache.embeds.find(
|
|
14075
14142
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
14076
14143
|
);
|
|
@@ -14078,7 +14145,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
14078
14145
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
14079
14146
|
}
|
|
14080
14147
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
14081
|
-
const hashedEmbedId =
|
|
14148
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14082
14149
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
14083
14150
|
cache,
|
|
14084
14151
|
masterKey,
|
|
@@ -14186,8 +14253,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
14186
14253
|
if (!embed) {
|
|
14187
14254
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
14188
14255
|
}
|
|
14189
|
-
const { createHash:
|
|
14190
|
-
const hashedEmbedId =
|
|
14256
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14257
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14191
14258
|
const embedKey = await this.resolveEmbedKey(
|
|
14192
14259
|
cache,
|
|
14193
14260
|
masterKey,
|
|
@@ -14303,8 +14370,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
14303
14370
|
if (!embed) {
|
|
14304
14371
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
14305
14372
|
}
|
|
14306
|
-
const { createHash:
|
|
14307
|
-
const hashedEmbedId =
|
|
14373
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14374
|
+
const hashedEmbedId = createHash16("sha256").update(embedId).digest("hex");
|
|
14308
14375
|
const embedKey = await this.resolveEmbedKey(
|
|
14309
14376
|
cache,
|
|
14310
14377
|
masterKey,
|
|
@@ -14445,9 +14512,30 @@ Required: ${schema.required.join(", ")}`
|
|
|
14445
14512
|
session.userEmailSalt
|
|
14446
14513
|
);
|
|
14447
14514
|
}
|
|
14515
|
+
async withFreshStoredSession(operation) {
|
|
14516
|
+
return withSessionRefreshLock(async () => {
|
|
14517
|
+
const previous = this.requireSession();
|
|
14518
|
+
const current = loadSession();
|
|
14519
|
+
if (!current && !this.explicitSession) {
|
|
14520
|
+
throw new Error("Login session was removed. Log in to the current profile before retrying.");
|
|
14521
|
+
}
|
|
14522
|
+
if (current && !this.explicitSession) {
|
|
14523
|
+
if (current.hashedEmail !== previous.hashedEmail || current.apiUrl !== previous.apiUrl) {
|
|
14524
|
+
throw new Error("Login account changed in another process. Retry using the current profile.");
|
|
14525
|
+
}
|
|
14526
|
+
this.session = current;
|
|
14527
|
+
this.http.replaceCookies(current.cookies);
|
|
14528
|
+
}
|
|
14529
|
+
return operation();
|
|
14530
|
+
});
|
|
14531
|
+
}
|
|
14532
|
+
loginRecoveryCommand() {
|
|
14533
|
+
const profile = process.env.OPENMATES_PROFILE?.trim();
|
|
14534
|
+
return profile ? `openmates --profile ${profile} login` : "openmates login";
|
|
14535
|
+
}
|
|
14448
14536
|
requireSession() {
|
|
14449
14537
|
if (!this.session) {
|
|
14450
|
-
throw new Error(
|
|
14538
|
+
throw new Error(`Not logged in. Run \`${this.loginRecoveryCommand()}\`.`);
|
|
14451
14539
|
}
|
|
14452
14540
|
return this.session;
|
|
14453
14541
|
}
|
|
@@ -14527,24 +14615,30 @@ Required: ${schema.required.join(", ")}`
|
|
|
14527
14615
|
* This method fetches a fresh ws_token and captures any rotated cookies.
|
|
14528
14616
|
*/
|
|
14529
14617
|
async refreshWsToken() {
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14533
|
-
res
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14618
|
+
return this.withFreshStoredSession(async () => {
|
|
14619
|
+
const session = this.requireSession();
|
|
14620
|
+
const previousRefreshToken = session.cookies.auth_refresh_token;
|
|
14621
|
+
let res;
|
|
14622
|
+
try {
|
|
14623
|
+
res = await this.http.post("/v1/auth/session", { session_id: session.sessionId }, this.getCliRequestHeaders());
|
|
14624
|
+
} catch {
|
|
14625
|
+
return null;
|
|
14626
|
+
}
|
|
14627
|
+
if (res.status >= 500) {
|
|
14628
|
+
throw new Error(`Session validation temporarily unavailable (HTTP ${res.status}). Retry shortly.`);
|
|
14629
|
+
}
|
|
14630
|
+
if (!res.ok || res.data.success === false) {
|
|
14631
|
+
purgeLocalPrivateData();
|
|
14632
|
+
this.session = null;
|
|
14633
|
+
throw new Error(`Session expired or invalid. Please run \`${this.loginRecoveryCommand()}\` to re-authenticate.`);
|
|
14634
|
+
}
|
|
14635
|
+
if (res.data.ws_token) {
|
|
14636
|
+
session.wsToken = res.data.ws_token;
|
|
14637
|
+
}
|
|
14638
|
+
session.cookies = this.http.getCookieMap();
|
|
14639
|
+
saveSession(session, { expectedRefreshToken: previousRefreshToken });
|
|
14640
|
+
return typeof res.data.user?.id === "string" ? res.data.user.id : typeof res.data.user?.user_id === "string" ? res.data.user.user_id : null;
|
|
14641
|
+
});
|
|
14548
14642
|
}
|
|
14549
14643
|
/**
|
|
14550
14644
|
* Ensure the local sync cache is up to date. If the cache is fresh,
|
|
@@ -14744,9 +14838,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
14744
14838
|
chats.splice(0, chats.length, ...reconciledChats);
|
|
14745
14839
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
14746
14840
|
if (reconciliation.deleted_chat_ids?.length) {
|
|
14747
|
-
const { createHash:
|
|
14841
|
+
const { createHash: createHash16 } = await import("crypto");
|
|
14748
14842
|
const deletedHashes = new Set(
|
|
14749
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
14843
|
+
reconciliation.deleted_chat_ids.map((id) => createHash16("sha256").update(id).digest("hex"))
|
|
14750
14844
|
);
|
|
14751
14845
|
const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
|
|
14752
14846
|
const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
|
|
@@ -20131,12 +20225,12 @@ var ACCOUNT_EXPORT_FORBIDDEN_VALUE_PATTERNS = [
|
|
|
20131
20225
|
];
|
|
20132
20226
|
async function writeAccountExportArchive(bundle, flags = {}) {
|
|
20133
20227
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
20134
|
-
const { join:
|
|
20228
|
+
const { join: join13, dirname: dirname11 } = await import("path");
|
|
20135
20229
|
const exportId = safeArchiveSegment(String(bundle.export.export_id ?? `export-${Date.now()}`));
|
|
20136
20230
|
const archiveFormat = flags.format === "directory" ? "directory" : "zip";
|
|
20137
20231
|
const password = typeof flags.password === "string" && flags.password.length > 0 ? flags.password : null;
|
|
20138
20232
|
if (password && archiveFormat !== "zip") throw new Error("Password-protected account export is only supported for zip format.");
|
|
20139
|
-
const outputPath = typeof flags.output === "string" ? flags.output :
|
|
20233
|
+
const outputPath = typeof flags.output === "string" ? flags.output : join13(process.cwd(), archiveFormat === "zip" ? `openmates-account-export-${exportId}${password ? ".zip.enc" : ".zip"}` : `openmates-account-export-${exportId}`);
|
|
20140
20234
|
const zip = archiveFormat === "zip" ? new JSZip3() : null;
|
|
20141
20235
|
const writtenFiles = [];
|
|
20142
20236
|
async function writeArchiveText(relativePath, content) {
|
|
@@ -20147,8 +20241,8 @@ async function writeAccountExportArchive(bundle, flags = {}) {
|
|
|
20147
20241
|
return;
|
|
20148
20242
|
}
|
|
20149
20243
|
const stagingDir = outputPath;
|
|
20150
|
-
const fullPath =
|
|
20151
|
-
await mkdir2(
|
|
20244
|
+
const fullPath = join13(stagingDir, relativePath);
|
|
20245
|
+
await mkdir2(dirname11(fullPath), { recursive: true });
|
|
20152
20246
|
await writeFile2(fullPath, content, "utf-8");
|
|
20153
20247
|
writtenFiles.push(relativePath);
|
|
20154
20248
|
}
|
|
@@ -20165,7 +20259,7 @@ async function writeAccountExportArchive(bundle, flags = {}) {
|
|
|
20165
20259
|
}
|
|
20166
20260
|
await writeAccountExportChatFiles(domainPayloads.chats ?? [], writeArchiveText);
|
|
20167
20261
|
if (archiveFormat === "directory") return { output: outputPath, format: "directory", files: writtenFiles.length };
|
|
20168
|
-
await mkdir2(
|
|
20262
|
+
await mkdir2(dirname11(outputPath), { recursive: true });
|
|
20169
20263
|
const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
20170
20264
|
await writeFile2(outputPath, password ? encryptZipBuffer(zipBuffer, password) : zipBuffer);
|
|
20171
20265
|
return { output: outputPath, format: "zip", files: writtenFiles.length, ...password ? { encrypted: true } : {} };
|
|
@@ -24298,10 +24392,115 @@ var OpenMatesBenchmark = class {
|
|
|
24298
24392
|
// src/cli.ts
|
|
24299
24393
|
import { createInterface as createInterface5 } from "readline/promises";
|
|
24300
24394
|
import { stdin as stdin3, stdout as stdout2 } from "process";
|
|
24301
|
-
|
|
24302
|
-
|
|
24303
|
-
|
|
24304
|
-
|
|
24395
|
+
|
|
24396
|
+
// src/taskActivityHistory.ts
|
|
24397
|
+
async function readActivityHistory(options) {
|
|
24398
|
+
const { query } = options;
|
|
24399
|
+
const maxEntries = options.maxEntries ?? Infinity;
|
|
24400
|
+
if (options.maxEntries !== void 0 && (!Number.isSafeInteger(maxEntries) || maxEntries < 1 || maxEntries > 200)) {
|
|
24401
|
+
throw new Error("--max-entries must be an integer from 1 to 200");
|
|
24402
|
+
}
|
|
24403
|
+
if (query !== void 0 && !query.trim()) throw new Error("Activity search requires a non-empty --query");
|
|
24404
|
+
const pageSize = options.pageSize ?? 100;
|
|
24405
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 200) throw new Error("--limit must be from 1 to 200");
|
|
24406
|
+
const entries = [];
|
|
24407
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24408
|
+
let cursor = options.cursor;
|
|
24409
|
+
let matched = 0;
|
|
24410
|
+
do {
|
|
24411
|
+
const page = await options.page(cursor, query === void 0 ? Math.min(pageSize, maxEntries - entries.length) : pageSize);
|
|
24412
|
+
for (const entry of page.entries) {
|
|
24413
|
+
if (query === void 0 || (entry.message || "").toLocaleLowerCase().includes(query.toLocaleLowerCase())) {
|
|
24414
|
+
matched++;
|
|
24415
|
+
if (entries.length < maxEntries) entries.push(entry);
|
|
24416
|
+
}
|
|
24417
|
+
}
|
|
24418
|
+
cursor = page.next_cursor || void 0;
|
|
24419
|
+
if (cursor && seen.has(cursor)) throw new Error("Activity pagination repeated a cursor");
|
|
24420
|
+
if (cursor) seen.add(cursor);
|
|
24421
|
+
} while (cursor && (query !== void 0 || entries.length < maxEntries));
|
|
24422
|
+
return { entries, next_cursor: cursor || null, truncated: Boolean(cursor) || matched > entries.length, matched };
|
|
24423
|
+
}
|
|
24424
|
+
|
|
24425
|
+
// src/taskActivityDelivery.ts
|
|
24426
|
+
import { createHash as createHash12 } from "crypto";
|
|
24427
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, renameSync as renameSync2, writeFileSync as writeFileSync4, rmSync as rmSync3, existsSync as existsSync7 } from "fs";
|
|
24428
|
+
import { join as join4 } from "path";
|
|
24429
|
+
import lockfile2 from "proper-lockfile";
|
|
24430
|
+
function activityDeliveryStore(scope, stateDir2 = resolveStateDir()) {
|
|
24431
|
+
const directory = join4(stateDir2, "task-activity-delivery", createHash12("sha256").update(scope).digest("hex"));
|
|
24432
|
+
mkdirSync4(directory, { recursive: true, mode: 448 });
|
|
24433
|
+
const pendingDirectory = join4(directory, "pending");
|
|
24434
|
+
const acknowledgedDirectory = join4(directory, "acknowledged");
|
|
24435
|
+
mkdirSync4(pendingDirectory, { recursive: true, mode: 448 });
|
|
24436
|
+
mkdirSync4(acknowledgedDirectory, { recursive: true, mode: 448 });
|
|
24437
|
+
const save = (path2, record) => {
|
|
24438
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
24439
|
+
writeFileSync4(temporary, JSON.stringify(record), { mode: 384 });
|
|
24440
|
+
renameSync2(temporary, path2);
|
|
24441
|
+
};
|
|
24442
|
+
const send = async (path2, record, create) => {
|
|
24443
|
+
if (!record.acknowledged) {
|
|
24444
|
+
const acknowledged = await create(record.input);
|
|
24445
|
+
if (acknowledged.entry_id !== record.input.entry_id) throw new Error("Activity acknowledgement identity mismatch");
|
|
24446
|
+
record.acknowledged = acknowledged;
|
|
24447
|
+
save(join4(acknowledgedDirectory, `${record.input.entry_id}.json`), record);
|
|
24448
|
+
rmSync3(path2, { force: true });
|
|
24449
|
+
}
|
|
24450
|
+
return record.acknowledged;
|
|
24451
|
+
};
|
|
24452
|
+
return {
|
|
24453
|
+
async deliver(id, build, create) {
|
|
24454
|
+
if (!/^[a-f0-9]{64}$/.test(id)) throw new Error("--delivery-id must be a SHA-256 hex identifier");
|
|
24455
|
+
const release2 = await lockfile2.lock(directory, { retries: { retries: 3, minTimeout: 100, maxTimeout: 500 }, stale: 12e4 });
|
|
24456
|
+
try {
|
|
24457
|
+
const acknowledgedPath = join4(acknowledgedDirectory, `${id}.json`);
|
|
24458
|
+
const path2 = existsSync7(acknowledgedPath) ? acknowledgedPath : join4(pendingDirectory, `${id}.json`);
|
|
24459
|
+
let record;
|
|
24460
|
+
try {
|
|
24461
|
+
record = JSON.parse(readFileSync6(path2, "utf8"));
|
|
24462
|
+
} catch (error) {
|
|
24463
|
+
if (error.code !== "ENOENT") throw error;
|
|
24464
|
+
record = { input: await build() };
|
|
24465
|
+
save(path2, record);
|
|
24466
|
+
}
|
|
24467
|
+
return await send(path2, record, create);
|
|
24468
|
+
} finally {
|
|
24469
|
+
await release2();
|
|
24470
|
+
}
|
|
24471
|
+
},
|
|
24472
|
+
async flush(create) {
|
|
24473
|
+
const release2 = await lockfile2.lock(directory, { retries: 0, stale: 12e4 });
|
|
24474
|
+
try {
|
|
24475
|
+
const names = readdirSync2(pendingDirectory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name));
|
|
24476
|
+
let pending = names.length;
|
|
24477
|
+
let attempted = 0;
|
|
24478
|
+
for (const name of names) {
|
|
24479
|
+
const path2 = join4(pendingDirectory, name);
|
|
24480
|
+
const record = JSON.parse(readFileSync6(path2, "utf8"));
|
|
24481
|
+
if (record.acknowledged) continue;
|
|
24482
|
+
if (attempted >= 3) break;
|
|
24483
|
+
attempted++;
|
|
24484
|
+
try {
|
|
24485
|
+
await send(path2, record, create);
|
|
24486
|
+
pending--;
|
|
24487
|
+
} catch {
|
|
24488
|
+
break;
|
|
24489
|
+
}
|
|
24490
|
+
}
|
|
24491
|
+
return { pending, attempted };
|
|
24492
|
+
} finally {
|
|
24493
|
+
await release2();
|
|
24494
|
+
}
|
|
24495
|
+
}
|
|
24496
|
+
};
|
|
24497
|
+
}
|
|
24498
|
+
|
|
24499
|
+
// src/cli.ts
|
|
24500
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, realpathSync as realpathSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
24501
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24502
|
+
import { basename as basename4, dirname as dirname10 } from "path";
|
|
24503
|
+
import { createHash as createHash15, randomBytes as randomBytes9, randomUUID as randomUUID15 } from "crypto";
|
|
24305
24504
|
import { arch as arch2, platform as platform3 } from "os";
|
|
24306
24505
|
import { parse as parseYaml } from "yaml";
|
|
24307
24506
|
import WebSocket2 from "ws";
|
|
@@ -26479,13 +26678,13 @@ function formatTs(ts) {
|
|
|
26479
26678
|
}
|
|
26480
26679
|
|
|
26481
26680
|
// src/server.ts
|
|
26482
|
-
import { execFileSync as
|
|
26483
|
-
import { createHash as
|
|
26484
|
-
import { appendFileSync, chmodSync as
|
|
26681
|
+
import { execFileSync as execFileSync4, execSync, spawn as nodeSpawn, spawnSync } from "child_process";
|
|
26682
|
+
import { createHash as createHash13, randomBytes as randomBytes8, randomUUID as randomUUID11 } from "crypto";
|
|
26683
|
+
import { appendFileSync, chmodSync as chmodSync5, closeSync as closeSync2, copyFileSync as copyFileSync2, cpSync, existsSync as existsSync10, mkdirSync as mkdirSync7, mkdtempSync, openSync as openSync2, readFileSync as readFileSync8, readSync, readdirSync as readdirSync5, rmSync as rmSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
26485
26684
|
import { createInterface as createInterface3 } from "readline";
|
|
26486
26685
|
import { createInterface as createPromptInterface } from "readline/promises";
|
|
26487
26686
|
import { homedir as homedir6 } from "os";
|
|
26488
|
-
import { dirname as dirname6, join as
|
|
26687
|
+
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
26489
26688
|
|
|
26490
26689
|
// src/branding.ts
|
|
26491
26690
|
var OPENMATES_WORDMARK = "OPENMATES";
|
|
@@ -26521,33 +26720,33 @@ function renderSupportStartReminder() {
|
|
|
26521
26720
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
26522
26721
|
import {
|
|
26523
26722
|
copyFileSync,
|
|
26524
|
-
existsSync as
|
|
26525
|
-
mkdirSync as
|
|
26526
|
-
readdirSync as
|
|
26527
|
-
rmSync as
|
|
26723
|
+
existsSync as existsSync8,
|
|
26724
|
+
mkdirSync as mkdirSync5,
|
|
26725
|
+
readdirSync as readdirSync3,
|
|
26726
|
+
rmSync as rmSync4,
|
|
26528
26727
|
symlinkSync
|
|
26529
26728
|
} from "fs";
|
|
26530
26729
|
import { createRequire as createRequire3 } from "module";
|
|
26531
|
-
import { dirname as dirname3, join as
|
|
26532
|
-
var UI_PACKAGE_RELATIVE_PATH =
|
|
26533
|
-
var GENERATED_LOCALES_RELATIVE_PATH =
|
|
26534
|
-
var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH =
|
|
26730
|
+
import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
|
|
26731
|
+
var UI_PACKAGE_RELATIVE_PATH = join5("frontend", "packages", "ui");
|
|
26732
|
+
var GENERATED_LOCALES_RELATIVE_PATH = join5(UI_PACKAGE_RELATIVE_PATH, "src", "i18n", "locales");
|
|
26733
|
+
var BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH = join5(UI_PACKAGE_RELATIVE_PATH, "scripts", "build-translations.js");
|
|
26535
26734
|
var REQUIRED_LOCALE_JSON = "en.json";
|
|
26536
26735
|
function sourceInstallLocalesPath(rootPath) {
|
|
26537
|
-
return
|
|
26736
|
+
return join5(rootPath, GENERATED_LOCALES_RELATIVE_PATH);
|
|
26538
26737
|
}
|
|
26539
26738
|
function ensureSourceInstallTranslations(installPath, sourcePath) {
|
|
26540
26739
|
const localeDir = sourceInstallLocalesPath(installPath);
|
|
26541
|
-
const requiredLocalePath =
|
|
26542
|
-
if (
|
|
26740
|
+
const requiredLocalePath = join5(localeDir, REQUIRED_LOCALE_JSON);
|
|
26741
|
+
if (existsSync8(requiredLocalePath)) {
|
|
26543
26742
|
return { status: "already_present", localeDir, copiedFiles: 0 };
|
|
26544
26743
|
}
|
|
26545
26744
|
const copiedFiles = copyGeneratedLocaleJson(sourcePath, installPath);
|
|
26546
|
-
if (
|
|
26745
|
+
if (existsSync8(requiredLocalePath)) {
|
|
26547
26746
|
return { status: "copied", localeDir, copiedFiles };
|
|
26548
26747
|
}
|
|
26549
26748
|
runBuildTranslationsScript(installPath);
|
|
26550
|
-
if (!
|
|
26749
|
+
if (!existsSync8(requiredLocalePath)) {
|
|
26551
26750
|
throw new Error(`Generated locale JSON was not created at ${requiredLocalePath}.`);
|
|
26552
26751
|
}
|
|
26553
26752
|
return { status: "built", localeDir, copiedFiles };
|
|
@@ -26558,31 +26757,31 @@ function copyGeneratedLocaleJson(sourcePath, installPath) {
|
|
|
26558
26757
|
const targetRoot = resolve4(installPath);
|
|
26559
26758
|
if (sourceRoot === targetRoot) return 0;
|
|
26560
26759
|
const sourceLocaleDir = sourceInstallLocalesPath(sourceRoot);
|
|
26561
|
-
if (!
|
|
26760
|
+
if (!existsSync8(join5(sourceLocaleDir, REQUIRED_LOCALE_JSON))) return 0;
|
|
26562
26761
|
const targetLocaleDir = sourceInstallLocalesPath(targetRoot);
|
|
26563
|
-
|
|
26762
|
+
mkdirSync5(targetLocaleDir, { recursive: true });
|
|
26564
26763
|
let copiedFiles = 0;
|
|
26565
|
-
for (const entry of
|
|
26764
|
+
for (const entry of readdirSync3(sourceLocaleDir, { withFileTypes: true })) {
|
|
26566
26765
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
26567
|
-
copyFileSync(
|
|
26766
|
+
copyFileSync(join5(sourceLocaleDir, entry.name), join5(targetLocaleDir, entry.name));
|
|
26568
26767
|
copiedFiles += 1;
|
|
26569
26768
|
}
|
|
26570
26769
|
return copiedFiles;
|
|
26571
26770
|
}
|
|
26572
26771
|
function runBuildTranslationsScript(installPath) {
|
|
26573
|
-
const uiPackageDir =
|
|
26574
|
-
const buildScript =
|
|
26575
|
-
if (!
|
|
26772
|
+
const uiPackageDir = join5(installPath, UI_PACKAGE_RELATIVE_PATH);
|
|
26773
|
+
const buildScript = join5(installPath, BUILD_TRANSLATIONS_SCRIPT_RELATIVE_PATH);
|
|
26774
|
+
if (!existsSync8(buildScript)) {
|
|
26576
26775
|
throw new Error(`Translation build script not found at ${buildScript}.`);
|
|
26577
26776
|
}
|
|
26578
26777
|
const requireFromCli = createRequire3(import.meta.url);
|
|
26579
26778
|
const yamlPackageDir = dirname3(requireFromCli.resolve("yaml/package.json"));
|
|
26580
|
-
const nodeModulesDir =
|
|
26581
|
-
const yamlLink =
|
|
26582
|
-
const hadNodeModulesDir =
|
|
26583
|
-
const hadYamlPackage =
|
|
26779
|
+
const nodeModulesDir = join5(uiPackageDir, "node_modules");
|
|
26780
|
+
const yamlLink = join5(nodeModulesDir, "yaml");
|
|
26781
|
+
const hadNodeModulesDir = existsSync8(nodeModulesDir);
|
|
26782
|
+
const hadYamlPackage = existsSync8(yamlLink);
|
|
26584
26783
|
if (!hadYamlPackage) {
|
|
26585
|
-
|
|
26784
|
+
mkdirSync5(nodeModulesDir, { recursive: true });
|
|
26586
26785
|
symlinkSync(yamlPackageDir, yamlLink, process.platform === "win32" ? "junction" : "dir");
|
|
26587
26786
|
}
|
|
26588
26787
|
try {
|
|
@@ -26591,15 +26790,15 @@ function runBuildTranslationsScript(installPath) {
|
|
|
26591
26790
|
stdio: "inherit"
|
|
26592
26791
|
});
|
|
26593
26792
|
} finally {
|
|
26594
|
-
if (!hadYamlPackage)
|
|
26595
|
-
if (!hadNodeModulesDir &&
|
|
26596
|
-
|
|
26793
|
+
if (!hadYamlPackage) rmSync4(yamlLink, { recursive: true, force: true });
|
|
26794
|
+
if (!hadNodeModulesDir && existsSync8(nodeModulesDir) && readdirSync3(nodeModulesDir).length === 0) {
|
|
26795
|
+
rmSync4(nodeModulesDir, { recursive: true, force: true });
|
|
26597
26796
|
}
|
|
26598
26797
|
}
|
|
26599
26798
|
}
|
|
26600
26799
|
|
|
26601
26800
|
// src/serverPlanning.ts
|
|
26602
|
-
import { dirname as dirname4, join as
|
|
26801
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "path";
|
|
26603
26802
|
var CORE_WORKER_SERVICES = [
|
|
26604
26803
|
"task-worker",
|
|
26605
26804
|
"user-init-worker",
|
|
@@ -26809,10 +27008,10 @@ function parseServerRole(value) {
|
|
|
26809
27008
|
throw new Error(`Unsupported server role '${value}'. Use core, upload, or preview.`);
|
|
26810
27009
|
}
|
|
26811
27010
|
function defaultOpenMatesCloudOverlayPath(openMatesPath) {
|
|
26812
|
-
return resolve5(
|
|
27011
|
+
return resolve5(join6(dirname4(resolve5(openMatesPath)), OPENMATESCLOUD_OVERLAY_DIR));
|
|
26813
27012
|
}
|
|
26814
27013
|
function defaultOpenMatesCloudComposeFile(overlayPath) {
|
|
26815
|
-
return resolve5(
|
|
27014
|
+
return resolve5(join6(overlayPath, OPENMATESCLOUD_COMPOSE_FILE));
|
|
26816
27015
|
}
|
|
26817
27016
|
function planOpenMatesCloudOverlay(input) {
|
|
26818
27017
|
const deploymentMode = input.deploymentMode ?? "self_host";
|
|
@@ -26955,12 +27154,11 @@ function planUpdate(input) {
|
|
|
26955
27154
|
function planBackup(input) {
|
|
26956
27155
|
const role = parseServerRole(input.role);
|
|
26957
27156
|
const contentsByRole = {
|
|
26958
|
-
core: ["postgres-dump", "
|
|
26959
|
-
upload: ["
|
|
26960
|
-
preview: ["runtime-env", "runtime-config", "
|
|
27157
|
+
core: ["postgres-dump", "runtime-env", "runtime-config", "manifest", "checksums"],
|
|
27158
|
+
upload: ["runtime-env", "runtime-config", "manifest", "checksums"],
|
|
27159
|
+
preview: ["runtime-env", "runtime-config", "manifest", "checksums"]
|
|
26961
27160
|
};
|
|
26962
27161
|
const contents = [...contentsByRole[role]];
|
|
26963
|
-
if (input.includeObservability) contents.push("openobserve-data", "prometheus-data");
|
|
26964
27162
|
return { role, contents, fileMode: 384 };
|
|
26965
27163
|
}
|
|
26966
27164
|
function planRestore(input) {
|
|
@@ -27193,9 +27391,38 @@ function shouldAutoInstallRuntimeMonitoringServices(env) {
|
|
|
27193
27391
|
return env.OPENMATES_SKIP_RUNTIME_MONITORING !== "1";
|
|
27194
27392
|
}
|
|
27195
27393
|
|
|
27394
|
+
// src/serverBackupArchive.ts
|
|
27395
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
27396
|
+
import { chmodSync as chmodSync3, lstatSync, readdirSync as readdirSync4, renameSync as renameSync3, rmSync as rmSync5 } from "fs";
|
|
27397
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
27398
|
+
function assertRegularBackupTree(path2) {
|
|
27399
|
+
const stat2 = lstatSync(path2);
|
|
27400
|
+
if (stat2.isDirectory()) {
|
|
27401
|
+
for (const entry of readdirSync4(path2)) assertRegularBackupTree(`${path2}/${entry}`);
|
|
27402
|
+
return;
|
|
27403
|
+
}
|
|
27404
|
+
if (!stat2.isFile() || stat2.nlink !== 1) {
|
|
27405
|
+
throw new Error(`Backup archive refused unsafe entry: ${path2}`);
|
|
27406
|
+
}
|
|
27407
|
+
}
|
|
27408
|
+
function publishServerBackupArchive(sourceDir, archivePath, options = {}) {
|
|
27409
|
+
assertRegularBackupTree(sourceDir);
|
|
27410
|
+
const temporaryArchivePath = `${archivePath}.tmp-${randomUUID8()}`;
|
|
27411
|
+
const previousUmask = process.umask(63);
|
|
27412
|
+
try {
|
|
27413
|
+
execFileSync3(options.tarCommand ?? "tar", ["-czf", temporaryArchivePath, "-C", sourceDir, "."], { stdio: "pipe" });
|
|
27414
|
+
chmodSync3(temporaryArchivePath, 384);
|
|
27415
|
+
renameSync3(temporaryArchivePath, archivePath);
|
|
27416
|
+
chmodSync3(archivePath, 384);
|
|
27417
|
+
} finally {
|
|
27418
|
+
process.umask(previousUmask);
|
|
27419
|
+
rmSync5(temporaryArchivePath, { force: true });
|
|
27420
|
+
}
|
|
27421
|
+
}
|
|
27422
|
+
|
|
27196
27423
|
// src/serverHealth.ts
|
|
27197
27424
|
import dnsModule, { promises as dns } from "dns";
|
|
27198
|
-
import { createHmac as createHmac4, randomUUID as
|
|
27425
|
+
import { createHmac as createHmac4, randomUUID as randomUUID9 } from "crypto";
|
|
27199
27426
|
import { promises as fs } from "fs";
|
|
27200
27427
|
import { isIP } from "net";
|
|
27201
27428
|
import { request as httpsRequest } from "https";
|
|
@@ -27578,7 +27805,7 @@ async function resolveGenericWebhookTarget(rawUrl, allowLocalDevelopmentFixture
|
|
|
27578
27805
|
async function sendGenericWebhook(target, secret, payload, allowLocalDevelopmentFixture = false) {
|
|
27579
27806
|
const { url, addresses } = await resolveGenericWebhookTarget(target, allowLocalDevelopmentFixture);
|
|
27580
27807
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
27581
|
-
const eventId =
|
|
27808
|
+
const eventId = randomUUID9();
|
|
27582
27809
|
const signed = signRuntimeWebhookPayload(payload, secret, timestamp, eventId);
|
|
27583
27810
|
const selected = addresses[0];
|
|
27584
27811
|
await new Promise((resolve11, reject) => {
|
|
@@ -27787,16 +28014,16 @@ function signRuntimeWebhookPayload(payload, secret, timestamp, eventId) {
|
|
|
27787
28014
|
|
|
27788
28015
|
// src/serverUpdateState.ts
|
|
27789
28016
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
27790
|
-
import { chmodSync as
|
|
27791
|
-
import { dirname as dirname5, join as
|
|
28017
|
+
import { chmodSync as chmodSync4, closeSync, existsSync as existsSync9, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
28018
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
27792
28019
|
function serverUpdateStatusFile(installPath, role) {
|
|
27793
|
-
return
|
|
28020
|
+
return join7(installPath, ".openmates", `${role}-update-status.json`);
|
|
27794
28021
|
}
|
|
27795
28022
|
function readServerUpdateStatus(installPath, role) {
|
|
27796
28023
|
const filePath = serverUpdateStatusFile(installPath, role);
|
|
27797
|
-
if (!
|
|
28024
|
+
if (!existsSync9(filePath)) return {};
|
|
27798
28025
|
try {
|
|
27799
|
-
const parsed = JSON.parse(
|
|
28026
|
+
const parsed = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
27800
28027
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27801
28028
|
return { statusReadError: "invalid_update_status" };
|
|
27802
28029
|
}
|
|
@@ -27807,23 +28034,23 @@ function readServerUpdateStatus(installPath, role) {
|
|
|
27807
28034
|
}
|
|
27808
28035
|
function writeServerUpdateStatus(installPath, role, status) {
|
|
27809
28036
|
const filePath = serverUpdateStatusFile(installPath, role);
|
|
27810
|
-
|
|
28037
|
+
mkdirSync6(dirname5(filePath), { recursive: true, mode: 448 });
|
|
27811
28038
|
const temporaryPath = `${filePath}.${process.pid}.${randomBytes7(4).toString("hex")}.tmp`;
|
|
27812
|
-
|
|
28039
|
+
writeFileSync5(temporaryPath, `${JSON.stringify({ role, updated_at: (/* @__PURE__ */ new Date()).toISOString(), ...status }, null, 2)}
|
|
27813
28040
|
`, { mode: 384 });
|
|
27814
|
-
|
|
27815
|
-
|
|
28041
|
+
renameSync4(temporaryPath, filePath);
|
|
28042
|
+
chmodSync4(filePath, 384);
|
|
27816
28043
|
}
|
|
27817
28044
|
function acquireServerUpdateLock(installPath) {
|
|
27818
|
-
const stateDir2 =
|
|
27819
|
-
const lockPath =
|
|
27820
|
-
|
|
28045
|
+
const stateDir2 = join7(installPath, ".openmates");
|
|
28046
|
+
const lockPath = join7(stateDir2, "server-update.lock");
|
|
28047
|
+
mkdirSync6(stateDir2, { recursive: true, mode: 448 });
|
|
27821
28048
|
let descriptor;
|
|
27822
28049
|
try {
|
|
27823
28050
|
descriptor = openSync(lockPath, "wx", 384);
|
|
27824
28051
|
} catch (error) {
|
|
27825
28052
|
if (error.code !== "EEXIST") throw error;
|
|
27826
|
-
const lockOwner =
|
|
28053
|
+
const lockOwner = readFileSync7(lockPath, "utf8").trim();
|
|
27827
28054
|
const lockPid = Number.parseInt(lockOwner.split(":", 1)[0] ?? "", 10);
|
|
27828
28055
|
let stale = false;
|
|
27829
28056
|
try {
|
|
@@ -27835,17 +28062,17 @@ function acquireServerUpdateLock(installPath) {
|
|
|
27835
28062
|
throw new Error(stale ? "A stale OpenMates server update lock exists for this installation; verify no update is running, then remove .openmates/server-update.lock explicitly." : "Another OpenMates server update is already running for this installation.");
|
|
27836
28063
|
}
|
|
27837
28064
|
const ownershipToken = `${process.pid}:${randomBytes7(16).toString("hex")}`;
|
|
27838
|
-
|
|
28065
|
+
writeFileSync5(descriptor, ownershipToken);
|
|
27839
28066
|
return () => {
|
|
27840
28067
|
closeSync(descriptor);
|
|
27841
|
-
if (
|
|
27842
|
-
|
|
28068
|
+
if (existsSync9(lockPath) && readFileSync7(lockPath, "utf8").trim() === ownershipToken) {
|
|
28069
|
+
rmSync6(lockPath);
|
|
27843
28070
|
}
|
|
27844
28071
|
};
|
|
27845
28072
|
}
|
|
27846
28073
|
|
|
27847
28074
|
// src/serverQuickTest.ts
|
|
27848
|
-
import { randomUUID as
|
|
28075
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
27849
28076
|
var QUICK_TEST_RESPONSE_TIMEOUT_MS = 12e4;
|
|
27850
28077
|
var QUICK_TEST_WEB_QUERY = "site:openmates.org OpenMates";
|
|
27851
28078
|
var QUICK_TEST_EXPECTED_RESPONSE = "server quick test passed";
|
|
@@ -27911,7 +28138,7 @@ function decideQuickServerTestAction(input) {
|
|
|
27911
28138
|
async function runQuickServerTest(client, options = {}) {
|
|
27912
28139
|
const now = options.now ?? Date.now;
|
|
27913
28140
|
const checks = [];
|
|
27914
|
-
const chatId =
|
|
28141
|
+
const chatId = randomUUID10();
|
|
27915
28142
|
let chatMayExist = false;
|
|
27916
28143
|
checks.push({ id: "account.session", status: "passed", duration_ms: 0 });
|
|
27917
28144
|
const createStarted = now();
|
|
@@ -28011,22 +28238,22 @@ function containsBoundedWebResult(value) {
|
|
|
28011
28238
|
}
|
|
28012
28239
|
|
|
28013
28240
|
// src/server.ts
|
|
28014
|
-
var SOURCE_COMPOSE_FILE =
|
|
28241
|
+
var SOURCE_COMPOSE_FILE = join8("backend", "core", "docker-compose.yml");
|
|
28015
28242
|
var ROLE_IMAGE_COMPOSE_FILES = {
|
|
28016
|
-
core:
|
|
28017
|
-
upload:
|
|
28018
|
-
preview:
|
|
28243
|
+
core: join8("backend", "core", "docker-compose.selfhost.yml"),
|
|
28244
|
+
upload: join8("backend", "upload", "docker-compose.yml"),
|
|
28245
|
+
preview: join8("backend", "preview", "docker-compose.preview.yml")
|
|
28019
28246
|
};
|
|
28020
28247
|
var ROLE_TEMPLATE_FILES = {
|
|
28021
|
-
core:
|
|
28022
|
-
upload:
|
|
28023
|
-
preview:
|
|
28248
|
+
core: join8("core", "docker-compose.selfhost.yml"),
|
|
28249
|
+
upload: join8("upload", "docker-compose.yml"),
|
|
28250
|
+
preview: join8("preview", "docker-compose.preview.yml")
|
|
28024
28251
|
};
|
|
28025
|
-
var CORE_NO_WEBAPP_TEMPLATE_FILE =
|
|
28026
|
-
var CORE_PROMTAIL_CONFIG_FILE =
|
|
28027
|
-
var CORE_ALERTMANAGER_CONFIG_FILE =
|
|
28028
|
-
var COMPOSE_OVERRIDE2 =
|
|
28029
|
-
var DEFAULT_INSTALL_PATH =
|
|
28252
|
+
var CORE_NO_WEBAPP_TEMPLATE_FILE = join8("core", "docker-compose.no-webapp.yml");
|
|
28253
|
+
var CORE_PROMTAIL_CONFIG_FILE = join8("backend", "core", "monitoring", "promtail", "promtail-config.yaml");
|
|
28254
|
+
var CORE_ALERTMANAGER_CONFIG_FILE = join8("backend", "core", "monitoring", "alertmanager", "alertmanager.yml");
|
|
28255
|
+
var COMPOSE_OVERRIDE2 = join8("backend", "core", "docker-compose.override.yml");
|
|
28256
|
+
var DEFAULT_INSTALL_PATH = join8(homedir6(), "openmates");
|
|
28030
28257
|
var REPO_URL = "https://github.com/glowingkitty/OpenMates.git";
|
|
28031
28258
|
var ROLE_PROVENANCE_SERVICE = {
|
|
28032
28259
|
core: "api",
|
|
@@ -28042,6 +28269,7 @@ var HEALTH_REQUEST_TIMEOUT_MS = 5e3;
|
|
|
28042
28269
|
var CHECKSUM_BUFFER_BYTES = 1024 * 1024;
|
|
28043
28270
|
var ENV_BACKUP_PREFIX = ".env.openmates-backup-";
|
|
28044
28271
|
var ENV_BACKUP_RETENTION_COUNT = 5;
|
|
28272
|
+
var BACKUP_FORMAT_VERSION = 2;
|
|
28045
28273
|
var IMAGE_CHANNEL_TAGS = {
|
|
28046
28274
|
stable: MAIN_BRANCH,
|
|
28047
28275
|
main: MAIN_BRANCH,
|
|
@@ -28115,8 +28343,8 @@ scrape_configs:
|
|
|
28115
28343
|
- labels:
|
|
28116
28344
|
level: log_level
|
|
28117
28345
|
`;
|
|
28118
|
-
var BACKEND_CONFIG_FILE =
|
|
28119
|
-
var IMAGE_RUNTIME_CONFIG_FILE =
|
|
28346
|
+
var BACKEND_CONFIG_FILE = join8("backend", "config", "backend_config.yml");
|
|
28347
|
+
var IMAGE_RUNTIME_CONFIG_FILE = join8("config", "backend_config.yml");
|
|
28120
28348
|
var LOCAL_AI_MODELS_FILE = "local-ai-models.yml";
|
|
28121
28349
|
var OFF_BY_DEFAULT_FEATURES = /* @__PURE__ */ new Map([
|
|
28122
28350
|
["embed:code:application", "Application previews are still unstable"],
|
|
@@ -28252,9 +28480,9 @@ function runInteractive(cmd, args, cwd) {
|
|
|
28252
28480
|
});
|
|
28253
28481
|
}
|
|
28254
28482
|
function beginEngineeringRuntimeOperation(installPath, operationType, services) {
|
|
28255
|
-
const manager =
|
|
28256
|
-
const sharedConfig =
|
|
28257
|
-
if (!
|
|
28483
|
+
const manager = join8(installPath, "scripts", "engineering_control_plane.py");
|
|
28484
|
+
const sharedConfig = join8(homedir6(), ".config", "openmates", "engineering-control-plane.env");
|
|
28485
|
+
if (!existsSync10(manager) || !existsSync10(sharedConfig)) return null;
|
|
28258
28486
|
const requestedBy = process.env.OPENCODE_SESSION_ID || `openmates-cli:${process.pid}`;
|
|
28259
28487
|
const args = [
|
|
28260
28488
|
manager,
|
|
@@ -28268,7 +28496,7 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
|
|
|
28268
28496
|
"dev-stack"
|
|
28269
28497
|
];
|
|
28270
28498
|
for (const service of services) args.push("--service", service);
|
|
28271
|
-
const output =
|
|
28499
|
+
const output = execFileSync4("python3", args, {
|
|
28272
28500
|
cwd: installPath,
|
|
28273
28501
|
encoding: "utf-8",
|
|
28274
28502
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -28278,8 +28506,8 @@ function beginEngineeringRuntimeOperation(installPath, operationType, services)
|
|
|
28278
28506
|
return parsed.operation_key;
|
|
28279
28507
|
}
|
|
28280
28508
|
function finishEngineeringRuntimeOperation(installPath, operationKey, status) {
|
|
28281
|
-
const manager =
|
|
28282
|
-
|
|
28509
|
+
const manager = join8(installPath, "scripts", "engineering_control_plane.py");
|
|
28510
|
+
execFileSync4(
|
|
28283
28511
|
"python3",
|
|
28284
28512
|
[manager, "operation", "update", "--operation-key", operationKey, "--status", status],
|
|
28285
28513
|
{ cwd: installPath, stdio: ["ignore", "ignore", "pipe"] }
|
|
@@ -28307,8 +28535,8 @@ function loadConfigForInstallPath(installPath) {
|
|
|
28307
28535
|
}
|
|
28308
28536
|
function getInstallMode(installPath, config = loadConfigForInstallPath(installPath)) {
|
|
28309
28537
|
if (config?.installMode) return config.installMode;
|
|
28310
|
-
if (
|
|
28311
|
-
if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) =>
|
|
28538
|
+
if (existsSync10(join8(installPath, SOURCE_COMPOSE_FILE))) return "source";
|
|
28539
|
+
if (Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync10(join8(installPath, composeFile)))) return "image";
|
|
28312
28540
|
return "source";
|
|
28313
28541
|
}
|
|
28314
28542
|
function getServerRole(flags, config) {
|
|
@@ -28439,26 +28667,26 @@ function composeArgs(installPath, withOverrides, installMode = getInstallMode(in
|
|
|
28439
28667
|
withOverrides,
|
|
28440
28668
|
installMode,
|
|
28441
28669
|
role,
|
|
28442
|
-
overrideExists:
|
|
28670
|
+
overrideExists: existsSync10(join8(installPath, COMPOSE_OVERRIDE2)),
|
|
28443
28671
|
deploymentMode,
|
|
28444
28672
|
overlayPath,
|
|
28445
28673
|
overlayComposeFile,
|
|
28446
|
-
overlayExists: deploymentMode === "official_cloud" &&
|
|
28674
|
+
overlayExists: deploymentMode === "official_cloud" && existsSync10(resolvedOverlayPath) && existsSync10(overlayComposeFile)
|
|
28447
28675
|
});
|
|
28448
28676
|
}
|
|
28449
28677
|
function ensureGitWorkDirEnv(installPath) {
|
|
28450
|
-
const envPath =
|
|
28451
|
-
if (!
|
|
28452
|
-
const content =
|
|
28678
|
+
const envPath = join8(installPath, ".env");
|
|
28679
|
+
if (!existsSync10(envPath)) return;
|
|
28680
|
+
const content = readFileSync8(envPath, "utf-8");
|
|
28453
28681
|
const lineRegex = /^GIT_WORK_DIR=.*$/m;
|
|
28454
28682
|
const value = `GIT_WORK_DIR=${installPath}`;
|
|
28455
28683
|
if (lineRegex.test(content)) {
|
|
28456
28684
|
const next = content.replace(lineRegex, value);
|
|
28457
|
-
if (next !== content)
|
|
28685
|
+
if (next !== content) writeFileSync6(envPath, next);
|
|
28458
28686
|
return;
|
|
28459
28687
|
}
|
|
28460
28688
|
const separator2 = content.endsWith("\n") ? "" : "\n";
|
|
28461
|
-
|
|
28689
|
+
writeFileSync6(envPath, `${content}${separator2}${value}
|
|
28462
28690
|
`);
|
|
28463
28691
|
}
|
|
28464
28692
|
function requireDocker() {
|
|
@@ -28479,7 +28707,7 @@ function requireGit() {
|
|
|
28479
28707
|
}
|
|
28480
28708
|
function getPackageVersion() {
|
|
28481
28709
|
try {
|
|
28482
|
-
const packageJson = JSON.parse(
|
|
28710
|
+
const packageJson = JSON.parse(readFileSync8(new URL("../package.json", import.meta.url), "utf-8"));
|
|
28483
28711
|
return packageJson.version ?? "";
|
|
28484
28712
|
} catch {
|
|
28485
28713
|
return "";
|
|
@@ -28572,20 +28800,20 @@ async function fetchText(url) {
|
|
|
28572
28800
|
return response.text();
|
|
28573
28801
|
}
|
|
28574
28802
|
function packagedTemplatePath(role) {
|
|
28575
|
-
return
|
|
28803
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", ROLE_TEMPLATE_FILES[role]);
|
|
28576
28804
|
}
|
|
28577
28805
|
function packagedNoWebappTemplatePath() {
|
|
28578
|
-
return
|
|
28806
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", CORE_NO_WEBAPP_TEMPLATE_FILE);
|
|
28579
28807
|
}
|
|
28580
28808
|
function packagedCaddyTemplatePath(role) {
|
|
28581
|
-
return
|
|
28809
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", "caddy", role, "Caddyfile");
|
|
28582
28810
|
}
|
|
28583
28811
|
function packagedCoreAlertmanagerTemplatePath() {
|
|
28584
|
-
return
|
|
28812
|
+
return join8(dirname6(new URL(import.meta.url).pathname), "..", "templates", "core", "monitoring", "alertmanager", "alertmanager.yml");
|
|
28585
28813
|
}
|
|
28586
28814
|
function readOfficialCloudNoWebappComposeTemplate() {
|
|
28587
28815
|
const packaged = packagedNoWebappTemplatePath();
|
|
28588
|
-
if (
|
|
28816
|
+
if (existsSync10(packaged)) return readFileSync8(packaged, "utf-8");
|
|
28589
28817
|
return [
|
|
28590
28818
|
"# backend/core/docker-compose.no-webapp.yml",
|
|
28591
28819
|
"#",
|
|
@@ -28600,51 +28828,51 @@ function readOfficialCloudNoWebappComposeTemplate() {
|
|
|
28600
28828
|
}
|
|
28601
28829
|
function ensureOfficialCloudNoWebappComposeFile(installPath, deploymentMode, role) {
|
|
28602
28830
|
if (deploymentMode !== "official_cloud" || role !== "core") return;
|
|
28603
|
-
const filePath =
|
|
28604
|
-
if (
|
|
28605
|
-
|
|
28606
|
-
|
|
28831
|
+
const filePath = join8(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE);
|
|
28832
|
+
if (existsSync10(filePath)) return;
|
|
28833
|
+
mkdirSync7(dirname6(filePath), { recursive: true });
|
|
28834
|
+
writeFileSync6(filePath, readOfficialCloudNoWebappComposeTemplate());
|
|
28607
28835
|
}
|
|
28608
28836
|
function fileHash(path2) {
|
|
28609
|
-
if (!
|
|
28610
|
-
return
|
|
28837
|
+
if (!existsSync10(path2)) return null;
|
|
28838
|
+
return createHash13("sha256").update(readFileSync8(path2)).digest("hex");
|
|
28611
28839
|
}
|
|
28612
28840
|
async function loadSelfHostComposeTemplate(templateRef, role) {
|
|
28613
28841
|
const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
|
|
28614
28842
|
if (templateDir) {
|
|
28615
|
-
return
|
|
28843
|
+
return readFileSync8(join8(resolve6(templateDir), ROLE_TEMPLATE_FILES[role]), "utf-8");
|
|
28616
28844
|
}
|
|
28617
28845
|
const overrideUrl = process.env.OPENMATES_SELFHOST_COMPOSE_URL;
|
|
28618
28846
|
if (overrideUrl) {
|
|
28619
28847
|
return fetchText(overrideUrl);
|
|
28620
28848
|
}
|
|
28621
28849
|
const packaged = packagedTemplatePath(role);
|
|
28622
|
-
if (
|
|
28850
|
+
if (existsSync10(packaged)) return readFileSync8(packaged, "utf-8");
|
|
28623
28851
|
return fetchText(`https://raw.githubusercontent.com/glowingkitty/OpenMates/${templateRef}/${ROLE_IMAGE_COMPOSE_FILES[role]}`);
|
|
28624
28852
|
}
|
|
28625
28853
|
async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
|
|
28626
|
-
const roleDir =
|
|
28627
|
-
const vaultConfigDir =
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28854
|
+
const roleDir = join8(installPath, "backend", role === "core" ? "core" : role);
|
|
28855
|
+
const vaultConfigDir = join8(roleDir, "vault", "config");
|
|
28856
|
+
mkdirSync7(vaultConfigDir, { recursive: true });
|
|
28857
|
+
mkdirSync7(join8(installPath, "config", "providers"), { recursive: true });
|
|
28858
|
+
writeFileSync6(join8(installPath, ROLE_IMAGE_COMPOSE_FILES[role]), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion()), role));
|
|
28631
28859
|
if (role === "core") {
|
|
28632
|
-
|
|
28860
|
+
writeFileSync6(join8(installPath, OFFICIAL_CLOUD_NO_WEBAPP_COMPOSE_FILE), readOfficialCloudNoWebappComposeTemplate());
|
|
28633
28861
|
}
|
|
28634
28862
|
if (role === "core") {
|
|
28635
|
-
const promtailConfigPath =
|
|
28636
|
-
|
|
28637
|
-
|
|
28863
|
+
const promtailConfigPath = join8(installPath, CORE_PROMTAIL_CONFIG_FILE);
|
|
28864
|
+
mkdirSync7(dirname6(promtailConfigPath), { recursive: true });
|
|
28865
|
+
writeFileSync6(promtailConfigPath, SELFHOST_PROMTAIL_CONFIG_TEMPLATE);
|
|
28638
28866
|
const alertmanagerTemplatePath = packagedCoreAlertmanagerTemplatePath();
|
|
28639
|
-
if (!
|
|
28640
|
-
const alertmanagerConfigPath =
|
|
28641
|
-
|
|
28867
|
+
if (!existsSync10(alertmanagerTemplatePath)) throw new Error(`Packaged Alertmanager config not found: ${alertmanagerTemplatePath}`);
|
|
28868
|
+
const alertmanagerConfigPath = join8(installPath, CORE_ALERTMANAGER_CONFIG_FILE);
|
|
28869
|
+
mkdirSync7(dirname6(alertmanagerConfigPath), { recursive: true });
|
|
28642
28870
|
copyFileSync2(alertmanagerTemplatePath, alertmanagerConfigPath);
|
|
28643
28871
|
}
|
|
28644
|
-
|
|
28872
|
+
writeFileSync6(join8(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
|
|
28645
28873
|
ensureImageRuntimeConfig(installPath);
|
|
28646
|
-
const envPath =
|
|
28647
|
-
let envContent =
|
|
28874
|
+
const envPath = join8(installPath, ".env");
|
|
28875
|
+
let envContent = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
|
|
28648
28876
|
envContent = setEnvIfEmpty(envContent, "DATABASE_ADMIN_PASSWORD", randomHex(12));
|
|
28649
28877
|
envContent = setEnvIfEmpty(envContent, "DATABASE_PASSWORD", randomHex(12));
|
|
28650
28878
|
envContent = setEnvIfEmpty(envContent, "DIRECTUS_TOKEN", randomHex(32));
|
|
@@ -28656,13 +28884,13 @@ async function writeImageModeRuntimeFiles(installPath, imageTag, role) {
|
|
|
28656
28884
|
envContent = setEnvVar(envContent, "OPENMATES_IMAGE_TAG", imageTag);
|
|
28657
28885
|
envContent = setEnvVar(envContent, "OPENMATES_IMAGE_REGISTRY", DEFAULT_IMAGE_REGISTRY);
|
|
28658
28886
|
envContent = setEnvVar(envContent, "GIT_WORK_DIR", installPath);
|
|
28659
|
-
|
|
28887
|
+
writeFileSync6(envPath, envContent.endsWith("\n") ? envContent : `${envContent}
|
|
28660
28888
|
`);
|
|
28661
28889
|
}
|
|
28662
28890
|
function getImageTagFromEnv(installPath, config) {
|
|
28663
|
-
const envPath =
|
|
28664
|
-
if (
|
|
28665
|
-
const envTag = getEnvVar(
|
|
28891
|
+
const envPath = join8(installPath, ".env");
|
|
28892
|
+
if (existsSync10(envPath)) {
|
|
28893
|
+
const envTag = getEnvVar(readFileSync8(envPath, "utf-8"), "OPENMATES_IMAGE_TAG");
|
|
28666
28894
|
if (envTag) return envTag;
|
|
28667
28895
|
}
|
|
28668
28896
|
return config?.imageTag ?? "";
|
|
@@ -28695,8 +28923,8 @@ async function waitForServerHealth(installPath, role = "core", options = {}) {
|
|
|
28695
28923
|
}
|
|
28696
28924
|
throw new Error(`Updated ${role} server did not pass health checks in time. Tried ${healthUrl}.`);
|
|
28697
28925
|
}
|
|
28698
|
-
const envPath =
|
|
28699
|
-
const envContent =
|
|
28926
|
+
const envPath = join8(installPath, ".env");
|
|
28927
|
+
const envContent = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
|
|
28700
28928
|
const urls = deriveSelfHostCliUrls(envContent);
|
|
28701
28929
|
const apiHealthUrl = `${trailingSlashTrimmed(urls.apiUrl)}/health`;
|
|
28702
28930
|
const appUrl = trailingSlashTrimmed(urls.appUrl);
|
|
@@ -28719,9 +28947,9 @@ function defaultCloneBranchForVersion(version) {
|
|
|
28719
28947
|
return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : null;
|
|
28720
28948
|
}
|
|
28721
28949
|
function hasLlmCredentials(envPath) {
|
|
28722
|
-
if (!
|
|
28950
|
+
if (!existsSync10(envPath)) return false;
|
|
28723
28951
|
if (hasLocalAiModels(dirname6(envPath))) return true;
|
|
28724
|
-
const content =
|
|
28952
|
+
const content = readFileSync8(envPath, "utf-8");
|
|
28725
28953
|
for (const line of content.split("\n")) {
|
|
28726
28954
|
const trimmed = line.trim();
|
|
28727
28955
|
if (trimmed.startsWith("#") || !trimmed) continue;
|
|
@@ -28736,8 +28964,8 @@ function hasLlmCredentials(envPath) {
|
|
|
28736
28964
|
return false;
|
|
28737
28965
|
}
|
|
28738
28966
|
function warnIfMissingLlmCredentials(installPath) {
|
|
28739
|
-
const envPath =
|
|
28740
|
-
if (!
|
|
28967
|
+
const envPath = join8(installPath, ".env");
|
|
28968
|
+
if (!existsSync10(envPath)) {
|
|
28741
28969
|
console.error(
|
|
28742
28970
|
"No .env file found. Run 'openmates server install' first, or create .env from .env.example."
|
|
28743
28971
|
);
|
|
@@ -28762,22 +28990,22 @@ function printJson(data) {
|
|
|
28762
28990
|
console.log(JSON.stringify(data, null, 2));
|
|
28763
28991
|
}
|
|
28764
28992
|
function localModelsOverlayPath(installPath, installMode = getInstallMode(installPath)) {
|
|
28765
|
-
if (installMode === "source") return
|
|
28766
|
-
return
|
|
28993
|
+
if (installMode === "source") return join8(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
|
|
28994
|
+
return join8(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
|
|
28767
28995
|
}
|
|
28768
28996
|
function imageBackendConfigPath(installPath) {
|
|
28769
|
-
return
|
|
28997
|
+
return join8(installPath, IMAGE_RUNTIME_CONFIG_FILE);
|
|
28770
28998
|
}
|
|
28771
28999
|
function ensureImageRuntimeConfig(installPath) {
|
|
28772
29000
|
const configPath = imageBackendConfigPath(installPath);
|
|
28773
|
-
if (
|
|
28774
|
-
|
|
28775
|
-
|
|
29001
|
+
if (existsSync10(configPath)) return;
|
|
29002
|
+
mkdirSync7(dirname6(configPath), { recursive: true });
|
|
29003
|
+
writeFileSync6(configPath, renderFeatureOverrides({ enabled: [], disabled: [] }));
|
|
28776
29004
|
}
|
|
28777
29005
|
function readLocalModelOverlay(path2) {
|
|
28778
|
-
if (!
|
|
29006
|
+
if (!existsSync10(path2)) return { providers: [] };
|
|
28779
29007
|
try {
|
|
28780
|
-
const parsed = JSON.parse(
|
|
29008
|
+
const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
28781
29009
|
return { providers: Array.isArray(parsed.providers) ? parsed.providers : [] };
|
|
28782
29010
|
} catch (error) {
|
|
28783
29011
|
throw new Error(
|
|
@@ -28786,15 +29014,15 @@ function readLocalModelOverlay(path2) {
|
|
|
28786
29014
|
}
|
|
28787
29015
|
}
|
|
28788
29016
|
function writeLocalModelOverlay(path2, overlay) {
|
|
28789
|
-
|
|
28790
|
-
|
|
29017
|
+
mkdirSync7(dirname6(path2), { recursive: true });
|
|
29018
|
+
writeFileSync6(path2, `${JSON.stringify(overlay, null, 2)}
|
|
28791
29019
|
`);
|
|
28792
29020
|
}
|
|
28793
29021
|
function hasLocalAiModels(installPath) {
|
|
28794
|
-
const imagePath =
|
|
28795
|
-
const sourcePath =
|
|
29022
|
+
const imagePath = join8(installPath, "config", "providers", LOCAL_AI_MODELS_FILE);
|
|
29023
|
+
const sourcePath = join8(installPath, "backend", "providers", LOCAL_AI_MODELS_FILE);
|
|
28796
29024
|
return [imagePath, sourcePath].some((path2) => {
|
|
28797
|
-
if (!
|
|
29025
|
+
if (!existsSync10(path2)) return false;
|
|
28798
29026
|
try {
|
|
28799
29027
|
return readLocalModelOverlay(path2).providers.some((provider) => provider.models.length > 0);
|
|
28800
29028
|
} catch {
|
|
@@ -28835,10 +29063,10 @@ function nowStamp() {
|
|
|
28835
29063
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28836
29064
|
}
|
|
28837
29065
|
function backupRoot(installPath) {
|
|
28838
|
-
return
|
|
29066
|
+
return join8(installPath, "backups");
|
|
28839
29067
|
}
|
|
28840
29068
|
function roleBackupDir(installPath, role) {
|
|
28841
|
-
return
|
|
29069
|
+
return join8(backupRoot(installPath), role);
|
|
28842
29070
|
}
|
|
28843
29071
|
function persistQuickServerTestOutcome(installPath, role, outcome) {
|
|
28844
29072
|
const current = readServerUpdateStatus(installPath, role);
|
|
@@ -28939,13 +29167,13 @@ function targetSourceLinks(imageTag, templateRef) {
|
|
|
28939
29167
|
};
|
|
28940
29168
|
}
|
|
28941
29169
|
function installedImageMetadata(input) {
|
|
28942
|
-
const containerId =
|
|
29170
|
+
const containerId = execFileSync4(
|
|
28943
29171
|
"docker",
|
|
28944
29172
|
[...composeArgs(input.installPath, input.withOverrides, "image", input.role), "ps", "-q", ROLE_PROVENANCE_SERVICE[input.role]],
|
|
28945
29173
|
{ cwd: input.installPath, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
28946
29174
|
).trim();
|
|
28947
29175
|
if (!containerId) throw new Error("updated_container_unavailable");
|
|
28948
|
-
const labels = JSON.parse(
|
|
29176
|
+
const labels = JSON.parse(execFileSync4(
|
|
28949
29177
|
"docker",
|
|
28950
29178
|
["inspect", "--format", "{{json .Config.Labels}}", containerId],
|
|
28951
29179
|
{ cwd: input.installPath, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
@@ -28987,15 +29215,15 @@ function sourceUpdateMetadata(installPath) {
|
|
|
28987
29215
|
};
|
|
28988
29216
|
}
|
|
28989
29217
|
function copyIfExists(source, destination) {
|
|
28990
|
-
if (!
|
|
28991
|
-
|
|
29218
|
+
if (!existsSync10(source)) return;
|
|
29219
|
+
mkdirSync7(dirname6(destination), { recursive: true });
|
|
28992
29220
|
cpSync(source, destination, { recursive: true, force: true });
|
|
28993
29221
|
}
|
|
28994
29222
|
function readEnvMap(installPath) {
|
|
28995
|
-
const envPath =
|
|
28996
|
-
if (!
|
|
29223
|
+
const envPath = join8(installPath, ".env");
|
|
29224
|
+
if (!existsSync10(envPath)) return {};
|
|
28997
29225
|
const values = {};
|
|
28998
|
-
for (const line of
|
|
29226
|
+
for (const line of readFileSync8(envPath, "utf-8").split("\n")) {
|
|
28999
29227
|
const trimmed = line.trim();
|
|
29000
29228
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
29001
29229
|
const eqIdx = trimmed.indexOf("=");
|
|
@@ -29005,32 +29233,32 @@ function readEnvMap(installPath) {
|
|
|
29005
29233
|
return values;
|
|
29006
29234
|
}
|
|
29007
29235
|
function envPathForInstall(installPath) {
|
|
29008
|
-
return
|
|
29236
|
+
return join8(installPath, ".env");
|
|
29009
29237
|
}
|
|
29010
29238
|
function readEnvContent(installPath) {
|
|
29011
29239
|
const envPath = envPathForInstall(installPath);
|
|
29012
|
-
return
|
|
29240
|
+
return existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
|
|
29013
29241
|
}
|
|
29014
29242
|
function pruneEnvBackups(installPath) {
|
|
29015
|
-
const backups =
|
|
29243
|
+
const backups = readdirSync5(installPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.startsWith(ENV_BACKUP_PREFIX)).map((entry) => entry.name).sort();
|
|
29016
29244
|
for (const backup of backups.slice(0, Math.max(0, backups.length - ENV_BACKUP_RETENTION_COUNT))) {
|
|
29017
|
-
|
|
29245
|
+
rmSync7(join8(installPath, backup), { force: true });
|
|
29018
29246
|
}
|
|
29019
29247
|
}
|
|
29020
29248
|
function backupEnvFile(installPath) {
|
|
29021
29249
|
const envPath = envPathForInstall(installPath);
|
|
29022
|
-
if (!
|
|
29023
|
-
const backupPath =
|
|
29250
|
+
if (!existsSync10(envPath)) return null;
|
|
29251
|
+
const backupPath = join8(installPath, `${ENV_BACKUP_PREFIX}${nowStamp()}`);
|
|
29024
29252
|
copyFileSync2(envPath, backupPath);
|
|
29025
|
-
|
|
29253
|
+
chmodSync5(backupPath, 384);
|
|
29026
29254
|
pruneEnvBackups(installPath);
|
|
29027
29255
|
return backupPath;
|
|
29028
29256
|
}
|
|
29029
29257
|
function writeEnvContent(installPath, content) {
|
|
29030
29258
|
const envPath = envPathForInstall(installPath);
|
|
29031
|
-
|
|
29032
|
-
|
|
29033
|
-
|
|
29259
|
+
mkdirSync7(dirname6(envPath), { recursive: true });
|
|
29260
|
+
writeFileSync6(envPath, content, { mode: 384 });
|
|
29261
|
+
chmodSync5(envPath, 384);
|
|
29034
29262
|
}
|
|
29035
29263
|
function writeDeploymentModeEnv(installPath, deploymentMode, overlayPath) {
|
|
29036
29264
|
let content = readEnvContent(installPath);
|
|
@@ -29044,7 +29272,7 @@ function writeDeploymentModeEnv(installPath, deploymentMode, overlayPath) {
|
|
|
29044
29272
|
}
|
|
29045
29273
|
const resolvedOverlayPath = resolve6(overlayPath ?? defaultOpenMatesCloudOverlayPath(installPath));
|
|
29046
29274
|
const overlayComposeFile = defaultOpenMatesCloudComposeFile(resolvedOverlayPath);
|
|
29047
|
-
if (!
|
|
29275
|
+
if (!existsSync10(resolvedOverlayPath) || !existsSync10(overlayComposeFile)) {
|
|
29048
29276
|
throw new Error(
|
|
29049
29277
|
`OpenMatesCloud official-cloud mode requires an overlay checkout with ${overlayComposeFile}. Pass --openmatescloud-path <dir> or create the sibling OpenMatesCloud repository.`
|
|
29050
29278
|
);
|
|
@@ -29203,7 +29431,7 @@ for item in checks:
|
|
|
29203
29431
|
print(json.dumps({"ok": True, "presence": presence}))
|
|
29204
29432
|
`;
|
|
29205
29433
|
try {
|
|
29206
|
-
const output =
|
|
29434
|
+
const output = execFileSync4(
|
|
29207
29435
|
"docker",
|
|
29208
29436
|
["exec", "-e", `OPENMATES_SECRET_CHECKS=${JSON.stringify(checks)}`, container, "python", "-c", script],
|
|
29209
29437
|
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -29241,7 +29469,7 @@ function formatSecretPreflight(preflight) {
|
|
|
29241
29469
|
return parts.join("; ");
|
|
29242
29470
|
}
|
|
29243
29471
|
function hashFile(path2) {
|
|
29244
|
-
const hash =
|
|
29472
|
+
const hash = createHash13("sha256");
|
|
29245
29473
|
const fd = openSync2(path2, "r");
|
|
29246
29474
|
const buffer = Buffer.allocUnsafe(CHECKSUM_BUFFER_BYTES);
|
|
29247
29475
|
try {
|
|
@@ -29258,8 +29486,8 @@ function hashFile(path2) {
|
|
|
29258
29486
|
function writeChecksums(rootDir) {
|
|
29259
29487
|
const lines = [];
|
|
29260
29488
|
const walk = (dir) => {
|
|
29261
|
-
for (const entry of
|
|
29262
|
-
const path2 =
|
|
29489
|
+
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
29490
|
+
const path2 = join8(dir, entry.name);
|
|
29263
29491
|
if (entry.isDirectory()) {
|
|
29264
29492
|
walk(path2);
|
|
29265
29493
|
continue;
|
|
@@ -29270,13 +29498,13 @@ function writeChecksums(rootDir) {
|
|
|
29270
29498
|
}
|
|
29271
29499
|
};
|
|
29272
29500
|
walk(rootDir);
|
|
29273
|
-
|
|
29501
|
+
writeFileSync6(join8(rootDir, "checksums.sha256"), `${lines.sort().join("\n")}
|
|
29274
29502
|
`);
|
|
29275
29503
|
}
|
|
29276
29504
|
function verifyChecksums(rootDir) {
|
|
29277
|
-
const checksumsPath =
|
|
29278
|
-
if (!
|
|
29279
|
-
for (const line of
|
|
29505
|
+
const checksumsPath = join8(rootDir, "checksums.sha256");
|
|
29506
|
+
if (!existsSync10(checksumsPath)) throw new Error("Backup archive is missing checksums.sha256.");
|
|
29507
|
+
for (const line of readFileSync8(checksumsPath, "utf-8").split("\n")) {
|
|
29280
29508
|
const trimmed = line.trim();
|
|
29281
29509
|
if (!trimmed) continue;
|
|
29282
29510
|
const match = trimmed.match(/^([a-f0-9]{64}) {2}(.+)$/);
|
|
@@ -29285,8 +29513,8 @@ function verifyChecksums(rootDir) {
|
|
|
29285
29513
|
if (relative3.startsWith("/") || relative3.split(/[\\/]/).includes("..")) {
|
|
29286
29514
|
throw new Error(`Unsafe checksum path in backup archive: ${relative3}`);
|
|
29287
29515
|
}
|
|
29288
|
-
const filePath =
|
|
29289
|
-
if (!
|
|
29516
|
+
const filePath = join8(rootDir, relative3);
|
|
29517
|
+
if (!existsSync10(filePath)) throw new Error(`Backup archive is missing checksummed file: ${relative3}`);
|
|
29290
29518
|
const actual = hashFile(filePath);
|
|
29291
29519
|
if (actual !== match[1]) throw new Error(`Backup checksum mismatch for ${relative3}.`);
|
|
29292
29520
|
}
|
|
@@ -29294,27 +29522,28 @@ function verifyChecksums(rootDir) {
|
|
|
29294
29522
|
function createServerBackup(installPath, role, options = {}) {
|
|
29295
29523
|
const plan = planBackup({ role, includeObservability: options.includeObservability });
|
|
29296
29524
|
const backupDir = roleBackupDir(installPath, role);
|
|
29297
|
-
|
|
29298
|
-
const archivePath = options.output ? resolve6(options.output) :
|
|
29299
|
-
|
|
29525
|
+
mkdirSync7(backupDir, { recursive: true, mode: 448 });
|
|
29526
|
+
const archivePath = options.output ? resolve6(options.output) : join8(backupDir, options.preUpdate ? `latest-pre-update-${role}.tar.gz` : `openmates-${role}-${nowStamp()}.tar.gz`);
|
|
29527
|
+
mkdirSync7(dirname6(archivePath), { recursive: true, mode: 448 });
|
|
29528
|
+
const tempDir = mkdtempSync(join8(backupDir, ".tmp-"));
|
|
29300
29529
|
const env = readEnvMap(installPath);
|
|
29301
29530
|
const manifest = {
|
|
29302
29531
|
role,
|
|
29532
|
+
backup_format_version: BACKUP_FORMAT_VERSION,
|
|
29303
29533
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29304
29534
|
cli_version: getPackageVersion(),
|
|
29305
|
-
image_tag: getEnvVar(
|
|
29306
|
-
|
|
29307
|
-
|
|
29535
|
+
image_tag: getEnvVar(existsSync10(join8(installPath, ".env")) ? readFileSync8(join8(installPath, ".env"), "utf-8") : "", "OPENMATES_IMAGE_TAG"),
|
|
29536
|
+
recovery_scope: role === "core" ? "database-and-runtime-only" : "runtime-only",
|
|
29537
|
+
include_observability: false,
|
|
29538
|
+
contents: plan.contents.filter((content) => content !== "runtime-env" || existsSync10(join8(installPath, ".env"))).filter((content) => content !== "runtime-config" || existsSync10(join8(installPath, "config")))
|
|
29308
29539
|
};
|
|
29309
29540
|
try {
|
|
29310
|
-
|
|
29311
|
-
|
|
29312
|
-
copyIfExists(join7(installPath, ".env"), join7(tempDir, "runtime", ".env"));
|
|
29313
|
-
copyIfExists(join7(installPath, "config"), join7(tempDir, "runtime", "config"));
|
|
29541
|
+
copyIfExists(join8(installPath, ".env"), join8(tempDir, "runtime", ".env"));
|
|
29542
|
+
copyIfExists(join8(installPath, "config"), join8(tempDir, "runtime", "config"));
|
|
29314
29543
|
if (role === "core") {
|
|
29315
29544
|
const databaseUser = env.DATABASE_USERNAME || "directus";
|
|
29316
29545
|
const databaseName = env.DATABASE_NAME || "directus";
|
|
29317
|
-
const dumpFile = openSync2(
|
|
29546
|
+
const dumpFile = openSync2(join8(tempDir, "postgres.sql"), "w");
|
|
29318
29547
|
try {
|
|
29319
29548
|
const result = spawnSync(
|
|
29320
29549
|
"docker",
|
|
@@ -29329,47 +29558,52 @@ function createServerBackup(installPath, role, options = {}) {
|
|
|
29329
29558
|
closeSync2(dumpFile);
|
|
29330
29559
|
}
|
|
29331
29560
|
}
|
|
29332
|
-
|
|
29333
|
-
|
|
29334
|
-
[join7(installPath, "backend", "core", "extensions"), join7(tempDir, "directus-extensions")],
|
|
29335
|
-
[join7(installPath, "backend", role, "vault"), join7(tempDir, `${role}-vault-config`)]
|
|
29336
|
-
]) {
|
|
29337
|
-
copyIfExists(item[0], item[1]);
|
|
29338
|
-
}
|
|
29561
|
+
writeFileSync6(join8(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
29562
|
+
`, { mode: 384 });
|
|
29339
29563
|
writeChecksums(tempDir);
|
|
29340
|
-
|
|
29341
|
-
chmodSync4(archivePath, plan.fileMode);
|
|
29564
|
+
publishServerBackupArchive(tempDir, archivePath);
|
|
29342
29565
|
return archivePath;
|
|
29343
29566
|
} finally {
|
|
29344
|
-
|
|
29567
|
+
rmSync7(tempDir, { recursive: true, force: true });
|
|
29345
29568
|
}
|
|
29346
29569
|
}
|
|
29347
29570
|
function restoreServerBackup(installPath, role, file) {
|
|
29348
29571
|
const archivePath = resolve6(file);
|
|
29349
|
-
if (!
|
|
29350
|
-
|
|
29351
|
-
const tempDir = mkdtempSync(
|
|
29572
|
+
if (!existsSync10(archivePath)) throw new Error(`Backup file not found: ${archivePath}`);
|
|
29573
|
+
mkdirSync7(roleBackupDir(installPath, role), { recursive: true, mode: 448 });
|
|
29574
|
+
const tempDir = mkdtempSync(join8(roleBackupDir(installPath, role), ".restore-"));
|
|
29352
29575
|
const env = readEnvMap(installPath);
|
|
29353
29576
|
try {
|
|
29354
29577
|
execSync(`tar -xzf ${shellQuote(archivePath)} -C ${shellQuote(tempDir)}`, { stdio: "pipe" });
|
|
29355
29578
|
verifyChecksums(tempDir);
|
|
29356
|
-
const manifestPath =
|
|
29357
|
-
if (!
|
|
29358
|
-
const manifest = JSON.parse(
|
|
29579
|
+
const manifestPath = join8(tempDir, "manifest.json");
|
|
29580
|
+
if (!existsSync10(manifestPath)) throw new Error("Backup archive is missing manifest.json.");
|
|
29581
|
+
const manifest = JSON.parse(readFileSync8(manifestPath, "utf-8"));
|
|
29359
29582
|
if (manifest.role !== role) throw new Error(`Backup role '${manifest.role}' does not match requested role '${role}'.`);
|
|
29360
|
-
|
|
29361
|
-
|
|
29362
|
-
|
|
29363
|
-
|
|
29583
|
+
if (role === "core" && manifest.backup_format_version === BACKUP_FORMAT_VERSION && manifest.recovery_scope !== "full-core") {
|
|
29584
|
+
throw new Error("This backup records database and runtime state only; refusing unsafe full core restore.");
|
|
29585
|
+
}
|
|
29586
|
+
copyIfExists(join8(tempDir, "runtime", ".env"), join8(installPath, ".env"));
|
|
29587
|
+
copyIfExists(join8(tempDir, "runtime", "config"), join8(installPath, "config"));
|
|
29588
|
+
const postgresDump = join8(tempDir, "postgres.sql");
|
|
29589
|
+
if (role === "core" && existsSync10(postgresDump)) {
|
|
29364
29590
|
const databaseUser = env.DATABASE_USERNAME || "directus";
|
|
29365
29591
|
const databaseName = env.DATABASE_NAME || "directus";
|
|
29366
|
-
|
|
29367
|
-
|
|
29368
|
-
|
|
29369
|
-
|
|
29592
|
+
const dumpFile = openSync2(postgresDump, "r");
|
|
29593
|
+
try {
|
|
29594
|
+
const result = spawnSync(
|
|
29595
|
+
"docker",
|
|
29596
|
+
["exec", "-i", "cms-database", "psql", "-v", "ON_ERROR_STOP=1", "-U", databaseUser, databaseName],
|
|
29597
|
+
{ encoding: "utf-8", stdio: [dumpFile, "pipe", "pipe"] }
|
|
29598
|
+
);
|
|
29599
|
+
if (result.error) throw result.error;
|
|
29600
|
+
if (result.status !== 0) throw new Error(result.stderr || `psql exited with status ${result.status}`);
|
|
29601
|
+
} finally {
|
|
29602
|
+
closeSync2(dumpFile);
|
|
29603
|
+
}
|
|
29370
29604
|
}
|
|
29371
29605
|
} finally {
|
|
29372
|
-
|
|
29606
|
+
rmSync7(tempDir, { recursive: true, force: true });
|
|
29373
29607
|
}
|
|
29374
29608
|
}
|
|
29375
29609
|
function restoreStopServices(installPath, withOverrides, installMode, role) {
|
|
@@ -29638,11 +29872,11 @@ async function serverRegister(flags) {
|
|
|
29638
29872
|
const installPath = resolveServerPath(
|
|
29639
29873
|
typeof flags.path === "string" ? flags : { ...flags, path: process.cwd() }
|
|
29640
29874
|
);
|
|
29641
|
-
const envPath =
|
|
29642
|
-
if (!
|
|
29643
|
-
throw new Error(`Source Compose file not found: ${
|
|
29875
|
+
const envPath = join8(installPath, ".env");
|
|
29876
|
+
if (!existsSync10(join8(installPath, SOURCE_COMPOSE_FILE))) {
|
|
29877
|
+
throw new Error(`Source Compose file not found: ${join8(installPath, SOURCE_COMPOSE_FILE)}`);
|
|
29644
29878
|
}
|
|
29645
|
-
if (!
|
|
29879
|
+
if (!existsSync10(envPath)) {
|
|
29646
29880
|
throw new Error(`Runtime env file not found: ${envPath}`);
|
|
29647
29881
|
}
|
|
29648
29882
|
const gitRoot = resolve6(exec("git rev-parse --show-toplevel", installPath));
|
|
@@ -29664,7 +29898,7 @@ async function serverRegister(flags) {
|
|
|
29664
29898
|
}
|
|
29665
29899
|
const overlayPath = getOpenMatesCloudOverlayPath(flags, null) ?? readEnvMap(installPath).OPENMATES_CLOUD_OVERLAY_PATH ?? defaultOpenMatesCloudOverlayPath(installPath);
|
|
29666
29900
|
const overlayComposeFile = defaultOpenMatesCloudComposeFile(overlayPath);
|
|
29667
|
-
if (deploymentMode === "official_cloud" && (!
|
|
29901
|
+
if (deploymentMode === "official_cloud" && (!existsSync10(overlayPath) || !existsSync10(overlayComposeFile))) {
|
|
29668
29902
|
throw new Error(`OpenMatesCloud overlay path is required for official-cloud mode: ${overlayPath}`);
|
|
29669
29903
|
}
|
|
29670
29904
|
const runtimePlan = planServerRuntime({
|
|
@@ -29674,7 +29908,7 @@ async function serverRegister(flags) {
|
|
|
29674
29908
|
includeWebapp: deploymentMode !== "official_cloud"
|
|
29675
29909
|
});
|
|
29676
29910
|
const defaultServices = hasServiceFilter(flags) ? selectedComposeServices(role, flags) : runtimePlan.defaultServices;
|
|
29677
|
-
const cliUrls = deriveSelfHostCliUrls(
|
|
29911
|
+
const cliUrls = deriveSelfHostCliUrls(readFileSync8(envPath, "utf-8"));
|
|
29678
29912
|
const config = {
|
|
29679
29913
|
installPath,
|
|
29680
29914
|
installedAt: existing?.installPath === installPath ? existing.installedAt : Date.now(),
|
|
@@ -29716,27 +29950,27 @@ async function serverInstall(flags) {
|
|
|
29716
29950
|
withAlerts: flags["with-alerts"] === true,
|
|
29717
29951
|
includeWebapp: deploymentMode !== "official_cloud"
|
|
29718
29952
|
});
|
|
29719
|
-
if (
|
|
29953
|
+
if (existsSync10(join8(installPath, SOURCE_COMPOSE_FILE)) || Object.values(ROLE_IMAGE_COMPOSE_FILES).some((composeFile) => existsSync10(join8(installPath, composeFile)))) {
|
|
29720
29954
|
console.error(`OpenMates already exists at ${installPath}.`);
|
|
29721
29955
|
console.error("Use 'openmates server update' to update, or choose a different --path.");
|
|
29722
29956
|
process.exit(1);
|
|
29723
29957
|
}
|
|
29724
29958
|
if (!fromSource) {
|
|
29725
29959
|
requireDocker();
|
|
29726
|
-
|
|
29960
|
+
mkdirSync7(installPath, { recursive: true });
|
|
29727
29961
|
if (typeof flags["env-path"] === "string") {
|
|
29728
29962
|
const envSource = resolve6(flags["env-path"]);
|
|
29729
|
-
if (!
|
|
29963
|
+
if (!existsSync10(envSource)) {
|
|
29730
29964
|
throw new Error(`Env file not found: ${envSource}`);
|
|
29731
29965
|
}
|
|
29732
|
-
copyFileSync2(envSource,
|
|
29966
|
+
copyFileSync2(envSource, join8(installPath, ".env"));
|
|
29733
29967
|
console.error(`Copied ${envSource} to ${installPath}/.env`);
|
|
29734
29968
|
}
|
|
29735
29969
|
const imageTag = typeof flags["image-tag"] === "string" ? flags["image-tag"] : getDefaultImageTag();
|
|
29736
29970
|
console.error(`Preparing OpenMates image-mode install at ${installPath}...`);
|
|
29737
29971
|
await writeImageModeRuntimeFiles(installPath, imageTag, role);
|
|
29738
29972
|
const overlay2 = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
|
|
29739
|
-
const cliUrls2 = deriveSelfHostCliUrls(
|
|
29973
|
+
const cliUrls2 = deriveSelfHostCliUrls(readFileSync8(join8(installPath, ".env"), "utf-8"));
|
|
29740
29974
|
try {
|
|
29741
29975
|
exec("docker network create openmates", installPath);
|
|
29742
29976
|
} catch {
|
|
@@ -29763,7 +29997,7 @@ async function serverInstall(flags) {
|
|
|
29763
29997
|
if (flags.json === true) {
|
|
29764
29998
|
printJson({ command: "install", status: "success", path: installPath, mode: "image", imageTag });
|
|
29765
29999
|
} else {
|
|
29766
|
-
const firstInvite = getEnvVar(
|
|
30000
|
+
const firstInvite = getEnvVar(readFileSync8(join8(installPath, ".env"), "utf-8"), "SELF_HOST_FIRST_INVITE_CODE");
|
|
29767
30001
|
console.log(`
|
|
29768
30002
|
OpenMates installed at ${installPath}`);
|
|
29769
30003
|
console.log(`Mode: image (${DEFAULT_IMAGE_REGISTRY}, tag ${imageTag})`);
|
|
@@ -29801,10 +30035,10 @@ CLI default API: ${cliUrls2.apiUrl}`);
|
|
|
29801
30035
|
}
|
|
29802
30036
|
if (typeof flags["env-path"] === "string") {
|
|
29803
30037
|
const envSource = resolve6(flags["env-path"]);
|
|
29804
|
-
if (!
|
|
30038
|
+
if (!existsSync10(envSource)) {
|
|
29805
30039
|
throw new Error(`Env file not found: ${envSource}`);
|
|
29806
30040
|
}
|
|
29807
|
-
copyFileSync2(envSource,
|
|
30041
|
+
copyFileSync2(envSource, join8(installPath, ".env"));
|
|
29808
30042
|
console.error(`Copied ${envSource} to ${installPath}/.env`);
|
|
29809
30043
|
}
|
|
29810
30044
|
const translationResult = ensureSourceInstallTranslations(installPath, sourcePath);
|
|
@@ -29823,9 +30057,9 @@ CLI default API: ${cliUrls2.apiUrl}`);
|
|
|
29823
30057
|
console.error("Setup script failed. Check the output above for details.");
|
|
29824
30058
|
process.exit(setupCode);
|
|
29825
30059
|
}
|
|
29826
|
-
const envPath =
|
|
30060
|
+
const envPath = join8(installPath, ".env");
|
|
29827
30061
|
const overlay = writeDeploymentModeEnv(installPath, deploymentMode, requestedOverlayPath);
|
|
29828
|
-
const cliUrls = deriveSelfHostCliUrls(
|
|
30062
|
+
const cliUrls = deriveSelfHostCliUrls(existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "");
|
|
29829
30063
|
saveServerConfig({
|
|
29830
30064
|
installPath,
|
|
29831
30065
|
installedAt: Date.now(),
|
|
@@ -29866,15 +30100,15 @@ async function installContinuousUpdateService(flags) {
|
|
|
29866
30100
|
const channel = typeof flags.channel === "string" ? flags.channel : config?.imageChannel ?? "main";
|
|
29867
30101
|
const window = typeof flags.window === "string" ? flags.window : "02:00-04:00 UTC";
|
|
29868
30102
|
const plan = planContinuousUpdateService({ role, channel, window });
|
|
29869
|
-
const servicePath =
|
|
29870
|
-
const timerPath =
|
|
30103
|
+
const servicePath = join8("/etc", "systemd", "system", plan.serviceName);
|
|
30104
|
+
const timerPath = join8("/etc", "systemd", "system", plan.timerName);
|
|
29871
30105
|
if (flags["dry-run"] === true || flags.json === true) {
|
|
29872
30106
|
printJson({ command: "update install-service", status: "planned", role, servicePath, timerPath, unit: plan.unit, timer: plan.timer });
|
|
29873
30107
|
return;
|
|
29874
30108
|
}
|
|
29875
30109
|
try {
|
|
29876
|
-
|
|
29877
|
-
|
|
30110
|
+
writeFileSync6(servicePath, plan.unit, { mode: 420 });
|
|
30111
|
+
writeFileSync6(timerPath, plan.timer, { mode: 420 });
|
|
29878
30112
|
execSync("systemctl daemon-reload", { stdio: "pipe" });
|
|
29879
30113
|
execSync(`systemctl enable --now ${shellQuote(plan.timerName)}`, { stdio: "pipe" });
|
|
29880
30114
|
} catch (error) {
|
|
@@ -29885,13 +30119,13 @@ async function installContinuousUpdateService(flags) {
|
|
|
29885
30119
|
console.log(`Installed ${plan.timerName}.`);
|
|
29886
30120
|
}
|
|
29887
30121
|
function runRuntimeVerification(installPath, role, config) {
|
|
29888
|
-
const envText =
|
|
30122
|
+
const envText = existsSync10(join8(installPath, ".env")) ? readFileSync8(join8(installPath, ".env"), "utf-8") : "";
|
|
29889
30123
|
const mode = resolveRuntimeDeploymentMode({
|
|
29890
30124
|
envText,
|
|
29891
|
-
overlayExists:
|
|
30125
|
+
overlayExists: existsSync10(config?.openMatesCloudOverlayPath ?? defaultOpenMatesCloudOverlayPath(installPath))
|
|
29892
30126
|
});
|
|
29893
|
-
const backupPath =
|
|
29894
|
-
const plan = planRuntimeVerification({ role, deploymentMode: mode.effectiveMode, hasVerifiedBackup:
|
|
30127
|
+
const backupPath = join8(roleBackupDir(installPath, role), `latest-pre-update-${role}.tar.gz`);
|
|
30128
|
+
const plan = planRuntimeVerification({ role, deploymentMode: mode.effectiveMode, hasVerifiedBackup: existsSync10(backupPath) });
|
|
29895
30129
|
const verifierService = { core: "api", upload: "app-uploads", preview: "preview" }[role];
|
|
29896
30130
|
const installMode = getInstallMode(installPath, config);
|
|
29897
30131
|
const withOverrides = config?.composeProfile === "full";
|
|
@@ -29992,7 +30226,7 @@ async function runUpdateCompletionEmailGate(input) {
|
|
|
29992
30226
|
deliveryId: deliveryPlan.deliveryId
|
|
29993
30227
|
};
|
|
29994
30228
|
}
|
|
29995
|
-
const deliveryId = deliveryPlan.deliveryId ??
|
|
30229
|
+
const deliveryId = deliveryPlan.deliveryId ?? randomUUID11();
|
|
29996
30230
|
const deliveryPendingAt = deliveryPlan.pendingAt ?? completedAt;
|
|
29997
30231
|
const previousAttempts = deliveryPlan.previousAttempts ?? 0;
|
|
29998
30232
|
const { role: _role, updated_at: _updatedAt, ...currentStatus } = readServerUpdateStatus(input.installPath, input.role);
|
|
@@ -30098,10 +30332,10 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30098
30332
|
[operationalPlan.watchdogTimerName, operationalPlan.watchdogTimer]
|
|
30099
30333
|
] : []
|
|
30100
30334
|
].filter((entry) => Boolean(entry[0] && entry[1]));
|
|
30101
|
-
const reportStatePath =
|
|
30102
|
-
const reportMetricsDir =
|
|
30103
|
-
const startedMetricPath =
|
|
30104
|
-
const successMetricPath =
|
|
30335
|
+
const reportStatePath = join8(installPath, ".openmates", "runtime-health", `${role}-operational-report.json`);
|
|
30336
|
+
const reportMetricsDir = join8(installPath, ".openmates", "runtime-health", "metrics");
|
|
30337
|
+
const startedMetricPath = join8(reportMetricsDir, "operational-report-started.prom");
|
|
30338
|
+
const successMetricPath = join8(reportMetricsDir, "operational-report.prom");
|
|
30105
30339
|
const operationalUnitNames = [
|
|
30106
30340
|
operationalPlan.digestServiceName,
|
|
30107
30341
|
operationalPlan.digestTimerName,
|
|
@@ -30109,18 +30343,18 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30109
30343
|
operationalPlan.watchdogTimerName
|
|
30110
30344
|
];
|
|
30111
30345
|
if (operationalPlan.scheduleEnabled) {
|
|
30112
|
-
|
|
30113
|
-
|
|
30114
|
-
if (!
|
|
30115
|
-
|
|
30346
|
+
mkdirSync7(dirname6(reportStatePath), { recursive: true, mode: 448 });
|
|
30347
|
+
mkdirSync7(reportMetricsDir, { recursive: true, mode: 448 });
|
|
30348
|
+
if (!existsSync10(reportStatePath)) {
|
|
30349
|
+
writeFileSync6(
|
|
30116
30350
|
reportStatePath,
|
|
30117
30351
|
`${JSON.stringify({ incidentOpen: false, monitoringStartedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
30118
30352
|
`,
|
|
30119
30353
|
{ mode: 384 }
|
|
30120
30354
|
);
|
|
30121
30355
|
}
|
|
30122
|
-
if (!
|
|
30123
|
-
|
|
30356
|
+
if (!existsSync10(startedMetricPath)) {
|
|
30357
|
+
writeFileSync6(
|
|
30124
30358
|
startedMetricPath,
|
|
30125
30359
|
`operational_report_monitoring_started_timestamp_seconds{environment="${operationalPlan.environment}"} ${Math.floor(Date.now() / 1e3)}
|
|
30126
30360
|
`,
|
|
@@ -30128,21 +30362,21 @@ async function installRuntimeMonitoringServices(installPath, role) {
|
|
|
30128
30362
|
);
|
|
30129
30363
|
}
|
|
30130
30364
|
} else {
|
|
30131
|
-
const installedOperationalUnits = operationalUnitNames.filter((name) =>
|
|
30365
|
+
const installedOperationalUnits = operationalUnitNames.filter((name) => existsSync10(join8("/etc", "systemd", "system", name)));
|
|
30132
30366
|
const installedOperationalTimers = installedOperationalUnits.filter((name) => name.endsWith(".timer"));
|
|
30133
30367
|
if (installedOperationalTimers.length) {
|
|
30134
|
-
|
|
30368
|
+
execFileSync4("systemctl", ["disable", "--now", ...installedOperationalTimers], { stdio: "pipe" });
|
|
30135
30369
|
}
|
|
30136
|
-
for (const name of installedOperationalUnits)
|
|
30370
|
+
for (const name of installedOperationalUnits) rmSync7(join8("/etc", "systemd", "system", name));
|
|
30137
30371
|
for (const path2 of [reportStatePath, startedMetricPath, successMetricPath]) {
|
|
30138
|
-
if (
|
|
30372
|
+
if (existsSync10(path2)) rmSync7(path2);
|
|
30139
30373
|
}
|
|
30140
30374
|
}
|
|
30141
|
-
for (const [name, content] of files)
|
|
30375
|
+
for (const [name, content] of files) writeFileSync6(join8("/etc", "systemd", "system", name), content, { mode: 420 });
|
|
30142
30376
|
execSync("systemctl daemon-reload", { stdio: "pipe" });
|
|
30143
30377
|
const timers = files.filter(([name]) => name.endsWith(".timer")).map(([name]) => name);
|
|
30144
30378
|
execSync(`systemctl enable --now ${timers.map(shellQuote).join(" ")}`, { stdio: "pipe" });
|
|
30145
|
-
for (const timer of timers)
|
|
30379
|
+
for (const timer of timers) execFileSync4("systemctl", ["is-active", "--quiet", timer], { stdio: "pipe" });
|
|
30146
30380
|
}
|
|
30147
30381
|
async function autoInstallRuntimeMonitoringServices(installPath, role) {
|
|
30148
30382
|
if (!shouldAutoInstallRuntimeMonitoringServices(process.env)) {
|
|
@@ -30160,10 +30394,10 @@ async function serverUpdate(client, rest, flags) {
|
|
|
30160
30394
|
const filePath = serverUpdateStatusFile(installPath2, role2);
|
|
30161
30395
|
const status = readServerUpdateStatus(installPath2, role2);
|
|
30162
30396
|
if (flags.json === true) {
|
|
30163
|
-
printJson(
|
|
30397
|
+
printJson(existsSync10(filePath) ? status : { role: role2, status: "unknown" });
|
|
30164
30398
|
return;
|
|
30165
30399
|
}
|
|
30166
|
-
if (!
|
|
30400
|
+
if (!existsSync10(filePath)) {
|
|
30167
30401
|
console.log(`No update status recorded for ${role2}.`);
|
|
30168
30402
|
return;
|
|
30169
30403
|
}
|
|
@@ -30349,7 +30583,7 @@ async function serverUpdate(client, rest, flags) {
|
|
|
30349
30583
|
try {
|
|
30350
30584
|
await autoInstallRuntimeMonitoringServices(installPath, role);
|
|
30351
30585
|
} catch (error) {
|
|
30352
|
-
const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${
|
|
30586
|
+
const restoreCommand = safetyPlan.backupName ? `openmates server restore --role ${role} --file ${join8(roleBackupDir(installPath, role), safetyPlan.backupName)}` : null;
|
|
30353
30587
|
writeServerUpdateStatus(installPath, role, {
|
|
30354
30588
|
status: "degraded",
|
|
30355
30589
|
targetImageTag: target.tag,
|
|
@@ -30675,11 +30909,11 @@ async function serverMonitoring(rest, flags) {
|
|
|
30675
30909
|
const state2 = await readOperationalReportState(installPath, role);
|
|
30676
30910
|
const nextState2 = accepted ? { ...state2, incidentOpen: false, incidentOpenedAt: void 0, lastAcceptedReportAt: (/* @__PURE__ */ new Date()).toISOString() } : state2;
|
|
30677
30911
|
await writeOperationalReportState(installPath, role, nextState2);
|
|
30678
|
-
const metricsDir =
|
|
30679
|
-
|
|
30912
|
+
const metricsDir = join8(installPath, ".openmates", "runtime-health", "metrics");
|
|
30913
|
+
mkdirSync7(metricsDir, { recursive: true, mode: 448 });
|
|
30680
30914
|
if (accepted) {
|
|
30681
|
-
|
|
30682
|
-
|
|
30915
|
+
writeFileSync6(
|
|
30916
|
+
join8(metricsDir, "operational-report.prom"),
|
|
30683
30917
|
`operational_report_last_success_timestamp_seconds{environment="${output2.receipts[0]?.environment ?? environment}"} ${Math.floor(Date.now() / 1e3)}
|
|
30684
30918
|
`,
|
|
30685
30919
|
{ mode: 384 }
|
|
@@ -30697,11 +30931,11 @@ async function serverMonitoring(rest, flags) {
|
|
|
30697
30931
|
destinationSource: receipt.destination_source,
|
|
30698
30932
|
fallbackUsed: receipt.fallback_used
|
|
30699
30933
|
}));
|
|
30700
|
-
const receiptDir =
|
|
30701
|
-
|
|
30934
|
+
const receiptDir = join8(installPath, ".openmates", "runtime-health", "receipts");
|
|
30935
|
+
mkdirSync7(receiptDir, { recursive: true, mode: 448 });
|
|
30702
30936
|
const receiptMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
|
|
30703
30937
|
for (const receipt of redactedReceipts) {
|
|
30704
|
-
appendFileSync(
|
|
30938
|
+
appendFileSync(join8(receiptDir, `${receiptMonth}.jsonl`), `${JSON.stringify(receipt)}
|
|
30705
30939
|
`, { mode: 384 });
|
|
30706
30940
|
}
|
|
30707
30941
|
const displayed = { command: "monitoring digest", reportId: output2.report_id, reportSha256: output2.report_sha256, deliveryState: output2.delivery_state, receipts: redactedReceipts };
|
|
@@ -30905,14 +31139,14 @@ async function serverFeatures(rest, flags) {
|
|
|
30905
31139
|
const installPath = resolveServerPath(flags);
|
|
30906
31140
|
const installMode = getInstallMode(installPath);
|
|
30907
31141
|
if (installMode === "image") ensureImageRuntimeConfig(installPath);
|
|
30908
|
-
const configPath = installMode === "image" ? imageBackendConfigPath(installPath) :
|
|
30909
|
-
if (!
|
|
31142
|
+
const configPath = installMode === "image" ? imageBackendConfigPath(installPath) : join8(installPath, BACKEND_CONFIG_FILE);
|
|
31143
|
+
if (!existsSync10(configPath)) {
|
|
30910
31144
|
throw new Error(`Backend config not found at ${configPath}. Run 'openmates server install' first or pass --path <dir>.`);
|
|
30911
31145
|
}
|
|
30912
|
-
const content =
|
|
31146
|
+
const content = readFileSync8(configPath, "utf-8");
|
|
30913
31147
|
const overrides = parseFeatureOverrides(content);
|
|
30914
31148
|
const writeOverrides = (nextOverrides) => {
|
|
30915
|
-
|
|
31149
|
+
writeFileSync6(configPath, updateFeatureOverridesContent(content, nextOverrides));
|
|
30916
31150
|
console.log(`Updated ${configPath}`);
|
|
30917
31151
|
console.log("Restart the server for feature changes to take effect: openmates server restart");
|
|
30918
31152
|
};
|
|
@@ -31145,7 +31379,7 @@ async function serverBackup(rest, flags) {
|
|
|
31145
31379
|
const role = getServerRole(flags, config);
|
|
31146
31380
|
if (action === "list") {
|
|
31147
31381
|
const dir = roleBackupDir(installPath, role);
|
|
31148
|
-
const files =
|
|
31382
|
+
const files = existsSync10(dir) ? readdirSync5(dir).filter((item) => item.endsWith(".tar.gz")).sort() : [];
|
|
31149
31383
|
if (flags.json === true) {
|
|
31150
31384
|
printJson({ role, backupDir: dir, files });
|
|
31151
31385
|
return;
|
|
@@ -31155,7 +31389,7 @@ async function serverBackup(rest, flags) {
|
|
|
31155
31389
|
console.log(" none");
|
|
31156
31390
|
return;
|
|
31157
31391
|
}
|
|
31158
|
-
for (const file of files) console.log(` ${
|
|
31392
|
+
for (const file of files) console.log(` ${join8(dir, file)}`);
|
|
31159
31393
|
return;
|
|
31160
31394
|
}
|
|
31161
31395
|
requireDocker();
|
|
@@ -31240,7 +31474,7 @@ async function serverUninstall(flags) {
|
|
|
31240
31474
|
} catch {
|
|
31241
31475
|
}
|
|
31242
31476
|
try {
|
|
31243
|
-
|
|
31477
|
+
rmSync7(installPath, { recursive: true, force: true });
|
|
31244
31478
|
console.error(` Removed installation directory: ${installPath}`);
|
|
31245
31479
|
} catch (e) {
|
|
31246
31480
|
console.error(` Could not remove ${installPath}: ${e instanceof Error ? e.message : e}`);
|
|
@@ -31315,7 +31549,7 @@ async function serverCaddy(rest, flags) {
|
|
|
31315
31549
|
const role = getServerRole(flags, config);
|
|
31316
31550
|
const appliedPath = typeof flags.config === "string" ? flags.config : "/etc/caddy/Caddyfile";
|
|
31317
31551
|
const templatePath = packagedCaddyTemplatePath(role);
|
|
31318
|
-
if (!
|
|
31552
|
+
if (!existsSync10(templatePath)) throw new Error(`Packaged Caddy template not found: ${templatePath}`);
|
|
31319
31553
|
const plan = planCaddyCommand({ role, action, appliedPath });
|
|
31320
31554
|
const payload = {
|
|
31321
31555
|
...plan,
|
|
@@ -31334,7 +31568,7 @@ async function serverCaddy(rest, flags) {
|
|
|
31334
31568
|
return;
|
|
31335
31569
|
}
|
|
31336
31570
|
if (action === "diff") {
|
|
31337
|
-
if (!
|
|
31571
|
+
if (!existsSync10(appliedPath)) throw new Error(`Applied Caddyfile not found: ${appliedPath}`);
|
|
31338
31572
|
try {
|
|
31339
31573
|
execSync(`diff -u ${shellQuote(appliedPath)} ${shellQuote(templatePath)}`, { stdio: "inherit" });
|
|
31340
31574
|
} catch (error) {
|
|
@@ -31355,9 +31589,9 @@ WARNING: This will replace ${appliedPath} with the packaged ${role} Caddyfile.`)
|
|
|
31355
31589
|
}
|
|
31356
31590
|
}
|
|
31357
31591
|
const backupPath = `${appliedPath}.openmates-backup-${nowStamp()}`;
|
|
31358
|
-
const hadExistingCaddyfile =
|
|
31592
|
+
const hadExistingCaddyfile = existsSync10(appliedPath);
|
|
31359
31593
|
try {
|
|
31360
|
-
|
|
31594
|
+
mkdirSync7(dirname6(appliedPath), { recursive: true });
|
|
31361
31595
|
if (hadExistingCaddyfile) copyFileSync2(appliedPath, backupPath);
|
|
31362
31596
|
copyFileSync2(templatePath, appliedPath);
|
|
31363
31597
|
execSync("systemctl reload caddy", { stdio: "inherit" });
|
|
@@ -48913,9 +49147,9 @@ function basenameFromUrl(url) {
|
|
|
48913
49147
|
return withoutQuery.split("/").pop()?.replace(/[^a-zA-Z0-9._-]/g, "_") ?? "";
|
|
48914
49148
|
}
|
|
48915
49149
|
function extensionFromUrl(url) {
|
|
48916
|
-
const
|
|
48917
|
-
const dotIndex =
|
|
48918
|
-
return dotIndex > 0 ?
|
|
49150
|
+
const basename5 = basenameFromUrl(url);
|
|
49151
|
+
const dotIndex = basename5.lastIndexOf(".");
|
|
49152
|
+
return dotIndex > 0 ? basename5.slice(dotIndex + 1).toLowerCase() : "";
|
|
48919
49153
|
}
|
|
48920
49154
|
function extensionFromLanguage(language) {
|
|
48921
49155
|
const normalized = language.toLowerCase();
|
|
@@ -49527,6 +49761,14 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
49527
49761
|
}
|
|
49528
49762
|
}
|
|
49529
49763
|
},
|
|
49764
|
+
tasks: {
|
|
49765
|
+
daily_meeting_and_orchestration: {
|
|
49766
|
+
text: "Daily meeting & orchestration",
|
|
49767
|
+
description: {
|
|
49768
|
+
text: "Choose today's priorities and coordinate project tasks and chats through completion."
|
|
49769
|
+
}
|
|
49770
|
+
}
|
|
49771
|
+
},
|
|
49530
49772
|
videos: {
|
|
49531
49773
|
analyze_video: {
|
|
49532
49774
|
text: "Analyze video",
|
|
@@ -50482,6 +50724,12 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
50482
50724
|
}
|
|
50483
50725
|
},
|
|
50484
50726
|
calendar: {
|
|
50727
|
+
list_calendars: {
|
|
50728
|
+
text: "List Calendars",
|
|
50729
|
+
description: {
|
|
50730
|
+
text: "List calendars, timezones, and access roles in a connected account."
|
|
50731
|
+
}
|
|
50732
|
+
},
|
|
50485
50733
|
get_events: {
|
|
50486
50734
|
text: "Search",
|
|
50487
50735
|
description: {
|
|
@@ -56671,6 +56919,9 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
56671
56919
|
generating_title: {
|
|
56672
56920
|
text: "Processing..."
|
|
56673
56921
|
},
|
|
56922
|
+
selecting_mate_and_model: {
|
|
56923
|
+
text: "Selecting mate & AI model..."
|
|
56924
|
+
},
|
|
56674
56925
|
selecting_mate: {
|
|
56675
56926
|
text: "Selecting optimal mate..."
|
|
56676
56927
|
},
|
|
@@ -59616,6 +59867,21 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
59616
59867
|
text: "You are an expert education and career counselor. Your role is to help users discover the right degree and field of study through thoughtful, strategic questioning. Use the Socratic method to guide them toward self-discovery rather than just giving advice. Help them understand their true interests, strengths, and values. Ask follow-up questions to dig deeper and uncover patterns. Provide personalized recommendations based on their responses."
|
|
59617
59868
|
}
|
|
59618
59869
|
},
|
|
59870
|
+
tasks_daily_meeting_and_orchestration: {
|
|
59871
|
+
text: "Daily meeting & orchestration",
|
|
59872
|
+
description: {
|
|
59873
|
+
text: "Choose today's priorities and coordinate project tasks and chats through completion."
|
|
59874
|
+
},
|
|
59875
|
+
process: {
|
|
59876
|
+
text: "- Clarify your weekly priorities and today's focus, one question at a time\n- Review the last working day's chats, progress, and remaining tasks\n- Agree on goals and completion criteria before starting work\n- Coordinate project subchats, check progress, and resolve shared blockers\n- Summarize results and suggest the most relevant tasks to work on next"
|
|
59877
|
+
},
|
|
59878
|
+
how_to_use: {
|
|
59879
|
+
text: "- Let's have a **daily meeting** for my shop launch and decide what matters today.\n- **Coordinate today's work** on my research project after reviewing our previous chats.\n- All our chats are finished. **What should we work on next** toward today's goal?"
|
|
59880
|
+
},
|
|
59881
|
+
systemprompt: {
|
|
59882
|
+
text: "Help the user choose today's outcomes and coordinate work on their own projects.\nAsk one question per message, include **Recommendation:** with a brief reason and\n**Examples:** with concrete outcomes, then wait for the answer. Reuse decisions\nalready made. Keep replies concise and use the user's language.\n\nUse OpenMates tasks and task activities as the record of priorities, assignments,\nlearnings, decisions, blockers, and handoffs. Search before creating duplicates.\nUse only information accessible to this user in the selected project or team.\nPrivate history and task search require the authorized connected client; missing\naccess is not proof that no previous work exists. Never invent tool results or\nclaim a task update, chat action, or scheduled check without acknowledgement.\nUse available tools; explain missing capabilities and continue the useful parts\nof the meeting without promising unsupported automation. Preview requests are\nread-only: propose work without changing tasks, scheduling, or starting chats.\n\nFollow these steps:\n\n### Step 1: Establish today's focus\n\nAsk about this week's priorities, using recorded priorities as context. If unclear,\nestablish today's most useful outcome first. Clarify project scope, available time,\nor deadlines only when needed. Surface urgent issues when they affect the plan.\n\n### Step 2: Review the last working day\n\nIn the user's timezone, search backward from yesterday through at most 30 calendar\ndays for the latest day with relevant chat activity. Review every accessible chat\nactive on that day, including completed work, linked task activities, and subchat\nfindings. Group subchats with their parent and deduplicate copied history. Automatic\nnotifications alone do not establish a working day. Paused chats are resumption\ncandidates, but review unpaused chats too. Check today's active work for duplicates.\nIf the full window is empty, plan from tasks and user priorities. Disclose missing\naccess or partial results. Compare intended outcomes with actual progress and blockers.\n\n### Step 3: Propose assignments and get approval\n\nRecommend which chats to resume, replace, defer to todo/backlog, or stop pursuing.\nPreserve useful findings in task activities. For each assignment give the task,\ngoal, next instruction, completion criteria, required checks, scope limits,\ndependencies, and next meaningful checkpoint. Resume existing chats by default.\nIf conflicting context or repeated misunderstanding warrants a fresh subchat,\ntransfer requirements, decisions, saved work, and remaining checks; keep one owner.\n\nGet approval for the daily plan before starting or resuming work. Make clear which\ntasks OpenMates will execute and which require the user; only execute tasks assigned\nto OpenMates. Plan approval covers in-focus additions and urgent corrections, within\nexisting permissions and budgets. Cancellation still requires a user decision.\n\n### Step 4: Start or resume approved work\n\nUse OpenMates subchats for independent assignments and available chat controls for\nresumption. Give each subchat its approved goal, limits, task link, and completion\ncriteria. Verify acceptance before recording it as started. Preserve existing chat\nand task identity when resuming; reconcile uncertain starts before retrying. Keep\ncoordination in this meeting chat and avoid duplicate owners or recursive delegation.\nAsk workers to report important progress, learnings, decisions, and blockers so\nthey can be recorded in task activity. Do not post routine command or heartbeat logs.\n\n### Step 5: Monitor and handle changes\n\nFor each started/resumed chat, check at 5, 10, 15, and 20 minutes, then every 20\nminutes. Each new worker starts its own cadence. Where supported, use OpenMates\nfollow-ups in this meeting chat for approved scheduled checks. Use a full AI\nfollow-up only for an actual monitoring action; a passive reminder cannot inspect\nwork. Track acknowledged follow-ups, avoid duplicate schedules, preserve timing\nacross continuations, and cancel pending checks when work finishes or the day ends.\nIf scheduling or live progress access is unavailable, say so and use explicit\nuser-requested check-ins. Do not claim background supervision from prompt text alone.\n\nCompare actual results with the assignment. Leave healthy work uninterrupted;\ncorrect urgent drift within approved scope. Honor acceptance and scope changes.\nCoordinate one recovery effort for shared blockers, request user-only action once,\nverify recovery, and continue unaffected work.\n\nFor repeated failed approaches, absent measurable progress, or disproportionate\neffort, explain evidence and recommend narrowing or cancellation. Wait for the\nuser's decision; pause orchestration of only that chat and keep monitoring others.\nReport if the affected worker remains running. Do not cancel it or send further\ncontinuation instructions while waiting.\n\nIf an additional request fits today's focus, reuse/create the task and start it\nwithin approval. Otherwise capture it in todo/backlog. Preserve explicit deadlines;\nsuggest due dates only with a reason. Ask before materially changing today's focus.\n\n### Step 6: Summarize results and ask what comes next\n\nVerify outcomes and required checks before marking tasks done. Idle, paused,\nblocked, and awaiting-input chats are not complete. Ensure important outcomes,\ndecisions, and next steps are recorded in task activities.\n\nOnce all coordinated chats are done, summarize outcomes and fetch still-open tasks.\nRank by relevance to today's focus: direct outcomes, enabling dependencies, related\nimprovements, then unrelated work. Include task links, deadlines, blockers, remaining\nwork, and brief reasons. Label partial lists. Ask **What would you like to work on\nnext?** with a recommendation and concrete examples. Wait before starting another\nbatch, even within today's focus. If nothing remains, suggest ending the day or\nchoosing a new goal. If chats are blocked, ask the needed blocker decision instead.\n\n### Step 7: Close the day\n\nAt any step, respect the user's stop or end-of-day instruction. Cancel remaining\nmonitoring follow-ups and preserve handoffs in task activities. Paused work is\nneither done nor cancelled. At the next daily meeting, reassess it against the new\npriorities before resuming."
|
|
59883
|
+
}
|
|
59884
|
+
},
|
|
59619
59885
|
videos_analyze_video: {
|
|
59620
59886
|
text: "Analyze video",
|
|
59621
59887
|
description: {
|
|
@@ -70488,10 +70754,10 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
70488
70754
|
}
|
|
70489
70755
|
|
|
70490
70756
|
// src/benchmark.ts
|
|
70491
|
-
import { createHash as
|
|
70492
|
-
import { existsSync as
|
|
70757
|
+
import { createHash as createHash14, randomUUID as randomUUID12 } from "crypto";
|
|
70758
|
+
import { existsSync as existsSync11, mkdtempSync as mkdtempSync2, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
70493
70759
|
import { tmpdir } from "os";
|
|
70494
|
-
import { dirname as dirname7, join as
|
|
70760
|
+
import { dirname as dirname7, join as join9, resolve as resolve8 } from "path";
|
|
70495
70761
|
import { fileURLToPath } from "url";
|
|
70496
70762
|
var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
|
|
70497
70763
|
var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
|
|
@@ -70904,7 +71170,7 @@ async function handleBenchmark(client, subcommand, rest, flags) {
|
|
|
70904
71170
|
const caseIds = parseCaseIds(flags.case);
|
|
70905
71171
|
const dryRun = flags["dry-run"] === true;
|
|
70906
71172
|
const output = typeof flags.output === "string" ? flags.output : void 0;
|
|
70907
|
-
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] :
|
|
71173
|
+
const runId = typeof flags["run-id"] === "string" ? flags["run-id"] : randomUUID12();
|
|
70908
71174
|
const imagePath = typeof flags.image === "string" ? resolve8(flags.image) : defaultImageFixturePath();
|
|
70909
71175
|
if (!dryRun && flags["confirm-spend-credits"] !== true) {
|
|
70910
71176
|
throw new Error(
|
|
@@ -71006,7 +71272,7 @@ async function handlePromptBudgetBenchmark(client, rest, flags) {
|
|
|
71006
71272
|
if (resumeArtifact) {
|
|
71007
71273
|
validatePromptBudgetResumeArtifact(resumeArtifact, { phase, targetModel, judgeModel });
|
|
71008
71274
|
}
|
|
71009
|
-
const runId = stringFlag(flags["run-id"]) ?? resumeArtifact?.runId ??
|
|
71275
|
+
const runId = stringFlag(flags["run-id"]) ?? resumeArtifact?.runId ?? randomUUID12();
|
|
71010
71276
|
const result = makePromptBudgetBaseResult({
|
|
71011
71277
|
phase,
|
|
71012
71278
|
runId,
|
|
@@ -71377,7 +71643,7 @@ async function buildPromptWithAttachments(client, benchmarkCase, model, imagePat
|
|
|
71377
71643
|
${attachment.messageSuffix}`, embeds: attachment.embeds };
|
|
71378
71644
|
}
|
|
71379
71645
|
async function prepareImageAttachment(client, imagePath) {
|
|
71380
|
-
if (!
|
|
71646
|
+
if (!existsSync11(imagePath)) throw new Error(`Benchmark image not found: ${imagePath}`);
|
|
71381
71647
|
const processed = processFiles([imagePath], null);
|
|
71382
71648
|
if (processed.blocked.length > 0 || processed.errors.length > 0 || processed.embeds.length === 0) {
|
|
71383
71649
|
const reason = [...processed.blocked, ...processed.errors].map((entry) => entry.error).join("; ") || "no image embed produced";
|
|
@@ -71567,7 +71833,7 @@ function buildLongContextHistory() {
|
|
|
71567
71833
|
}
|
|
71568
71834
|
function appendHistory(history, role, content) {
|
|
71569
71835
|
history.push({
|
|
71570
|
-
message_id:
|
|
71836
|
+
message_id: randomUUID12(),
|
|
71571
71837
|
role,
|
|
71572
71838
|
sender_name: role === "user" ? "User" : "Assistant",
|
|
71573
71839
|
content,
|
|
@@ -71657,10 +71923,10 @@ function loadProviderPricing() {
|
|
|
71657
71923
|
const providersDir = findProvidersDir();
|
|
71658
71924
|
const pricing = /* @__PURE__ */ new Map();
|
|
71659
71925
|
if (!providersDir) return pricing;
|
|
71660
|
-
for (const fileName of
|
|
71926
|
+
for (const fileName of readdirSync6(providersDir)) {
|
|
71661
71927
|
if (!fileName.endsWith(".yml")) continue;
|
|
71662
|
-
const filePath =
|
|
71663
|
-
const text =
|
|
71928
|
+
const filePath = join9(providersDir, fileName);
|
|
71929
|
+
const text = readFileSync9(filePath, "utf-8");
|
|
71664
71930
|
const provider = parseProviderId(text) ?? fileName.replace(/\.yml$/, "");
|
|
71665
71931
|
for (const modelPricing of parseModelPricing(text, provider)) {
|
|
71666
71932
|
pricing.set(`${modelPricing.provider}/${modelPricing.modelId}`, modelPricing);
|
|
@@ -71714,10 +71980,10 @@ function findProvidersDir() {
|
|
|
71714
71980
|
const currentFile = fileURLToPath(import.meta.url);
|
|
71715
71981
|
let current = dirname7(currentFile);
|
|
71716
71982
|
for (let index = 0; index < 8; index += 1) {
|
|
71717
|
-
const candidate =
|
|
71718
|
-
if (
|
|
71719
|
-
const parentCandidate =
|
|
71720
|
-
if (
|
|
71983
|
+
const candidate = join9(current, "backend", "providers");
|
|
71984
|
+
if (existsSync11(candidate)) return candidate;
|
|
71985
|
+
const parentCandidate = join9(current, "..", "..", "backend", "providers");
|
|
71986
|
+
if (existsSync11(parentCandidate)) return resolve8(parentCandidate);
|
|
71721
71987
|
const next = dirname7(current);
|
|
71722
71988
|
if (next === current) break;
|
|
71723
71989
|
current = next;
|
|
@@ -71922,21 +72188,21 @@ function hashPromptBudgetCorpus(cases) {
|
|
|
71922
72188
|
return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
|
|
71923
72189
|
}
|
|
71924
72190
|
function stableHash(value) {
|
|
71925
|
-
return
|
|
72191
|
+
return createHash14("sha256").update(JSON.stringify(value)).digest("hex");
|
|
71926
72192
|
}
|
|
71927
72193
|
function defaultImageFixturePath() {
|
|
71928
|
-
const fixtureDir =
|
|
71929
|
-
const fixturePath =
|
|
71930
|
-
if (
|
|
71931
|
-
const tempDir = mkdtempSync2(
|
|
71932
|
-
const tempPath =
|
|
71933
|
-
|
|
72194
|
+
const fixtureDir = join9(dirname7(fileURLToPath(import.meta.url)), "..", "fixtures");
|
|
72195
|
+
const fixturePath = join9(fixtureDir, "brandenburger-tor.png");
|
|
72196
|
+
if (existsSync11(fixturePath)) return fixturePath;
|
|
72197
|
+
const tempDir = mkdtempSync2(join9(tmpdir(), "openmates-benchmark-"));
|
|
72198
|
+
const tempPath = join9(tempDir, "brandenburger-tor.svg");
|
|
72199
|
+
writeFileSync7(tempPath, FIXTURE_IMAGE_SVG, "utf-8");
|
|
71934
72200
|
return tempPath;
|
|
71935
72201
|
}
|
|
71936
72202
|
function writeBenchmarkResult(result, flags, output) {
|
|
71937
72203
|
const json = `${JSON.stringify(result, null, 2)}
|
|
71938
72204
|
`;
|
|
71939
|
-
if (output)
|
|
72205
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
71940
72206
|
if (flags.json === true || output) {
|
|
71941
72207
|
process.stdout.write(json);
|
|
71942
72208
|
return;
|
|
@@ -71960,7 +72226,7 @@ function writeBenchmarkResult(result, flags, output) {
|
|
|
71960
72226
|
function writePromptBudgetResult(result, flags, output) {
|
|
71961
72227
|
const json = `${JSON.stringify(result, null, 2)}
|
|
71962
72228
|
`;
|
|
71963
|
-
if (output)
|
|
72229
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
71964
72230
|
if (flags.json === true || output) {
|
|
71965
72231
|
process.stdout.write(json);
|
|
71966
72232
|
return;
|
|
@@ -71986,7 +72252,7 @@ function writePromptBudgetResult(result, flags, output) {
|
|
|
71986
72252
|
function writePromptBudgetComparison(result, flags, output) {
|
|
71987
72253
|
const json = `${JSON.stringify(result, null, 2)}
|
|
71988
72254
|
`;
|
|
71989
|
-
if (output)
|
|
72255
|
+
if (output) writeFileSync7(output, json, "utf-8");
|
|
71990
72256
|
if (flags.json === true || output) {
|
|
71991
72257
|
process.stdout.write(json);
|
|
71992
72258
|
return;
|
|
@@ -72036,7 +72302,7 @@ function comparePromptBudgetArtifacts(flags) {
|
|
|
72036
72302
|
};
|
|
72037
72303
|
}
|
|
72038
72304
|
function readPromptBudgetResult(path2) {
|
|
72039
|
-
const parsed = JSON.parse(
|
|
72305
|
+
const parsed = JSON.parse(readFileSync9(path2, "utf-8"));
|
|
72040
72306
|
if (!parsed || typeof parsed !== "object") throw new Error(`Invalid prompt-budget artifact: ${path2}`);
|
|
72041
72307
|
return parsed;
|
|
72042
72308
|
}
|
|
@@ -73434,22 +73700,22 @@ function clamp(value, min, max) {
|
|
|
73434
73700
|
// src/remoteAccess.ts
|
|
73435
73701
|
import { homedir as homedir7 } from "os";
|
|
73436
73702
|
import {
|
|
73437
|
-
chmodSync as
|
|
73703
|
+
chmodSync as chmodSync6,
|
|
73438
73704
|
closeSync as closeSync3,
|
|
73439
73705
|
constants,
|
|
73440
|
-
existsSync as
|
|
73706
|
+
existsSync as existsSync12,
|
|
73441
73707
|
fstatSync,
|
|
73442
|
-
mkdirSync as
|
|
73708
|
+
mkdirSync as mkdirSync8,
|
|
73443
73709
|
openSync as openSync3,
|
|
73444
|
-
readFileSync as
|
|
73445
|
-
readdirSync as
|
|
73710
|
+
readFileSync as readFileSync10,
|
|
73711
|
+
readdirSync as readdirSync7,
|
|
73446
73712
|
readSync as readSync2,
|
|
73447
73713
|
realpathSync,
|
|
73448
73714
|
statSync as statSync2,
|
|
73449
|
-
writeFileSync as
|
|
73715
|
+
writeFileSync as writeFileSync8
|
|
73450
73716
|
} from "fs";
|
|
73451
73717
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
73452
|
-
import { join as
|
|
73718
|
+
import { join as join10, resolve as resolve9, relative as relative2 } from "path";
|
|
73453
73719
|
|
|
73454
73720
|
// src/projectFileRisk.ts
|
|
73455
73721
|
var SECRET_OR_ENVIRONMENT_PATTERNS = [
|
|
@@ -73572,7 +73838,7 @@ function resolveRemoteAccessRoots(pathFlag, cwd = process.cwd()) {
|
|
|
73572
73838
|
const roots = [];
|
|
73573
73839
|
for (const value of requested) {
|
|
73574
73840
|
const candidate = resolve9(cwd, value);
|
|
73575
|
-
if (!
|
|
73841
|
+
if (!existsSync12(candidate) || !statSync2(candidate).isDirectory()) {
|
|
73576
73842
|
throw new Error(`Remote source path does not exist or is not a directory: ${candidate}`);
|
|
73577
73843
|
}
|
|
73578
73844
|
const canonical = realpathSync(candidate);
|
|
@@ -73586,7 +73852,7 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73586
73852
|
const visit = (directory, approvedRoot) => {
|
|
73587
73853
|
let entries;
|
|
73588
73854
|
try {
|
|
73589
|
-
entries =
|
|
73855
|
+
entries = readdirSync7(directory, { withFileTypes: true });
|
|
73590
73856
|
} catch (error) {
|
|
73591
73857
|
const code = error.code;
|
|
73592
73858
|
if (code === "EACCES" || code === "EPERM") {
|
|
@@ -73600,7 +73866,7 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73600
73866
|
}
|
|
73601
73867
|
for (const entry of entries) {
|
|
73602
73868
|
if (entry.name === ".git" || entry.isSymbolicLink() || !entry.isDirectory()) continue;
|
|
73603
|
-
visit(
|
|
73869
|
+
visit(join10(directory, entry.name), approvedRoot);
|
|
73604
73870
|
}
|
|
73605
73871
|
};
|
|
73606
73872
|
for (const root of roots) visit(root, root);
|
|
@@ -73610,8 +73876,8 @@ function discoverRemoteAccessRepositories(roots) {
|
|
|
73610
73876
|
};
|
|
73611
73877
|
}
|
|
73612
73878
|
function remoteAccessSourceType(rootPath) {
|
|
73613
|
-
const gitMarker =
|
|
73614
|
-
return
|
|
73879
|
+
const gitMarker = join10(rootPath, ".git");
|
|
73880
|
+
return existsSync12(gitMarker) ? "local_git_repository" : "local_folder";
|
|
73615
73881
|
}
|
|
73616
73882
|
function listRemoteAccessDirectory(options) {
|
|
73617
73883
|
const root = realpathSync(options.sourceRoot);
|
|
@@ -73624,9 +73890,9 @@ function listRemoteAccessDirectory(options) {
|
|
|
73624
73890
|
const entries = [];
|
|
73625
73891
|
let omitted = 0;
|
|
73626
73892
|
let excluded = 0;
|
|
73627
|
-
for (const entry of
|
|
73628
|
-
const entryPath = relative2(root,
|
|
73629
|
-
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, entryPath) || classifyProjectFileRisk(entryPath, options.userProtectedPatterns ?? []).isHighRisk || entry.isFile() && isBinaryFile(
|
|
73893
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
73894
|
+
const entryPath = relative2(root, join10(directory, entry.name)).replace(/\\/g, "/");
|
|
73895
|
+
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, entryPath) || classifyProjectFileRisk(entryPath, options.userProtectedPatterns ?? []).isHighRisk || entry.isFile() && isBinaryFile(join10(directory, entry.name))) {
|
|
73630
73896
|
excluded += 1;
|
|
73631
73897
|
continue;
|
|
73632
73898
|
}
|
|
@@ -73854,7 +74120,7 @@ function remoteAccessOperationErrorCode(error) {
|
|
|
73854
74120
|
return "operation_failed";
|
|
73855
74121
|
}
|
|
73856
74122
|
function isGitIgnoredPath(sourceRoot, relativePath) {
|
|
73857
|
-
if (!
|
|
74123
|
+
if (!existsSync12(join10(sourceRoot, ".git"))) return false;
|
|
73858
74124
|
const result = spawnSync2("git", ["check-ignore", "--quiet", "--", relativePath], {
|
|
73859
74125
|
cwd: sourceRoot,
|
|
73860
74126
|
stdio: "ignore"
|
|
@@ -73884,13 +74150,13 @@ function waitForAbort2(signal, timeoutMs) {
|
|
|
73884
74150
|
}
|
|
73885
74151
|
function resolveRemoteCachePath(sourceId, homeDirectory = homedir7()) {
|
|
73886
74152
|
assertSafeSourceId(sourceId);
|
|
73887
|
-
return
|
|
74153
|
+
return join10(homeDirectory, ".openmates", "remote-cache", sourceId);
|
|
73888
74154
|
}
|
|
73889
74155
|
function startRemoteAccessSource(input) {
|
|
73890
74156
|
assertSafeSourceId(input.sourceId);
|
|
73891
74157
|
const homeDirectory = input.homeDirectory ?? homedir7();
|
|
73892
74158
|
const requestedRootPath = resolve9(input.rootPath);
|
|
73893
|
-
if (!
|
|
74159
|
+
if (!existsSync12(requestedRootPath) || !statSync2(requestedRootPath).isDirectory()) {
|
|
73894
74160
|
throw new Error(`Remote source path does not exist or is not a directory: ${requestedRootPath}`);
|
|
73895
74161
|
}
|
|
73896
74162
|
const rootPath = realpathSync(requestedRootPath);
|
|
@@ -73908,14 +74174,14 @@ function startRemoteAccessSource(input) {
|
|
|
73908
74174
|
};
|
|
73909
74175
|
const sources = listRemoteAccessSources(homeDirectory).filter((entry) => entry.sourceId !== input.sourceId);
|
|
73910
74176
|
saveRemoteAccessSources([...sources, source], homeDirectory);
|
|
73911
|
-
|
|
74177
|
+
mkdirSync8(source.cachePath, { recursive: true, mode: 448 });
|
|
73912
74178
|
return source;
|
|
73913
74179
|
}
|
|
73914
74180
|
function listRemoteAccessSources(homeDirectory = homedir7()) {
|
|
73915
74181
|
const filePath = remoteAccessStorePath(homeDirectory);
|
|
73916
|
-
if (!
|
|
74182
|
+
if (!existsSync12(filePath)) return [];
|
|
73917
74183
|
try {
|
|
73918
|
-
const parsed = JSON.parse(
|
|
74184
|
+
const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
73919
74185
|
if (!Array.isArray(parsed.sources)) {
|
|
73920
74186
|
throw new Error("Remote source store is missing the sources array");
|
|
73921
74187
|
}
|
|
@@ -73972,13 +74238,13 @@ function searchRemoteSourceWithoutRg(query, sourceRoot, maxResults, userProtecte
|
|
|
73972
74238
|
const directory = directories.pop();
|
|
73973
74239
|
let entries;
|
|
73974
74240
|
try {
|
|
73975
|
-
entries =
|
|
74241
|
+
entries = readdirSync7(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
73976
74242
|
} catch {
|
|
73977
74243
|
excluded += 1;
|
|
73978
74244
|
continue;
|
|
73979
74245
|
}
|
|
73980
74246
|
for (const entry of entries) {
|
|
73981
|
-
const absolutePath =
|
|
74247
|
+
const absolutePath = join10(directory, entry.name);
|
|
73982
74248
|
const relativePath = relative2(root, absolutePath).replace(/\\/g, "/");
|
|
73983
74249
|
if (entry.name === ".git" || entry.isSymbolicLink() || isGitIgnoredPath(root, relativePath) || classifyProjectFileRisk(relativePath, userProtectedPatterns).isHighRisk) {
|
|
73984
74250
|
excluded += 1;
|
|
@@ -74147,7 +74413,7 @@ function assertInsideRoot(sourceRoot, path2) {
|
|
|
74147
74413
|
}
|
|
74148
74414
|
function openedDescriptorPath(descriptor, requestedPath) {
|
|
74149
74415
|
const procPath = `/proc/self/fd/${descriptor}`;
|
|
74150
|
-
if (
|
|
74416
|
+
if (existsSync12(procPath)) return realpathSync(procPath);
|
|
74151
74417
|
return realpathSync(requestedPath);
|
|
74152
74418
|
}
|
|
74153
74419
|
function isBinaryFile(path2) {
|
|
@@ -74179,16 +74445,16 @@ function normalizeBound(value, maximum, label) {
|
|
|
74179
74445
|
return result;
|
|
74180
74446
|
}
|
|
74181
74447
|
function remoteAccessStorePath(homeDirectory) {
|
|
74182
|
-
return
|
|
74448
|
+
return join10(homeDirectory, ".openmates", "remote-sources.json");
|
|
74183
74449
|
}
|
|
74184
74450
|
function saveRemoteAccessSources(sources, homeDirectory) {
|
|
74185
74451
|
const filePath = remoteAccessStorePath(homeDirectory);
|
|
74186
|
-
const stateDir2 =
|
|
74187
|
-
|
|
74188
|
-
|
|
74189
|
-
|
|
74452
|
+
const stateDir2 = join10(homeDirectory, ".openmates");
|
|
74453
|
+
mkdirSync8(stateDir2, { recursive: true, mode: 448 });
|
|
74454
|
+
chmodSync6(stateDir2, 448);
|
|
74455
|
+
writeFileSync8(filePath, `${JSON.stringify({ sources }, null, 2)}
|
|
74190
74456
|
`, { mode: 384 });
|
|
74191
|
-
|
|
74457
|
+
chmodSync6(filePath, 384);
|
|
74192
74458
|
}
|
|
74193
74459
|
function assertRemoteAccessSourceRecord(value, index) {
|
|
74194
74460
|
if (typeof value !== "object" || value === null) {
|
|
@@ -74206,7 +74472,7 @@ function assertRemoteAccessSourceRecord(value, index) {
|
|
|
74206
74472
|
}
|
|
74207
74473
|
|
|
74208
74474
|
// src/projectRequester.ts
|
|
74209
|
-
import { randomUUID as
|
|
74475
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
74210
74476
|
var REMOTE_PROTOCOL_TIMEOUT_MS = 45e3;
|
|
74211
74477
|
var REMOTE_POLL_INTERVAL_MS = 250;
|
|
74212
74478
|
var MAX_REMOTE_RESULT_BYTES = 200 * 1024;
|
|
@@ -74232,8 +74498,8 @@ async function requestProjectRemoteOperation(options) {
|
|
|
74232
74498
|
keyEpoch = discovery.keyEpoch;
|
|
74233
74499
|
routingIdentity = discovery.routingIdentity;
|
|
74234
74500
|
}
|
|
74235
|
-
const requestingClientId =
|
|
74236
|
-
const requestId =
|
|
74501
|
+
const requestingClientId = randomUUID13();
|
|
74502
|
+
const requestId = randomUUID13();
|
|
74237
74503
|
const identity = await buildIdentity(
|
|
74238
74504
|
options.client,
|
|
74239
74505
|
options.projectId,
|
|
@@ -74294,9 +74560,9 @@ async function requestProjectRemoteOperation(options) {
|
|
|
74294
74560
|
return response.result;
|
|
74295
74561
|
}
|
|
74296
74562
|
async function discoverRouting(options, timeoutMs) {
|
|
74297
|
-
const requestId =
|
|
74298
|
-
const requestingClientId =
|
|
74299
|
-
const nonce =
|
|
74563
|
+
const requestId = randomUUID13();
|
|
74564
|
+
const requestingClientId = randomUUID13();
|
|
74565
|
+
const nonce = randomUUID13();
|
|
74300
74566
|
const encryptedEnvelope = await encryptWithAesGcmCombined(JSON.stringify({
|
|
74301
74567
|
type: "routing_discovery",
|
|
74302
74568
|
requesting_client_id: requestingClientId,
|
|
@@ -74394,13 +74660,68 @@ function remoteErrorMessage(code) {
|
|
|
74394
74660
|
|
|
74395
74661
|
// src/selfUpdate.ts
|
|
74396
74662
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
74397
|
-
import { readFileSync as
|
|
74663
|
+
import { readFileSync as readFileSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, renameSync as renameSync5 } from "fs";
|
|
74664
|
+
import { homedir as homedir8 } from "os";
|
|
74665
|
+
import { dirname as dirname8, basename as basename3, join as join11 } from "path";
|
|
74666
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
74398
74667
|
var PACKAGE_NAME = "openmates";
|
|
74399
|
-
|
|
74668
|
+
function channelFile() {
|
|
74669
|
+
return join11(homedir8(), ".openmates", "updates.json");
|
|
74670
|
+
}
|
|
74671
|
+
function parseChannel(value) {
|
|
74672
|
+
if (value === "dev") return "dev";
|
|
74673
|
+
if (value === "stable" || value === "main") return "stable";
|
|
74674
|
+
throw new Error("--channel must be dev, stable, or main (alias for stable)");
|
|
74675
|
+
}
|
|
74676
|
+
function savedChannel() {
|
|
74677
|
+
try {
|
|
74678
|
+
return parseChannel(JSON.parse(readFileSync11(channelFile(), "utf8")).channel);
|
|
74679
|
+
} catch (error) {
|
|
74680
|
+
if (error.code === "ENOENT") {
|
|
74681
|
+
return getCliPackageVersion().includes("-") ? "dev" : "stable";
|
|
74682
|
+
}
|
|
74683
|
+
throw error;
|
|
74684
|
+
}
|
|
74685
|
+
}
|
|
74686
|
+
function persistSelfUpdateChannel(plan) {
|
|
74687
|
+
if (plan.dryRun || !plan.persistChannel) return;
|
|
74688
|
+
const path2 = channelFile();
|
|
74689
|
+
mkdirSync9(dirname8(path2), { recursive: true, mode: 448 });
|
|
74690
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
74691
|
+
writeFileSync9(temporary, JSON.stringify({ channel: plan.channel }) + "\n", { mode: 384 });
|
|
74692
|
+
renameSync5(temporary, path2);
|
|
74693
|
+
}
|
|
74694
|
+
function installedNpmPrefix(moduleUrl = import.meta.url, platform4 = process.platform) {
|
|
74695
|
+
const packageRoot = dirname8(dirname8(fileURLToPath2(moduleUrl)));
|
|
74696
|
+
const modules = dirname8(packageRoot);
|
|
74697
|
+
if (basename3(packageRoot) !== PACKAGE_NAME || basename3(modules) !== "node_modules") return null;
|
|
74698
|
+
if (platform4 === "win32") return dirname8(modules);
|
|
74699
|
+
return basename3(dirname8(modules)) === "lib" ? dirname8(dirname8(modules)) : null;
|
|
74700
|
+
}
|
|
74701
|
+
function compareVersions(left, right) {
|
|
74702
|
+
const parse = (value) => {
|
|
74703
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
|
|
74704
|
+
if (!match) throw new Error(`Cannot compare CLI version '${value}'`);
|
|
74705
|
+
return { core: match.slice(1, 4).map(BigInt), pre: match[4]?.split(".") };
|
|
74706
|
+
};
|
|
74707
|
+
const a = parse(left), b = parse(right);
|
|
74708
|
+
for (let i = 0; i < 3; i++) if (a.core[i] !== b.core[i]) return a.core[i] > b.core[i] ? 1 : -1;
|
|
74709
|
+
if (!a.pre || !b.pre) return a.pre ? -1 : b.pre ? 1 : 0;
|
|
74710
|
+
for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
|
|
74711
|
+
const x = a.pre[i], y = b.pre[i];
|
|
74712
|
+
if (x === y) continue;
|
|
74713
|
+
if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
|
|
74714
|
+
const nx = /^\d+$/.test(x), ny = /^\d+$/.test(y);
|
|
74715
|
+
if (nx && ny) return BigInt(x) > BigInt(y) ? 1 : BigInt(x) < BigInt(y) ? -1 : 0;
|
|
74716
|
+
if (nx !== ny) return nx ? -1 : 1;
|
|
74717
|
+
return x > y ? 1 : -1;
|
|
74718
|
+
}
|
|
74719
|
+
return 0;
|
|
74720
|
+
}
|
|
74400
74721
|
var SAFE_TARGET_RE = /^[a-zA-Z0-9._@+~:-]+$/;
|
|
74401
74722
|
function getCliPackageVersion() {
|
|
74402
74723
|
try {
|
|
74403
|
-
const packageJson = JSON.parse(
|
|
74724
|
+
const packageJson = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf-8"));
|
|
74404
74725
|
return packageJson.version ?? "unknown";
|
|
74405
74726
|
} catch {
|
|
74406
74727
|
return "unknown";
|
|
@@ -74408,10 +74729,14 @@ function getCliPackageVersion() {
|
|
|
74408
74729
|
}
|
|
74409
74730
|
function buildSelfUpdatePlan(flags) {
|
|
74410
74731
|
const packageManager = parsePackageManager(flags["package-manager"]);
|
|
74411
|
-
const
|
|
74732
|
+
const channel = flags.channel === void 0 ? savedChannel() : parseChannel(flags.channel);
|
|
74733
|
+
const target = parseTarget(flags.version ?? (channel === "dev" ? "alpha" : "latest"));
|
|
74412
74734
|
const packageSpec = `${PACKAGE_NAME}@${target}`;
|
|
74413
74735
|
const { command, args } = commandForPackageManager(packageManager, packageSpec);
|
|
74414
74736
|
return {
|
|
74737
|
+
channel,
|
|
74738
|
+
persistChannel: flags.channel !== void 0,
|
|
74739
|
+
allowDowngrade: flags["allow-downgrade"] === true,
|
|
74415
74740
|
packageManager,
|
|
74416
74741
|
command,
|
|
74417
74742
|
args,
|
|
@@ -74434,10 +74759,15 @@ function checkSelfUpdateStatus(plan) {
|
|
|
74434
74759
|
return {
|
|
74435
74760
|
currentVersion: plan.currentVersion,
|
|
74436
74761
|
latestVersion: latest.version,
|
|
74437
|
-
updateAvailable: plan.currentVersion
|
|
74762
|
+
updateAvailable: compareVersions(latest.version, plan.currentVersion) > 0 || plan.allowDowngrade && compareVersions(latest.version, plan.currentVersion) !== 0,
|
|
74438
74763
|
checkError: null
|
|
74439
74764
|
};
|
|
74440
74765
|
}
|
|
74766
|
+
function pinSelfUpdatePlan(plan, version) {
|
|
74767
|
+
const packageSpec = `${PACKAGE_NAME}@${parseTarget(version)}`;
|
|
74768
|
+
const { command, args } = commandForPackageManager(plan.packageManager, packageSpec);
|
|
74769
|
+
return { ...plan, target: version, packageSpec, command, args };
|
|
74770
|
+
}
|
|
74441
74771
|
function runSelfUpdate(plan, options = {}) {
|
|
74442
74772
|
if (plan.dryRun) return;
|
|
74443
74773
|
const result = spawnSync3(plan.command, plan.args, {
|
|
@@ -74455,6 +74785,12 @@ function runSelfUpdate(plan, options = {}) {
|
|
|
74455
74785
|
${output}` : "";
|
|
74456
74786
|
throw new Error(`${plan.command} ${plan.args.join(" ")} failed with exit code ${result.status ?? "unknown"}${suffix}`);
|
|
74457
74787
|
}
|
|
74788
|
+
if (installedNpmPrefix()) {
|
|
74789
|
+
const installed = getCliPackageVersion();
|
|
74790
|
+
if (installed !== plan.target) {
|
|
74791
|
+
throw new Error(`CLI upgrade installed version ${installed}; expected ${plan.target}. Retry the upgrade with --verbose to inspect package-manager output.`);
|
|
74792
|
+
}
|
|
74793
|
+
}
|
|
74458
74794
|
}
|
|
74459
74795
|
function resolveTargetVersion(target) {
|
|
74460
74796
|
const forced = process.env.OPENMATES_CLI_LATEST_VERSION;
|
|
@@ -74464,7 +74800,8 @@ function resolveTargetVersion(target) {
|
|
|
74464
74800
|
}
|
|
74465
74801
|
const result = spawnSync3(commandName("npm"), ["view", `${PACKAGE_NAME}@${target}`, "version"], {
|
|
74466
74802
|
encoding: "utf-8",
|
|
74467
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
74803
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
74804
|
+
timeout: 3e4
|
|
74468
74805
|
});
|
|
74469
74806
|
if (result.error) return { version: null, error: result.error.message };
|
|
74470
74807
|
if (result.status !== 0) {
|
|
@@ -74480,7 +74817,7 @@ function parsePackageManager(value) {
|
|
|
74480
74817
|
throw new Error("--package-manager must be one of npm, pnpm, yarn, or bun");
|
|
74481
74818
|
}
|
|
74482
74819
|
function parseTarget(value) {
|
|
74483
|
-
if (value === void 0) return
|
|
74820
|
+
if (value === void 0) return "latest";
|
|
74484
74821
|
if (typeof value !== "string" || value.length === 0) {
|
|
74485
74822
|
throw new Error("--version requires a version or npm dist-tag");
|
|
74486
74823
|
}
|
|
@@ -74500,15 +74837,16 @@ function commandForPackageManager(packageManager, packageSpec) {
|
|
|
74500
74837
|
if (packageManager === "pnpm") return { command: commandName("pnpm"), args: ["add", "-g", packageSpec] };
|
|
74501
74838
|
if (packageManager === "yarn") return { command: commandName("yarn"), args: ["global", "add", packageSpec] };
|
|
74502
74839
|
if (packageManager === "bun") return { command: commandName("bun"), args: ["install", "-g", packageSpec] };
|
|
74503
|
-
|
|
74840
|
+
const prefix = installedNpmPrefix();
|
|
74841
|
+
return { command: commandName("npm"), args: ["install", "-g", packageSpec, ...prefix ? ["--prefix", prefix] : []] };
|
|
74504
74842
|
}
|
|
74505
74843
|
function commandName(name) {
|
|
74506
74844
|
return process.platform === "win32" ? `${name}.cmd` : name;
|
|
74507
74845
|
}
|
|
74508
74846
|
|
|
74509
74847
|
// src/workRecovery.ts
|
|
74510
|
-
import { mkdirSync as
|
|
74511
|
-
import { dirname as
|
|
74848
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
74849
|
+
import { dirname as dirname9 } from "path";
|
|
74512
74850
|
import { stringify } from "yaml";
|
|
74513
74851
|
async function projectPlanAssumption(plan, masterKey, assumption) {
|
|
74514
74852
|
const key = await planKeyFromRecord(plan.encrypted, masterKey);
|
|
@@ -74687,18 +75025,18 @@ function collectMismatchPaths(left, right, path2, mismatches) {
|
|
|
74687
75025
|
}
|
|
74688
75026
|
function writeWorkRecoveryAtomically(path2, document) {
|
|
74689
75027
|
validateWorkRecoveryDocument(document);
|
|
74690
|
-
|
|
75028
|
+
mkdirSync10(dirname9(path2), { recursive: true, mode: 448 });
|
|
74691
75029
|
const temporaryPath = `${path2}.tmp`;
|
|
74692
|
-
|
|
74693
|
-
|
|
75030
|
+
writeFileSync10(temporaryPath, stringify(document, { sortMapEntries: true, lineWidth: 0 }), { encoding: "utf8", mode: 384 });
|
|
75031
|
+
renameSync6(temporaryPath, path2);
|
|
74694
75032
|
}
|
|
74695
75033
|
|
|
74696
75034
|
// src/revolutBusinessCertificate.ts
|
|
74697
|
-
import { execFileSync as
|
|
74698
|
-
import { createSign, randomUUID as
|
|
74699
|
-
import { chmodSync as
|
|
74700
|
-
import { homedir as
|
|
74701
|
-
import { join as
|
|
75035
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
75036
|
+
import { createSign, randomUUID as randomUUID14 } from "crypto";
|
|
75037
|
+
import { chmodSync as chmodSync7, existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync12 } from "fs";
|
|
75038
|
+
import { homedir as homedir9 } from "os";
|
|
75039
|
+
import { join as join12, resolve as resolve10 } from "path";
|
|
74702
75040
|
var REVOLUT_BUSINESS_CERTIFICATE_DOCS_URL = "https://developer.revolut.com/docs/guides/manage-accounts/get-started/make-your-first-api-request#generate-a-private-and-a-public-certificate";
|
|
74703
75041
|
var REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI = "https://app.dev.openmates.org/oauth/revolut-business/callback";
|
|
74704
75042
|
var REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI = "https://openmates.org/oauth/revolut-business/callback";
|
|
@@ -74708,7 +75046,7 @@ var REVOLUT_BUSINESS_AUDIENCE = "https://revolut.com";
|
|
|
74708
75046
|
var CERTIFICATE_DAYS = "1825";
|
|
74709
75047
|
var RSA_BITS = "2048";
|
|
74710
75048
|
function defaultRevolutBusinessCertificateDir(environment) {
|
|
74711
|
-
return
|
|
75049
|
+
return join12(homedir9(), ".openmates", "revolut-business", environment);
|
|
74712
75050
|
}
|
|
74713
75051
|
function defaultRevolutBusinessRedirectUri(environment) {
|
|
74714
75052
|
return environment === "sandbox" ? REVOLUT_BUSINESS_DEFAULT_SANDBOX_REDIRECT_URI : REVOLUT_BUSINESS_DEFAULT_PRODUCTION_REDIRECT_URI;
|
|
@@ -74731,7 +75069,7 @@ async function exchangeRevolutBusinessAuthorizationCode(options) {
|
|
|
74731
75069
|
if (!clientId) throw new Error("Missing Revolut Business client ID.");
|
|
74732
75070
|
const code = extractRevolutBusinessAuthorizationCode(options.codeOrRedirectUrl);
|
|
74733
75071
|
if (!code) throw new Error("Missing Revolut Business authorization code.");
|
|
74734
|
-
const privateKeyPem = options.privateKeyPem ??
|
|
75072
|
+
const privateKeyPem = options.privateKeyPem ?? readFileSync12(options.privateKeyPath ?? join12(defaultRevolutBusinessCertificateDir(environment), "privatecert.pem"), "utf8");
|
|
74735
75073
|
const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
|
|
74736
75074
|
const clientAssertion = generateRevolutBusinessClientAssertion({ clientId, privateKeyPem, redirectUri });
|
|
74737
75075
|
const response = await fetch(environment === "sandbox" ? "https://sandbox-b2b.revolut.com/api/1.0/auth/token" : "https://b2b.revolut.com/api/1.0/auth/token", {
|
|
@@ -74769,7 +75107,7 @@ function generateRevolutBusinessClientAssertion(options) {
|
|
|
74769
75107
|
aud: REVOLUT_BUSINESS_AUDIENCE,
|
|
74770
75108
|
exp: now + 3600,
|
|
74771
75109
|
iat: now,
|
|
74772
|
-
jti:
|
|
75110
|
+
jti: randomUUID14()
|
|
74773
75111
|
});
|
|
74774
75112
|
const unsigned = `${header2}.${payload}`;
|
|
74775
75113
|
const signer = createSign("RSA-SHA256");
|
|
@@ -74784,17 +75122,17 @@ function generateRevolutBusinessCertificate(options = {}) {
|
|
|
74784
75122
|
const redirectUri = (options.redirectUri ?? defaultRevolutBusinessRedirectUri(environment)).trim();
|
|
74785
75123
|
if (!title) throw new Error("Revolut certificate title must not be empty.");
|
|
74786
75124
|
if (!redirectUri.startsWith("https://")) throw new Error("Revolut OAuth redirect URI must start with https://.");
|
|
74787
|
-
|
|
74788
|
-
|
|
74789
|
-
const privateKeyPath =
|
|
74790
|
-
const publicCertificatePath =
|
|
74791
|
-
if (!options.overwrite && (
|
|
75125
|
+
mkdirSync11(outputDir, { recursive: true, mode: 448 });
|
|
75126
|
+
chmodSync7(outputDir, 448);
|
|
75127
|
+
const privateKeyPath = join12(outputDir, "privatecert.pem");
|
|
75128
|
+
const publicCertificatePath = join12(outputDir, "publiccert.cer");
|
|
75129
|
+
if (!options.overwrite && (existsSync13(privateKeyPath) || existsSync13(publicCertificatePath))) {
|
|
74792
75130
|
throw new Error(`Revolut certificate files already exist in ${outputDir}. Re-run with --overwrite to replace them.`);
|
|
74793
75131
|
}
|
|
74794
75132
|
try {
|
|
74795
|
-
|
|
74796
|
-
|
|
74797
|
-
|
|
75133
|
+
execFileSync5("openssl", ["genrsa", "-out", privateKeyPath, RSA_BITS], { stdio: "ignore" });
|
|
75134
|
+
chmodSync7(privateKeyPath, 384);
|
|
75135
|
+
execFileSync5(
|
|
74798
75136
|
"openssl",
|
|
74799
75137
|
[
|
|
74800
75138
|
"req",
|
|
@@ -74822,7 +75160,7 @@ function generateRevolutBusinessCertificate(options = {}) {
|
|
|
74822
75160
|
outputDir,
|
|
74823
75161
|
privateKeyPath,
|
|
74824
75162
|
publicCertificatePath,
|
|
74825
|
-
publicCertificatePem:
|
|
75163
|
+
publicCertificatePem: readFileSync12(publicCertificatePath, "utf8").trim()
|
|
74826
75164
|
};
|
|
74827
75165
|
}
|
|
74828
75166
|
function escapeOpenSslSubjectValue(value) {
|
|
@@ -74848,6 +75186,15 @@ async function main() {
|
|
|
74848
75186
|
assertTrustedAccountGuardEnvironment(parsed.flags);
|
|
74849
75187
|
assertTrustedAccountCommandAllowed(command);
|
|
74850
75188
|
}
|
|
75189
|
+
if (parsed.flags.profile !== void 0) {
|
|
75190
|
+
const profile = parsed.flags.profile;
|
|
75191
|
+
if (typeof profile !== "string" || !profile) throw new Error("--profile requires a profile name");
|
|
75192
|
+
resolveStateDir({ profile, stateDir: "" });
|
|
75193
|
+
if (process.env.OPENMATES_STATE_DIR?.trim()) {
|
|
75194
|
+
throw new Error("--profile cannot be combined with OPENMATES_STATE_DIR");
|
|
75195
|
+
}
|
|
75196
|
+
process.env.OPENMATES_PROFILE = profile;
|
|
75197
|
+
}
|
|
74851
75198
|
const client = OpenMatesClient.load({
|
|
74852
75199
|
apiUrl: typeof parsed.flags["api-url"] === "string" ? parsed.flags["api-url"] : void 0
|
|
74853
75200
|
});
|
|
@@ -75213,6 +75560,9 @@ function assertTrustedAccountCommandAllowed(command) {
|
|
|
75213
75560
|
}
|
|
75214
75561
|
}
|
|
75215
75562
|
function assertTrustedAccountGuardEnvironment(flags, environment = process.env) {
|
|
75563
|
+
if (flags.profile !== void 0 && flags.profile !== TRUST_GUARD_PROFILE) {
|
|
75564
|
+
throw new Error(`Trusted OpenCode CLI commands cannot override profile ${TRUST_GUARD_PROFILE}.`);
|
|
75565
|
+
}
|
|
75216
75566
|
if (environment.OPENMATES_PROFILE !== TRUST_GUARD_PROFILE) {
|
|
75217
75567
|
throw new Error(`Trusted OpenCode CLI commands require OPENMATES_PROFILE=${TRUST_GUARD_PROFILE}.`);
|
|
75218
75568
|
}
|
|
@@ -75250,13 +75600,19 @@ function handleSupport(flags) {
|
|
|
75250
75600
|
console.log(renderSupportInfo());
|
|
75251
75601
|
}
|
|
75252
75602
|
function handleSelfUpdate(command, flags) {
|
|
75253
|
-
|
|
75603
|
+
let plan = buildSelfUpdatePlan(flags);
|
|
75254
75604
|
const status = checkSelfUpdateStatus(plan);
|
|
75255
|
-
|
|
75605
|
+
if (status.checkError) throw new Error(`Update check failed: ${status.checkError}`);
|
|
75606
|
+
const shouldInstall = status.updateAvailable === true;
|
|
75607
|
+
if (!plan.dryRun && shouldInstall && status.latestVersion) {
|
|
75608
|
+
plan = pinSelfUpdatePlan(plan, status.latestVersion);
|
|
75609
|
+
}
|
|
75256
75610
|
if (flags.json === true) {
|
|
75257
75611
|
if (!plan.dryRun && shouldInstall) runSelfUpdate(plan, { verbose: flags.verbose === true });
|
|
75612
|
+
persistSelfUpdateChannel(plan);
|
|
75258
75613
|
printJson2({
|
|
75259
75614
|
command,
|
|
75615
|
+
channel: plan.channel,
|
|
75260
75616
|
status: status.updateAvailable === false ? "up_to_date" : plan.dryRun ? "planned" : "success",
|
|
75261
75617
|
current_version: plan.currentVersion,
|
|
75262
75618
|
latest_version: status.latestVersion,
|
|
@@ -75273,6 +75629,7 @@ function handleSelfUpdate(command, flags) {
|
|
|
75273
75629
|
console.log("");
|
|
75274
75630
|
console.log("Checking for updates...");
|
|
75275
75631
|
console.log("");
|
|
75632
|
+
console.log(`Release channel: ${plan.channel}`);
|
|
75276
75633
|
console.log(`Current version: ${plan.currentVersion}`);
|
|
75277
75634
|
console.log(`Latest version: ${status.latestVersion ?? "unknown"}`);
|
|
75278
75635
|
if (status.checkError) {
|
|
@@ -75288,11 +75645,13 @@ function handleSelfUpdate(command, flags) {
|
|
|
75288
75645
|
return;
|
|
75289
75646
|
}
|
|
75290
75647
|
if (status.updateAvailable === false) {
|
|
75648
|
+
persistSelfUpdateChannel(plan);
|
|
75291
75649
|
console.log("OpenMates CLI is already up to date.");
|
|
75292
75650
|
return;
|
|
75293
75651
|
}
|
|
75294
75652
|
console.log(`Updating OpenMates CLI with ${plan.packageManager}...`);
|
|
75295
75653
|
runSelfUpdate(plan, { verbose: flags.verbose === true });
|
|
75654
|
+
persistSelfUpdateChannel(plan);
|
|
75296
75655
|
console.log(`Installed OpenMates CLI ${status.latestVersion ?? plan.target}.`);
|
|
75297
75656
|
console.log("");
|
|
75298
75657
|
console.log("OpenMates is up to date.");
|
|
@@ -75305,6 +75664,7 @@ function handleCliVersion(flags) {
|
|
|
75305
75664
|
if (flags.json === true) {
|
|
75306
75665
|
printJson2({
|
|
75307
75666
|
command: "version",
|
|
75667
|
+
channel: plan.channel,
|
|
75308
75668
|
current_version: status.currentVersion,
|
|
75309
75669
|
latest_version: status.latestVersion,
|
|
75310
75670
|
update_available: status.updateAvailable,
|
|
@@ -75314,6 +75674,7 @@ function handleCliVersion(flags) {
|
|
|
75314
75674
|
return;
|
|
75315
75675
|
}
|
|
75316
75676
|
console.log(`OpenMates CLI ${status.currentVersion}`);
|
|
75677
|
+
console.log(`Release channel: ${plan.channel}`);
|
|
75317
75678
|
if (status.checkError) {
|
|
75318
75679
|
console.log(`Update check failed: ${status.checkError}`);
|
|
75319
75680
|
return;
|
|
@@ -75336,35 +75697,55 @@ async function handleTasks(client, subcommand, rest, flags, redactor) {
|
|
|
75336
75697
|
const action = rest[0] ?? "list";
|
|
75337
75698
|
const task = await requiredResolvedTask(client, masterKey, rest[1], scope, `activity ${action}`);
|
|
75338
75699
|
const context = { teamId: scope.teamId, personal: scope.personal };
|
|
75339
|
-
|
|
75340
|
-
|
|
75341
|
-
|
|
75342
|
-
|
|
75343
|
-
|
|
75344
|
-
|
|
75345
|
-
|
|
75346
|
-
|
|
75347
|
-
|
|
75348
|
-
|
|
75349
|
-
|
|
75350
|
-
|
|
75351
|
-
|
|
75352
|
-
|
|
75353
|
-
|
|
75700
|
+
const actor = flags["as-assignee"] === true ? "assignee" : "user";
|
|
75701
|
+
const delivery = () => activityDeliveryStore(JSON.stringify([
|
|
75702
|
+
client.getSession().apiUrl,
|
|
75703
|
+
client.getSession().hashedEmail,
|
|
75704
|
+
scope.teamId || "personal",
|
|
75705
|
+
task.taskId,
|
|
75706
|
+
actor
|
|
75707
|
+
]));
|
|
75708
|
+
const createActivity = (input) => client.createUserTaskActivity(task.taskId, input, {
|
|
75709
|
+
...context,
|
|
75710
|
+
...actor === "assignee" ? { actorMode: "assignee" } : {}
|
|
75711
|
+
});
|
|
75712
|
+
if (action === "flush") {
|
|
75713
|
+
printJson2(await delivery().flush(createActivity));
|
|
75714
|
+
return;
|
|
75715
|
+
}
|
|
75716
|
+
if (action === "list" || action === "search") {
|
|
75717
|
+
const pending = flags["flush-pending"] === true ? await delivery().flush(createActivity) : void 0;
|
|
75718
|
+
const history = await readActivityHistory({
|
|
75719
|
+
maxEntries: typeof flags["max-entries"] === "string" ? Number(flags["max-entries"]) : action === "search" ? 200 : void 0,
|
|
75720
|
+
pageSize: typeof flags.limit === "string" ? Number(flags.limit) : 100,
|
|
75721
|
+
cursor: typeof flags.cursor === "string" ? flags.cursor : void 0,
|
|
75722
|
+
...action === "search" ? { query: typeof flags.query === "string" ? flags.query : "" } : {},
|
|
75723
|
+
page: async (cursor, limit) => {
|
|
75724
|
+
const page = await client.listUserTaskActivity(task.taskId, {
|
|
75725
|
+
...context,
|
|
75726
|
+
cursor,
|
|
75727
|
+
limit,
|
|
75728
|
+
newestFirst: flags["newest-first"] === true
|
|
75729
|
+
});
|
|
75730
|
+
return { entries: await decryptTaskActivityEntries(task, masterKey, page.entries), next_cursor: page.next_cursor };
|
|
75731
|
+
}
|
|
75732
|
+
});
|
|
75733
|
+
if (flags.json === true) printJson2({ ...history, entries: history.entries.map(taskActivityToJson), delivery: pending });
|
|
75734
|
+
else {
|
|
75735
|
+
console.log(renderTaskActivityList(history.entries));
|
|
75736
|
+
if (history.truncated) console.log(`Activity history truncated. Search all history: openmates tasks activity search ${task.shortId} --query "text" --max-entries 200`);
|
|
75737
|
+
if (history.next_cursor) console.log(`Next cursor: ${history.next_cursor}`);
|
|
75738
|
+
}
|
|
75354
75739
|
return;
|
|
75355
75740
|
}
|
|
75356
75741
|
if (action === "add") {
|
|
75357
75742
|
const message = typeof flags.message === "string" ? flags.message : rest.slice(2).join(" ");
|
|
75358
75743
|
if (!message.trim()) throw new Error("Missing comment. Usage: openmates tasks activity add <task-id> --message <text>");
|
|
75359
|
-
const
|
|
75360
|
-
const
|
|
75361
|
-
|
|
75362
|
-
|
|
75363
|
-
|
|
75364
|
-
...context,
|
|
75365
|
-
...flags["as-assignee"] === true ? { actorMode: "assignee" } : {}
|
|
75366
|
-
})
|
|
75367
|
-
);
|
|
75744
|
+
const deliveryId = typeof flags["delivery-id"] === "string" ? flags["delivery-id"] : void 0;
|
|
75745
|
+
const build = () => buildCreateTaskActivityInput(task, masterKey, { message, ...deliveryId ? { entryId: deliveryId } : {} });
|
|
75746
|
+
const record = deliveryId ? await delivery().deliver(deliveryId, build, createActivity) : await createActivity(await build());
|
|
75747
|
+
const entry = await decryptTaskActivityEntry(task, masterKey, record);
|
|
75748
|
+
if (entry.message !== message) throw new Error("Activity delivery id already belongs to a different milestone message");
|
|
75368
75749
|
if (flags.json === true) printJson2({ entry: taskActivityToJson(entry) });
|
|
75369
75750
|
else console.log(renderTaskActivityList([entry]));
|
|
75370
75751
|
return;
|
|
@@ -75381,7 +75762,7 @@ async function handleTasks(client, subcommand, rest, flags, redactor) {
|
|
|
75381
75762
|
else console.log(renderTaskActivityList([entry]));
|
|
75382
75763
|
return;
|
|
75383
75764
|
}
|
|
75384
|
-
throw new Error("Usage: openmates tasks activity list|add|delete <task-id> [<entry-id>] [--message <text>]");
|
|
75765
|
+
throw new Error("Usage: openmates tasks activity list|search|add|flush|delete <task-id> [<entry-id>] [--message <text>]");
|
|
75385
75766
|
}
|
|
75386
75767
|
if (rest[0] === "add-to-project") {
|
|
75387
75768
|
rejectRemoteCopyFlags(flags);
|
|
@@ -76081,7 +76462,7 @@ async function resolveAddToProjectStorageChoice(client, project, flags, objectTy
|
|
|
76081
76462
|
async function createEncryptedProjectItem(client, project, input) {
|
|
76082
76463
|
const timestamp = nowSeconds3();
|
|
76083
76464
|
return client.createProjectItem(project.projectId, {
|
|
76084
|
-
project_item_id:
|
|
76465
|
+
project_item_id: randomUUID15(),
|
|
76085
76466
|
folder_id: input.folderId ?? null,
|
|
76086
76467
|
item_type: input.itemType,
|
|
76087
76468
|
target_id: input.targetId,
|
|
@@ -76275,7 +76656,7 @@ async function handlePlans(client, subcommand, rest, flags, redactor) {
|
|
|
76275
76656
|
const encryptedSources = proof.length ? await encryptPlanAssumptionSources(plan, masterKey, proof) : void 0;
|
|
76276
76657
|
if (action === "create" || action === "add") {
|
|
76277
76658
|
const assumption = await client.createPlanAssumption(plan.planId, {
|
|
76278
|
-
assumption_id: typeof flags.id === "string" ? flags.id :
|
|
76659
|
+
assumption_id: typeof flags.id === "string" ? flags.id : randomUUID15(),
|
|
76279
76660
|
encrypted_text: await encryptPlanAssumptionSources(plan, masterKey, requiredStringFlag(flags.text ?? rest.slice(2).join(" "), "--text <assumption>")),
|
|
76280
76661
|
status: typeof flags.status === "string" ? flags.status : void 0,
|
|
76281
76662
|
linked_sub_chat_id: typeof flags["sub-chat"] === "string" ? flags["sub-chat"] : void 0,
|
|
@@ -76317,7 +76698,7 @@ async function handlePlans(client, subcommand, rest, flags, redactor) {
|
|
|
76317
76698
|
if (action === "diff") throw new Error("Revision diffs are reviewed in the authenticated web review page; use submit-for-review to print its URL.");
|
|
76318
76699
|
if (action !== "submit-for-review") throw new Error("Usage: openmates plans revisions list|submit-for-review|status|diff <plan-id>");
|
|
76319
76700
|
const snapshot = JSON.stringify(planToJson(plan));
|
|
76320
|
-
const revision = await client.submitPlanRevision(plan.planId, { fingerprint:
|
|
76701
|
+
const revision = await client.submitPlanRevision(plan.planId, { fingerprint: createHash15("sha256").update(snapshot).digest("hex"), encrypted_snapshot: await encryptPlanAssumptionSources(plan, masterKey, snapshot), created_at: nowSeconds3() });
|
|
76321
76702
|
const reviewUrl = `${deriveAppUrl(client.apiUrl)}/plans/${plan.planId}/review?revision=${revision.revision_id}`;
|
|
76322
76703
|
if (flags.json === true) printJson2({ revision, review_url: reviewUrl });
|
|
76323
76704
|
else console.log(`Plan revision submitted for web review: ${reviewUrl}`);
|
|
@@ -76873,8 +77254,8 @@ async function handleWorkRecovery(client, subcommand, rest, flags) {
|
|
|
76873
77254
|
}
|
|
76874
77255
|
if (subcommand === "validate" || subcommand === "restore") {
|
|
76875
77256
|
const path2 = requiredStringFlag(flags.file ?? rest[0], "--file <projection.yml>");
|
|
76876
|
-
if (!
|
|
76877
|
-
const document = parseYaml(
|
|
77257
|
+
if (!existsSync14(path2)) throw new Error(`Recovery projection does not exist: ${path2}`);
|
|
77258
|
+
const document = parseYaml(readFileSync13(path2, "utf8"));
|
|
76878
77259
|
validateWorkRecoveryDocument(document);
|
|
76879
77260
|
if (subcommand === "validate") {
|
|
76880
77261
|
if (flags.json === true) printJson2({ valid: true });
|
|
@@ -77200,7 +77581,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
|
|
|
77200
77581
|
const name = requiredStringFlag(rest[0] ?? flags.name, "project name");
|
|
77201
77582
|
const projectKey = randomBytes9(32);
|
|
77202
77583
|
const wrapping = await client.projectWrappingKey(context);
|
|
77203
|
-
const projectId =
|
|
77584
|
+
const projectId = randomUUID15();
|
|
77204
77585
|
const timestamp = nowSeconds3();
|
|
77205
77586
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
77206
77587
|
value: typeof flags.slug === "string" ? flags.slug : name,
|
|
@@ -77222,7 +77603,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
|
|
|
77222
77603
|
last_opened_at: timestamp,
|
|
77223
77604
|
key_wrappers: wrapping.teamId ? [{
|
|
77224
77605
|
key_type: "team",
|
|
77225
|
-
hashed_team_id:
|
|
77606
|
+
hashed_team_id: createHash15("sha256").update(wrapping.teamId).digest("hex"),
|
|
77226
77607
|
team_key_epoch: 1,
|
|
77227
77608
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
77228
77609
|
wrapper_version: 1,
|
|
@@ -77308,7 +77689,7 @@ async function handleProjects(client, subcommand, rest, flags, redactor) {
|
|
|
77308
77689
|
const proposal = isShortWorkspaceAsk(instruction) ? { name: instruction, description: "", icon: "folder", color: "default" } : extractRecord(await client.planProjectAsk({ instruction }), "proposed_project");
|
|
77309
77690
|
const projectKey = randomBytes9(32);
|
|
77310
77691
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
77311
|
-
const projectId =
|
|
77692
|
+
const projectId = randomUUID15();
|
|
77312
77693
|
const name = requiredString(proposal, "name", instruction);
|
|
77313
77694
|
const slugMetadata = await buildEncryptedObjectSlugMetadata({
|
|
77314
77695
|
value: typeof flags.slug === "string" ? flags.slug : name,
|
|
@@ -77705,7 +78086,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
77705
78086
|
if (action === "get") {
|
|
77706
78087
|
const output = requiredStringFlag(flags.output ?? flags.file ?? rest[2], "--output <path>");
|
|
77707
78088
|
const image = await client.getTeamProfileImage(teamId);
|
|
77708
|
-
|
|
78089
|
+
writeFileSync11(output, image.data, { mode: 384 });
|
|
77709
78090
|
if (flags.json === true) printJson2({ output, content_type: image.contentType, size_bytes: image.data.byteLength });
|
|
77710
78091
|
else console.log(`Team profile image written: ${output}`);
|
|
77711
78092
|
return;
|
|
@@ -77864,7 +78245,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
77864
78245
|
const output = requiredStringFlag(flags.output, "--output <path>");
|
|
77865
78246
|
const result = await client.exportTeamData(teamId);
|
|
77866
78247
|
const artifact = result.artifact && typeof result.artifact === "object" ? result.artifact : result;
|
|
77867
|
-
|
|
78248
|
+
writeFileSync11(output, `${JSON.stringify(artifact, null, 2)}
|
|
77868
78249
|
`, { encoding: "utf-8", mode: 384 });
|
|
77869
78250
|
if (flags.json === true) printJson2({ ...result, output });
|
|
77870
78251
|
else console.log(`Team export written: ${output}`);
|
|
@@ -77872,7 +78253,7 @@ async function handleTeams(client, subcommand, rest, flags) {
|
|
|
77872
78253
|
}
|
|
77873
78254
|
if (subcommand === "import") {
|
|
77874
78255
|
const file = requiredStringFlag(flags.file ?? rest[0], "--file <path>");
|
|
77875
|
-
const artifact = JSON.parse(
|
|
78256
|
+
const artifact = JSON.parse(readFileSync13(file, "utf-8"));
|
|
77876
78257
|
let teamId = typeof flags.team === "string" ? flags.team : typeof flags["destination-team"] === "string" ? flags["destination-team"] : void 0;
|
|
77877
78258
|
if (!teamId && typeof flags["new-team-name"] === "string") {
|
|
77878
78259
|
const team = await client.createTeam({ name: flags["new-team-name"] });
|
|
@@ -78207,7 +78588,7 @@ async function handleRemoteAccess(client, subcommand, rest, flags) {
|
|
|
78207
78588
|
const projects = await loadProjects(client, masterKey, hostingFlags, hostingContext);
|
|
78208
78589
|
const bindings = await resolveRemoteAccessBindings(client, masterKey, projects, candidateRoots, hostingFlags, hostingContext);
|
|
78209
78590
|
if (bindings.length === 0) throw new Error("No Project folders were approved for remote access.");
|
|
78210
|
-
const sourceSessionId =
|
|
78591
|
+
const sourceSessionId = randomUUID15();
|
|
78211
78592
|
const controller = new AbortController();
|
|
78212
78593
|
const stop = () => controller.abort();
|
|
78213
78594
|
process.once("SIGINT", stop);
|
|
@@ -78314,8 +78695,8 @@ async function resolveRemoteAccessBindings(client, masterKey, projects, candidat
|
|
|
78314
78695
|
const answer = (await promptLine2(`Create ${unresolved.length} missing Project${unresolved.length === 1 ? "" : "s"}? [y/N] `)).trim().toLowerCase();
|
|
78315
78696
|
if (answer !== "y" && answer !== "yes") throw new Error("Remote access Project creation cancelled.");
|
|
78316
78697
|
for (const rootPath of unresolved) {
|
|
78317
|
-
const project = await createEncryptedRemoteAccessProject(client, masterKey,
|
|
78318
|
-
resolved.push({ rootPath, project, sourceId:
|
|
78698
|
+
const project = await createEncryptedRemoteAccessProject(client, masterKey, basename4(rootPath), context);
|
|
78699
|
+
resolved.push({ rootPath, project, sourceId: randomUUID15() });
|
|
78319
78700
|
}
|
|
78320
78701
|
}
|
|
78321
78702
|
const bindings = [];
|
|
@@ -78326,7 +78707,7 @@ async function resolveRemoteAccessBindings(client, masterKey, projects, candidat
|
|
|
78326
78707
|
projectId: item.project.projectId,
|
|
78327
78708
|
rootPath: item.rootPath,
|
|
78328
78709
|
sourceType,
|
|
78329
|
-
displayName:
|
|
78710
|
+
displayName: basename4(item.rootPath)
|
|
78330
78711
|
});
|
|
78331
78712
|
const remoteSources = await client.listProjectSources(item.project.projectId, context);
|
|
78332
78713
|
const remoteSource = remoteSources.find((entry) => entry.source_id === source.sourceId);
|
|
@@ -78352,7 +78733,7 @@ async function resolveRemoteAccessBindings(client, masterKey, projects, candidat
|
|
|
78352
78733
|
}
|
|
78353
78734
|
async function createEncryptedRemoteAccessProject(client, masterKey, name, context) {
|
|
78354
78735
|
const projectKey = randomBytes9(32);
|
|
78355
|
-
const projectId =
|
|
78736
|
+
const projectId = randomUUID15();
|
|
78356
78737
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
78357
78738
|
const wrapping = await client.projectWrappingKey(context);
|
|
78358
78739
|
const payload = {
|
|
@@ -78369,7 +78750,7 @@ async function createEncryptedRemoteAccessProject(client, masterKey, name, conte
|
|
|
78369
78750
|
last_opened_at: timestamp,
|
|
78370
78751
|
key_wrappers: wrapping.teamId ? [{
|
|
78371
78752
|
key_type: "team",
|
|
78372
|
-
hashed_team_id:
|
|
78753
|
+
hashed_team_id: createHash15("sha256").update(wrapping.teamId).digest("hex"),
|
|
78373
78754
|
team_key_epoch: 1,
|
|
78374
78755
|
encrypted_project_key: await encryptBytesWithAesGcm(projectKey, wrapping.key),
|
|
78375
78756
|
wrapper_version: 1,
|
|
@@ -79169,16 +79550,16 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79169
79550
|
}
|
|
79170
79551
|
}
|
|
79171
79552
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
79172
|
-
const { join:
|
|
79553
|
+
const { join: join13 } = await import("path");
|
|
79173
79554
|
if (useZip) {
|
|
79174
|
-
const tmpDir =
|
|
79555
|
+
const tmpDir = join13(outputDir, `.${filenameBase}_tmp`);
|
|
79175
79556
|
await mkdir2(tmpDir, { recursive: true });
|
|
79176
|
-
await writeFile2(
|
|
79177
|
-
await writeFile2(
|
|
79557
|
+
await writeFile2(join13(tmpDir, `${filenameBase}.yml`), yamlContent);
|
|
79558
|
+
await writeFile2(join13(tmpDir, `${filenameBase}.md`), mdContent);
|
|
79178
79559
|
if (codeEmbeds.length > 0) {
|
|
79179
79560
|
for (const ce of codeEmbeds) {
|
|
79180
79561
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
79181
|
-
const fullPath =
|
|
79562
|
+
const fullPath = join13(tmpDir, "code", fpath);
|
|
79182
79563
|
await mkdir2(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
79183
79564
|
recursive: true
|
|
79184
79565
|
});
|
|
@@ -79186,13 +79567,13 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79186
79567
|
}
|
|
79187
79568
|
}
|
|
79188
79569
|
if (transcriptEmbeds.length > 0) {
|
|
79189
|
-
const tDir =
|
|
79570
|
+
const tDir = join13(tmpDir, "transcripts");
|
|
79190
79571
|
await mkdir2(tDir, { recursive: true });
|
|
79191
79572
|
for (const te of transcriptEmbeds) {
|
|
79192
|
-
await writeFile2(
|
|
79573
|
+
await writeFile2(join13(tDir, te.filename), te.content);
|
|
79193
79574
|
}
|
|
79194
79575
|
}
|
|
79195
|
-
const zipPath =
|
|
79576
|
+
const zipPath = join13(outputDir, `${filenameBase}.zip`);
|
|
79196
79577
|
const { execSync: execSync2 } = await import("child_process");
|
|
79197
79578
|
try {
|
|
79198
79579
|
execSync2(`cd "${tmpDir}" && zip -r "${zipPath}" .`, { stdio: "pipe" });
|
|
@@ -79207,17 +79588,17 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79207
79588
|
);
|
|
79208
79589
|
}
|
|
79209
79590
|
} else {
|
|
79210
|
-
const chatDir =
|
|
79591
|
+
const chatDir = join13(outputDir, filenameBase);
|
|
79211
79592
|
await mkdir2(chatDir, { recursive: true });
|
|
79212
79593
|
const written = [];
|
|
79213
|
-
await writeFile2(
|
|
79594
|
+
await writeFile2(join13(chatDir, `${filenameBase}.yml`), yamlContent);
|
|
79214
79595
|
written.push(`${filenameBase}.yml`);
|
|
79215
|
-
await writeFile2(
|
|
79596
|
+
await writeFile2(join13(chatDir, `${filenameBase}.md`), mdContent);
|
|
79216
79597
|
written.push(`${filenameBase}.md`);
|
|
79217
79598
|
if (codeEmbeds.length > 0) {
|
|
79218
79599
|
for (const ce of codeEmbeds) {
|
|
79219
79600
|
const fpath = ce.filePath ?? ce.filename ?? `${ce.embedId.slice(0, 8)}.${getExtForLang(ce.language)}`;
|
|
79220
|
-
const fullPath =
|
|
79601
|
+
const fullPath = join13(chatDir, "code", fpath);
|
|
79221
79602
|
await mkdir2(fullPath.substring(0, fullPath.lastIndexOf("/")), {
|
|
79222
79603
|
recursive: true
|
|
79223
79604
|
});
|
|
@@ -79226,10 +79607,10 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79226
79607
|
}
|
|
79227
79608
|
}
|
|
79228
79609
|
if (transcriptEmbeds.length > 0) {
|
|
79229
|
-
const tDir =
|
|
79610
|
+
const tDir = join13(chatDir, "transcripts");
|
|
79230
79611
|
await mkdir2(tDir, { recursive: true });
|
|
79231
79612
|
for (const te of transcriptEmbeds) {
|
|
79232
|
-
await writeFile2(
|
|
79613
|
+
await writeFile2(join13(tDir, te.filename), te.content);
|
|
79233
79614
|
written.push(`transcripts/${te.filename}`);
|
|
79234
79615
|
}
|
|
79235
79616
|
}
|
|
@@ -79265,7 +79646,7 @@ ${deleted}/${resolved.length} chat(s) deleted.`);
|
|
|
79265
79646
|
printJson2({
|
|
79266
79647
|
chat_id: chat.id,
|
|
79267
79648
|
title: chat.title,
|
|
79268
|
-
output_dir: useZip ?
|
|
79649
|
+
output_dir: useZip ? join13(outputDir, `${filenameBase}.zip`) : join13(outputDir, filenameBase),
|
|
79269
79650
|
files,
|
|
79270
79651
|
code_embeds: codeEmbeds.length,
|
|
79271
79652
|
transcript_embeds: transcriptEmbeds.length
|
|
@@ -79585,7 +79966,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
79585
79966
|
const messages = rawMessages.map((message) => {
|
|
79586
79967
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
79587
79968
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
79588
|
-
const hashedMessageId = messageId ?
|
|
79969
|
+
const hashedMessageId = messageId ? createHash15("sha256").update(messageId).digest("hex") : null;
|
|
79589
79970
|
const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
|
|
79590
79971
|
return {
|
|
79591
79972
|
id: messageId,
|
|
@@ -79791,7 +80172,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
79791
80172
|
if (subcommand === "validate") {
|
|
79792
80173
|
const file = typeof flags.file === "string" ? flags.file : "";
|
|
79793
80174
|
if (!file) throw new Error("Missing --file. Example: openmates workflows validate --file workflow.yml");
|
|
79794
|
-
const validation = await client.validateWorkflowYaml(
|
|
80175
|
+
const validation = await client.validateWorkflowYaml(readFileSync13(file, "utf8"));
|
|
79795
80176
|
if (flags.json === true) {
|
|
79796
80177
|
printJson2(validation);
|
|
79797
80178
|
} else {
|
|
@@ -79807,7 +80188,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
79807
80188
|
if (subcommand === "create") {
|
|
79808
80189
|
const yamlFile = typeof flags.file === "string" ? flags.file : "";
|
|
79809
80190
|
if (yamlFile) {
|
|
79810
|
-
const result = await client.createWorkflowYaml(
|
|
80191
|
+
const result = await client.createWorkflowYaml(readFileSync13(yamlFile, "utf8"));
|
|
79811
80192
|
if (flags.json === true) {
|
|
79812
80193
|
printJson2(result);
|
|
79813
80194
|
} else {
|
|
@@ -79841,7 +80222,7 @@ async function handleWorkflows(client, subcommand, rest, flags, redactor) {
|
|
|
79841
80222
|
const workflowId = rest[0] ? await requiredResolvedWorkflowId(client, rest[0], flags, "update") : void 0;
|
|
79842
80223
|
const yamlFile = typeof flags.file === "string" ? flags.file : "";
|
|
79843
80224
|
if (!workflowId || !yamlFile) throw new Error("Missing workflow ID or --file. Example: openmates workflows update <id> --file workflow.yml");
|
|
79844
|
-
const result = await client.updateWorkflowYaml(workflowId,
|
|
80225
|
+
const result = await client.updateWorkflowYaml(workflowId, readFileSync13(yamlFile, "utf8"));
|
|
79845
80226
|
if (typeof flags.slug === "string") {
|
|
79846
80227
|
result.workflow = await client.updateWorkflow(result.workflow.id, { slug: flags.slug });
|
|
79847
80228
|
}
|
|
@@ -80291,7 +80672,7 @@ function buildImagesAiDetectionSummary(uploadResult, filePath) {
|
|
|
80291
80672
|
const classification = classifyImagesAiDetection(score);
|
|
80292
80673
|
return {
|
|
80293
80674
|
file: filePath,
|
|
80294
|
-
filename: uploadResult.filename ||
|
|
80675
|
+
filename: uploadResult.filename || basename4(filePath),
|
|
80295
80676
|
content_type: uploadResult.content_type,
|
|
80296
80677
|
embed_id: uploadResult.embed_id,
|
|
80297
80678
|
deduplicated: uploadResult.deduplicated,
|
|
@@ -80443,7 +80824,7 @@ function buildCodeImageToHtmlRequest(positionals, flags) {
|
|
|
80443
80824
|
);
|
|
80444
80825
|
}
|
|
80445
80826
|
const request = {
|
|
80446
|
-
image_base64:
|
|
80827
|
+
image_base64: readFileSync13(file).toString("base64"),
|
|
80447
80828
|
mime_type: inferImageToHtmlMimeType(file),
|
|
80448
80829
|
filename: file.split(/[\\/]/).pop() ?? file
|
|
80449
80830
|
};
|
|
@@ -81322,7 +81703,7 @@ async function handleEmbeds(client, subcommand, rest, flags) {
|
|
|
81322
81703
|
throw new Error("Embed version content was not available after local reconstruction.");
|
|
81323
81704
|
}
|
|
81324
81705
|
if (typeof flags.output === "string") {
|
|
81325
|
-
|
|
81706
|
+
writeFileSync11(flags.output, result.content, "utf-8");
|
|
81326
81707
|
if (flags.json === true) {
|
|
81327
81708
|
printJson2({ ...result, output: flags.output });
|
|
81328
81709
|
} else {
|
|
@@ -81606,12 +81987,12 @@ async function parseAccountImportFile(parserFormat, file, flags = {}) {
|
|
|
81606
81987
|
const selectedSource = requestedSource;
|
|
81607
81988
|
if (parserFormat === "generic") {
|
|
81608
81989
|
if (selectedSource !== "gemini" && selectedSource !== "other") throw new Error("Generic account import requires --source gemini or --source other.");
|
|
81609
|
-
return parseGenericImportBuffer(payload,
|
|
81990
|
+
return parseGenericImportBuffer(payload, basename4(file), selectedSource);
|
|
81610
81991
|
}
|
|
81611
|
-
if (parserFormat === "claude") return parseClaudeImportBuffer(payload,
|
|
81612
|
-
if (parserFormat === "chatgpt") return parseChatGPTImportBuffer(payload,
|
|
81613
|
-
if (parserFormat === "opencode") return parseOpenCodeImportBuffer(payload,
|
|
81614
|
-
return parseOpenMatesImportBuffer(payload,
|
|
81992
|
+
if (parserFormat === "claude") return parseClaudeImportBuffer(payload, basename4(file), selectedSource);
|
|
81993
|
+
if (parserFormat === "chatgpt") return parseChatGPTImportBuffer(payload, basename4(file), selectedSource);
|
|
81994
|
+
if (parserFormat === "opencode") return parseOpenCodeImportBuffer(payload, basename4(file), selectedSource);
|
|
81995
|
+
return parseOpenMatesImportBuffer(payload, basename4(file), typeof flags.password === "string" ? flags.password : void 0, selectedSource);
|
|
81615
81996
|
}
|
|
81616
81997
|
async function runAccountImport(client, parserFormat, file, flags) {
|
|
81617
81998
|
if (parserFormat === "generic" && typeof flags.source !== "string") throw new Error("Generic account import requires --source gemini or --source other.");
|
|
@@ -81650,7 +82031,7 @@ async function runAccountImport(client, parserFormat, file, flags) {
|
|
|
81650
82031
|
if (flags.yes !== true) {
|
|
81651
82032
|
await confirmOrExit(`Import ${selectedCount} chat(s) into new encrypted OpenMates chats? [y/N] `);
|
|
81652
82033
|
}
|
|
81653
|
-
const importId = typeof preview.import_id === "string" ? preview.import_id :
|
|
82034
|
+
const importId = typeof preview.import_id === "string" ? preview.import_id : randomUUID15();
|
|
81654
82035
|
const selectedChats = parsed.chats.slice(0, selectedCount);
|
|
81655
82036
|
const selectedFingerprints = selectedChats.map((chat) => chat.source_fingerprint);
|
|
81656
82037
|
const confirmation = await client.confirmAccountImport(importId, selectedFingerprints);
|
|
@@ -81981,11 +82362,11 @@ function parseYamlScalar(value) {
|
|
|
81981
82362
|
}
|
|
81982
82363
|
async function saveDownloadedDocument(document, output) {
|
|
81983
82364
|
const { mkdir: mkdir2, writeFile: writeFile2 } = await import("fs/promises");
|
|
81984
|
-
const { join:
|
|
82365
|
+
const { join: join13, basename: basename5, dirname: dirname11 } = await import("path");
|
|
81985
82366
|
const target = typeof output === "string" ? output : ".";
|
|
81986
|
-
const filename =
|
|
81987
|
-
const filePath = target.endsWith(".pdf") ? target :
|
|
81988
|
-
await mkdir2(
|
|
82367
|
+
const filename = basename5(document.filename || "document.pdf");
|
|
82368
|
+
const filePath = target.endsWith(".pdf") ? target : join13(target, filename);
|
|
82369
|
+
await mkdir2(dirname11(filePath), { recursive: true });
|
|
81989
82370
|
await writeFile2(filePath, document.data);
|
|
81990
82371
|
return filePath;
|
|
81991
82372
|
}
|
|
@@ -82144,8 +82525,8 @@ async function handleFinance(client, subcommand, _rest, flags) {
|
|
|
82144
82525
|
refresh_token_envelope: connectedAccount.refreshTokenBundle
|
|
82145
82526
|
}
|
|
82146
82527
|
] : [],
|
|
82147
|
-
chatId: `cli-finance-${
|
|
82148
|
-
messageId: `cli-finance-message-${
|
|
82528
|
+
chatId: `cli-finance-${randomUUID15()}`,
|
|
82529
|
+
messageId: `cli-finance-message-${randomUUID15()}`,
|
|
82149
82530
|
apiKey: resolveApiKey(flags) ?? void 0,
|
|
82150
82531
|
promptInjectionProtection: false
|
|
82151
82532
|
});
|
|
@@ -82157,7 +82538,7 @@ async function handleFinance(client, subcommand, _rest, flags) {
|
|
|
82157
82538
|
}
|
|
82158
82539
|
function readFinanceCsvStatements(flags) {
|
|
82159
82540
|
const paths = parseCsvFlag(flags.csv) ?? [];
|
|
82160
|
-
return paths.map((filePath) => ({ filename:
|
|
82541
|
+
return paths.map((filePath) => ({ filename: basename4(filePath), content: readFileSync13(filePath, "utf8") }));
|
|
82161
82542
|
}
|
|
82162
82543
|
function buildFinanceCheckAccountsInput(flags, csvStatements, connectedAccount) {
|
|
82163
82544
|
const input = {
|
|
@@ -82386,7 +82767,7 @@ async function handleRevolutBusinessConnect(client, rest, flags) {
|
|
|
82386
82767
|
if (!clientId.trim()) throw new Error("ClientID is required to connect Revolut Business.");
|
|
82387
82768
|
const code = providedCode || await promptPlainText("Code from OpenMates callback page: ");
|
|
82388
82769
|
if (!code.trim()) throw new Error("Authorization code is required to connect Revolut Business.");
|
|
82389
|
-
const privateKeyPem =
|
|
82770
|
+
const privateKeyPem = readFileSync13(result.privateKeyPath, "utf8");
|
|
82390
82771
|
const setup = await client.exchangeRevolutBusinessSetupCode({
|
|
82391
82772
|
clientId: clientId.trim(),
|
|
82392
82773
|
code: code.trim(),
|
|
@@ -82452,7 +82833,7 @@ async function promptPlainText(question) {
|
|
|
82452
82833
|
}
|
|
82453
82834
|
async function writeSecretFile(filePath, content, force = false) {
|
|
82454
82835
|
const { mkdir: mkdir2, writeFile: writeFile2, stat: stat2 } = await import("fs/promises");
|
|
82455
|
-
const { dirname:
|
|
82836
|
+
const { dirname: dirname11 } = await import("path");
|
|
82456
82837
|
try {
|
|
82457
82838
|
await stat2(filePath);
|
|
82458
82839
|
if (!force) throw new Error(`${filePath} already exists. Use --force to overwrite.`);
|
|
@@ -82462,7 +82843,7 @@ async function writeSecretFile(filePath, content, force = false) {
|
|
|
82462
82843
|
}
|
|
82463
82844
|
if (error instanceof Error && !("code" in error)) throw error;
|
|
82464
82845
|
}
|
|
82465
|
-
await mkdir2(
|
|
82846
|
+
await mkdir2(dirname11(filePath), { recursive: true });
|
|
82466
82847
|
await writeFile2(filePath, content, { mode: 384 });
|
|
82467
82848
|
return filePath;
|
|
82468
82849
|
}
|
|
@@ -85607,6 +85988,7 @@ Commands:
|
|
|
85607
85988
|
|
|
85608
85989
|
Flags:
|
|
85609
85990
|
--json Output raw JSON instead of formatted output
|
|
85991
|
+
--profile <name> Use an isolated login profile (also OPENMATES_PROFILE)
|
|
85610
85992
|
--api-url <url> Override API base URL (default: installed self-host server, then https://api.openmates.org)
|
|
85611
85993
|
--api-key <key> Optional API key override (or set OPENMATES_API_KEY)
|
|
85612
85994
|
--version Show CLI version and update availability
|
|
@@ -85628,12 +86010,16 @@ function printSelfUpdateHelp() {
|
|
|
85628
86010
|
openmates update [--version <version|tag>] [--package-manager <name>] [--dry-run] [--verbose] [--json]
|
|
85629
86011
|
openmates upgrade [--version <version|tag>] [--package-manager <name>] [--dry-run] [--verbose] [--json]
|
|
85630
86012
|
|
|
85631
|
-
Updates the globally installed openmates package
|
|
86013
|
+
Updates the globally installed openmates package using the saved release channel.
|
|
86014
|
+
Stable installations default to stable; prereleases default to dev.
|
|
86015
|
+
Channel changes persist only after a successful update or up-to-date check.
|
|
85632
86016
|
|
|
85633
86017
|
Options:
|
|
85634
|
-
--version <version|tag> Install a specific npm version or dist-tag (
|
|
86018
|
+
--version <version|tag> Install a specific npm version or dist-tag (one-time override)
|
|
85635
86019
|
--package-manager <name> npm, pnpm, yarn, or bun (default: detect, then npm)
|
|
85636
86020
|
--dry-run Print the package-manager command without running it
|
|
86021
|
+
--channel <dev|stable|main> Save release channel (dev uses npm alpha; stable/main uses latest)
|
|
86022
|
+
--allow-downgrade Explicitly allow installing an older version
|
|
85637
86023
|
--verbose Stream package-manager output during installation
|
|
85638
86024
|
--json Output the update plan/result as JSON`);
|
|
85639
86025
|
}
|
|
@@ -85969,8 +86355,10 @@ function printTasksHelp() {
|
|
|
85969
86355
|
openmates tasks reorder <task-id|short-id> [--before <task-id>] [--after <task-id>] [--position <n>] [--status <status>] [--json]
|
|
85970
86356
|
openmates tasks dependencies list <task-id|short-id> [--json]
|
|
85971
86357
|
openmates tasks dependencies add|remove <task-id|short-id> --target plan:<id>|task:<id> [--recovery-root <external-root>] [--json]
|
|
85972
|
-
openmates tasks activity list <task-id|short-id> [--
|
|
85973
|
-
openmates tasks activity
|
|
86358
|
+
openmates tasks activity list <task-id|short-id> [--max-entries <1-200>] [--cursor <cursor>] [--newest-first] [--json]
|
|
86359
|
+
openmates tasks activity search <task-id|short-id> --query <text> [--max-entries <1-200>] [--json]
|
|
86360
|
+
openmates tasks activity add <task-id|short-id> --message <text> [--as-assignee] [--delivery-id <sha256>] [--json]
|
|
86361
|
+
openmates tasks activity flush <task-id|short-id> [--as-assignee] [--json]
|
|
85974
86362
|
openmates tasks activity delete <task-id|short-id> <entry-id> [--json]
|
|
85975
86363
|
|
|
85976
86364
|
Chat-scoped aliases:
|
|
@@ -86440,7 +86828,7 @@ async function handleDocs(client, subcommand, rest, flags) {
|
|
|
86440
86828
|
}
|
|
86441
86829
|
if (subcommand === "download") {
|
|
86442
86830
|
const { writeFile: writeFile2, mkdir: mkdir2 } = await import("fs/promises");
|
|
86443
|
-
const { join:
|
|
86831
|
+
const { join: join13, dirname: dirname11 } = await import("path");
|
|
86444
86832
|
if (flags.all === true) {
|
|
86445
86833
|
const outputDir = typeof flags.output === "string" ? flags.output : "./openmates-docs";
|
|
86446
86834
|
const tree = await client.listDocs();
|
|
@@ -86449,8 +86837,8 @@ async function handleDocs(client, subcommand, rest, flags) {
|
|
|
86449
86837
|
let count = 0;
|
|
86450
86838
|
for (const slug2 of slugs) {
|
|
86451
86839
|
const content2 = await client.getDoc(slug2);
|
|
86452
|
-
const filePath =
|
|
86453
|
-
await mkdir2(
|
|
86840
|
+
const filePath = join13(outputDir, `${slug2}.md`);
|
|
86841
|
+
await mkdir2(dirname11(filePath), { recursive: true });
|
|
86454
86842
|
await writeFile2(filePath, content2, "utf-8");
|
|
86455
86843
|
count++;
|
|
86456
86844
|
process.stderr.write(`\r Downloaded ${count}/${slugs.length}`);
|
|
@@ -86522,8 +86910,8 @@ function isCliEntrypoint() {
|
|
|
86522
86910
|
if (!entrypoint) return false;
|
|
86523
86911
|
try {
|
|
86524
86912
|
const invokedPath = realpathSync2(entrypoint);
|
|
86525
|
-
const modulePath = realpathSync2(
|
|
86526
|
-
return invokedPath === modulePath ||
|
|
86913
|
+
const modulePath = realpathSync2(fileURLToPath3(import.meta.url));
|
|
86914
|
+
return invokedPath === modulePath || basename4(invokedPath) === "cli.js" && dirname10(invokedPath) === dirname10(modulePath);
|
|
86527
86915
|
} catch {
|
|
86528
86916
|
return false;
|
|
86529
86917
|
}
|