@sleep2agi/commhub-server 0.9.0-preview.27 → 0.9.0-preview.29

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.
Files changed (56) hide show
  1. package/bin/commhub.ts +4 -0
  2. package/package.json +4 -4
  3. package/src/ack-create-request-transport.test.ts +159 -0
  4. package/src/ack-create-request.test.ts +5 -5
  5. package/src/admin-networks-http.test.ts +84 -0
  6. package/src/api-host-supervisors-fallback.test.ts +1 -6
  7. package/src/auth.ts +88 -14
  8. package/src/dashboard-slash-routing-http.test.ts +103 -0
  9. package/src/db-adapter-guard.test.ts +85 -0
  10. package/src/db-adapter.ts +64 -23
  11. package/src/db.ts +279 -32
  12. package/src/ext-token-shared.test.ts +60 -0
  13. package/src/ext-validation-regression.test.ts +10 -17
  14. package/src/external-schedule-edits-http.test.ts +316 -0
  15. package/src/external-schedule-edits.ts +314 -0
  16. package/src/file-download-authz-dev-open.test.ts +1 -1
  17. package/src/file-download-authz.test.ts +21 -1
  18. package/src/file-download-date-bucket.test.ts +1 -1
  19. package/src/file-network-scope-dev-open.test.ts +1 -1
  20. package/src/file-network-scope.test.ts +1 -1
  21. package/src/health-redaction.test.ts +12 -0
  22. package/src/index.ts +6 -2
  23. package/src/network-name-http.test.ts +71 -0
  24. package/src/network-scope.ts +20 -0
  25. package/src/node-attrs.test.ts +1 -1
  26. package/src/peer-reply-atomic.test.ts +455 -0
  27. package/src/peer-reply-capability.test.ts +70 -0
  28. package/src/require-explicit-test-db.ts +9 -0
  29. package/src/rest-explicit-columns-http.test.ts +149 -0
  30. package/src/rest-identity.test.ts +120 -0
  31. package/src/rest-identity.ts +93 -0
  32. package/src/rest-projections.ts +67 -0
  33. package/src/scheduled-run-terminal.test.ts +268 -0
  34. package/src/scheduled-tasks-http.test.ts +460 -0
  35. package/src/scheduled-tasks.ts +538 -0
  36. package/src/send-reply-agent-warning.test.ts +38 -38
  37. package/src/server.ts +315 -60
  38. package/src/shared/external-schedule-contract.ts +60 -0
  39. package/src/skillhub-http.test.ts +88 -0
  40. package/src/task-auth-origin-http.test.ts +87 -0
  41. package/src/task-auth-origin.test.ts +24 -0
  42. package/src/task-auth-origin.ts +7 -0
  43. package/src/task-consumption.test.ts +329 -0
  44. package/src/task-diagnostic-http.test.ts +145 -0
  45. package/src/task-diagnostic.test.ts +82 -0
  46. package/src/task-diagnostic.ts +89 -0
  47. package/src/task-idempotency-mcp.test.ts +137 -0
  48. package/src/task-idempotency.test.ts +60 -0
  49. package/src/task-idempotency.ts +75 -0
  50. package/src/task-lifecycle-watcher.test.ts +160 -0
  51. package/src/task-lifecycle-watcher.ts +49 -0
  52. package/src/task-network-resolution-http.test.ts +137 -0
  53. package/src/tasks-pagination-http.test.ts +137 -0
  54. package/src/tools.ts +635 -107
  55. package/src/uploads-http.test.ts +1 -3
  56. package/src/uploads.ts +14 -3
@@ -7,12 +7,12 @@
7
7
  // this file already exercises the same code path). The hub binds to
8
8
  // an ephemeral port so multiple test runs don't collide.
9
9
  import { describe, expect, test, beforeAll, afterAll } from "bun:test";
10
+ import "./require-explicit-test-db.js";
10
11
  import { mkdtempSync, rmSync, existsSync } from "fs";
11
12
  import { join } from "path";
12
13
  import { tmpdir } from "os";
13
14
  import { register, login } from "./auth.js";
14
15
 
15
- const SERVER_DB = mkdtempSync(join(tmpdir(), "anet-upload-http-db-")) + "/commhub.db";
16
16
  const UPLOADS_DIR = mkdtempSync(join(tmpdir(), "anet-upload-http-fs-"));
17
17
  // #438 corrective: private bootServer({ port: 0 }) instance — the OS
18
18
  // assigns the port and BASE is derived from the ACTUAL bound port, so
@@ -28,7 +28,6 @@ beforeAll(async () => {
28
28
  // Bootstrap a real admin + token on the temp DB so the server can
29
29
  // resolve it via requireAuth (we don't want to short-circuit auth —
30
30
  // we want to validate the production code path).
31
- process.env.COMMHUB_DB = SERVER_DB;
32
31
  process.env.COMMHUB_UPLOADS_DIR = UPLOADS_DIR;
33
32
  process.env.HOST = "127.0.0.1";
34
33
 
@@ -64,7 +63,6 @@ beforeAll(async () => {
64
63
  afterAll(() => {
65
64
  try { server?.stop?.(true); } catch {}
66
65
  try { rmSync(UPLOADS_DIR, { recursive: true, force: true }); } catch {}
67
- try { rmSync(SERVER_DB, { recursive: true, force: true }); } catch {}
68
66
  });
69
67
 
70
68
  function authHeaders(): Record<string, string> {
package/src/uploads.ts CHANGED
@@ -53,6 +53,17 @@ export const UPLOAD_RATE_MAX_ENTRIES = 50_000;
53
53
  // traversal.
54
54
  export const FILE_ID_REGEX = /^[A-Za-z0-9_-]{8,64}$/;
55
55
 
56
+ // Stored extension tokens have one invariant at every path boundary:
57
+ // either no extension, or a leading dot followed by 1-16 ASCII
58
+ // alphanumeric characters. Keep this separate from sanitizeExt(), which
59
+ // parses a client filename and therefore deliberately has different
60
+ // capture/anchoring semantics.
61
+ const EXT_TOKEN_REGEX = /^\.[A-Za-z0-9]{1,16}$/;
62
+
63
+ function isValidExtToken(ext: unknown): ext is string {
64
+ return typeof ext === "string" && (ext === "" || EXT_TOKEN_REGEX.test(ext));
65
+ }
66
+
56
67
  export type GeneratedFile = {
57
68
  fileId: string;
58
69
  ext: string;
@@ -138,7 +149,7 @@ export function isValidCalendarBucket(bucket: unknown): bucket is string {
138
149
  */
139
150
  export function buildStoragePath(fileId: string, ext: string, opts: { uploadsRoot?: string; now?: Date } = {}): GeneratedFile {
140
151
  if (!FILE_ID_REGEX.test(fileId)) throw new Error(`file_id "${fileId}" is invalid`);
141
- if (ext && !/^\.[A-Za-z0-9]{1,16}$/.test(ext)) throw new Error(`ext "${ext}" is invalid`);
152
+ if (!isValidExtToken(ext)) throw new Error(`ext "${ext}" is invalid`);
142
153
  const dateBucket = getDateBucket(opts.now);
143
154
  const root = opts.uploadsRoot ?? getUploadsRoot();
144
155
  const relativePath = join(dateBucket, fileId + ext);
@@ -162,7 +173,7 @@ export function buildStoragePath(fileId: string, ext: string, opts: { uploadsRoo
162
173
  */
163
174
  export function pathForExistingBlob(dateBucket: string, fileId: string, ext: string, opts: { uploadsRoot?: string } = {}): GeneratedFile {
164
175
  if (!FILE_ID_REGEX.test(fileId)) throw new Error(`file_id "${fileId}" is invalid`);
165
- if (ext && !/^\.[A-Za-z0-9]{1,16}$/.test(ext)) throw new Error(`ext "${ext}" is invalid`);
176
+ if (!isValidExtToken(ext)) throw new Error(`ext "${ext}" is invalid`);
166
177
  if (typeof dateBucket !== "string" || !DATE_BUCKET_REGEX.test(dateBucket)) {
167
178
  throw new Error(`date_bucket "${dateBucket}" is invalid`);
168
179
  }
@@ -216,7 +227,7 @@ export function validateIndexEntry(entry: unknown): entry is UploadIndexEntry {
216
227
  return (
217
228
  typeof e.file_id === "string" && FILE_ID_REGEX.test(e.file_id) &&
218
229
  typeof e.date_bucket === "string" && /^\d{4}-\d{2}-\d{2}$/.test(e.date_bucket) &&
219
- typeof e.ext === "string" && (e.ext === "" || /^\.[A-Za-z0-9]{1,16}$/.test(e.ext)) &&
230
+ isValidExtToken(e.ext) &&
220
231
  typeof e.size === "number" && e.size >= 0 && e.size <= MAX_UPLOAD_BYTES
221
232
  );
222
233
  }