hazo_files 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20847,14 +20847,17 @@ var HAZO_FILE_QUOTAS_TABLE_SCHEMA = {
20847
20847
  // src/providers/app-file-server.ts
20848
20848
  import { createHmac } from "crypto";
20849
20849
  import {
20850
+ copyFileSync,
20850
20851
  existsSync as existsSync2,
20851
20852
  mkdirSync,
20853
+ readdirSync,
20852
20854
  readFileSync as readFileSync2,
20855
+ renameSync,
20853
20856
  rmSync,
20854
20857
  statSync,
20855
20858
  writeFileSync
20856
20859
  } from "fs";
20857
- import { dirname as dirname2, join as join4, normalize, sep } from "path";
20860
+ import { basename as basename2, dirname as dirname2, join as join4, normalize, sep } from "path";
20858
20861
  var AppFileServerProvider = class {
20859
20862
  constructor(opts) {
20860
20863
  this.provider_tag = "app_file_server";
@@ -20917,6 +20920,51 @@ var AppFileServerProvider = class {
20917
20920
  return { ok: false, error: "write_denied", message: String(err) };
20918
20921
  }
20919
20922
  }
20923
+ async list(prefix) {
20924
+ const abs = this.resolve(prefix);
20925
+ if (!existsSync2(abs)) return [];
20926
+ const stat = statSync(abs);
20927
+ if (!stat.isDirectory()) {
20928
+ return [{ path: prefix, name: basename2(prefix), size: stat.size, isDirectory: false }];
20929
+ }
20930
+ const entries = [];
20931
+ this.walkDir(abs, prefix, entries);
20932
+ return entries;
20933
+ }
20934
+ walkDir(absDir, logicalDir, out) {
20935
+ const base = logicalDir.endsWith("/") ? logicalDir : `${logicalDir}/`;
20936
+ for (const item of readdirSync(absDir, { withFileTypes: true })) {
20937
+ const logicalPath = `${base}${item.name}`;
20938
+ const absPath = join4(absDir, item.name);
20939
+ if (item.isDirectory()) {
20940
+ out.push({ path: logicalPath, name: item.name, size: 0, isDirectory: true });
20941
+ this.walkDir(absPath, logicalPath, out);
20942
+ } else {
20943
+ const st = statSync(absPath);
20944
+ out.push({ path: logicalPath, name: item.name, size: st.size, isDirectory: false });
20945
+ }
20946
+ }
20947
+ }
20948
+ async move(from, to) {
20949
+ const absFrom = this.resolve(from);
20950
+ const absTo = this.resolve(to);
20951
+ if (!existsSync2(absFrom)) throw new Error(`Not found: ${from}`);
20952
+ mkdirSync(dirname2(absTo), { recursive: true });
20953
+ try {
20954
+ renameSync(absFrom, absTo);
20955
+ } catch (err) {
20956
+ const e = err;
20957
+ if (e.code === "EXDEV") {
20958
+ copyFileSync(absFrom, absTo);
20959
+ rmSync(absFrom, { force: true });
20960
+ } else {
20961
+ throw err;
20962
+ }
20963
+ }
20964
+ }
20965
+ async rename(from, to) {
20966
+ return this.move(from, to);
20967
+ }
20920
20968
  };
20921
20969
  async function streamToBuffer(s) {
20922
20970
  const chunks = [];
@@ -20955,6 +21003,39 @@ var InMemoryProvider = class {
20955
21003
  async probe() {
20956
21004
  return { ok: true };
20957
21005
  }
21006
+ async list(prefix) {
21007
+ const base = prefix.replace(/\/$/, "");
21008
+ const matchPrefix = `${base}/`;
21009
+ const entries = [];
21010
+ const seenDirs = /* @__PURE__ */ new Set();
21011
+ for (const [key, buf] of this.store.entries()) {
21012
+ if (key !== base && !key.startsWith(matchPrefix)) continue;
21013
+ const rel = key.startsWith(matchPrefix) ? key.slice(matchPrefix.length) : "";
21014
+ if (!rel) {
21015
+ entries.push({ path: key, name: key.split("/").pop(), size: buf.length, isDirectory: false });
21016
+ continue;
21017
+ }
21018
+ const parts = rel.split("/");
21019
+ for (let i = 1; i < parts.length; i++) {
21020
+ const dirPath = matchPrefix + parts.slice(0, i).join("/");
21021
+ if (!seenDirs.has(dirPath)) {
21022
+ seenDirs.add(dirPath);
21023
+ entries.push({ path: dirPath, name: parts[i - 1], size: 0, isDirectory: true });
21024
+ }
21025
+ }
21026
+ entries.push({ path: key, name: parts[parts.length - 1], size: buf.length, isDirectory: false });
21027
+ }
21028
+ return entries;
21029
+ }
21030
+ async move(from, to) {
21031
+ const buf = this.store.get(from);
21032
+ if (!buf) throw new Error(`Not found: ${from}`);
21033
+ this.store.set(to, buf);
21034
+ this.store.delete(from);
21035
+ }
21036
+ async rename(from, to) {
21037
+ return this.move(from, to);
21038
+ }
20958
21039
  /** Test-only escape hatch — exposes the internal store for assertions. */
20959
21040
  snapshot() {
20960
21041
  return new Map(this.store);
@@ -21050,6 +21131,60 @@ var GoogleDriveProvider = class {
21050
21131
  const id = await this.lookupFileId(path4);
21051
21132
  return `https://drive.google.com/uc?id=${id}&export=download`;
21052
21133
  }
21134
+ async list(prefix) {
21135
+ const segments = prefix.split("/").filter(Boolean);
21136
+ let parentId;
21137
+ try {
21138
+ parentId = await this.resolvePath(segments, { create: false });
21139
+ } catch {
21140
+ return [];
21141
+ }
21142
+ const res = await this.drive.files.list({
21143
+ q: `'${parentId}' in parents and trashed=false`,
21144
+ driveId: this.driveId,
21145
+ corpora: "drive",
21146
+ includeItemsFromAllDrives: true,
21147
+ fields: "files(id, name, mimeType, size)"
21148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21149
+ });
21150
+ const files = res.data.files ?? [];
21151
+ const base = prefix.endsWith("/") ? prefix : `${prefix}/`;
21152
+ return files.map((f) => ({
21153
+ path: `${base}${f.name}`,
21154
+ name: f.name,
21155
+ size: Number(f.size ?? 0),
21156
+ isDirectory: f.mimeType === "application/vnd.google-apps.folder"
21157
+ }));
21158
+ }
21159
+ async move(from, to) {
21160
+ const id = await this.lookupFileId(from);
21161
+ const fromSegs = from.split("/").filter(Boolean);
21162
+ fromSegs.pop();
21163
+ const toSegs = to.split("/").filter(Boolean);
21164
+ const newName = toSegs.pop();
21165
+ const oldParentId = await this.resolvePath(fromSegs, { create: false });
21166
+ const newParentId = await this.resolvePath(toSegs, { create: true });
21167
+ await this.drive.files.update({
21168
+ fileId: id,
21169
+ addParents: newParentId,
21170
+ removeParents: oldParentId,
21171
+ requestBody: { name: newName },
21172
+ supportsAllDrives: true,
21173
+ fields: "id, parents"
21174
+ });
21175
+ await this.cache.invalidate(from);
21176
+ }
21177
+ async rename(from, to) {
21178
+ const id = await this.lookupFileId(from);
21179
+ const segs = to.split("/").filter(Boolean);
21180
+ const newName = segs[segs.length - 1];
21181
+ await this.drive.files.update({
21182
+ fileId: id,
21183
+ requestBody: { name: newName },
21184
+ supportsAllDrives: true,
21185
+ fields: "id, name"
21186
+ });
21187
+ }
21053
21188
  async resolvePath(segments, opts) {
21054
21189
  let parentId = this.driveId;
21055
21190
  const accumulated = [];
@@ -21167,6 +21302,132 @@ async function migrateToV3(executor, dbType, tableName) {
21167
21302
  }
21168
21303
  }
21169
21304
 
21305
+ // src/server/external-file-link.ts
21306
+ function assert_absolute_http(base) {
21307
+ if (!/^https?:\/\//i.test(base)) {
21308
+ throw new Error(`external_file_link: serve_base must be an absolute http(s) URL, got "${base}"`);
21309
+ }
21310
+ }
21311
+ async function external_file_link(file_id, deps, opts = {}) {
21312
+ const record2 = opts.metadata ?? await deps.metadataService.findById(file_id);
21313
+ if (!record2) throw new Error(`external_file_link: file not found (${file_id})`);
21314
+ const storage_type = record2.storage_type;
21315
+ if (storage_type === "local" || storage_type === "app_file_server") {
21316
+ const base = opts.serve_base;
21317
+ if (!base) {
21318
+ throw new Error(`external_file_link: serve_base is required for ${storage_type} storage (file ${file_id})`);
21319
+ }
21320
+ assert_absolute_http(base);
21321
+ const path4 = opts.serve_path ?? "/api/files/serve";
21322
+ const url2 = `${base.replace(/\/$/, "")}${path4}?file_id=${encodeURIComponent(file_id)}`;
21323
+ return { url: url2, link_type: "app_gated", storage_type };
21324
+ }
21325
+ if (storage_type === "google_drive") {
21326
+ const url2 = read_drive_link(record2);
21327
+ if (!url2) throw new Error(`external_file_link: no Google Drive link for file ${file_id}`);
21328
+ return { url: url2, link_type: "provider_managed", storage_type };
21329
+ }
21330
+ if (storage_type === "dropbox") {
21331
+ throw new Error("external_file_link: Dropbox storage is not yet supported");
21332
+ }
21333
+ throw new Error(`external_file_link: Unsupported storage_type "${storage_type}" (file ${file_id})`);
21334
+ }
21335
+ function read_drive_link(record2) {
21336
+ let data = {};
21337
+ try {
21338
+ data = JSON.parse(record2.file_data || "{}");
21339
+ } catch {
21340
+ data = {};
21341
+ }
21342
+ const nested = data.metadata ?? {};
21343
+ const webViewLink = data.webViewLink ?? nested.webViewLink;
21344
+ if (typeof webViewLink === "string" && webViewLink) return webViewLink;
21345
+ const driveId = data.driveId ?? nested.driveId;
21346
+ if (typeof driveId === "string" && driveId) return `https://drive.google.com/file/d/${driveId}/view`;
21347
+ return null;
21348
+ }
21349
+ async function external_file_links(file_ids, deps, opts = {}) {
21350
+ const out = {};
21351
+ await Promise.all(
21352
+ file_ids.map(async (id) => {
21353
+ try {
21354
+ out[id] = await external_file_link(id, deps, opts);
21355
+ } catch (err) {
21356
+ out[id] = { error: err instanceof Error ? err.message : String(err) };
21357
+ }
21358
+ })
21359
+ );
21360
+ return out;
21361
+ }
21362
+
21363
+ // src/server/file-serve-handler.ts
21364
+ import { Readable as Readable3 } from "stream";
21365
+ function default_get_file_id(request) {
21366
+ return new URL(request.url).searchParams.get("file_id");
21367
+ }
21368
+ function to_body(data) {
21369
+ if (Buffer.isBuffer(data)) return data;
21370
+ return Readable3.toWeb(data);
21371
+ }
21372
+ var INLINE_SAFE_TYPES = /* @__PURE__ */ new Set([
21373
+ "image/png",
21374
+ "image/jpeg",
21375
+ "image/jpg",
21376
+ "image/gif",
21377
+ "image/webp",
21378
+ "image/avif",
21379
+ "application/pdf",
21380
+ "text/plain"
21381
+ ]);
21382
+ function safe_content_headers(mime, filename) {
21383
+ const safe_name = filename.replace(/"/g, "");
21384
+ const inline_safe = INLINE_SAFE_TYPES.has(mime) || mime.startsWith("video/") || mime.startsWith("audio/");
21385
+ return {
21386
+ "Content-Type": inline_safe ? mime : "application/octet-stream",
21387
+ "Content-Disposition": `${inline_safe ? "inline" : "attachment"}; filename="${safe_name}"`,
21388
+ "X-Content-Type-Options": "nosniff",
21389
+ "Content-Security-Policy": "sandbox"
21390
+ };
21391
+ }
21392
+ function createFileServeHandler(opts) {
21393
+ const get_file_id = opts.get_file_id ?? default_get_file_id;
21394
+ return async function handle(request) {
21395
+ const file_id = get_file_id(request);
21396
+ if (!file_id) return new Response("Missing file_id", { status: 400 });
21397
+ if (opts.authenticate) {
21398
+ let user = null;
21399
+ try {
21400
+ user = await opts.authenticate({ request, file_id });
21401
+ } catch {
21402
+ user = null;
21403
+ }
21404
+ if (!user) return new Response("Unauthorized", { status: 401 });
21405
+ const metadata2 = await opts.metadataService.findById(file_id);
21406
+ if (!metadata2) return new Response("Not found", { status: 404 });
21407
+ if (opts.authorize) {
21408
+ const allowed = await opts.authorize({ request, file_id, user, metadata: metadata2 });
21409
+ if (!allowed) return new Response("Forbidden", { status: 403 });
21410
+ }
21411
+ return serve(opts.provider, metadata2);
21412
+ }
21413
+ const metadata = await opts.metadataService.findById(file_id);
21414
+ if (!metadata) return new Response("Not found", { status: 404 });
21415
+ return serve(opts.provider, metadata);
21416
+ };
21417
+ }
21418
+ async function serve(provider, metadata) {
21419
+ try {
21420
+ const data = await provider.get(metadata.file_path);
21421
+ const name = metadata.original_filename || metadata.filename || getBaseName(metadata.file_path);
21422
+ return new Response(to_body(data), {
21423
+ status: 200,
21424
+ headers: safe_content_headers(getMimeType(name), name)
21425
+ });
21426
+ } catch {
21427
+ return new Response("Internal error", { status: 500 });
21428
+ }
21429
+ }
21430
+
21170
21431
  // src/server/index.ts
21171
21432
  var log6 = createLogger6("hazo_files");
21172
21433
  try {
@@ -21243,6 +21504,7 @@ export {
21243
21504
  createFileManager,
21244
21505
  createFileMetadataService,
21245
21506
  createFileRef,
21507
+ createFileServeHandler,
21246
21508
  createFolderItem,
21247
21509
  createGoogleDriveAuth,
21248
21510
  createGoogleDriveModule,
@@ -21261,6 +21523,8 @@ export {
21261
21523
  createVariableSegment,
21262
21524
  deepMerge,
21263
21525
  errorResult,
21526
+ external_file_link,
21527
+ external_file_links,
21264
21528
  filterItems,
21265
21529
  formatBytes,
21266
21530
  formatCounter,
@@ -30,6 +30,22 @@ interface ProbeResult {
30
30
  /** Free-form detail for logging. */
31
31
  message?: string;
32
32
  }
33
+ /**
34
+ * A single entry returned by `FileStorageProvider.list()`.
35
+ *
36
+ * `path` is the same logical namespace used by `put`/`get` — callers can pass
37
+ * it directly back to those methods without any transformation.
38
+ */
39
+ interface FileEntry {
40
+ /** Logical path within the provider (same namespace as put/get). */
41
+ path: StoragePath;
42
+ /** Bare filename or directory name (the last segment of `path`). */
43
+ name: string;
44
+ /** Size in bytes. 0 for directories. */
45
+ size: number;
46
+ /** True when this entry represents a directory. */
47
+ isDirectory: boolean;
48
+ }
33
49
  /**
34
50
  * Storage provider abstraction. Every method MUST be idempotent at the
35
51
  * data-content level — re-invoking put with identical body is allowed.
@@ -44,6 +60,24 @@ interface FileStorageProvider {
44
60
  getSignedUrl(path: StoragePath, opts?: SignedUrlOpts): Promise<string>;
45
61
  /** Used by validation cron + onboarding step 2. */
46
62
  probe(): Promise<ProbeResult>;
63
+ /**
64
+ * List all entries (files and sub-directories) under a logical path prefix.
65
+ * Results are recursive — all descendants are included.
66
+ * Returns an empty array when the prefix does not exist.
67
+ */
68
+ list(prefix: StoragePath): Promise<FileEntry[]>;
69
+ /**
70
+ * Move bytes from `from` to `to`, creating any intermediate directories.
71
+ * The source is removed on success.
72
+ * Throws with a message containing "Not found" when `from` does not exist.
73
+ */
74
+ move(from: StoragePath, to: StoragePath): Promise<void>;
75
+ /**
76
+ * Rename a file or directory. `to` is the full new logical path.
77
+ * Implementations may delegate to `move`.
78
+ * Throws with a message containing "Not found" when `from` does not exist.
79
+ */
80
+ rename(from: StoragePath, to: StoragePath): Promise<void>;
47
81
  }
48
82
 
49
83
  declare class InMemoryProvider implements FileStorageProvider {
@@ -55,6 +89,9 @@ declare class InMemoryProvider implements FileStorageProvider {
55
89
  exists(path: string): Promise<boolean>;
56
90
  getSignedUrl(path: string, _opts?: SignedUrlOpts): Promise<string>;
57
91
  probe(): Promise<ProbeResult>;
92
+ list(prefix: string): Promise<FileEntry[]>;
93
+ move(from: string, to: string): Promise<void>;
94
+ rename(from: string, to: string): Promise<void>;
58
95
  /** Test-only escape hatch — exposes the internal store for assertions. */
59
96
  snapshot(): Map<string, Buffer>;
60
97
  }
@@ -30,6 +30,22 @@ interface ProbeResult {
30
30
  /** Free-form detail for logging. */
31
31
  message?: string;
32
32
  }
33
+ /**
34
+ * A single entry returned by `FileStorageProvider.list()`.
35
+ *
36
+ * `path` is the same logical namespace used by `put`/`get` — callers can pass
37
+ * it directly back to those methods without any transformation.
38
+ */
39
+ interface FileEntry {
40
+ /** Logical path within the provider (same namespace as put/get). */
41
+ path: StoragePath;
42
+ /** Bare filename or directory name (the last segment of `path`). */
43
+ name: string;
44
+ /** Size in bytes. 0 for directories. */
45
+ size: number;
46
+ /** True when this entry represents a directory. */
47
+ isDirectory: boolean;
48
+ }
33
49
  /**
34
50
  * Storage provider abstraction. Every method MUST be idempotent at the
35
51
  * data-content level — re-invoking put with identical body is allowed.
@@ -44,6 +60,24 @@ interface FileStorageProvider {
44
60
  getSignedUrl(path: StoragePath, opts?: SignedUrlOpts): Promise<string>;
45
61
  /** Used by validation cron + onboarding step 2. */
46
62
  probe(): Promise<ProbeResult>;
63
+ /**
64
+ * List all entries (files and sub-directories) under a logical path prefix.
65
+ * Results are recursive — all descendants are included.
66
+ * Returns an empty array when the prefix does not exist.
67
+ */
68
+ list(prefix: StoragePath): Promise<FileEntry[]>;
69
+ /**
70
+ * Move bytes from `from` to `to`, creating any intermediate directories.
71
+ * The source is removed on success.
72
+ * Throws with a message containing "Not found" when `from` does not exist.
73
+ */
74
+ move(from: StoragePath, to: StoragePath): Promise<void>;
75
+ /**
76
+ * Rename a file or directory. `to` is the full new logical path.
77
+ * Implementations may delegate to `move`.
78
+ * Throws with a message containing "Not found" when `from` does not exist.
79
+ */
80
+ rename(from: StoragePath, to: StoragePath): Promise<void>;
47
81
  }
48
82
 
49
83
  declare class InMemoryProvider implements FileStorageProvider {
@@ -55,6 +89,9 @@ declare class InMemoryProvider implements FileStorageProvider {
55
89
  exists(path: string): Promise<boolean>;
56
90
  getSignedUrl(path: string, _opts?: SignedUrlOpts): Promise<string>;
57
91
  probe(): Promise<ProbeResult>;
92
+ list(prefix: string): Promise<FileEntry[]>;
93
+ move(from: string, to: string): Promise<void>;
94
+ rename(from: string, to: string): Promise<void>;
58
95
  /** Test-only escape hatch — exposes the internal store for assertions. */
59
96
  snapshot(): Map<string, Buffer>;
60
97
  }
@@ -55,6 +55,39 @@ var InMemoryProvider = class {
55
55
  async probe() {
56
56
  return { ok: true };
57
57
  }
58
+ async list(prefix) {
59
+ const base = prefix.replace(/\/$/, "");
60
+ const matchPrefix = `${base}/`;
61
+ const entries = [];
62
+ const seenDirs = /* @__PURE__ */ new Set();
63
+ for (const [key, buf] of this.store.entries()) {
64
+ if (key !== base && !key.startsWith(matchPrefix)) continue;
65
+ const rel = key.startsWith(matchPrefix) ? key.slice(matchPrefix.length) : "";
66
+ if (!rel) {
67
+ entries.push({ path: key, name: key.split("/").pop(), size: buf.length, isDirectory: false });
68
+ continue;
69
+ }
70
+ const parts = rel.split("/");
71
+ for (let i = 1; i < parts.length; i++) {
72
+ const dirPath = matchPrefix + parts.slice(0, i).join("/");
73
+ if (!seenDirs.has(dirPath)) {
74
+ seenDirs.add(dirPath);
75
+ entries.push({ path: dirPath, name: parts[i - 1], size: 0, isDirectory: true });
76
+ }
77
+ }
78
+ entries.push({ path: key, name: parts[parts.length - 1], size: buf.length, isDirectory: false });
79
+ }
80
+ return entries;
81
+ }
82
+ async move(from, to) {
83
+ const buf = this.store.get(from);
84
+ if (!buf) throw new Error(`Not found: ${from}`);
85
+ this.store.set(to, buf);
86
+ this.store.delete(from);
87
+ }
88
+ async rename(from, to) {
89
+ return this.move(from, to);
90
+ }
58
91
  /** Test-only escape hatch — exposes the internal store for assertions. */
59
92
  snapshot() {
60
93
  return new Map(this.store);
@@ -29,6 +29,39 @@ var InMemoryProvider = class {
29
29
  async probe() {
30
30
  return { ok: true };
31
31
  }
32
+ async list(prefix) {
33
+ const base = prefix.replace(/\/$/, "");
34
+ const matchPrefix = `${base}/`;
35
+ const entries = [];
36
+ const seenDirs = /* @__PURE__ */ new Set();
37
+ for (const [key, buf] of this.store.entries()) {
38
+ if (key !== base && !key.startsWith(matchPrefix)) continue;
39
+ const rel = key.startsWith(matchPrefix) ? key.slice(matchPrefix.length) : "";
40
+ if (!rel) {
41
+ entries.push({ path: key, name: key.split("/").pop(), size: buf.length, isDirectory: false });
42
+ continue;
43
+ }
44
+ const parts = rel.split("/");
45
+ for (let i = 1; i < parts.length; i++) {
46
+ const dirPath = matchPrefix + parts.slice(0, i).join("/");
47
+ if (!seenDirs.has(dirPath)) {
48
+ seenDirs.add(dirPath);
49
+ entries.push({ path: dirPath, name: parts[i - 1], size: 0, isDirectory: true });
50
+ }
51
+ }
52
+ entries.push({ path: key, name: parts[parts.length - 1], size: buf.length, isDirectory: false });
53
+ }
54
+ return entries;
55
+ }
56
+ async move(from, to) {
57
+ const buf = this.store.get(from);
58
+ if (!buf) throw new Error(`Not found: ${from}`);
59
+ this.store.set(to, buf);
60
+ this.store.delete(from);
61
+ }
62
+ async rename(from, to) {
63
+ return this.move(from, to);
64
+ }
32
65
  /** Test-only escape hatch — exposes the internal store for assertions. */
33
66
  snapshot() {
34
67
  return new Map(this.store);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_files",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "File management including integration to cloud files",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -102,9 +102,9 @@
102
102
  "@types/react": "^18.3.3",
103
103
  "@types/react-dom": "^18.3.0",
104
104
  "dropbox": "^10.34.0",
105
- "hazo_core": "^1.0.0",
105
+ "hazo_core": "^1.1.0",
106
106
  "hazo_jobs": "^0.12.0",
107
- "hazo_llm_api": "^2.0.0",
107
+ "hazo_llm_api": "^2.1.0",
108
108
  "jsdom": "^28.1.0",
109
109
  "react": "^18.2.0",
110
110
  "react-dom": "^18.2.0",
@@ -120,14 +120,14 @@
120
120
  "@dnd-kit/utilities": "^3.0.0",
121
121
  "dropbox": "^10.0.0",
122
122
  "googleapis": "^140.0.0",
123
- "hazo_connect": "^3.0.0",
124
- "hazo_core": "^1.0.0",
125
- "hazo_debug": "^3.1.0",
123
+ "hazo_connect": "^3.4.1",
124
+ "hazo_core": "^1.1.0",
125
+ "hazo_debug": "^3.1.1",
126
126
  "hazo_jobs": "^0.12.0",
127
- "hazo_llm_api": "^2.0.0",
128
- "hazo_logs": "^2.0.0",
129
- "hazo_secure": "^1.0.1",
130
- "hazo_ui": "^3.0.1",
127
+ "hazo_llm_api": "^2.1.0",
128
+ "hazo_logs": "^2.0.3",
129
+ "hazo_secure": "^1.2.0",
130
+ "hazo_ui": "^3.2.1",
131
131
  "react": "^18.0.0",
132
132
  "react-dom": "^18.0.0",
133
133
  "server-only": "^0.0.1",