@or3/cloud 0.1.100 → 0.1.107
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/assets/Caddyfile +23 -2
- package/assets/dashboard-lifecycle.mjs +6 -0
- package/assets/dashboard-operator.mjs +17 -0
- package/dist/cli.mjs +200 -1
- package/package.json +4 -4
package/assets/Caddyfile
CHANGED
|
@@ -5,13 +5,34 @@
|
|
|
5
5
|
header {
|
|
6
6
|
Strict-Transport-Security "max-age=31536000"
|
|
7
7
|
X-Content-Type-Options "nosniff"
|
|
8
|
-
X-Frame-Options "DENY"
|
|
9
8
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
10
9
|
Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Only the two host-owned bridge documents can be embedded. Ordinary app
|
|
13
|
+
# pages still deny every ancestor, including same-origin frames.
|
|
14
|
+
@hostDocument {
|
|
15
|
+
not path /or3/portable-frame /streamsaver/mitm.html
|
|
16
|
+
}
|
|
17
|
+
header @hostDocument {
|
|
18
|
+
X-Frame-Options "DENY"
|
|
11
19
|
# Nuxt needs same-origin scripts plus inline hydration styles/scripts. The
|
|
12
20
|
# browser exchanges OpenRouter PKCE authorization codes directly with the
|
|
13
21
|
# OpenRouter API, so that origin must remain available to connect-src.
|
|
14
|
-
Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://openrouter.ai; media-src 'self' blob:; worker-src 'self' blob:; frame-src '
|
|
22
|
+
Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://openrouter.ai; media-src 'self' blob:; worker-src 'self' blob:; frame-src 'self'; upgrade-insecure-requests"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Preserve the portable route's hash-authorized, opaque-origin sandbox CSP.
|
|
26
|
+
# Applying the app policy here would block its worker or weaken containment.
|
|
27
|
+
@portableFrame path /or3/portable-frame
|
|
28
|
+
header @portableFrame X-Frame-Options "SAMEORIGIN"
|
|
29
|
+
|
|
30
|
+
# The fallback streaming exporter uses this bundled same-origin bridge to
|
|
31
|
+
# register its download service worker; it cannot use an opaque origin.
|
|
32
|
+
@downloadBridge path /streamsaver/mitm.html
|
|
33
|
+
header @downloadBridge {
|
|
34
|
+
X-Frame-Options "SAMEORIGIN"
|
|
35
|
+
Content-Security-Policy "default-src 'none'; script-src 'unsafe-inline'; connect-src 'self'; worker-src 'self'; frame-ancestors 'self'; base-uri 'none'; form-action 'none'"
|
|
15
36
|
}
|
|
16
37
|
|
|
17
38
|
encode zstd gzip
|
|
@@ -20354,6 +20354,10 @@ async function writeEnvFileDetailed(updates, options = {}) {
|
|
|
20354
20354
|
|
|
20355
20355
|
// ../../shared/admin/configuration.ts
|
|
20356
20356
|
var siteSettingPaths = {
|
|
20357
|
+
OR3_STORAGE_CAPACITY_WARNING_PERCENT: ["storageCapacityWarningPercent"],
|
|
20358
|
+
OR3_STORAGE_CAPACITY_CRITICAL_PERCENT: ["storageCapacityCriticalPercent"],
|
|
20359
|
+
OR3_CHAT_TOOL_ITERATIONS: ["chatToolIterations"],
|
|
20360
|
+
OR3_CHAT_GENERATED_IMAGES: ["chatGeneratedImages"],
|
|
20357
20361
|
OR3_SITE_NAME: ["or3", "site", "name"],
|
|
20358
20362
|
OR3_SITE_DESCRIPTION: ["or3", "site", "description"],
|
|
20359
20363
|
OR3_LOGO_URL: ["or3", "site", "logoUrl"],
|
|
@@ -20851,6 +20855,8 @@ var ALLOWED_ENV_KEYS = new Set([
|
|
|
20851
20855
|
"OR3_CLOUD_STORAGE_ENABLED",
|
|
20852
20856
|
"NUXT_PUBLIC_STORAGE_PROVIDER",
|
|
20853
20857
|
"OR3_STORAGE_FS_ROOT",
|
|
20858
|
+
"OR3_STORAGE_FS_CLEANUP_NAMESPACE_ID",
|
|
20859
|
+
"OR3_STORAGE_RETENTION_WORKSPACES",
|
|
20854
20860
|
"OR3_STORAGE_FS_TOKEN_SECRET",
|
|
20855
20861
|
"OR3_STORAGE_FS_URL_TTL_SECONDS",
|
|
20856
20862
|
"OR3_ADMIN_USERNAME",
|
|
@@ -317,6 +317,22 @@ async function readLastReceipt() {
|
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
/** Read-only allowlist: a recorded snapshot is not current checksum evidence. */
|
|
321
|
+
export function projectRecoveryState(state) {
|
|
322
|
+
const safeId = value => typeof value === 'string' && /^[A-Za-z0-9_-]{1,100}$/.test(value) ? value : null;
|
|
323
|
+
const safeTime = value => typeof value === 'string' && value.length <= 64 && Number.isFinite(Date.parse(value)) ? value : null;
|
|
324
|
+
const snapshotId = safeId(state?.rollback?.backupId);
|
|
325
|
+
const pending = state?.incompleteOperation;
|
|
326
|
+
return {
|
|
327
|
+
observedAt: new Date().toISOString(), source: 'managed_operator',
|
|
328
|
+
snapshot: snapshotId ? { id: snapshotId, capturedAt: safeTime(state.rollback.createdAt), verification: 'operator_reported' } : null,
|
|
329
|
+
coverage: ['canonical', 'auth', 'files', 'configuration', 'billing_trials_support', 'credential_escrow', 'independent_copy'].map(component => ({ component, status: 'unknown', verifiedAt: null })),
|
|
330
|
+
lastRecoveryTestAt: null,
|
|
331
|
+
incompleteOperation: pending && safeId(pending.id) && ['init', 'update', 'restore', 'adopt', 'credentials-reset', 'backup', 'rollback'].includes(pending.operation)
|
|
332
|
+
? { id: pending.id, operation: pending.operation, startedAt: safeTime(pending.startedAt) } : null,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
320
336
|
async function status(includeReceipt = false) {
|
|
321
337
|
const version = await currentVersion();
|
|
322
338
|
const [job, checked] = await Promise.all([readJob(), readReleaseCheck()]);
|
|
@@ -992,6 +1008,7 @@ async function main() {
|
|
|
992
1008
|
if (await service.handle(service.controller, request, response)) return;
|
|
993
1009
|
} catch { return send(response, 503, { code: 'controller_unavailable' }); }
|
|
994
1010
|
}
|
|
1011
|
+
if (request.method === 'GET' && request.url === '/v2/recovery') return send(response, 200, { protocolVersion: 2, recovery: projectRecoveryState(await managedState()) });
|
|
995
1012
|
if (request.method === 'GET' && route === '/status') return reply(200, await status(isV2));
|
|
996
1013
|
if (request.method === 'POST' && route === '/check') {
|
|
997
1014
|
if (!consumeRateLimit(checkAttempts, Date.now(), 30, 60_000)) {
|
package/dist/cli.mjs
CHANGED
|
@@ -179,7 +179,7 @@ import { readFileSync } from "node:fs";
|
|
|
179
179
|
import { join, resolve } from "node:path";
|
|
180
180
|
import { fileURLToPath } from "node:url";
|
|
181
181
|
var PACKAGE_ROOT = resolve(fileURLToPath(new URL("../", import.meta.url)));
|
|
182
|
-
var PACKAGE_VERSION = "0.1.
|
|
182
|
+
var PACKAGE_VERSION = "0.1.107";
|
|
183
183
|
var IMAGE_REPOSITORY = "ghcr.io/saluana/or3-chat";
|
|
184
184
|
var ASSET_ROOT = resolve(fileURLToPath(new URL("../assets/", import.meta.url)));
|
|
185
185
|
var SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
@@ -402,6 +402,8 @@ var ALLOWED_ENV_KEYS = new Set([
|
|
|
402
402
|
"OR3_CLOUD_STORAGE_ENABLED",
|
|
403
403
|
"NUXT_PUBLIC_STORAGE_PROVIDER",
|
|
404
404
|
"OR3_STORAGE_FS_ROOT",
|
|
405
|
+
"OR3_STORAGE_FS_CLEANUP_NAMESPACE_ID",
|
|
406
|
+
"OR3_STORAGE_RETENTION_WORKSPACES",
|
|
405
407
|
"OR3_STORAGE_FS_TOKEN_SECRET",
|
|
406
408
|
"OR3_STORAGE_FS_URL_TTL_SECONDS",
|
|
407
409
|
"OR3_ADMIN_USERNAME",
|
|
@@ -4518,6 +4520,201 @@ import { join as join24 } from "node:path";
|
|
|
4518
4520
|
|
|
4519
4521
|
// src/backup/restore.ts
|
|
4520
4522
|
import { join as join21 } from "node:path";
|
|
4523
|
+
|
|
4524
|
+
// src/backup/generation-restore.ts
|
|
4525
|
+
function rebindRestoredGenerations(fs, path, crypto, db, root, options = {}) {
|
|
4526
|
+
const preserveMissing = options.previousSafetySnapshot === true;
|
|
4527
|
+
const fail = () => {
|
|
4528
|
+
throw new Error("Restored generation identity or payload is invalid");
|
|
4529
|
+
};
|
|
4530
|
+
const info = (name) => {
|
|
4531
|
+
try {
|
|
4532
|
+
return fs.lstatSync(name, { bigint: true });
|
|
4533
|
+
} catch (error) {
|
|
4534
|
+
if (error.code === "ENOENT")
|
|
4535
|
+
return null;
|
|
4536
|
+
throw error;
|
|
4537
|
+
}
|
|
4538
|
+
};
|
|
4539
|
+
const dir = (name) => {
|
|
4540
|
+
const s = info(name);
|
|
4541
|
+
if (!s?.isDirectory())
|
|
4542
|
+
fail();
|
|
4543
|
+
return s;
|
|
4544
|
+
};
|
|
4545
|
+
const json = (name) => {
|
|
4546
|
+
const s = info(name);
|
|
4547
|
+
if (!s?.isFile() || s.nlink !== 1n || s.size > 4096n)
|
|
4548
|
+
fail();
|
|
4549
|
+
return JSON.parse(fs.readFileSync(name, "utf8"));
|
|
4550
|
+
};
|
|
4551
|
+
const base = path.join(root, "generations-v1");
|
|
4552
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((row) => row.name));
|
|
4553
|
+
const required = new Set;
|
|
4554
|
+
if (tables.has("storage_object_generations")) {
|
|
4555
|
+
for (const row of db.prepare("SELECT generation_id FROM storage_object_generations WHERE storage_provider_id = 'fs'").all())
|
|
4556
|
+
required.add(String(row.generation_id));
|
|
4557
|
+
}
|
|
4558
|
+
if (tables.has("storage_generation_uploads")) {
|
|
4559
|
+
for (const row of db.prepare("SELECT generation_id FROM storage_generation_uploads WHERE storage_provider_id = 'fs' AND ready_receipt_id IS NOT NULL").all())
|
|
4560
|
+
required.add(String(row.generation_id));
|
|
4561
|
+
}
|
|
4562
|
+
if (!info(base)) {
|
|
4563
|
+
if (required.size && !preserveMissing)
|
|
4564
|
+
fail();
|
|
4565
|
+
return;
|
|
4566
|
+
}
|
|
4567
|
+
dir(root);
|
|
4568
|
+
dir(base);
|
|
4569
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
4570
|
+
const namespaceFile = path.join(base, "namespace.json");
|
|
4571
|
+
if (!info(namespaceFile) && preserveMissing)
|
|
4572
|
+
return;
|
|
4573
|
+
const ns = json(namespaceFile);
|
|
4574
|
+
if (ns.version !== 1 || !uuid.test(ns.namespaceId))
|
|
4575
|
+
fail();
|
|
4576
|
+
if (!tables.has("storage_object_generations"))
|
|
4577
|
+
fail();
|
|
4578
|
+
const workspaceRoot = path.join(base, "workspaces");
|
|
4579
|
+
if (!info(workspaceRoot) && preserveMissing)
|
|
4580
|
+
return;
|
|
4581
|
+
dir(workspaceRoot);
|
|
4582
|
+
const changes = [];
|
|
4583
|
+
for (const workspace of fs.readdirSync(workspaceRoot)) {
|
|
4584
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(workspace))
|
|
4585
|
+
fail();
|
|
4586
|
+
const work = path.join(workspaceRoot, workspace);
|
|
4587
|
+
dir(work);
|
|
4588
|
+
for (const id2 of fs.readdirSync(work)) {
|
|
4589
|
+
if (!uuid.test(id2))
|
|
4590
|
+
fail();
|
|
4591
|
+
const slot = path.join(work, id2);
|
|
4592
|
+
dir(slot);
|
|
4593
|
+
const file = path.join(slot, "ready.json");
|
|
4594
|
+
if (!info(file)) {
|
|
4595
|
+
if (required.has(id2) && !preserveMissing)
|
|
4596
|
+
fail();
|
|
4597
|
+
continue;
|
|
4598
|
+
}
|
|
4599
|
+
const ready = json(file);
|
|
4600
|
+
if (ready.version !== 1 || ready.namespaceId !== ns.namespaceId || ready.workspaceId !== workspace || ready.generationId !== id2 || !/^sha256:[a-f0-9]{64}$/.test(ready.hash) || !Number.isSafeInteger(ready.sizeBytes) || ready.sizeBytes < 0 || !uuid.test(ready.readyReceiptId) || ready.storageId !== `fs-generation-v1:${ns.namespaceId}:${workspace}:${id2}:${ready.hash.slice(7)}`)
|
|
4601
|
+
fail();
|
|
4602
|
+
const generation = db.prepare("SELECT * FROM storage_object_generations WHERE generation_id = ?").get(id2);
|
|
4603
|
+
const upload = tables.has("storage_generation_uploads") ? db.prepare("SELECT * FROM storage_generation_uploads WHERE generation_id = ?").get(id2) : null;
|
|
4604
|
+
const record = generation ?? upload;
|
|
4605
|
+
if (!record || record.workspace_id !== workspace || record.storage_provider_id !== "fs" || record.storage_id !== ready.storageId || record.hash.replace(/^sha256:/i, "").toLowerCase() !== ready.hash.slice(7) || record.size_bytes !== ready.sizeBytes)
|
|
4606
|
+
fail();
|
|
4607
|
+
if (upload && (upload.namespace_id !== ns.namespaceId || upload.ready_receipt_id && upload.ready_receipt_id !== ready.readyReceiptId))
|
|
4608
|
+
fail();
|
|
4609
|
+
required.delete(id2);
|
|
4610
|
+
const payload = path.join(slot, "payload");
|
|
4611
|
+
const payloadInfo = info(payload);
|
|
4612
|
+
const needsBlob = generation?.state === "verified";
|
|
4613
|
+
if (!payloadInfo) {
|
|
4614
|
+
if (needsBlob && !preserveMissing)
|
|
4615
|
+
fail();
|
|
4616
|
+
continue;
|
|
4617
|
+
}
|
|
4618
|
+
dir(payload);
|
|
4619
|
+
if (generation?.state === "deleted" || !generation && upload?.state === "abandoned")
|
|
4620
|
+
fail();
|
|
4621
|
+
const entries = fs.readdirSync(payload);
|
|
4622
|
+
const links = new Map;
|
|
4623
|
+
for (const entry of entries) {
|
|
4624
|
+
if (entry !== "blob" && !/^\.upload-[0-9a-f-]{36}$/.test(entry))
|
|
4625
|
+
fail();
|
|
4626
|
+
const s = info(path.join(payload, entry));
|
|
4627
|
+
if (!s?.isFile())
|
|
4628
|
+
fail();
|
|
4629
|
+
const key = `${s.dev}:${s.ino}`;
|
|
4630
|
+
const previous = links.get(key);
|
|
4631
|
+
links.set(key, { count: (previous?.count ?? 0n) + 1n, expected: s.nlink });
|
|
4632
|
+
}
|
|
4633
|
+
if ([...links.values()].some((value) => value.count !== value.expected))
|
|
4634
|
+
fail();
|
|
4635
|
+
const blob = path.join(payload, "blob");
|
|
4636
|
+
if (info(blob)) {
|
|
4637
|
+
const fd = fs.openSync(blob, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
4638
|
+
try {
|
|
4639
|
+
const hash = crypto.createHash("sha256"), buffer = Buffer.alloc(65536);
|
|
4640
|
+
let size = 0, count = 0;
|
|
4641
|
+
while ((count = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) {
|
|
4642
|
+
hash.update(buffer.subarray(0, count));
|
|
4643
|
+
size += count;
|
|
4644
|
+
}
|
|
4645
|
+
if (size !== ready.sizeBytes || hash.digest("hex") !== ready.hash.slice(7))
|
|
4646
|
+
fail();
|
|
4647
|
+
} finally {
|
|
4648
|
+
fs.closeSync(fd);
|
|
4649
|
+
}
|
|
4650
|
+
} else if (needsBlob) {
|
|
4651
|
+
if (!preserveMissing)
|
|
4652
|
+
fail();
|
|
4653
|
+
continue;
|
|
4654
|
+
}
|
|
4655
|
+
changes.push({ file, ready: { ...ready, payloadDevice: String(payloadInfo.dev), payloadInode: String(payloadInfo.ino) } });
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
if (required.size && !preserveMissing)
|
|
4659
|
+
fail();
|
|
4660
|
+
for (const { file, ready } of changes) {
|
|
4661
|
+
const temp = file + ".restore-" + crypto.randomUUID();
|
|
4662
|
+
const fd = fs.openSync(temp, "wx", 384);
|
|
4663
|
+
try {
|
|
4664
|
+
fs.writeFileSync(fd, JSON.stringify(ready));
|
|
4665
|
+
fs.fsyncSync(fd);
|
|
4666
|
+
} finally {
|
|
4667
|
+
fs.closeSync(fd);
|
|
4668
|
+
}
|
|
4669
|
+
fs.renameSync(temp, file);
|
|
4670
|
+
const directory = fs.openSync(path.dirname(file), "r");
|
|
4671
|
+
try {
|
|
4672
|
+
fs.fsyncSync(directory);
|
|
4673
|
+
} finally {
|
|
4674
|
+
fs.closeSync(directory);
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
|
|
4679
|
+
// src/backup/restore.ts
|
|
4680
|
+
async function restoreGenerationIdentities(directory, state, env, backupPath, options = {}) {
|
|
4681
|
+
await readManifest(backupPath, directory);
|
|
4682
|
+
const script = `
|
|
4683
|
+
const fs = require('node:fs'), path = require('node:path'), crypto = require('node:crypto');
|
|
4684
|
+
const root = path.resolve(process.env.OR3_STORAGE_FS_ROOT || '/data/storage');
|
|
4685
|
+
if (root !== '/data' && !root.startsWith('/data/')) throw new Error('Generation restore requires managed storage');
|
|
4686
|
+
{
|
|
4687
|
+
const Database = require('/app/.output/server/node_modules/better-sqlite3');
|
|
4688
|
+
const db = new Database(process.env.OR3_SQLITE_DB_PATH, { readonly: true, fileMustExist: true });
|
|
4689
|
+
try {
|
|
4690
|
+
if (db.pragma('quick_check', { simple: true }) !== 'ok') throw new Error('Restored SQLite integrity check failed');
|
|
4691
|
+
(${rebindRestoredGenerations.toString()})(fs, path, crypto, db, root, ${JSON.stringify({ previousSafetySnapshot: options.previousSafetySnapshot === true })});
|
|
4692
|
+
} finally { db.close(); }
|
|
4693
|
+
}`;
|
|
4694
|
+
const result = await run("docker", [
|
|
4695
|
+
"run",
|
|
4696
|
+
"--rm",
|
|
4697
|
+
"--network",
|
|
4698
|
+
"none",
|
|
4699
|
+
"--read-only",
|
|
4700
|
+
"--security-opt",
|
|
4701
|
+
"no-new-privileges:true",
|
|
4702
|
+
"--cap-drop",
|
|
4703
|
+
"ALL",
|
|
4704
|
+
"-v",
|
|
4705
|
+
`${state.volumeName}:/data`,
|
|
4706
|
+
"--env",
|
|
4707
|
+
`OR3_SQLITE_DB_PATH=${env.OR3_SQLITE_DB_PATH ?? ""}`,
|
|
4708
|
+
"--env",
|
|
4709
|
+
`OR3_STORAGE_FS_ROOT=${env.OR3_STORAGE_FS_ROOT ?? "/data/storage"}`,
|
|
4710
|
+
"--entrypoint",
|
|
4711
|
+
"sh",
|
|
4712
|
+
env.OR3_IMAGE,
|
|
4713
|
+
...containerNodeCommand(script).slice(1)
|
|
4714
|
+
], directory);
|
|
4715
|
+
if (!result.ok)
|
|
4716
|
+
throw new Error(`Restored generation verification failed. ${redact(result.stderr, secretValues(env))}`);
|
|
4717
|
+
}
|
|
4521
4718
|
function assertFilesystemPayloadHash(key, readChunk, createHash3) {
|
|
4522
4719
|
const match = /^(sha256|md5)_([a-fA-F0-9]+)$/.exec(key);
|
|
4523
4720
|
const algorithm = match?.[1];
|
|
@@ -4647,6 +4844,7 @@ async function restoreBackupData(directory, state, env, backupPath, options = {}
|
|
|
4647
4844
|
if (!snapshotCheck.ok) {
|
|
4648
4845
|
throw new Error(`Restored snapshot integrity verification failed. ${redact(snapshotCheck.stderr, secretValues(restoredEnv))}`);
|
|
4649
4846
|
}
|
|
4847
|
+
await restoreGenerationIdentities(directory, state, restoredEnv, backupPath, { previousSafetySnapshot: options.previousSafetySnapshot });
|
|
4650
4848
|
await options.beforeStart?.();
|
|
4651
4849
|
await startProject(directory, state.mode, restoredEnv);
|
|
4652
4850
|
return manifest2;
|
|
@@ -25902,6 +26100,7 @@ async function runUpdateCommand(directory, flags, asJson) {
|
|
|
25902
26100
|
await copyAssets(loaded.directory, state.mode);
|
|
25903
26101
|
if (migrateLegacyVolume || recreateDataVolume) {
|
|
25904
26102
|
await restoreVolumeArchive(loaded.directory, state.mode, nextEnv, backup.backupDir);
|
|
26103
|
+
await restoreGenerationIdentities(loaded.directory, state, nextEnv, backup.backupDir);
|
|
25905
26104
|
}
|
|
25906
26105
|
} catch (error62) {
|
|
25907
26106
|
await updatePending(loaded.directory, state, { phase: "restoring-previous" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@or3/cloud",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.107",
|
|
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:345b221ce8716552bccbc0bebfbc9fe5d698cd4b486ab18b3a398c924eb48b7d",
|
|
23
|
+
"operatorImageDigest": "sha256:82df52a0b952907d7a1dbcdb2eebe411e654b3f6ad47439d1a89901399f18061",
|
|
24
|
+
"sourceRevision": "68b263584dc64d0e3a7b0035a90845d438678cd4"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|