@frockbot/cloudflare 0.0.0 → 0.7.291

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 (88) hide show
  1. package/build-artifact.ts +130 -0
  2. package/build-flutter-web.ts +508 -0
  3. package/deployment-config/README.md +414 -0
  4. package/deployment-config/cli.ts +228 -0
  5. package/deployment-config/generate.ts +689 -0
  6. package/deployment-config/jsonc.ts +18 -0
  7. package/deployment-config/profile-schema.generated.ts +391 -0
  8. package/deployment-config/profile.schema.json +358 -0
  9. package/deployment-config/profile.ts +124 -0
  10. package/migrations/0001_better_auth.sql +15 -0
  11. package/migrations/0002_drop_account_issuer.sql +8 -0
  12. package/package.json +61 -5
  13. package/release-version.ts +20 -0
  14. package/src/account-admission.ts +179 -0
  15. package/src/account-deletion.ts +274 -0
  16. package/src/admin-entrypoint.ts +194 -0
  17. package/src/admin-identities.ts +26 -0
  18. package/src/audit.ts +103 -0
  19. package/src/auth-package.access.ts +24 -0
  20. package/src/auth-package.ts +36 -0
  21. package/src/avatar-state-cleanup.ts +107 -0
  22. package/src/billing-computer.ts +267 -0
  23. package/src/billing-readiness.ts +21 -0
  24. package/src/billing.ts +461 -0
  25. package/src/bot-capabilities.ts +480 -0
  26. package/src/bot-recovery.ts +1 -0
  27. package/src/bot-state-channel.ts +702 -0
  28. package/src/bot-state.ts +3995 -0
  29. package/src/bot-template-cleanup.ts +98 -0
  30. package/src/bot-title-cleanup.ts +104 -0
  31. package/src/brand-icon.png +0 -0
  32. package/src/brand-logo.ts +2 -0
  33. package/src/brand.ts +36 -0
  34. package/src/client-compatibility.ts +51 -0
  35. package/src/compaction-announcement-cleanup.ts +113 -0
  36. package/src/computer-egress.ts +110 -0
  37. package/src/computer-host.ts +92 -0
  38. package/src/computer-screenshot-cleanup.ts +78 -0
  39. package/src/contracts.ts +832 -0
  40. package/src/debug.ts +272 -0
  41. package/src/default-packages-marker-cleanup.ts +39 -0
  42. package/src/deployment-policy-admin-host.ts +121 -0
  43. package/src/deployment-policy.ts +426 -0
  44. package/src/directory-profile-cleanup.ts +101 -0
  45. package/src/durable-rpc.ts +753 -0
  46. package/src/durable-session.ts +21 -0
  47. package/src/entry-boundary.ts +103 -0
  48. package/src/frock-ai.ts +338 -0
  49. package/src/gateway.ts +1392 -0
  50. package/src/group-chat.ts +736 -0
  51. package/src/hidden-bot-notifications-cleanup.ts +32 -0
  52. package/src/index.ts +2798 -0
  53. package/src/insights.ts +15 -0
  54. package/src/machine-messages-cleanup.ts +121 -0
  55. package/src/machine-socket.ts +164 -0
  56. package/src/memory-records.ts +181 -0
  57. package/src/memory.ts +145 -0
  58. package/src/model-rates.ts +252 -0
  59. package/src/native-auth.ts +1006 -0
  60. package/src/native-sessions.ts +121 -0
  61. package/src/notification-state-cleanup.ts +18 -0
  62. package/src/ollama-web-search-cleanup.ts +69 -0
  63. package/src/package-page-shapes-cleanup.ts +190 -0
  64. package/src/plugin-egress.ts +31 -0
  65. package/src/plugin-page-route.ts +72 -0
  66. package/src/plugin-panels-cleanup.ts +101 -0
  67. package/src/prepared-input-cleanup.ts +105 -0
  68. package/src/production-secrets.ts +484 -0
  69. package/src/project-cleanup.ts +123 -0
  70. package/src/project-events-cleanup.ts +152 -0
  71. package/src/publication-state-cleanup.ts +92 -0
  72. package/src/push.ts +498 -0
  73. package/src/request-body.ts +165 -0
  74. package/src/routine-state-cleanup.ts +57 -0
  75. package/src/search.ts +88 -0
  76. package/src/sidebar-label-cleanup.ts +165 -0
  77. package/src/skill-index-cleanup.ts +53 -0
  78. package/src/supersede-cleanup.ts +162 -0
  79. package/src/test-chat-cleanup.ts +93 -0
  80. package/src/uploads.ts +486 -0
  81. package/src/user-application.ts +1068 -0
  82. package/src/user-configuration.ts +4491 -0
  83. package/src/voice-assistant.ts +4999 -0
  84. package/src/voice-dictation.ts +743 -0
  85. package/src/working-context-cleanup.ts +96 -0
  86. package/src/workspace.ts +201 -0
  87. package/wrangler.jsonc +385 -0
  88. package/README.md +0 -3
@@ -0,0 +1,123 @@
1
+ // Disposable cleanup for what Projects left in the User Durable Object.
2
+ //
3
+ // Group Chats replaced Projects. The Project catalogue and each Bot's joined
4
+ // list go, with the generation records of the Project Memory roots and their
5
+ // files in object storage. Memory kept a Project's facts in its shared
6
+ // `groupChat` scope under the Project's slug; a Group Chat's scope is keyed
7
+ // by the group's id, so every such scope not keyed by one is a Project's and
8
+ // is purged. Object storage is paged, so a large root finishes over several
9
+ // wakes; the receipt carries the cursor until it is done.
10
+
11
+ import { isGroupIdV1 } from "@frockbot/app/groups/shared";
12
+ import {
13
+ WORKSPACE_CONFLICT_PREFIX,
14
+ WORKSPACE_GENERATION_PREFIX,
15
+ } from "@frockbot/core/durable";
16
+ import { WORKSPACE_OBJECT_PREFIX } from "@frockbot/core/workspace-store";
17
+ import type { MemoryEngineV1 } from "@frockbot/app/memory/engine";
18
+ import { decodeMemoryScopeKeyV1 } from "@frockbot/app/memory/records";
19
+
20
+ const RECEIPT = "maintenance:project-removal:2026-09-23";
21
+ const RETIRED_KEY = "memory:projects";
22
+ const RETIRED_ROOT = "project-memory:";
23
+ const PAGE = 50;
24
+ const OBJECT_PAGES = 4;
25
+
26
+ interface CleanupStorage {
27
+ get(key: string): Promise<unknown>;
28
+ put(key: string, value: unknown): Promise<void>;
29
+ delete(key: string): Promise<boolean>;
30
+ list(options: {
31
+ prefix: string;
32
+ limit: number;
33
+ start?: string;
34
+ }): Promise<Map<string, unknown>>;
35
+ }
36
+
37
+ interface CleanupBucket {
38
+ list(options: {
39
+ prefix: string;
40
+ limit: number;
41
+ cursor?: string;
42
+ }): Promise<{ keys: string[]; cursor?: string; truncated: boolean }>;
43
+ delete(key: string): Promise<void>;
44
+ }
45
+
46
+ async function deletePrefix(
47
+ storage: CleanupStorage,
48
+ prefix: string,
49
+ ): Promise<number> {
50
+ let deleted = 0;
51
+ for (;;) {
52
+ const listed = await storage.list({ prefix, limit: PAGE });
53
+ for (const key of listed.keys()) {
54
+ await storage.delete(key);
55
+ deleted += 1;
56
+ }
57
+ if (listed.size < PAGE) return deleted;
58
+ }
59
+ }
60
+
61
+ export async function cleanRetiredProjectsV1(
62
+ storage: CleanupStorage,
63
+ options: {
64
+ userId?: string;
65
+ bucket?: CleanupBucket;
66
+ engine?: Pick<MemoryEngineV1, "scopeKeysOfKind" | "purgeScope">;
67
+ },
68
+ ): Promise<void> {
69
+ const stored = await storage.get(RECEIPT);
70
+ const receipt =
71
+ stored && typeof stored === "object"
72
+ ? (stored as { done?: boolean; cursor?: string })
73
+ : undefined;
74
+ if (receipt?.done) return;
75
+ let keys = (await storage.delete(RETIRED_KEY)) ? 1 : 0;
76
+ keys += await deletePrefix(storage, `${RETIRED_KEY}:`);
77
+ keys += await deletePrefix(
78
+ storage,
79
+ `${WORKSPACE_GENERATION_PREFIX}${RETIRED_ROOT}`,
80
+ );
81
+ keys += await deletePrefix(
82
+ storage,
83
+ `${WORKSPACE_CONFLICT_PREFIX}${RETIRED_ROOT}`,
84
+ );
85
+ let scopes = 0;
86
+ for (const scopeKey of options.engine?.scopeKeysOfKind("groupChat") ?? []) {
87
+ if (isGroupIdV1(decodeMemoryScopeKeyV1(scopeKey).groupChatId)) continue;
88
+ options.engine!.purgeScope(scopeKey);
89
+ scopes += 1;
90
+ }
91
+ let cursor = receipt?.cursor;
92
+ let objects = 0;
93
+ let done = true;
94
+ if (options.userId && options.bucket) {
95
+ const prefix = `${WORKSPACE_OBJECT_PREFIX}/${RETIRED_ROOT}${encodeURIComponent(options.userId)}:`;
96
+ done = false;
97
+ for (let page = 0; page < OBJECT_PAGES; page += 1) {
98
+ const listed = await options.bucket.list({
99
+ prefix,
100
+ limit: PAGE,
101
+ ...(cursor ? { cursor } : {}),
102
+ });
103
+ for (const key of listed.keys) {
104
+ if (!key.startsWith(prefix)) continue;
105
+ await options.bucket.delete(key);
106
+ objects += 1;
107
+ }
108
+ if (!listed.truncated) {
109
+ done = true;
110
+ break;
111
+ }
112
+ cursor = listed.cursor;
113
+ }
114
+ }
115
+ await storage.put(RECEIPT, {
116
+ schemaVersion: 1,
117
+ done,
118
+ ...(done || !cursor ? {} : { cursor }),
119
+ keys,
120
+ scopes,
121
+ objects,
122
+ });
123
+ }
@@ -0,0 +1,152 @@
1
+ // Disposable cleanup for the Session events Projects left behind.
2
+ //
3
+ // Group Chats replaced Projects. A Memory event's `project` scope reads as
4
+ // `group` now, and its `projectId` as `groupId`: Memory already kept a
5
+ // Project's facts in its shared `groupChat` scope under that id, so the
6
+ // record stays true. The two Project membership events become the Memory
7
+ // Package's call and result they were, so no event is dropped and no
8
+ // sequence number moves. Each Session is rewritten in its own transaction;
9
+ // the walk is once per Bot.
10
+
11
+ import {
12
+ LATEST_EVENTS_KEY,
13
+ SESSION_EVENT_LOG_INDEX_PREFIX,
14
+ SessionEventLog,
15
+ type SessionEventLogStorage,
16
+ } from "@frockbot/core/durable";
17
+
18
+ const RECEIPT = "maintenance:project-events:2026-09-23";
19
+ const PAGE = 50;
20
+
21
+ interface CleanupStorage extends SessionEventLogStorage {
22
+ transaction<T>(body: (tx: SessionEventLogStorage) => Promise<T>): Promise<T>;
23
+ }
24
+
25
+ type Stored = Record<string, unknown>;
26
+
27
+ function scoped(entry: unknown): unknown {
28
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
29
+ return entry;
30
+ }
31
+ const { projectId, ...rest } = entry as Stored;
32
+ const scope = rest.scope === "project" ? "group" : rest.scope;
33
+ return projectId === undefined
34
+ ? { ...rest, scope }
35
+ : { ...rest, scope, groupId: projectId };
36
+ }
37
+
38
+ function each(value: unknown): unknown {
39
+ return Array.isArray(value) ? value.map(scoped) : value;
40
+ }
41
+
42
+ /** The replacement for one stored event, or `undefined` to keep it. */
43
+ export function withoutProjectShapesV1(event: Stored): Stored | undefined {
44
+ const replaced = replacement(event);
45
+ // An event already in today's shape is left alone, so a second walk after
46
+ // an interrupted one writes nothing it already wrote.
47
+ return replaced && JSON.stringify(replaced) !== JSON.stringify(event)
48
+ ? replaced
49
+ : undefined;
50
+ }
51
+
52
+ function replacement(event: Stored): Stored | undefined {
53
+ const common = { seq: event.seq, timestamp: event.timestamp };
54
+ switch (event.type) {
55
+ case "memory/injected":
56
+ return {
57
+ ...event,
58
+ sources: each(event.sources),
59
+ facts: each(event.facts),
60
+ omissions: each(event.omissions),
61
+ ...(Object.hasOwn(event, "faded") ? { faded: each(event.faded) } : {}),
62
+ };
63
+ case "memory/write-intent":
64
+ case "memory/written":
65
+ return scoped(event) as Stored;
66
+ case "memory/project-intent":
67
+ return {
68
+ type: "package/tool-call",
69
+ ...common,
70
+ turn: event.turn,
71
+ step: event.step,
72
+ effectId: event.effectId,
73
+ packageId: "memory",
74
+ callId: event.effectId,
75
+ name: `project_${String(event.action)}`,
76
+ input: { project: event.projectId },
77
+ };
78
+ case "memory/project-changed": {
79
+ const projects = Array.isArray(event.projects) ? event.projects : [];
80
+ return {
81
+ type: "package/tool-result",
82
+ ...common,
83
+ turn: event.turn,
84
+ step: event.step,
85
+ effectId: event.effectId,
86
+ packageId: "memory",
87
+ callId: event.effectId,
88
+ name: `project_${String(event.action)}`,
89
+ content: `Projects: ${projects.join(", ") || "none"}`,
90
+ isError: false,
91
+ };
92
+ }
93
+ default:
94
+ return undefined;
95
+ }
96
+ }
97
+
98
+ export async function cleanProjectEventsV1(
99
+ storage: CleanupStorage,
100
+ ): Promise<void> {
101
+ if (await storage.get(RECEIPT)) return;
102
+ const sessions: string[] = [];
103
+ let start: string | undefined;
104
+ for (;;) {
105
+ const listed = await storage.list({
106
+ prefix: SESSION_EVENT_LOG_INDEX_PREFIX,
107
+ limit: PAGE,
108
+ ...(start ? { start } : {}),
109
+ });
110
+ for (const key of listed.keys()) {
111
+ sessions.push(
112
+ decodeURIComponent(key.slice(SESSION_EVENT_LOG_INDEX_PREFIX.length)),
113
+ );
114
+ }
115
+ if (listed.size < PAGE) break;
116
+ start = `${[...listed.keys()].at(-1)}\0`;
117
+ }
118
+ let events = 0;
119
+ const unreadable: string[] = [];
120
+ for (const sessionId of sessions) {
121
+ try {
122
+ events += await storage.transaction((tx) =>
123
+ new SessionEventLog(tx).repairStoredEvents(
124
+ sessionId,
125
+ withoutProjectShapesV1,
126
+ ),
127
+ );
128
+ } catch {
129
+ // A throw here would reset the object on every wake. A log this cannot
130
+ // read was unreadable before it ran, and is left as it was.
131
+ unreadable.push(sessionId);
132
+ }
133
+ }
134
+ // The pre-paging log, if a Bot still has one, is read the same way.
135
+ const legacy = await storage.get<unknown[]>(LATEST_EVENTS_KEY);
136
+ if (Array.isArray(legacy)) {
137
+ let changed = false;
138
+ const repaired = legacy.map((event) => {
139
+ if (!event || typeof event !== "object") return event;
140
+ const replacement = withoutProjectShapesV1(event as Stored);
141
+ if (replacement) changed = true;
142
+ return replacement ?? event;
143
+ });
144
+ if (changed) await storage.put(LATEST_EVENTS_KEY, repaired);
145
+ }
146
+ await storage.put(RECEIPT, {
147
+ schemaVersion: 1,
148
+ sessions: sessions.length,
149
+ events,
150
+ unreadable,
151
+ });
152
+ }
@@ -0,0 +1,92 @@
1
+ // Disposable cleanup for the S3 invalidation log and pending stubs S5
2
+ // replaced with PublicationHead / ConversationUpdate records.
3
+ //
4
+ // Old channel events cannot be replayed as typed updates, and a pending
5
+ // marker without a kind is not a recoverable obligation. Live S5 pending
6
+ // records (with kind, entityId, revision) stay: they still need a drain.
7
+
8
+ import {
9
+ PUBLICATION_CURSOR_KEY,
10
+ PUBLICATION_PENDING_PREFIX,
11
+ RETIRED_CHANNEL_EVENT_PREFIX_V1,
12
+ RETIRED_CHANNEL_META_KEY_V1,
13
+ } from "@frockbot/core/durable";
14
+
15
+ const RECEIPT = "maintenance:publication:s5:2026-09-22";
16
+ const PAGE = 50;
17
+
18
+ interface CleanupStorage {
19
+ get(key: string): Promise<unknown>;
20
+ put(key: string, value: unknown): Promise<void>;
21
+ delete(key: string): Promise<boolean | void>;
22
+ list(options: {
23
+ prefix?: string;
24
+ limit?: number;
25
+ start?: string;
26
+ }): Promise<Map<string, unknown>>;
27
+ }
28
+
29
+ function record(value: unknown): Record<string, unknown> | undefined {
30
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
31
+ return undefined;
32
+ }
33
+ return value as Record<string, unknown>;
34
+ }
35
+
36
+ function isLivePending(value: unknown): boolean {
37
+ const stored = record(value);
38
+ return (
39
+ stored !== undefined &&
40
+ stored.schemaVersion === 1 &&
41
+ typeof stored.kind === "string" &&
42
+ typeof stored.entityId === "string" &&
43
+ Number.isSafeInteger(stored.cursor) &&
44
+ Number.isSafeInteger(stored.revision)
45
+ );
46
+ }
47
+
48
+ async function page(
49
+ storage: CleanupStorage,
50
+ prefix: string,
51
+ visit: (key: string, value: unknown) => Promise<void>,
52
+ ): Promise<void> {
53
+ let start: string | undefined;
54
+ for (;;) {
55
+ const listed = await storage.list({
56
+ prefix,
57
+ limit: PAGE,
58
+ ...(start ? { start } : {}),
59
+ });
60
+ if (listed.size === 0) return;
61
+ let last = "";
62
+ for (const [key, value] of listed) {
63
+ last = key;
64
+ if (start !== undefined && key === start) continue;
65
+ await visit(key, value);
66
+ }
67
+ if (listed.size < PAGE) return;
68
+ start = `${last}\0`;
69
+ }
70
+ }
71
+
72
+ export async function cleanRetiredPublicationStateV1(
73
+ storage: CleanupStorage,
74
+ ): Promise<void> {
75
+ if (await storage.get(RECEIPT)) return;
76
+ let removed = 0;
77
+ if (await storage.delete(RETIRED_CHANNEL_META_KEY_V1)) removed += 1;
78
+ if (await storage.delete(PUBLICATION_CURSOR_KEY)) removed += 1;
79
+ await page(storage, RETIRED_CHANNEL_EVENT_PREFIX_V1, async (key) => {
80
+ await storage.delete(key);
81
+ removed += 1;
82
+ });
83
+ await page(storage, PUBLICATION_PENDING_PREFIX, async (key, value) => {
84
+ if (isLivePending(value)) return;
85
+ await storage.delete(key);
86
+ removed += 1;
87
+ });
88
+ await storage.put(RECEIPT, {
89
+ schemaVersion: 1,
90
+ removed,
91
+ });
92
+ }