@boxd-sh/convex 0.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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +264 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +3 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +327 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +126 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/component/_generated/api.d.ts +52 -0
  12. package/dist/component/_generated/api.d.ts.map +1 -0
  13. package/dist/component/_generated/api.js +31 -0
  14. package/dist/component/_generated/api.js.map +1 -0
  15. package/dist/component/_generated/component.d.ts +357 -0
  16. package/dist/component/_generated/component.d.ts.map +1 -0
  17. package/dist/component/_generated/component.js +11 -0
  18. package/dist/component/_generated/component.js.map +1 -0
  19. package/dist/component/_generated/dataModel.d.ts +46 -0
  20. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  21. package/dist/component/_generated/dataModel.js +11 -0
  22. package/dist/component/_generated/dataModel.js.map +1 -0
  23. package/dist/component/_generated/server.d.ts +135 -0
  24. package/dist/component/_generated/server.d.ts.map +1 -0
  25. package/dist/component/_generated/server.js +80 -0
  26. package/dist/component/_generated/server.js.map +1 -0
  27. package/dist/component/boxd.d.ts +35 -0
  28. package/dist/component/boxd.d.ts.map +1 -0
  29. package/dist/component/boxd.js +144 -0
  30. package/dist/component/boxd.js.map +1 -0
  31. package/dist/component/convex.config.d.ts +6 -0
  32. package/dist/component/convex.config.d.ts.map +1 -0
  33. package/dist/component/convex.config.js +9 -0
  34. package/dist/component/convex.config.js.map +1 -0
  35. package/dist/component/errors.d.ts +25 -0
  36. package/dist/component/errors.d.ts.map +1 -0
  37. package/dist/component/errors.js +75 -0
  38. package/dist/component/errors.js.map +1 -0
  39. package/dist/component/exec.d.ts +25 -0
  40. package/dist/component/exec.d.ts.map +1 -0
  41. package/dist/component/exec.js +100 -0
  42. package/dist/component/exec.js.map +1 -0
  43. package/dist/component/executions.d.ts +70 -0
  44. package/dist/component/executions.d.ts.map +1 -0
  45. package/dist/component/executions.js +106 -0
  46. package/dist/component/executions.js.map +1 -0
  47. package/dist/component/files.d.ts +44 -0
  48. package/dist/component/files.d.ts.map +1 -0
  49. package/dist/component/files.js +105 -0
  50. package/dist/component/files.js.map +1 -0
  51. package/dist/component/limits.d.ts +33 -0
  52. package/dist/component/limits.d.ts.map +1 -0
  53. package/dist/component/limits.js +44 -0
  54. package/dist/component/limits.js.map +1 -0
  55. package/dist/component/machines.d.ts +337 -0
  56. package/dist/component/machines.d.ts.map +1 -0
  57. package/dist/component/machines.js +411 -0
  58. package/dist/component/machines.js.map +1 -0
  59. package/dist/component/records.d.ts +87 -0
  60. package/dist/component/records.d.ts.map +1 -0
  61. package/dist/component/records.js +51 -0
  62. package/dist/component/records.js.map +1 -0
  63. package/dist/component/schema.d.ts +138 -0
  64. package/dist/component/schema.d.ts.map +1 -0
  65. package/dist/component/schema.js +70 -0
  66. package/dist/component/schema.js.map +1 -0
  67. package/dist/component/sessions.d.ts +21 -0
  68. package/dist/component/sessions.d.ts.map +1 -0
  69. package/dist/component/sessions.js +59 -0
  70. package/dist/component/sessions.js.map +1 -0
  71. package/dist/component/validate.d.ts +15 -0
  72. package/dist/component/validate.d.ts.map +1 -0
  73. package/dist/component/validate.js +44 -0
  74. package/dist/component/validate.js.map +1 -0
  75. package/package.json +99 -0
  76. package/src/client/_generated/_ignore.ts +1 -0
  77. package/src/client/index.ts +199 -0
  78. package/src/component/_generated/api.ts +68 -0
  79. package/src/component/_generated/component.ts +430 -0
  80. package/src/component/_generated/dataModel.ts +60 -0
  81. package/src/component/_generated/server.ts +171 -0
  82. package/src/component/boxd.ts +188 -0
  83. package/src/component/convex.config.ts +9 -0
  84. package/src/component/errors.ts +105 -0
  85. package/src/component/exec.ts +134 -0
  86. package/src/component/executions.ts +115 -0
  87. package/src/component/files.ts +128 -0
  88. package/src/component/limits.ts +48 -0
  89. package/src/component/machines.ts +500 -0
  90. package/src/component/records.ts +71 -0
  91. package/src/component/schema.ts +77 -0
  92. package/src/component/sessions.ts +61 -0
  93. package/src/component/validate.ts +58 -0
  94. package/src/test.ts +28 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Run a command in a machine. Each run is recorded in `executions`
3
+ * (running → completed or failed), so the app can render live status and
4
+ * history from a reactive query.
5
+ *
6
+ * The call returns when the command exits. For work that can outlast the 10
7
+ * minute action ceiling, start it in the background inside the machine
8
+ * (`nohup ... &`) and poll it with later runs.
9
+ */
10
+ import { v } from "convex/values";
11
+ import { internal } from "./_generated/api.js";
12
+ import { action } from "./_generated/server.js";
13
+ import { withBoxd } from "./boxd.js";
14
+ import { errorMessage, isNotFound } from "./errors.js";
15
+ import { DEFAULT_EXEC_TIMEOUT_MS, MAX_EXEC_TIMEOUT_MS, MAX_RETURNED_OUTPUT, MAX_STORED_COMMAND, MAX_STORED_OUTPUT, truncate, } from "./limits.js";
16
+ import { requireMachine } from "./records.js";
17
+ import { boundedTimeout, requireAbsolutePath, requireNonEmpty, } from "./validate.js";
18
+ const SAFE_ARG = /^[A-Za-z0-9_\-./=:@%+,]+$/;
19
+ /** How the command reads in a shell, for the execution row. */
20
+ function displayCommand(command) {
21
+ if (typeof command === "string")
22
+ return command;
23
+ return command
24
+ .map((arg) => SAFE_ARG.test(arg) ? arg : `'${arg.replace(/'/g, `'"'"'`)}'`)
25
+ .join(" ");
26
+ }
27
+ export const run = action({
28
+ args: {
29
+ machineId: v.string(),
30
+ ownerId: v.optional(v.string()),
31
+ /** A shell command line, or an argv array that is quoted for you. */
32
+ command: v.union(v.string(), v.array(v.string())),
33
+ /** Absolute working directory. A missing one fails the command. */
34
+ cwd: v.optional(v.string()),
35
+ /** Extra environment for this command only. Not stored. */
36
+ env: v.optional(v.record(v.string(), v.string())),
37
+ /** Kill the command after this many ms. Default 540s, capped at 570s. */
38
+ timeoutMs: v.optional(v.number()),
39
+ },
40
+ returns: v.object({
41
+ executionId: v.id("executions"),
42
+ exitCode: v.number(),
43
+ success: v.boolean(),
44
+ stdout: v.string(),
45
+ stderr: v.string(),
46
+ }),
47
+ handler: async (ctx, args) => {
48
+ // Validate before anything is recorded or sent.
49
+ const timeout = boundedTimeout(args.timeoutMs, DEFAULT_EXEC_TIMEOUT_MS, MAX_EXEC_TIMEOUT_MS, "timeoutMs");
50
+ if (args.cwd !== undefined)
51
+ requireAbsolutePath(args.cwd);
52
+ const line = displayCommand(args.command);
53
+ requireNonEmpty(line, "command");
54
+ const machine = await requireMachine(ctx, args.machineId, args.ownerId);
55
+ const executionId = await ctx.runMutation(internal.executions.begin, {
56
+ machineId: machine.machineId,
57
+ ownerId: machine.ownerId,
58
+ command: truncate(line, MAX_STORED_COMMAND),
59
+ cwd: args.cwd,
60
+ timeoutMs: timeout,
61
+ });
62
+ try {
63
+ const result = await withBoxd(ctx, (boxd) => boxd.machines.exec(machine.machineId, {
64
+ command: args.command,
65
+ cwd: args.cwd,
66
+ env: args.env,
67
+ timeout,
68
+ }));
69
+ await ctx.runMutation(internal.executions.finish, {
70
+ executionId,
71
+ status: "completed",
72
+ exitCode: result.exitCode,
73
+ stdout: truncate(result.stdout, MAX_STORED_OUTPUT),
74
+ stderr: truncate(result.stderr, MAX_STORED_OUTPUT),
75
+ });
76
+ return {
77
+ executionId,
78
+ exitCode: result.exitCode,
79
+ success: result.success,
80
+ stdout: truncate(result.stdout, MAX_RETURNED_OUTPUT),
81
+ stderr: truncate(result.stderr, MAX_RETURNED_OUTPUT),
82
+ };
83
+ }
84
+ catch (error) {
85
+ await ctx.runMutation(internal.executions.finish, {
86
+ executionId,
87
+ status: "failed",
88
+ error: errorMessage(error),
89
+ });
90
+ if (isNotFound(error)) {
91
+ // boxd no longer has the machine, e.g. after its auto-destroy timer.
92
+ await ctx.runMutation(internal.machines.markDestroyed, {
93
+ machineId: machine.machineId,
94
+ });
95
+ }
96
+ throw error;
97
+ }
98
+ },
99
+ });
100
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/component/exec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Execution records: reactive queries over the `executions` table, plus the
3
+ * bookkeeping mutations `exec.run` writes through.
4
+ */
5
+ /** A machine's executions, newest first. Empty for another owner's machine. */
6
+ export declare const list: import("convex/server").RegisteredQuery<"public", {
7
+ ownerId?: string | undefined;
8
+ limit?: number | undefined;
9
+ machineId: string;
10
+ }, Promise<{
11
+ _id: import("convex/values").GenericId<"executions">;
12
+ _creationTime: number;
13
+ cwd?: string | undefined;
14
+ ownerId?: string | undefined;
15
+ exitCode?: number | undefined;
16
+ stdout?: string | undefined;
17
+ stderr?: string | undefined;
18
+ error?: string | undefined;
19
+ finishedAt?: number | undefined;
20
+ command: string;
21
+ machineId: string;
22
+ status: "running" | "completed" | "failed";
23
+ startedAt: number;
24
+ }[]>>;
25
+ export declare const get: import("convex/server").RegisteredQuery<"public", {
26
+ ownerId?: string | undefined;
27
+ executionId: string;
28
+ }, Promise<{
29
+ _id: import("convex/values").GenericId<"executions">;
30
+ _creationTime: number;
31
+ cwd?: string | undefined;
32
+ ownerId?: string | undefined;
33
+ exitCode?: number | undefined;
34
+ stdout?: string | undefined;
35
+ stderr?: string | undefined;
36
+ error?: string | undefined;
37
+ finishedAt?: number | undefined;
38
+ command: string;
39
+ machineId: string;
40
+ status: "running" | "completed" | "failed";
41
+ startedAt: number;
42
+ } | null>>;
43
+ /** Grace after the command's own timeout before the watchdog steps in. */
44
+ export declare const WATCHDOG_GRACE_MS = 60000;
45
+ /**
46
+ * Record a starting execution, and schedule a watchdog for it. The action
47
+ * marks the row finished itself, but an action that dies (a crash, a restart,
48
+ * running out of memory) never reaches its `catch`, and the row would stay
49
+ * "running" forever.
50
+ */
51
+ export declare const begin: import("convex/server").RegisteredMutation<"internal", {
52
+ cwd?: string | undefined;
53
+ ownerId?: string | undefined;
54
+ command: string;
55
+ machineId: string;
56
+ timeoutMs: number;
57
+ }, Promise<import("convex/values").GenericId<"executions">>>;
58
+ /** Fail an execution whose action stopped before it could record a result. */
59
+ export declare const expire: import("convex/server").RegisteredMutation<"internal", {
60
+ executionId: import("convex/values").GenericId<"executions">;
61
+ }, Promise<null>>;
62
+ export declare const finish: import("convex/server").RegisteredMutation<"internal", {
63
+ exitCode?: number | undefined;
64
+ stdout?: string | undefined;
65
+ stderr?: string | undefined;
66
+ error?: string | undefined;
67
+ executionId: import("convex/values").GenericId<"executions">;
68
+ status: "completed" | "failed";
69
+ }, Promise<null>>;
70
+ //# sourceMappingURL=executions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executions.d.ts","sourceRoot":"","sources":["../../src/component/executions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,+EAA+E;AAC/E,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;KAmBf,CAAC;AAEH,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;UAWd,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;;4DAsBhB,CAAC;AAEH,8EAA8E;AAC9E,eAAO,MAAM,MAAM;;iBAejB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;iBAiBjB,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Execution records: reactive queries over the `executions` table, plus the
3
+ * bookkeeping mutations `exec.run` writes through.
4
+ */
5
+ import { v } from "convex/values";
6
+ import { internal } from "./_generated/api.js";
7
+ import { internalMutation, query } from "./_generated/server.js";
8
+ import { clampLimit } from "./limits.js";
9
+ import { executionDoc } from "./records.js";
10
+ /** A machine's executions, newest first. Empty for another owner's machine. */
11
+ export const list = query({
12
+ args: {
13
+ machineId: v.string(),
14
+ ownerId: v.optional(v.string()),
15
+ limit: v.optional(v.number()),
16
+ },
17
+ returns: v.array(executionDoc),
18
+ handler: async (ctx, { machineId, ownerId, limit }) => {
19
+ const machine = await ctx.db
20
+ .query("machines")
21
+ .withIndex("by_machine_id", (q) => q.eq("machineId", machineId))
22
+ .unique();
23
+ if (!machine || machine.ownerId !== ownerId)
24
+ return [];
25
+ return await ctx.db
26
+ .query("executions")
27
+ .withIndex("by_machine", (q) => q.eq("machineId", machineId))
28
+ .order("desc")
29
+ .take(clampLimit(limit));
30
+ },
31
+ });
32
+ export const get = query({
33
+ args: { executionId: v.string(), ownerId: v.optional(v.string()) },
34
+ returns: v.union(v.null(), executionDoc),
35
+ handler: async (ctx, { executionId, ownerId }) => {
36
+ // Ids cross the component boundary as strings; a malformed one is simply
37
+ // not found.
38
+ const id = ctx.db.normalizeId("executions", executionId);
39
+ if (!id)
40
+ return null;
41
+ const row = await ctx.db.get("executions", id);
42
+ return row && row.ownerId === ownerId ? row : null;
43
+ },
44
+ });
45
+ /** Grace after the command's own timeout before the watchdog steps in. */
46
+ export const WATCHDOG_GRACE_MS = 60_000;
47
+ /**
48
+ * Record a starting execution, and schedule a watchdog for it. The action
49
+ * marks the row finished itself, but an action that dies (a crash, a restart,
50
+ * running out of memory) never reaches its `catch`, and the row would stay
51
+ * "running" forever.
52
+ */
53
+ export const begin = internalMutation({
54
+ args: {
55
+ machineId: v.string(),
56
+ ownerId: v.optional(v.string()),
57
+ command: v.string(),
58
+ cwd: v.optional(v.string()),
59
+ timeoutMs: v.number(),
60
+ },
61
+ returns: v.id("executions"),
62
+ handler: async (ctx, { timeoutMs, ...fields }) => {
63
+ const executionId = await ctx.db.insert("executions", {
64
+ ...fields,
65
+ status: "running",
66
+ startedAt: Date.now(),
67
+ });
68
+ await ctx.scheduler.runAfter(timeoutMs + WATCHDOG_GRACE_MS, internal.executions.expire, { executionId });
69
+ return executionId;
70
+ },
71
+ });
72
+ /** Fail an execution whose action stopped before it could record a result. */
73
+ export const expire = internalMutation({
74
+ args: { executionId: v.id("executions") },
75
+ returns: v.null(),
76
+ handler: async (ctx, { executionId }) => {
77
+ const row = await ctx.db.get("executions", executionId);
78
+ if (row?.status === "running") {
79
+ await ctx.db.patch("executions", executionId, {
80
+ status: "failed",
81
+ error: "the action running this command stopped before it recorded a result",
82
+ finishedAt: Date.now(),
83
+ });
84
+ }
85
+ return null;
86
+ },
87
+ });
88
+ export const finish = internalMutation({
89
+ args: {
90
+ executionId: v.id("executions"),
91
+ status: v.union(v.literal("completed"), v.literal("failed")),
92
+ exitCode: v.optional(v.number()),
93
+ stdout: v.optional(v.string()),
94
+ stderr: v.optional(v.string()),
95
+ error: v.optional(v.string()),
96
+ },
97
+ returns: v.null(),
98
+ handler: async (ctx, { executionId, ...result }) => {
99
+ await ctx.db.patch("executions", executionId, {
100
+ ...result,
101
+ finishedAt: Date.now(),
102
+ });
103
+ return null;
104
+ },
105
+ });
106
+ //# sourceMappingURL=executions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executions.js","sourceRoot":"","sources":["../../src/component/executions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Files inside a machine. Whole files cross the function boundary, so each
3
+ * is capped well below Convex's 16 MiB argument and return limits. Move
4
+ * bigger files with `exec` (for example `curl` to or from storage).
5
+ */
6
+ /** Largest file `readFile` returns or `writeFile` accepts. */
7
+ export declare const MAX_FILE_BYTES: number;
8
+ /** Read a file as UTF-8 text. A file that isn't valid UTF-8 is refused. */
9
+ export declare const readFile: import("convex/server").RegisteredAction<"public", {
10
+ ownerId?: string | undefined;
11
+ machineId: string;
12
+ path: string;
13
+ }, Promise<string>>;
14
+ /** Read a file byte for byte. */
15
+ export declare const readFileBytes: import("convex/server").RegisteredAction<"public", {
16
+ ownerId?: string | undefined;
17
+ machineId: string;
18
+ path: string;
19
+ }, Promise<ArrayBuffer>>;
20
+ /** Create or replace a file. The parent directory must exist. */
21
+ export declare const writeFile: import("convex/server").RegisteredAction<"public", {
22
+ ownerId?: string | undefined;
23
+ machineId: string;
24
+ path: string;
25
+ content: string | ArrayBuffer;
26
+ }, Promise<{
27
+ bytesWritten: number;
28
+ }>>;
29
+ /** The immediate contents of a directory. */
30
+ export declare const listDir: import("convex/server").RegisteredAction<"public", {
31
+ ownerId?: string | undefined;
32
+ machineId: string;
33
+ path: string;
34
+ }, Promise<{
35
+ entries: {
36
+ name: string;
37
+ isDir: boolean;
38
+ sizeBytes: number;
39
+ permissions: string;
40
+ modifiedAt: number;
41
+ }[];
42
+ truncated: boolean;
43
+ }>>;
44
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/component/files.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,8DAA8D;AAC9D,eAAO,MAAM,cAAc,QAAkB,CAAC;AA+B9C,2EAA2E;AAC3E,eAAO,MAAM,QAAQ;;;;mBAenB,CAAC;AAEH,iCAAiC;AACjC,eAAO,MAAM,aAAa;;;;wBAQxB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,SAAS;;;;;;;GAgBpB,CAAC;AAEH,6CAA6C;AAC7C,eAAO,MAAM,OAAO;;;;;;;;;;;;;GAiClB,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Files inside a machine. Whole files cross the function boundary, so each
3
+ * is capped well below Convex's 16 MiB argument and return limits. Move
4
+ * bigger files with `exec` (for example `curl` to or from storage).
5
+ */
6
+ import { v } from "convex/values";
7
+ import { action } from "./_generated/server.js";
8
+ import { withBoxd } from "./boxd.js";
9
+ import { invalidArgument } from "./errors.js";
10
+ import { requireMachine } from "./records.js";
11
+ import { requireAbsolutePath } from "./validate.js";
12
+ /** Largest file `readFile` returns or `writeFile` accepts. */
13
+ export const MAX_FILE_BYTES = 8 * 1024 * 1024;
14
+ const target = {
15
+ machineId: v.string(),
16
+ ownerId: v.optional(v.string()),
17
+ /** Absolute path inside the machine. */
18
+ path: v.string(),
19
+ };
20
+ function requireSize(bytes, path) {
21
+ if (bytes > MAX_FILE_BYTES) {
22
+ throw invalidArgument(`${path} is ${bytes} bytes, over the ${MAX_FILE_BYTES} byte limit. ` +
23
+ "Move large files with exec instead.");
24
+ }
25
+ }
26
+ async function download(ctx, args) {
27
+ requireAbsolutePath(args.path);
28
+ await requireMachine(ctx, args.machineId, args.ownerId);
29
+ const bytes = await withBoxd(ctx, (boxd) => boxd.machines.files.download(args.machineId, args.path));
30
+ requireSize(bytes.byteLength, args.path);
31
+ return bytes;
32
+ }
33
+ /** Read a file as UTF-8 text. A file that isn't valid UTF-8 is refused. */
34
+ export const readFile = action({
35
+ args: target,
36
+ returns: v.string(),
37
+ handler: async (ctx, args) => {
38
+ const bytes = await download(ctx, args);
39
+ try {
40
+ // Strict: a lenient decode would turn each bad byte into a 3-byte
41
+ // U+FFFD, and could push a binary file past the return limit.
42
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
43
+ }
44
+ catch {
45
+ throw invalidArgument(`${args.path} is not UTF-8 text. Read it with readFileBytes.`);
46
+ }
47
+ },
48
+ });
49
+ /** Read a file byte for byte. */
50
+ export const readFileBytes = action({
51
+ args: target,
52
+ returns: v.bytes(),
53
+ handler: async (ctx, args) => {
54
+ const bytes = await download(ctx, args);
55
+ // A standalone copy: `bytes` can be a view into a larger buffer.
56
+ return bytes.slice().buffer;
57
+ },
58
+ });
59
+ /** Create or replace a file. The parent directory must exist. */
60
+ export const writeFile = action({
61
+ args: { ...target, content: v.union(v.string(), v.bytes()) },
62
+ returns: v.object({ bytesWritten: v.number() }),
63
+ handler: async (ctx, { machineId, ownerId, path, content }) => {
64
+ requireAbsolutePath(path);
65
+ const data = typeof content === "string"
66
+ ? new TextEncoder().encode(content)
67
+ : new Uint8Array(content);
68
+ requireSize(data.byteLength, path);
69
+ await requireMachine(ctx, machineId, ownerId);
70
+ const bytesWritten = await withBoxd(ctx, (boxd) => boxd.machines.files.upload(machineId, path, data));
71
+ return { bytesWritten };
72
+ },
73
+ });
74
+ /** The immediate contents of a directory. */
75
+ export const listDir = action({
76
+ args: target,
77
+ returns: v.object({
78
+ entries: v.array(v.object({
79
+ name: v.string(),
80
+ isDir: v.boolean(),
81
+ sizeBytes: v.number(),
82
+ permissions: v.string(),
83
+ /** Epoch milliseconds. */
84
+ modifiedAt: v.number(),
85
+ })),
86
+ /** The directory held more entries than boxd returns in one listing. */
87
+ truncated: v.boolean(),
88
+ }),
89
+ handler: async (ctx, { machineId, ownerId, path }) => {
90
+ requireAbsolutePath(path);
91
+ await requireMachine(ctx, machineId, ownerId);
92
+ const listing = await withBoxd(ctx, (boxd) => boxd.machines.files.listDir(machineId, path));
93
+ return {
94
+ entries: listing.entries.map((entry) => ({
95
+ name: entry.name,
96
+ isDir: entry.isDir,
97
+ sizeBytes: entry.sizeBytes,
98
+ permissions: entry.permissions,
99
+ modifiedAt: entry.modifiedAt.getTime(),
100
+ })),
101
+ truncated: listing.truncated,
102
+ };
103
+ },
104
+ });
105
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/component/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Bounds that keep every value this component stores or returns inside
3
+ * Convex's limits: 1 MiB per document, 16 MiB per function return value,
4
+ * and a 10 minute ceiling on an action.
5
+ */
6
+ /** Stored per stream on an execution row: two of these stay far below 1 MiB. */
7
+ export declare const MAX_STORED_OUTPUT = 64000;
8
+ /** Stored command line on an execution row. */
9
+ export declare const MAX_STORED_COMMAND = 4000;
10
+ /** Stored error message on any row. */
11
+ export declare const MAX_STORED_ERROR = 4000;
12
+ /**
13
+ * Returned per stream from `exec`. A character is at most 3 UTF-8 bytes, so
14
+ * two streams at this cap stay under the 16 MiB return limit.
15
+ */
16
+ export declare const MAX_RETURNED_OUTPUT = 2000000;
17
+ /**
18
+ * A command that outlives its action would leave its execution row stuck at
19
+ * "running", because the action's `catch` never runs. So the remote command
20
+ * is always bounded below the 10 minute action ceiling.
21
+ */
22
+ export declare const DEFAULT_EXEC_TIMEOUT_MS = 540000;
23
+ export declare const MAX_EXEC_TIMEOUT_MS = 570000;
24
+ /** How long `create` and `fork` wait for the machine to accept an exec. */
25
+ export declare const DEFAULT_READY_TIMEOUT_MS = 120000;
26
+ export declare const MAX_READY_TIMEOUT_MS = 480000;
27
+ export declare const DEFAULT_LIST_LIMIT = 100;
28
+ export declare const MAX_LIST_LIMIT = 500;
29
+ /** Truncate to at most `max` characters, the marker included. */
30
+ export declare function truncate(text: string, max: number): string;
31
+ /** A caller-supplied page size, forced into `[1, MAX_LIST_LIMIT]`. */
32
+ export declare function clampLimit(limit: number | undefined): number;
33
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/component/limits.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,+CAA+C;AAC/C,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AACxC,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,UAAY,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAC/C,eAAO,MAAM,mBAAmB,SAAU,CAAC;AAE3C,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAChD,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAE5C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,cAAc,MAAM,CAAC;AAIlC,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI5D"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Bounds that keep every value this component stores or returns inside
3
+ * Convex's limits: 1 MiB per document, 16 MiB per function return value,
4
+ * and a 10 minute ceiling on an action.
5
+ */
6
+ /** Stored per stream on an execution row: two of these stay far below 1 MiB. */
7
+ export const MAX_STORED_OUTPUT = 64_000;
8
+ /** Stored command line on an execution row. */
9
+ export const MAX_STORED_COMMAND = 4_000;
10
+ /** Stored error message on any row. */
11
+ export const MAX_STORED_ERROR = 4_000;
12
+ /**
13
+ * Returned per stream from `exec`. A character is at most 3 UTF-8 bytes, so
14
+ * two streams at this cap stay under the 16 MiB return limit.
15
+ */
16
+ export const MAX_RETURNED_OUTPUT = 2_000_000;
17
+ /**
18
+ * A command that outlives its action would leave its execution row stuck at
19
+ * "running", because the action's `catch` never runs. So the remote command
20
+ * is always bounded below the 10 minute action ceiling.
21
+ */
22
+ export const DEFAULT_EXEC_TIMEOUT_MS = 540_000;
23
+ export const MAX_EXEC_TIMEOUT_MS = 570_000;
24
+ /** How long `create` and `fork` wait for the machine to accept an exec. */
25
+ export const DEFAULT_READY_TIMEOUT_MS = 120_000;
26
+ export const MAX_READY_TIMEOUT_MS = 480_000;
27
+ export const DEFAULT_LIST_LIMIT = 100;
28
+ export const MAX_LIST_LIMIT = 500;
29
+ const TRUNCATION_MARKER = "\n…[truncated]";
30
+ /** Truncate to at most `max` characters, the marker included. */
31
+ export function truncate(text, max) {
32
+ if (text.length <= max)
33
+ return text;
34
+ const keep = Math.max(0, max - TRUNCATION_MARKER.length);
35
+ return `${text.slice(0, keep)}${TRUNCATION_MARKER}`;
36
+ }
37
+ /** A caller-supplied page size, forced into `[1, MAX_LIST_LIMIT]`. */
38
+ export function clampLimit(limit) {
39
+ const value = Math.floor(limit ?? DEFAULT_LIST_LIMIT);
40
+ if (!Number.isFinite(value))
41
+ return DEFAULT_LIST_LIMIT;
42
+ return Math.min(Math.max(value, 1), MAX_LIST_LIMIT);
43
+ }
44
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/component/limits.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACxC,uCAAuC;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AACtC;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAE3C,iEAAiE;AACjE,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,GAAW;IAChD,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACzD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,iBAAiB,EAAE,CAAC;AACtD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,kBAAkB,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACvD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AACtD,CAAC"}