@or3/cloud 0.1.69 → 0.1.72
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/cli.mjs +36 -20
- package/package.json +4 -4
package/dist/cli.mjs
CHANGED
|
@@ -31,7 +31,7 @@ import { fileURLToPath } from "node:url";
|
|
|
31
31
|
import { createGunzip } from "node:zlib";
|
|
32
32
|
var execFile = promisify(execFileCallback);
|
|
33
33
|
var PACKAGE_ROOT = resolve(fileURLToPath(new URL("../", import.meta.url)));
|
|
34
|
-
var PACKAGE_VERSION = "0.1.
|
|
34
|
+
var PACKAGE_VERSION = "0.1.72";
|
|
35
35
|
var IMAGE_REPOSITORY = "ghcr.io/saluana/or3-chat";
|
|
36
36
|
var ASSET_ROOT = resolve(fileURLToPath(new URL("../assets/", import.meta.url)));
|
|
37
37
|
var LEGACY_STATE_SCHEMA_VERSION = 1;
|
|
@@ -368,8 +368,8 @@ async function run(command, args, cwd, environment) {
|
|
|
368
368
|
});
|
|
369
369
|
return {
|
|
370
370
|
ok: true,
|
|
371
|
-
stdout: result.stdout
|
|
372
|
-
stderr: result.stderr
|
|
371
|
+
stdout: result.stdout,
|
|
372
|
+
stderr: result.stderr,
|
|
373
373
|
command: printable,
|
|
374
374
|
exitCode: 0
|
|
375
375
|
};
|
|
@@ -494,7 +494,7 @@ function serializeCredentialValue(value) {
|
|
|
494
494
|
return serializeEnvValue(value);
|
|
495
495
|
}
|
|
496
496
|
function serializeEnv(values) {
|
|
497
|
-
return `${Object.entries(values).filter((
|
|
497
|
+
return `${Object.entries(values).filter((entry) => entry[1] !== undefined).map(([key, value]) => `${key}=${serializeEnvValue(value)}`).join(`
|
|
498
498
|
`)}
|
|
499
499
|
`;
|
|
500
500
|
}
|
|
@@ -1036,7 +1036,7 @@ async function removeManagedDataVolumeForRecreation(directory, state) {
|
|
|
1036
1036
|
} catch {
|
|
1037
1037
|
throw new Error(`Docker returned unreadable metadata for managed volume ${state.volumeName}.`);
|
|
1038
1038
|
}
|
|
1039
|
-
if (volume.Name !== state.volumeName || volume.Labels?.["com.docker.compose.project"] !== state.composeProject || volume.Labels
|
|
1039
|
+
if (volume.Name !== state.volumeName || volume.Labels?.["com.docker.compose.project"] !== state.composeProject || volume.Labels["com.docker.compose.volume"] !== "or3-data") {
|
|
1040
1040
|
throw new Error(`Volume ${state.volumeName} is not bound to this managed Compose deployment. Refusing to recreate it.`);
|
|
1041
1041
|
}
|
|
1042
1042
|
const removedVolume = await run("docker", ["volume", "rm", state.volumeName], directory);
|
|
@@ -1269,6 +1269,8 @@ async function assertImageReleaseIdentity(image, version) {
|
|
|
1269
1269
|
} catch {
|
|
1270
1270
|
throw new Error(`OR3 image ${image} has no readable release labels.`);
|
|
1271
1271
|
}
|
|
1272
|
+
if (!labels || typeof labels !== "object")
|
|
1273
|
+
throw new Error(`OR3 image ${image} has no readable release labels.`);
|
|
1272
1274
|
assertImageReleaseLabels(image, labels, version, packagedSourceRevision(version));
|
|
1273
1275
|
}
|
|
1274
1276
|
async function imageDigest(image) {
|
|
@@ -1636,6 +1638,7 @@ async function installManagedAssets(directory, assets) {
|
|
|
1636
1638
|
const token = randomBytes(4).toString("hex");
|
|
1637
1639
|
const staged = [];
|
|
1638
1640
|
let retainRollbackCopies = false;
|
|
1641
|
+
const canCleanRollback = () => !retainRollbackCopies;
|
|
1639
1642
|
try {
|
|
1640
1643
|
for (const [name, contents] of assets) {
|
|
1641
1644
|
const destination = join(directory, name);
|
|
@@ -1687,7 +1690,7 @@ async function installManagedAssets(directory, assets) {
|
|
|
1687
1690
|
await rm(entry.replacement, { force: true }).catch(() => {
|
|
1688
1691
|
return;
|
|
1689
1692
|
});
|
|
1690
|
-
if (entry.rollback &&
|
|
1693
|
+
if (entry.rollback && canCleanRollback())
|
|
1691
1694
|
await rm(entry.rollback, { force: true }).catch(() => {
|
|
1692
1695
|
return;
|
|
1693
1696
|
});
|
|
@@ -2330,6 +2333,7 @@ async function streamCommandToFile(command, args, destination, cwd, secrets = []
|
|
|
2330
2333
|
});
|
|
2331
2334
|
let stderr = "";
|
|
2332
2335
|
let timedOut = false;
|
|
2336
|
+
const hasTimedOut = () => timedOut;
|
|
2333
2337
|
const timeout = setTimeout(() => {
|
|
2334
2338
|
timedOut = true;
|
|
2335
2339
|
terminateChildProcess(child);
|
|
@@ -2347,7 +2351,7 @@ async function streamCommandToFile(command, args, destination, cwd, secrets = []
|
|
|
2347
2351
|
exit,
|
|
2348
2352
|
pipeline(child.stdout, createWriteStream(temporary, { flags: "wx", mode: 384 }))
|
|
2349
2353
|
]);
|
|
2350
|
-
if (
|
|
2354
|
+
if (hasTimedOut())
|
|
2351
2355
|
throw new Error(`${command} exceeded the ${STREAM_COMMAND_TIMEOUT_MS / 1000}-second archive deadline.`);
|
|
2352
2356
|
if (exitCode !== 0) {
|
|
2353
2357
|
throw new Error(`${command} ${args.join(" ")} exited with ${exitCode}. ${redact(stderr, secrets)}`.trim());
|
|
@@ -2375,6 +2379,7 @@ async function streamFileToCommand(command, args, source, cwd, secrets = []) {
|
|
|
2375
2379
|
});
|
|
2376
2380
|
let stderr = "";
|
|
2377
2381
|
let timedOut = false;
|
|
2382
|
+
const hasTimedOut = () => timedOut;
|
|
2378
2383
|
const timeout = setTimeout(() => {
|
|
2379
2384
|
timedOut = true;
|
|
2380
2385
|
terminateChildProcess(child);
|
|
@@ -2392,7 +2397,7 @@ async function streamFileToCommand(command, args, source, cwd, secrets = []) {
|
|
|
2392
2397
|
exit,
|
|
2393
2398
|
pipeline(createReadStream(source), child.stdin)
|
|
2394
2399
|
]);
|
|
2395
|
-
if (
|
|
2400
|
+
if (hasTimedOut())
|
|
2396
2401
|
throw new Error(`${command} exceeded the ${STREAM_COMMAND_TIMEOUT_MS / 1000}-second archive deadline.`);
|
|
2397
2402
|
if (exitCode !== 0) {
|
|
2398
2403
|
throw new Error(`${command} ${args.join(" ")} exited with ${exitCode}. ${redact(stderr, secrets)}`.trim());
|
|
@@ -3124,12 +3129,19 @@ async function collectTargetReadyChecks(directory, mode, env) {
|
|
|
3124
3129
|
}
|
|
3125
3130
|
}
|
|
3126
3131
|
checks.push(databasesOk ? { code: "database-integrity", status: "passed", detail: "auth.sqlite and sync.sqlite quick_check passed with managed ownership." } : { code: "database-integrity", status: "failed", detail: `SQLite integrity or ownership verification failed. ${redact(databaseCheck.stderr.trim(), secretValues(env))}` });
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3132
|
+
if (mode === "public") {
|
|
3133
|
+
const baseUrl = new URL(`https://${env.OR3_PUBLIC_DOMAIN}`);
|
|
3134
|
+
try {
|
|
3135
|
+
validateVerificationHealth(await verificationJson(baseUrl, "/api/health?deep=true"));
|
|
3136
|
+
checks.push({ code: "public-health", status: "passed", detail: `${baseUrl.origin} deep health reports the managed profile.` });
|
|
3137
|
+
} catch (error) {
|
|
3138
|
+
checks.push({ code: "public-health", status: "failed", detail: redact(error instanceof Error ? error.message : String(error)) });
|
|
3139
|
+
}
|
|
3140
|
+
} else {
|
|
3141
|
+
const internal = await run("docker", [
|
|
3142
|
+
...composeArgs(directory, mode, ["exec", "-T", "or3", ...containerNodeCommand(HEALTH_SCRIPT)])
|
|
3143
|
+
], directory);
|
|
3144
|
+
checks.push(internal.ok ? { code: "public-health", status: "passed", detail: "Container-internal deep health reports the managed profile." } : { code: "public-health", status: "failed", detail: `Container-internal deep health failed. ${redact(internal.stderr.trim(), secretValues(env))}` });
|
|
3133
3145
|
}
|
|
3134
3146
|
return { containerId, checks };
|
|
3135
3147
|
}
|
|
@@ -3778,7 +3790,7 @@ async function assessUpdate(directory, targetVersion, options = {}) {
|
|
|
3778
3790
|
}
|
|
3779
3791
|
try {
|
|
3780
3792
|
assertKnownStateSchema(state?.schemaVersion);
|
|
3781
|
-
checks.push({ code: "state-schema", status: "passed", detail: `Managed state schema ${String(state
|
|
3793
|
+
checks.push({ code: "state-schema", status: "passed", detail: `Managed state schema ${String(state.schemaVersion)} is supported.` });
|
|
3782
3794
|
} catch (error) {
|
|
3783
3795
|
findings.push({ code: "state-schema-unsupported", severity: "blocker", message: error instanceof Error ? error.message : String(error) });
|
|
3784
3796
|
}
|
|
@@ -4497,7 +4509,7 @@ async function doctorCommand(directory) {
|
|
|
4497
4509
|
}
|
|
4498
4510
|
function validateVerificationHealth(value) {
|
|
4499
4511
|
const health = value;
|
|
4500
|
-
if (!health || health.status !== "ok" || health.providers?.auth?.provider !== "basic-auth" || health.providers
|
|
4512
|
+
if (!health || health.status !== "ok" || health.providers?.auth?.provider !== "basic-auth" || health.providers.sync?.provider !== "sqlite" || health.providers.storage?.provider !== "fs") {
|
|
4501
4513
|
throw new Error("Public deep health does not report the managed Basic Auth + SQLite + filesystem profile.");
|
|
4502
4514
|
}
|
|
4503
4515
|
return health;
|
|
@@ -4517,7 +4529,7 @@ async function verificationJson(baseUrl, path, options = {}) {
|
|
|
4517
4529
|
accept: "application/json",
|
|
4518
4530
|
...options.body === undefined ? {} : { "content-type": "application/json" },
|
|
4519
4531
|
...options.cookie ? { cookie: options.cookie } : {},
|
|
4520
|
-
...method === "GET" ? {} : { origin: baseUrl.origin }
|
|
4532
|
+
...method === "GET" ? {} : { origin: baseUrl.origin, "x-or3-cloud-intent": "mutation" }
|
|
4521
4533
|
},
|
|
4522
4534
|
...options.body === undefined ? {} : { body: JSON.stringify(options.body) }
|
|
4523
4535
|
});
|
|
@@ -4525,7 +4537,11 @@ async function verificationJson(baseUrl, path, options = {}) {
|
|
|
4525
4537
|
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
4526
4538
|
throw new Error(`${response.url} returned HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
4527
4539
|
}
|
|
4528
|
-
|
|
4540
|
+
const value = await response.json();
|
|
4541
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4542
|
+
throw new Error(`${response.url} returned an invalid JSON object.`);
|
|
4543
|
+
}
|
|
4544
|
+
return value;
|
|
4529
4545
|
}
|
|
4530
4546
|
function sameOriginVerificationUrl(baseUrl, value, label) {
|
|
4531
4547
|
if (typeof value !== "string")
|
|
@@ -4592,7 +4608,7 @@ async function verifyPublicApplication(baseUrl, credentials) {
|
|
|
4592
4608
|
throw new Error("Public Basic Auth sign-in did not set a session cookie.");
|
|
4593
4609
|
try {
|
|
4594
4610
|
const session = await verificationJson(baseUrl, "/api/auth/session", { cookie });
|
|
4595
|
-
if (session.session?.user?.email !== email || !session.session
|
|
4611
|
+
if (session.session?.user?.email !== email || !session.session.workspace?.id) {
|
|
4596
4612
|
throw new Error("Public session hydration did not return the verification user and workspace.");
|
|
4597
4613
|
}
|
|
4598
4614
|
const workspaceId = String(session.session.workspace.id);
|
|
@@ -4760,7 +4776,7 @@ async function provisionManagedCredentialsOnce(baseUrl, email, password, adminUs
|
|
|
4760
4776
|
const appCookie = responseCookie(appSignIn, "Managed owner provisioning");
|
|
4761
4777
|
try {
|
|
4762
4778
|
const session = await verificationJson(baseUrl, "/api/auth/session", { cookie: appCookie });
|
|
4763
|
-
if (session.session?.user?.email !== email || !session.session
|
|
4779
|
+
if (session.session?.user?.email !== email || !session.session.workspace?.id) {
|
|
4764
4780
|
throw new Error("Managed owner provisioning did not create the expected account and workspace.");
|
|
4765
4781
|
}
|
|
4766
4782
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@or3/cloud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.72",
|
|
4
4
|
"description": "Install and operate the OR3 Cloud container distribution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"dashboardUpdateProtocol": 1,
|
|
20
20
|
"dashboardUpdateMinimumSourceVersion": "0.1.40",
|
|
21
21
|
"stateSchema": 1,
|
|
22
|
-
"imageDigest": "sha256:
|
|
23
|
-
"operatorImageDigest": "sha256:
|
|
24
|
-
"sourceRevision": "
|
|
22
|
+
"imageDigest": "sha256:c4b408ea3a5fbd647b26d6efe89ddad58f90be7528aa2b5b6ed20d6d41c928ac",
|
|
23
|
+
"operatorImageDigest": "sha256:05b03560594da28e436977b82a905aa9a7702668b5e4ac4daea8b54376a75813",
|
|
24
|
+
"sourceRevision": "90c5756fa313331df7be656fcbc1bce5a4164d6e"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|